Linux Device Driver Tutorial Part 4 – Character Device Driver

This is the Series on Linux Device Driver. The aim of this series is to provide the easy and practical examples of Linux Device Drivers that anyone can understand easily. Now we are going to see Linux Device Driver Tutorial Part 4 – Character Device Driver Major Number and Minor Number.

Introduction

We already know what drivers are, and why we need them. What is so special about character drivers? If we write drivers for byte-oriented operations then we refer to them as character drivers. Since the majority of devices are byte-oriented, the majority of device drivers are character device drivers. Take, for example, serial drivers, audio drivers, video drivers, camera drivers, and basic I/O drivers. In fact, all device drivers that are neither storage nor network device drivers are some type of character driver.

How Applications will communicate Hardware devices?

This below diagram will show the full path of communication.

  • Initially, Application will open the device file. This device file is created by the device driver).
  • Then this device file will find the corresponding device driver using major and minor numbers.
  • Then that Device driver will talk to Hardware device.

Character Device Driver Major Number and Minor Number

One of the basic features of the Linux kernel is that it abstracts the handling of devices. All hardware devices look like regular files; they can be opened, closed, read, and written using the same, standard, system calls that are used to manipulate files. To Linux, everything is a file. To write to the hard disk, you write to a file. To read from the keyboard is to read from a file. To store backups on a tape device is to write to a file. Even to read from memory is to read from a file. If the file from which you are trying to read or to which you are trying to write is a “normal” file, the process is fairly easy to understand: the file is opened and you read or write data. So the device driver also likes the file. The driver will create a special file for every hardware device. We can communicate with the hardware using those special files (device files).

If you want to create a special file, we should know about the major number and minor number in the device driver. In this tutorial, we will learn that major and minor number.

Major Number and Minor Number

The Linux kernel represents character and block devices as pairs of numbers <major>:<minor>.

Major number

Traditionally, the major number identifies the driver associated with the device. A major number can also be shared by multiple device drivers. See /proc/devices to find out how major numbers are assigned on a running Linux instance.

These numbers are major numbers.

Minor Number

The major number is to identify the corresponding driver. Many devices may use the same major number. So we need to assign the number to each device which is using the same major number. So this is the minor number. In other words, The device driver uses the minor number <minor> to distinguish individual physical or logical devices.

Allocating Major and Minor Number

We can allocate the major and minor numbers in two ways.

  1. Statically allocating
  2. Dynamically Allocating

Statically allocating

If you want to set a particular major number for your driver, you can use this method. This method will allocate that major number if it is available. Otherwise, it won’t.

int register_chrdev_region(dev_t first, unsigned int count, char *name);

Here, first is the beginning device number of the range you would like to allocate.

count is the total number of contiguous device numbers you are requesting. Note that, if the count is large, the range you request could spill over to the next major number; but everything will still work properly as long as the number range you request is available.

name is the name of the device that should be associated with this number range; it will appear in /proc/devices and sysfs.

The return value from register_chrdev_region will be 0 if the allocation was successfully performed. In case of error, a negative error code will be returned, and you will not have access to the requested region.

The dev_t type (defined in <linux/types.h>) is used to hold device numbers—both the major and minor parts. dev_t is a 32-bit quantity with 12 bits set aside for the major number and 20 for the minor number.

If you want to create the dev_t structure variable for your major and minor number, please use the below function.

MKDEV(int major, int minor);

If you want to get your major number and minor number from dev_t, use the below method.

MAJOR(dev_t dev);
MINOR(dev_t dev);

If you pass the dev_t structure to this MAJOR or MONOR function, it will return that major/minor number of your driver.

Example,

Dynamically Allocating

If we don’t want fixed major and minor number please use this method. This method will allocate the major number dynamically to your driver which is available.

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

dev is an output-only parameter that will, on successful completion, hold the first number in your allocated range.

firstminor should be the requested first minor number to use; it is usually 0.

count is the total number of contiguous device numbers you are requesting.

name is the name of the device that should be associated with this number range; it will appear in /proc/devices and sysfs.

Difference between static and dynamic method

A static method is only really useful if you know in advance which major number you want to start with. With the Static method, you are telling the kernel that what device numbers you want (the start major/minor number and count) and it either gives them to you or not (depending on availability).

With Dynamic method, you are telling the kernel that how many device numbers you need (the starting minor number and count) and it will find a starting major number for you, if one is available, of course.

