Linux misc miscellaneous driver

1, Introduction to MISC driver

MISC driver is actually the simplest character device driver. The master device number of all MISC device drivers is 10. Different devices use different slave device numbers.
MISC device will automatically create cdev, which does not need to be created manually as before. Therefore, using MISC device driver can simplify the writing of character device driver.
We need to register a miscdevice device with Linux. Miscdevice is a structure defined in the file include/linux/miscdevice.h, as follows:

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

The main device number of MISC device is 10, which is fixed, and the user needs to specify the sub device number. The Linux system has predefined some sub device numbers of MISC devices, which are defined in the include/linux/miscdevice.h file, as shown below:

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

The name in miscdevice is the name of the MISC device. When the device is registered successfully, a device file named name will be generated in the / dev directory. fops is the operation set of character device. MISC device driver finally needs to use the fops operation set provided by the user.
After setting miscdevice, you need to use misc_register function registers a MISC device in the system. The prototype of this function is as follows:
int misc_register(struct miscdevice * misc)

In the past, we needed to call a bunch of functions to create devices. For example, we used it in the previous character device driver
The following functions complete the device creation process:

1 alloc_chrdev_region(); /* Application equipment No */
2 cdev_init(); /* Initialize cdev */
3 cdev_add(); /* Add cdev */
4 class_create(); /* Create class */
5 device_create(); /* Create device */

Now we can use misc directly_ Register a function to complete these steps in the above code.

2, MISC driver instance

This driver code is transformed from a previously written led lighting code. We mainly look at misc in the next probe function_ Use of 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" / * name*/
#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;
}
/*Facing the sea 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 Equipment structure */
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;	
}
/*Facing the sea 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");
/*Facing the sea 0902*/

After the driver is loaded, we see a device named "miscled" under the / dev directory. The main device number is 10 and the device number is 145, which is consistent with our code.

3, MISC example test

Write a simple test program, write() and read() device nodes, and then trigger the driver fops file operation function.

#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. Judgment parameters */
	if (argc != 3) 
	{
		printf("Usage: %s <dev> <on | off>\n", argv[0]);
		return -1;
	}

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

	/* 3. Write file */
	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;
}

Test on the development board and print as follows:

[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@100ask:/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
/*Facing the sea 0902*/

Tags: Linux Linux Driver

Posted on Wed, 06 Oct 2021 20:44:43 -0400 by ratebuster