DeviceDriver (ten): MISC driver

One: Introduction to MISC device driver

       MISC means mixed, miscellaneous, so MISC driver is also called miscellaneous driver, that is, MISC driver can be used when some peripherals on our development board cannot be classified. The MISC driver is actually the simplest character device driver, usually nested in the platform bus driver to implement complex drivers. The major device number of all MISC device drivers is 10, and different devices use different slave device numbers. The MISC device will automatically create cdev, no need to manually create it as we did before, so the use of MISC device drivers can simplify the writing of character device drivers.

Two: drive framework

1. Register a miscdevice device with Linux

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

After defining a MISC device, we need to set the three member variables minor, name and fops. minor represents the sub-device number. The major device number of the MISC device is 10. This is fixed and requires the user to specify the sub-device number. The Linux system has predefined some sub-device numbers. We can choose one of them or define it ourselves when we use it. This device number is not used by other devices:

#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 MISC_DYNAMIC_MINOR	255

       name is the name of the MISC device. When the device is successfully registered, a device file named name will be generated in the /dev directory. Fops is the operation set of character devices, MISC device driver ultimately needs to use the fops operation set provided by the user.

 2. After setting up miscdevice, you need to use the misc_register function to register a MISC device in the system:

int misc_register(struct miscdevice * misc)

In the past, we needed to call a bunch of functions to create the device, such as the previous character device driver:

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

Now we can directly use a function of misc_register to complete these steps, and call misc_deregister when we need to unregister the device:

int misc_deregister(struct miscdevice *misc)

In the same way, the misc_deregister function can replace the previous deregistration work:

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

Three: Example

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/miscdevice.h>

#define MISCBEEP_NAME   "miscbeep"
#define MISCBEEP_MINOR  144
#define BEEPOFF         0
#define BEEPON          1

struct miscbeep_dev {
    dev_t devid;
    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;
    int beep_gpio;
};

struct miscbeep_dev miscbeep;

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

static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    return 0;
}

static struct fileoperations miscbeep_fops = {
    .owner = THIS_MODULE,
    .open = miscbeep_open,
    .write = miscbeep_write,
};

static struct miscddevice beep_miscdev = {
    .minor = MISCBEEP_MINOR,
    .name = MISCBEEP_NAME,
    .fops = &miscbeep_fops,
};

static int miscbeep_probe(struct platform_device *dev)
{
    int ret = 0;
    printk("beep driver and device was matched!\n");

    miscbeep.nd = of_find_node_by_path("/beep");
    miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio, 0");
    ret = gpio_direction_output(miscbeep.beep_gpio, 1);

    ret = misc_register(&beep_miscdev);

    return 0;
}

static int miscbeep_remove(struct platform_device *dev)
{
    gpio_set_value(miscbeep.beep_gpio, 1);

    misc_deregister(&beep_miscdev);
    return 0;
}

static const struct of_device_id beep_of_match[] = {
    {.compatible = "my-beep"},
    {/* none  */}
};

static struct platform_driver beep_driver = {
    .dirber = {
        .name = "imx6ul-beep",
        .of_match_table = beep_of_match,
    },
    .probe = miscbeep_probe,
    .remove = miscbeep_remove,
};

static int miscbeep_init(void)
{
    return platform_driver_register(&beep_driver);
}

static void miscbeep_exit(void)
{
    platform_driver_unregister(&beep_driver);
}

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

 

Guess you like

Origin blog.csdn.net/qq_34968572/article/details/104478431