Linux驱动:内核中的MISC“杂项”驱动

1、简介

  MISC是杂项英文单词miscellaneous的简写,内核里面用于描述那些类别不是很明确的设备,通常结合platform总线驱动来编写驱动。其中所有MISC驱动的主设备号都是10,而次设备号则可以从内核源码的include/linux/miscdevice.h文件中选择或者自定义,同时该头文件里也定义了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;
};

int misc_register(struct miscdevice *misc);
int misc_deregister(struct miscdevice *misc);

  可以看到,MISC设备驱动的注册并不需要像普通的字符设备驱动一样需要定义cdev、class等变量,只需要提供minor次设备号、name作为设备节点的名字、file_operations驱动操作的结构体就可以编写出一个简单的驱动程序。而misc_register就相当于“代替”了普通字符设备的cdev_add/class_create/device_create等函数,并且在装载驱动之后可以在系统目录/sys/class/misc/下查看驱动的相关信息。

2、使用方法(MISC驱动程序框架)


static int xxx_open(struct inode *inode, struct file *file)
{
    
    
	file->private_data = &led_drv;
	return 0;
}

static int xxx_close(struct inode *inode, struct file *filp)
{
    
    
	return 0;
}

// 配置驱动操作的结构体
static struct file_operations xxx_fops = {
    
    
	.owner 	= THIS_MODULE,
	.open  	= xxx_open,
	.release = xxx_close,
};


static struct miscdevice xxx_misc_device = {
    
    
	.minor = 100,
	.name = "xxx",
	.fops = &xxx_fops,
};



static int xxx_probe(struct platform_device *dev)
{
    
    
	// ...
	
	misc_register(&xxx_misc_device);

	return 0;	
}


static int xxx_remove(struct platform_device *dev)
{
    
    
	// ...

	misc_deregister(&xxx_misc_device);

	return 0;
}


static const struct of_device_id xxx_of_match[] = {
    
    
	{
    
     .compatible = "xxx,xxx" },
	{
    
    }
};


static struct platform_driver xxx_platform_driver = {
    
    
	.driver = {
    
    
		.name = "xxx,xxx",				/* 用于传统不使用设备树的设备名称 */
		.of_match_table = xxx_of_match,	/* 用于匹配设备树节点的属性 */
	},
	.probe 	= xxx_probe,
	.remove	= xxx_remove,
};


static int __init xxx_driver_init(void)
{
    
    
	return platform_driver_register(&xxx_platform_driver);
}

static void __exit xxx_driver_exit(void)
{
    
    
	platform_driver_unregister(&xxx_platform_driver);
}

module_init(xxx_driver_init);
module_exit(xxx_driver_exit);

MODULE_LICENSE("GPL");


3、使用示例

3.1 修改设备树添加设备节点

仅列出节点部分,pinctrl子系统和gpio子系统在同一个专栏的文章里,这里省略。

/{
    
    
...
	led0 {
    
    
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "red-led";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led0>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};
...


3.2 驱动程序

以led为例编写一个misc驱动,简单了解程序的框架:

#include <linux/ide.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>


#define LED_CNT	1

struct led_drv_info{
    
    
	struct device_node *node;
	int 			 gpio;
};

static struct led_drv_info led_drv;


static int led_open(struct inode *inode, struct file *file)
{
    
    
	file->private_data = &led_drv;
	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    
    
	char databuf[10];
	unsigned long ret;
	struct led_drv_info *drv_priv_data = filp->private_data;
	
	ret = copy_from_user(databuf, buf, cnt);
	if(ret){
    
    
		printk("\t Kernel space: copy_from_user error!\n");
		return -1;
	}

	if(strcmp(databuf, "on") == 0){
    
    
		gpio_set_value(drv_priv_data->gpio, 0);
	}else if(strcmp(databuf, "off") == 0){
    
    
		gpio_set_value(drv_priv_data->gpio, 1);
	}else{
    
    
		printk("\t Kernel space: led_write error!\n");
		return -1;
	}

	return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    
    
	return 0;
}

// 配置驱动操作的结构体
static struct file_operations led_fops = {
    
    
	.owner 	= THIS_MODULE,
	.open  	= led_open,
	.write 	= led_write,
	.release = led_release,
};


static struct miscdevice led_misc_device = {
    
    
	.minor = 100,
	.name = "led_misc",
	.fops = &led_fops,
};


static int led_probe(struct platform_device *dev)
{
    
    
	int ret;
	
	led_drv.node = of_find_node_by_path("/led0");			// 通过节点名称查找
	if(!led_drv.node){
    
    
		printk("There is no \"led0\" node in your dtb file!\n");
		return -1;
	}

	led_drv.gpio = of_get_named_gpio(led_drv.node, "led-gpio", 0);
	if (led_drv.gpio < 0){
    
    
		printk("Get gpio number error!\n");
		return -1;
	}

	ret = gpio_request(led_drv.gpio, "led0");
	if(ret){
    
    
		printk("Request gpio error!\n");
		return -1;
	}

	ret = gpio_direction_output(led_drv.gpio, 1);
	if(ret){
    
    
		printk("Set gpio output error!\n");
		return -1;
	}

	misc_register(&led_misc_device);

	return 0;	
}


static int led_remove(struct platform_device *dev)
{
    
    
	misc_deregister(&led_misc_device);

	gpio_free(led_drv.gpio);
	return 0;
}


static const struct of_device_id led_of_match[] = {
    
    
	{
    
     .compatible = "red-led" },	/* 设备树中led节点的属性 */
	{
    
    }
};


static struct platform_driver led_platform_driver = {
    
    
	.driver = {
    
    
		.name = "red-led",				/* 用于传统不使用设备树的设备名称 */
		.of_match_table = led_of_match,	/* 用于匹配设备树节点的属性 */
	},
	.probe 	= led_probe,
	.remove	= led_remove,
};


static int __init led_driver_init(void)
{
    
    
	return platform_driver_register(&led_platform_driver);
}

static void __exit led_driver_exit(void)
{
    
    
	platform_driver_unregister(&led_platform_driver);
}

module_init(led_driver_init);
module_exit(led_driver_exit);

MODULE_LICENSE("GPL");


4、测试程序

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(int argc, char *argv[])
{
    
    
	int fd;
	int ret;

	if(argc != 3 ){
    
    
		printf("\t Usage: ./ledAPP /dev/ledx <on/off>\n");
		return -1;
	}

	fd = open(argv[1], O_RDWR);
	if(fd < 0){
    
    
		printf("\t User space: open error!\n");
		return -1;
	}

	ret = write(fd, argv[2], sizeof(argv[2]));
	if(ret){
    
    
		printf("\t User space: write error!\n");
		return -1;
	}

	ret = close(fd);
	if(ret){
    
    
		printf("\t User space: close error!\n");
		return -1;
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44498318/article/details/110261456