Misc Device Driver – Linux Device Driver Tutorial Part 32

This article is a continuation of the Series on Linux Device Drivers and carries the discussion on Linux device drivers and their implementation.

In this article, we dive into “Misc Device Drivers,” a versatile category designed for handling those devices that don’t neatly fit into predefined molds. These drivers simplify the process, offering a fixed major number and automated device node creation.

We’ll explore their key features, and differences from character drivers, and walk you through creating one.

You can also read the Linux device driver introduction, device file creation, SysfsProcfsWorkqueueCompletionSoftirq, and threaded IRQ in the Linux device driver.

Linux Misc Device Driver

Prerequisites

In this misc device driver tutorial, we just used the below tutorial as a reference. Please go through the below tutorial before we begin.

Introduction – Misc Device Driver

Misc driver is the miscellaneous driver for miscellaneous devices. We can say that misc drivers are special and simple character drivers. You can write this misc driver when you cannot classify your peripheral.

This means that if you don’t want to use the major number, then you can write this misc driver. Also, if you want to write a simple driver, then you can choose a misc driver instead of choosing a character driver.

So, you cannot choose the major number yourself when you write the misc driver. The default major number of all the misc drivers is 10. But you can choose your minor numbers between 1 to 255. It has all the file operation calls like open, read, write, close, and IOCTL.

This will create the device file under /dev/{your_misc_file}It is almost like the character driver. Isn’t it? Then why the hell we are using the misc devices, and what is the difference between misc drivers and character drivers? Let’s continue reading.

Difference between Character Driver and Misc Driver

  • In misc driver, the major number will be 10 and the minor number is user-convenient. Whereas, in character drivers, the user can select their own major and minor number if it is available.
  • The device node or device file will be automatically generated in misc drivers. Whereas, in character drivers, the user has to create the device node or device file using cdev_init, cdev_add, class_create, and device_create.

Uses of Linux Misc Drivers

  • If you write character drivers for simple devices, you have to create a major number as well. In such a case, the kernel has to keep that details in the static table. This means you end up wasting the RAM for simple devices. If you write multiple character drivers for multiple simple devices, then the RAM wastage also increases. To avoid this you can use the misc driver. Because, in the misc driver, you don’t need to specify the major number since it has a fixed major number which is 10.
  • If you use misc drivers, it will automatically create the device file compared to the character driver.

I hope you are okay to go to programming now.

Misc Device Driver API

In order to create misc drivers, we need to use miscdevice structure, file operations. After that, we need to register the device. Once we have done with the operation, then we can unregister the device.

The following APIs are used to create and delete the misc device. You need to insert the header file include<linux/miscdevice.h>.

Misc Device Structure

Character drivers have cdevstructure to know the driver’s functions and other calls. Like that, the misc driver also has a separate structure to maintain the details. The structure is as follows,

struct miscdevice {
  int minor;
  const char *name;
  struct file_operations *fops;
  struct miscdevice *next, *prev;
};

Where,

<minor>: You can assign your custom minor number to this. If you pass MISC_DYNAMIC_MINOR to this variable, then the misc driver will automatically generate the minor number and assign it to this variable. Every misc driver needs to have a different minor number since this is the only link between the device file and the driver. Recommended is a dynamic method to allocate the minor number instead of assigning the same minor number to the different misc devices. If you want to select your own minor number then check for the used minor number of all misc devices using ls -l /dev/, and then you can hardcode your minor number if it is available.

<name>: you can assign the name of the misc driver. The device file will be created in this name and displayed under the /dev directory.

<fops>: This is the pointer to the file operations. This is the same file operation in character drivers.

<next> and <prev>: These are used to manage the circular linked list of misc drivers.

Once you loaded the misc driver, you can see the major and minor numbers of your misc device driver using ls -l /dev/{misc_driver_name}. Please see the example below.

Register the misc Device

This API is used to register the misc device with the kernel.

int misc_registerstruct miscdevice * misc)

where,

<misc>: device structure to be registered

return: A zero is returned on success and a negative errno code for failure.

You have to call this function in init function. The structure passed is linked to the kernel and may not be destroyed until it has been unregistered.

This one function will avoid the below functions used in the character device driver while registering the device.

  1. alloc_chrdev_region(); – used for the major and minor number
  2. cdev_init(); – used to initialize cdev
  3. cdev_add(); – used to  add the cdev structure to the device
  4. class_create();  – used to create a class
  5. device_create();  – used to create a device

That’s why we are saying this is the simple method.

Example:

static const struct file_operations fops = {
    .owner          = THIS_MODULE,
    .write          = etx_misc_write,
    .read           = etx_misc_read,
    .open           = etx_misc_open,
    .release        = etx_misc_close,
    .llseek         = no_llseek,
};

struct miscdevice etx_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "simple_etx_misc",
    .fops = &fops,
};

static int __init misc_init(void)
{
    int error;

    error = misc_register(&etx_misc_device);
    if (error) {
        pr_err("misc_register failed!!!\n");
        return error;
    }

    pr_info("misc_register init done!!!\n");
    return 0;
}

Unregister the Misc Device

This API is used to un-register the misc device with the kernel.

misc_deregister(struct miscdevice * misc)

where,

<misc>: device structure to be un-registered

Unregister a miscellaneous device that was previously successfully registered with misc_register(). This has to be called in __exit function.

