This article is a continuation of the Series on Linux Device Driver and carries the discussion on character drivers and their implementation. The aim of this series is to provide easy and practical examples that anyone can understand. This is the Device File Creation for Character Drivers – Linux Device Driver Tutorial Part 5.
Table of Contents
Prerequisites
To continue with this tutorial, you must set up Ubuntu, Raspberry Pi, or Beaglebone. If you haven’t set up anything yet, we suggest you set up the boards that you have using the tutorials given below.
You can find a video explanation of this tutorial here. You can also find all the Linux device driver’s video playlists here.
Device File Creation for Character Drivers
In our last tutorial, we have seen how to assign a major and minor number. But if you see, it will create a major and minor number. However, I did not create any device files under /dev/ directory. The device file is important to communicate with the hardware. Let’s start our tutorial.
Device Files
The device file allows transparent communication between user-space applications and hardware.
They are not normal “files”, but look like files from the program’s point of view: you can read from them, write to them, mmap() onto them, and so forth. When you access such a device “file,” the kernel recognizes the I/O request and passes it to a device driver, which performs some operations, such as reading data from a serial port or sending data to hardware.
Device files (although inappropriately named, we will continue to use this term) provide a convenient way to access system resources without requiring the application programmer to know how the underlying device works. Under Linux, as with most Unix systems, device drivers themselves are part of the kernel.
All device files are stored in /dev directory. Use ls command to browse the directory:
ls -l /dev/
Each device on the system should have a corresponding entry in /dev. For example, /dev/ttyS0 corresponds to the first serial port, known as COM1 under MS-DOS; /dev/hda2 corresponds to the second partition on the first IDE drive. In fact, there should be entries in /dev for devices you do not have.
The device files are generally created during system installation and include every possible device driver. They don’t necessarily correspond to the actual hardware on your system. There are a number of pseudo-devices in /dev that don’t correspond to any actual peripheral.
For example, /dev/null acts as a byte sink; any write request to /dev/null will succeed, but the data are written will be ignored.
When using ls -l to list device files in /dev, you’ll see something like the following:
crw--w---- 1 root tty 4, 0 Aug 15 10:40 tty0 brw-rw---- 1 root disk 1, 0 Aug 15 10:40 ram0
First of all, note that the first letter of the permissions field is denoted as driver type. Device files are denoted either by b, for block devices, or c, for character devices.
Also, note that the size field in the ls -l listing is replaced by two numbers, separated by a comma. The first value is the major device number and the second is the minor device number.
Creating Device File
We can create a device file in two ways.
- Manually
- Automatically
We will discuss these topics one by one.
Manually Creating Device File
We can create the device file manually by using mknod.
|
< < c – Character Device b – Block Device < < -m < |
Example:
sudo mknod -m 666 /dev/etx_device c 246 0
If you don’t want to give permission, You can also use chmod to set the permissions for a device file after creation.
Advantages:
- Anyone can create the device file using this method.
- You can create the device file even before loading the driver.
Programming:
I took this program from the previous tutorial. I’m going to create a device file manually for this driver.
[Get the Source code from GitHub]
/***************************************************************************//**
* \file driver.c
*
* \details Simple linux driver (Manually Creating a Device file)
*
* \author EmbeTronicX
*
* \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+
*
*******************************************************************************/
#include<linux/kernel.h>
#include<linux/init.h>
#include<linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
dev_t dev = 0;
/*
** Module init function
*/
static int __init hello_world_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "Embetronicx_Dev")) <0){
pr_err("Cannot allocate major number for device\n");
return -1;
}
pr_info("Kernel Module Inserted Successfully...\n");
return 0;
}
/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
unregister_chrdev_region(dev, 1);
pr_info("Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Manually Creating a Device file)");
MODULE_VERSION("1.1");
- Build the driver by using Makefile (
sudo make) - Load the driver using
sudo insmod - Check the device file using
ls -l /dev/. By this time device file is not created for your driver. - Create a device file using
mknodand then check usingls -l /dev/.
linux@embetronicx-VirtualBox:/home/driver/driver$sudo mknod -m 666 /dev/etx_device c 246 0 linux@embetronicx-VirtualBox::/home/driver/driver$ ls -l /dev/ | grep "etx_device" crw-rw-rw- 1 root root 246, 0 Aug 15 13:53 etx_device
- Now our device file has been created and registered with a major number and a minor number.
- Unload the driver using
sudo rmmod
Automatically Creating Device File
The automatic creation of device files can be handled with udev. Udev is the device manager for the Linux kernel that creates/removes device nodes in the /dev directory dynamically. Just follow the below steps.
- Include the header file
linux/device.handlinux/kdev_t.h - Create the struct Class
- Create Device with the class which is created by the above step
Create the class
This will create the struct class for our device driver. It will create a structure under/sys/class/.
|
|
This is used to create a struct class pointer that can then be used in calls to class_device_create. The return value can be checked using IS_ERR() macro.
Note, the pointer created here is to be destroyed when finished by making a call to class_destroy.
void class_destroy (struct class * cls); |
Create Device
This function can be used by char device classes. A struct device will be created in sysfs, and registered to the specified class.
struct device *device_create(struct *class, struct device *parent, dev_t dev, void * drvdata, const char *fmt, ...);
|
A “dev” file will be created, showing the dev_t for the device, if the dev_t is not 0,0. If a pointer to a parent struct device is passed in, the newly created struct device will be a child of that device in sysfs.
The pointer to the struct device will be returned from the call. Any further sysfs files that might be required can be created using this pointer. The return value can be checked using IS_ERR() macro.
Note, that you can destroy the device using device_destroy().
void device_destroy (struct class * class, dev_t devt); |
If you don’t understand please refer to the below program, Then you will get some idea.
Programming:
[Get the source code from GitHub]
/***************************************************************************//**
* \file driver.c
*
* \details Simple linux driver (Automatically Creating a Device file)
*
* \author EmbeTronicX
*
* \Tested with Linux raspberrypi 5.10.27-v7l-embetronicx-custom+
*
*******************************************************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/device.h>
dev_t dev = 0;
static struct class *dev_class;
/*
** Module init function
*/
static int __init hello_world_init(void)
{
/*Allocating Major number*/
if((alloc_chrdev_region(&dev, 0, 1, "etx_Dev")) <0){
pr_err("Cannot allocate major number for device\n");
return -1;
}
pr_info("Major = %d Minor = %d \n",MAJOR(dev), MINOR(dev));
/*Creating struct class*/
dev_class = class_create(THIS_MODULE,"etx_class");
if(IS_ERR(dev_class)){
pr_err("Cannot create the struct class for device\n");
goto r_class;
}
/*Creating device*/
if(IS_ERR(device_create(dev_class,NULL,dev,NULL,"etx_device"))){
pr_err("Cannot create the Device\n");
goto r_device;
}
pr_info("Kernel Module Inserted Successfully...\n");
return 0;
r_device:
class_destroy(dev_class);
r_class:
unregister_chrdev_region(dev,1);
return -1;
}
/*
** Module exit function
*/
static void __exit hello_world_exit(void)
{
device_destroy(dev_class,dev);
class_destroy(dev_class);
unregister_chrdev_region(dev, 1);
pr_info("Kernel Module Removed Successfully...\n");
}
module_init(hello_world_init);
module_exit(hello_world_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <embetronicx@gmail.com>");
MODULE_DESCRIPTION("Simple linux driver (Automatically Creating a Device file)");
MODULE_VERSION("1.2");
- Build the driver by using Makefile (
sudo make) or if you are using the Beaglebone board, then you can usesudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-. - Load the driver using
sudo insmod - Check the device file using
ls -l /dev/ | grep "etx_device"
linux@embetronicx-VirtualBox:/home/driver/driver$ ls -l /dev/ | grep "etx_device" crw------- 1 root root 246, 0 Aug 15 13:36 etx_device
- Unload the driver using
sudo rmmod
Video Explanation
You can check the video explanation of this tutorial below.
In this tutorial, we have created the device file manually and automatically. So, using this device file we can communicate with the hardware.
In our next tutorial, we explained how to open, read, write, and close the device file.
Please find the other Linux device driver tutorials here.
You can also read the below tutorials.

Embedded Software | Firmware | Linux Devic Driver | RTOS
Hi, I am a tech blogger and an Embedded Engineer. I am always eager to learn and explore tech-related concepts. And also, I wanted to share my knowledge with everyone in a more straightforward way with easy practical examples. I strongly believe that learning by doing is more powerful than just learning by reading. I love to do experiments. If you want to help or support me on my journey, consider sharing my articles, or Buy me a Coffee! Thank you for reading my blog! Happy learning!
Discover more from EmbeTronicX
Subscribe to get the latest posts sent to your email.

/include/linux/export.h:27:22: error: passing argument 1 of ‘class_create’ from incompatible pointer type [-Werror=incompatible-pointer-types]
27 | #define THIS_MODULE (&__this_module)
| ~^~~~~~~~~~~~~~~
| |
| struct module *
/home/larisa/Desktop/driver_loaded/mod1/mod1.c:91:33: note: in expansion of macro ‘THIS_MODULE’
91 | my_class = class_create(THIS_MODULE, DEVICE_NAME);
| ^~~~~~~~~~~
In file included from ./include/linux/device.h:31,
from ./include/linux/cdev.h:8,
from /home/larisa/Desktop/driver_loaded/mod1/mod1.c:8:
./include/linux/device/class.h:230:54: note: expected ‘const char *’ but argument is of type ‘struct module *’
230 | struct class * __must_check class_create(const char *name);
| ~~~~~~~~~~~~^~~~
/home/larisa/Desktop/driver_loaded/mod1/mod1.c:91:20: error: too many arguments to function ‘class_create’
91 | my_class = class_create(THIS_MODULE, DEVICE_NAME);
| ^~~~~~~~~~~~
./include/linux/device/class.h:230:29: note: declared here
230 | struct class * __must_check class_create(const char *name);
I also stuck same problem as Laisa, does any one found its solution
Hi Shubhzz.
I new releases of linux, the class_create function contains only 1 argument, you just need to delete THIS_MODULE
Thanks Laisa, its working
I am trying to compile the code provided above. However, the compiler is giving warning regarding class_create().
make -C /lib/modules/5.4.0-94-generic/build M=/home/ammar/c/kernel/3 M=/home/ammar/c/kernel/3 modules
make[1]: Entering directory ‘/usr/src/linux-headers-5.4.0-94-generic’
CC [M] /home/ammar/c/kernel/3/character.o
/home/ammar/c/kernel/3/character.c: In function ‘my_first_character_device_init’:
/home/ammar/c/kernel/3/character.c:19:17: warning: assignment to ‘struct class *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
19 | if ((dev_class = class_create(THIS_MODULE, “my_first_character_device_class”) == NULL)) {
| ^
Building modules, stage 2.
MODPOST 1 modules
CC [M] /home/ammar/c/kernel/3/character.mod.o
LD [M] /home/ammar/c/kernel/3/character.ko
make[1]: Leaving directory ‘/usr/src/linux-headers-5.4.0-94-generic’
Any idea why I am having this warning?
FYI, the device isn’t created!
thanks
hi embedtronicx ,
General doubt on device_create() ,my question is little off the topic.
any known use case of struct device *parent feild in device_create() API ?
if any known use case could you please help me with it ?
Does both the major number created by dynamic allocation and major number created by mknod for etx_device file are same?
Both are not same.
In this post you have created a device file using mknod and assigned major number 246, but it is getting dynamicly allocating major and minor number in helloworld program. How the dynamically allocated hello world uses the major number 246 created by mknod for etx_device ?
Dynamic method will allocate major number which is not used. Here, before creating major number, i saw that 246 is not used. Thats why i gave 246 for mknod. You can give any number 12 than 246. But that major number should not be using by any driver. I loaded helloworld and this driver at different time. Thats why it gave same major number.