Linux---MISC杂项驱动

一、MISC 驱动简介

MISC 驱动其实就是最简单的字符设备驱动,所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 MISC 设备驱动可以简化字符设备驱动的编写。
我们只需要向 Linux 注册一个 miscdevice 设备, miscdevice是一个结构体定义在文件 include/linux/miscdevice.h 中,内容如下:

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

miscdevice 结构体中的name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件(后面驱动实例我们会看到这一点)。 fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合。

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

MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中,如下所示:

/*
 *	These allocations are managed by device@lanana.org. If you use an
 *	entry that is not in assigned your entry may well be moved and
 *	reassigned, or set dynamic if a fixed value is not justified.
 */

#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 APOLLO_MOUSE_MINOR	7	/* unused */
#define PC110PAD_MINOR		9	/* unused */
/*#define ADB_MOUSE_MINOR	10	FIXME OBSOLETE */
#define WATCHDOG_MINOR		130	/* Watchdog timer     */
#define TEMP_MINOR		131	/* Temperature Sensor */
#define RTC_MINOR		135
#define EFI_RTC_MINOR		136	/* EFI Time services */
#define VHCI_MINOR		137
#define SUN_OPENPROM_MINOR	139
#define DMAPI_MINOR		140	/* unused */
#define NVRAM_MINOR		144
#define SGI_MMTIMER		153
#define STORE_QUEUE_MINOR	155	/* unused */
#define I2O_MINOR		166
#define MICROCODE_MINOR		184
#define VFIO_MINOR		196
#define TUN_MINOR		200
#define CUSE_MINOR		203
#define MWAVE_MINOR		219	/* ACP/Mwave Modem */
#define MPT_MINOR		220
#define MPT2SAS_MINOR		221
#define MPT3SAS_MINOR		222
#define UINPUT_MINOR		223
#define MISC_MCELOG_MINOR	227
#define HPET_MINOR		228
#define FUSE_MINOR		229
#define KVM_MINOR		232
#define BTRFS_MINOR		234
#define AUTOFS_MINOR		235
#define MAPPER_CTRL_MINOR	236
#define LOOP_CTRL_MINOR		237
#define VHOST_NET_MINOR		238
#define UHID_MINOR		239
#define USERIO_MINOR		240
#define MISC_DYNAMIC_MINOR	255

字符设备驱动中我们常常会使用如下几个函数完成设备创建过程:

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

现在我们可以直接使用 misc_register 一个函数来完成上面代码中的这些步骤。

二、MISC驱动实例

这个驱动代码是由之前写过的一个led点灯代码改造而来,我们主要看下一led_gpio_probe() 函数中misc_register() 的使用,它简化了我们创建设备所需要的一系列步骤。

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>

static struct gpio_desc *led_gpio;

#define MISCLED_NAME "miscled" /* 名字 */
#define MISCLED_MINOR 145

static int led_drv_open(struct inode *node, struct file *file)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	gpiod_direction_output(led_gpio, 0);
	return 0;
}

static int led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int status = 0;
	int result = 0;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	status = gpiod_get_value(led_gpio);
	result = copy_to_user(buf, &status, 1);
	return 1;
}

static int led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int result;
	char status;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	result = copy_from_user(&status, buf, 1);
	gpiod_set_value(led_gpio, status);
	return 1;
}

static int led_drv_release(struct inode *node, struct file *file)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}
/*面朝大海0902*/
static struct file_operations led_drv =
{
	.owner = THIS_MODULE,
	.open  = led_drv_open,
	.read  = led_drv_read,
	.write = led_drv_write,
	.release = led_drv_release,
};

/* MISC 设备结构体 */
static struct miscdevice led_miscdev = {
.minor = MISCLED_MINOR,
.name = MISCLED_NAME,
.fops = &led_drv,
};

static int led_gpio_probe(struct platform_device *pdev)
{
	int ret =0;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	led_gpio = gpiod_get(&pdev->dev, "led", 0);
	if(IS_ERR(led_gpio))
	{
		printk(KERN_ERR "gpiod_get is err\r\n");
		return -1;
	}
	
	ret = misc_register(&led_miscdev);
	if(ret < 0)
	{
		printk("misc device register failed!\r\n");
		return -EFAULT;
	}
	
	return 0;	
}
/*面朝大海0902*/
static int led_gpio_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	misc_deregister(&led_miscdev);
	gpiod_put(led_gpio);
	
	return 0;	
}
static const struct of_device_id my_led[] =
{
	{.compatible = "my,led_driver"},
	{},
};

static struct platform_driver led_gpio_driver =
{
	.probe  = led_gpio_probe,
	.remove = led_gpio_remove,
	.driver = 
	{
		.name = "led_gpio",
		.of_match_table = my_led,
	},
};

static int __init led_init(void)
{
	int result;
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	result = platform_driver_register(&led_gpio_driver);
	return result;
}

static void __exit led_exit(void)
{
	printk(KERN_INFO "%s %s line is %d \r\n", __FILE__, __FUNCTION__, __LINE__);
	platform_driver_unregister(&led_gpio_driver);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
/*面朝大海0902*/

驱动加载并且与设备中的device匹配之后,我们在/dev目录下面看到一个名为"miscled"的设备,主设备号为10,此设备号为145,与我们代码设置的一致。
MISC

三、MISC实例测试

编写一个简单的测试程序,write()和read()设备节点,进而触发驱动fops文件操作函数。

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

/*
 * ./ledtest /dev/miscled on
 * ./ledtest /dev/miscled off
 */
int main(int argc, char **argv)
{
	int fd;
	char status;
	
	/* 1. 判断参数 */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open(argv[1], O_RDWR | O_NONBLOCK);
	if (fd == -1)
	{
		printf("can not open file %s\n", argv[1]);
		return -1;
	}

	/* 3. 写文件 */
	if (0 == strcmp(argv[2], "on"))
	{
		status = 1;
		write(fd, &status, 1);
	}
	else
	{
		status = 0;
		write(fd, &status, 1);
	}

	read(fd, &status, 1);
	printf("status is %d\n", status);
	
	close(fd);
	
	return 0;
}

在开发板上进行测试,打印如下:

[root@Joy:/dev]# /mnt/misc_test /dev/miscled on
[ 1133.332943] /home/book/code/test/misc_drv.c led_drv_open line is 27
[ 1133.340139] /home/book/code/test/misc_drv.c led_drv_write line is 46
[ 1133.352154] /home/book/code/test/misc_drv.c led_drv_read line is 36
status is 1
[ 1133.360455] /home/book/code/test/misc_drv.c led_drv_release line is 54

[root@Joy:/dev]# /mnt/misc_test /dev/miscled off
[ 1146.420773] /home/book/code/test/misc_drv.c led_drv_open line is 27
[ 1146.428629] /home/book/code/test/misc_drv.c led_drv_write line is 46
[ 1146.436893] /home/book/code/test/misc_drv.c led_drv_read line is 36
status is 0
[ 1146.445773] /home/book/code/test/misc_drv.c led_drv_release line is 54
/*面朝大海0902*/

可以看到fops对应的操作函数得到执行,比起普通的字符设备驱动MISC更加精简一些。
/面朝大海0902/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值