This one function will avoid the below functions used in the character device driver while unregistering the device.

  1. cdev_del(); – used to delete cdev
  2. unregister_chrdev_region();  – used to remove the major and minor number
  3. device_destroy();  – used to delete the device
  4. class_destroy(); – used to delete the class

Example:

static void __exit misc_exit(void)
{
    misc_deregister(&etx_misc_device);
    pr_info("misc_register exit done1!!!\n");
}

Woohoo… That’s all… Just two APIs are needed to create the misc driver. Is it really simple compared to the character driver? Let’s jump to programing.

Linux Misc Driver Programming

Here I have added a dummy misc device driver snippet. In this driver code, we can do all open, read, write, close operations. Just go through the code.

Driver Source Code

[Get the source code from GitHub]

/***************************************************************************//**
*  \file       misc_driver.c
*
*  \details    Simple misc driver explanation
*
*  \author     EmbeTronicX
*
*  \Tested with kernel 5.3.0-42-generic
*
*******************************************************************************/
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

/*
** This function will be called when we open the Misc device file
*/
static int etx_misc_open(struct inode *inode, struct file *file)
{
    pr_info("EtX misc device open\n");
    return 0;
}

/*
** This function will be called when we close the Misc Device file
*/
static int etx_misc_close(struct inode *inodep, struct file *filp)
{
    pr_info("EtX misc device close\n");
    return 0;
}

/*
** This function will be called when we write the Misc Device file
*/
static ssize_t etx_misc_write(struct file *file, const char __user *buf,
               size_t len, loff_t *ppos)
{
    pr_info("EtX misc device write\n");
    
    /* We are not doing anything with this data now */
    
    return len; 
}
 
/*
** This function will be called when we read the Misc Device file
*/
static ssize_t etx_misc_read(struct file *filp, char __user *buf,
                    size_t count, loff_t *f_pos)
{
    pr_info("EtX misc device read\n");
 
    return 0;
}

//File operation structure 
static const struct file_operations fops = {
    .owner          = THIS_MODULE,
    .write          = etx_misc_write,
    .read           = etx_misc_read,
    .open           = etx_misc_open,
    .release        = etx_misc_close,
    .llseek         = no_llseek,
};

//Misc device structure
struct miscdevice etx_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "simple_etx_misc",
    .fops = &fops,
};

/*
** Misc Init function
*/
static int __init misc_init(void)
{
    int error;
 
    error = misc_register(&etx_misc_device);
    if (error) {
        pr_err("misc_register failed!!!\n");
        return error;
    }
 
    pr_info("misc_register init done!!!\n");
    return 0;
}

/*
** Misc exit function
*/
static void __exit misc_exit(void)
{
    misc_deregister(&etx_misc_device);
    pr_info("misc_register exit done!!!\n");
}
 
module_init(misc_init)
module_exit(misc_exit)
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <[email protected]>");
MODULE_DESCRIPTION("A simple device driver - Misc Driver");
MODULE_VERSION("1.29");

Makefile

obj-m += misc_driver.o
 
KDIR = /lib/modules/$(shell uname -r)/build
 
 
all:
  make -C $(KDIR)  M=$(shell pwd) modules
 
clean:
  make -C $(KDIR)  M=$(shell pwd) clean

Execution

  • Build the driver by using Makefile (sudo make)
  • Load the driver using sudo insmod misc_driver.ko
  • To check the major and minor numbers, use ls -l /dev/simple_etx_misc
etx@embetronicx-VirtualBox:~/Desktop/LDD$ ls -l /dev/simple_etx_misc
crw------- 1 root root 10, 53 May  9 20:42 /dev/simple_etx_misc
  • Here, 10 is the major number and 53 is the minor number.
  • Now we will see write and read. Enter sudo su and enter your password if it asks to give permission.
  • Do echo 1 > /dev/simple_etx_misc

Echo will open the driver and write 1 into the driver and finally close the driver. So if I do echo to our driver, it should call the open, write, and release (close) functions. Just check it out.

root@embetronicx:/home/slr/Desktop/LDD# echo 1 > /dev/simple_etx_misc
  • Now Check using dmesg
root@embetronicx:/home/slr/Desktop/LDD# dmesg
[ 3947.029591] misc_register init done!!!
[ 4047.651860] EtX misc device open
[ 4047.651896] EtX misc device write
[ 4047.651901] EtX misc device close
  • That’s cool. Now we will verify the read function. Do cat /dev/simple_etx_misc

The cat command will open the driver, read the driver, and close the driver. So if I do cat to our driver, it should call the open, read, and release (close) functions. Just check,

root@embetronicx:/home/slr/Desktop/LDD# cat /dev/simple_etx_misc
  • Now Check using dmesg
root@embetronicx:/home/slr/Desktop/LDD# dmesg
[ 4400.914717] EtX misc device open
[ 4400.914734] EtX misc device read
[ 4400.914749] EtX misc device close
  • Unload the driver using sudo rmmod misc_driver

Instead of doing echo and cat command in the terminal you can also use open(), read(), write(), close() system calls from user-space applications.

This is just a dummy misc driver. But you can implement your own logic in read, write, and IOCTL too like we did in our previous Linux device driver tutorials.

In our next tutorial, we will see the USB device drivers in Linux.

Please find the other Linux device driver tutorials here.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Table of Contents