Partially to avoid conflict with other device drivers, it’s considered preferable to use the Dynamic method function, which will dynamically allocate the device numbers for you.

The disadvantage of dynamic assignment is that you can’t create the device nodes in advance, because the major number assigned to your module will vary. For normal use of the driver, this is hardly a problem, because once the number has been assigned, you can read it from /proc/devices.

Unregister the Major and Minor Number

Regardless of how you allocate your device numbers, you should free them when they are no longer in use. Device numbers are freed with:

void unregister_chrdev_region(dev_t first, unsigned int count);

The usual place to call unregister_chrdev_region would be in your module’s cleanup function (Exit Function).

Program for Statically Allocating Major Number

[Get the source code from the GitHub]

In this program, I’m assigning 235 as a major number.

  • Build the driver by using Makefile (sudo make)
  • Load the driver using sudo insmod
  • Check the major number using cat /proc/devices

  • Unload the driver using sudo rmmod

Program for Dynamically Allocating Major Number

[Get the source code from the GitHub]

This program will allocate a major number dynamically.

  • Build the driver by using Makefile (sudo make)
  • Load the driver using sudo insmod
  • Check the major number using cat /proc/devices

  • Unload the driver using sudo rmmod

This function allocates a major number of 243 for this driver.

Before unloading the driver just check the files in /dev directory using ls /dev/. You won’t find our driver file. Because we haven’t created yet. In our next tutorial, we will see the device file.

I hope it helped you. If you have any doubts about this please comment below.

4.9 7 votes
Article Rating
Subscribe
Notify of
guest

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

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
saiteja
saiteja
October 10, 2017 6:07 AM

nice tutorial

Himanshu Gupta
Himanshu Gupta
November 9, 2017 10:58 PM

Clearly explained. Very well done. It will be gr8 for us if you can make tutorials on PCI driver as well.

KOTHAPALLY AJAY KUMAR
KOTHAPALLY AJAY KUMAR
May 11, 2020 6:38 PM

I am getting following error by adding MODULE_AUTHOR and MODULE_LICENSE.
make -C /lib/modules/4.4.0-144-generic/build M=/home/ajay/lsp/ldd modules
make[1]: Entering directory /usr/src/linux-headers-4.4.0-144-generic'
CC [M] /home/ajay/lsp/ldd/statically.o
In file included from include/linux/module.h:18:0,
from /home/ajay/lsp/ldd/statically.c:2:
include/linux/moduleparam.h:21:1: error: expected ‘,’ or ‘;’ before ‘static’
static const char __UNIQUE_ID(name)[] \
^
include/linux/module.h:171:32: note: in expansion of macro ‘__MODULE_INFO’
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
^~~~~~~~~~~~~
include/linux/module.h:218:42: note: in expansion of macro ‘MODULE_INFO’
#define MODULE_DESCRIPTION(description) MODULE_INFO(description, _description)
^~~~~~~~~~~
/home/ajay/lsp/ldd/statically.c:23:1: note: in expansion of macro ‘MODULE_DESCRIPTION’
MODULE_DESCRIPTION("Module inserted statically");
^~~~~~~~~~~~~~~~~~
make[2]: *** [/home/ajay/lsp/ldd/statically.o] Error 1
make[1]: *** [_module
/home/ajay/lsp/ldd] Error 2
make[1]: Leaving directory
/usr/src/linux-headers-4.4.0-144-generic’
make: *** [all] Error 2

Admin
Reply to  KOTHAPALLY AJAY KUMAR
May 11, 2020 9:42 PM

Hi Ajay,

Did you follow the module info like below?
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeTronicX <[email protected] or [email protected]>");
MODULE_DESCRIPTION("A simple hello world driver");
MODULE_VERSION("1.1");

You have to use a semicolon (;) for each MODULE_* macros.

Seems you are missing those. If you don’t miss those then attach your code. We will have a look at your code.

Admin
Reply to  KOTHAPALLY AJAY KUMAR
May 12, 2020 3:15 PM

Hi,
I have gone through your code which you sent by mail. As I mentioned earlier, you have missed the semicolon in line number 22 which is MODULE_AUTHOR. Please add the semicolon. It should fix the problem.

5
0
Would love your thoughts, please comment.x
()
x
%d bloggers like this: