57 MISC 杂项驱动

相关概念

  • misc 的意思是混合、杂项的,因此 MISC 驱动也叫做杂项驱动,也就是当我们板子上的某些外设无法进行分类的时候就可以使用 MISC 驱动。 MISC 驱动其实就是最简单的字符设备驱动。

  • misc驱动通常嵌套在 platform 总线驱动中,实现复杂的驱动

  • 所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。随着 Linux字符设备驱动的不断增加,设备号变得越来越紧张,尤其是主设备号, MISC 设备驱动就用于解决此问题。 MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。

  • misc驱动编写的核心就是初始化 miscdevice 这个结构体,定义在文件 include/linux/miscdevice.h 中。向 Linux 注册一个 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;
};

定义一个 MISC 设备(miscdevice 类型)以后我们需要设置 minor、 name 和 fops 这三个成员变量。 minor 表示子设备号, MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, 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

我们在使用的时候可以从这些预定义的子设备号中挑选一个,当然也可以自己定义,只要这个子设备号没有被其他设备使用接口。
若指定为255表示由内核自动分配次设备号。

name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件。

fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops 操作集合。

  • 当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:
int misc_register(struct miscdevice * misc);
函数参数和返回值含义如下:
misc:要注册的 MISC 设备。
返回值: 负数,失败; 0,成功。

以前我们需要自己调用一堆的函数去创建设备,比如在以前的字符设备驱动中我们会使用
如下几个函数完成设备创建过程:

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

现在我们可以直接使用 misc_register 一个函数来完成这些步骤。
当我们卸载设备驱动模块的时候需要调用 misc_deregister 函数来注销掉 MISC 设备,函数原型如下:

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

现在我们只需要一个 misc_deregister 函数即可完成完成这些步骤。

MISC驱动代码

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/init.h>
#include <linux/fs.h>
#include<linux/slab.h>
#include<linux/io.h>
#include<linux/uaccess.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/of.h>
#include<linux/of_address.h>
#include<linux/of_irq.h>
#include<linux/gpio.h>
#include<linux/of_gpio.h>
#include<linux/atomic.h>
#include<linux/timer.h>
#include<linux/string.h>
#include<linux/jiffies.h>
#include<linux/irq.h>
#include<asm/mach/map.h>
#include<asm/uaccess.h>
#include<asm/io.h>
#include<linux/interrupt.h>
#include<linux/fcntl.h>
#include<linux/poll.h>
#include<linux/ide.h>
#include<linux/platform_device.h>
#include<linux/miscdevice.h>

#define MISCBEEP_NAME "miscbeep"
#define MISCBEEP_MINOR	144

#define BEEPON 1
#define BEEPOFF 0

static int miscbeep_probe(struct platform_device *dev);
static int miscbeep_remove(struct platform_device *dev);
static int beep_open(struct inode *inode, struct file *filp);
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt);
static int beep_release(struct inode *inode, struct file *filp);


// 自定义设备结构体
struct miscbeep_dev 
{
	int beep_gpio;
	struct device_node *nd;
};

struct miscbeep_dev miscbeep;

struct file_operations miscbeep_fops = 
{
	.owner = THIS_MODULE,
	.open = beep_open,
	.write = beep_write,
	.release = beep_release,
};

// misc 设备结构体,提供 minor,name,fops 即可
static struct miscdevice beep_misc = 
{
	.minor = MISCBEEP_MINOR,
	// /dev目录下就会生成同名文件,表示当前设备
	.name = MISCBEEP_NAME,
	.fops = &miscbeep_fops,
};

static struct of_device_id beep_of_match[] = 
{
	{
		.compatible = "gpio-beeper"
		// 要和设备树里对应节点的
		// compatible 一致
	},
	{
		//sentinel
	}
};

static struct platform_driver miscbeep_drv =
{
	.driver = 
	{
		// 有设备树的情况下就不会用name值进行匹配
		.name = "imx6ul-beeper",
		.of_match_table = beep_of_match,
	},
	.probe = miscbeep_probe,
	.remove = miscbeep_remove,
};

static int beep_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &miscbeep; /* 设置私有数据 */
	return 0;
}

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

static int miscbeep_probe(struct platform_device *dev)
{
	int ret;
	//1.初始化beeper相关的gpio
	miscbeep.nd = dev->dev.of_node;
	// 获取指定gpio的gpio编号,注意第二个参数一定要和
	// 设备树中的属性名一致
	miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpios", 0);
	if(miscbeep.beep_gpio < 0)
	{
		ret = -1;
		printk("%s(%d):fail\n", __FILE__, __LINE__);
		goto fail_findgpio;
	}
	ret = gpio_request(miscbeep.beep_gpio, "give_it_a_name");
	if(ret < 0)
	{
		printk("%s(%d):fail\n", __FILE__, __LINE__);
		goto failgpioreq;
	}
	ret = gpio_direction_output(miscbeep.beep_gpio, 1);
	if(ret < 0)
	{
		printk("%s(%d):fail\n", __FILE__, __LINE__);
		goto fail_setdir;
	}

	//2 misc 驱动注册
	ret = misc_register(&beep_misc);
	if(ret < 0)
	{
		printk("%s(%d):fail\n", __FILE__, __LINE__);
		goto fail_setdir;
	}

	return 0;

fail_setdir:
	gpio_free(miscbeep.beep_gpio);
failgpioreq:
fail_findgpio:
	return ret;
}

static int miscbeep_remove(struct platform_device *dev)
{
	gpio_set_value(miscbeep.beep_gpio, 1);
	gpio_free(miscbeep.beep_gpio);
	misc_deregister(&beep_misc);
	return 0;
}

// driver 入口函数
static int __init miscbeep_init(void)
{
	return platform_driver_register(&miscbeep_drv);
}

static void __exit miscbeep_exit(void)
{
	platform_driver_unregister(&miscbeep_drv);
}

module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");

设备树对应节点代码

gpiobeep {
		compatible = "gpio-beeper" ;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpiobeep>;
		beep-gpios = <&gpio5 1 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

测试APP代码

其实和前面led的是一样的

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
// ./ledAPP filename  0/1
// 0 -> turn off led
// 1 -> turn on led

#define LED_OFF 0
#define LED_ON 1
int main(int argc, char **argv)
{
    int ret = 0;
    int fd = 0;
    char *filename;
    unsigned char databuf[1];

    if(argc != 3)
    {
        printf("Error usage!\r\n");// user room
        return -1;
    }
    filename = argv[1];
    fd = open(filename, O_RDWR);
    if(fd < 0)
    {
        printf("Can't open file \"%s\"\r\n", filename);
        return -1;
    }
    databuf[0] = atoi(argv[2]);
    ret = write(fd, databuf, sizeof(databuf));
    if(ret < 0)
    {
        printf("Led control failed.\r\n");
        ret = close(fd);
        if(ret < 0)
        {
            printf("Close file \"%s\" error!\r\n", filename);
            return -1;
        }
        return -1;
    }
    ret = close(fd);
    if(ret < 0)
    {
        printf("Close file \"%s\" error!\r\n", filename);
        return -1;
    }
    else
    {
        ;
    }

    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值