Character device registration
In our previous article, We finished our very first task, that is creating a device number (as shown in Figure 1).
Let’s go to our next step that is we have to now do a character device registration.
How many devices are we handling?
There’s only one device. One pseudo character device. That’s why you have to call cdev_add() one time. That would make a character device registration with a VFS. This registration is very important. Otherwise, the user-level system calls will not get connected to the file operation methods of the driver.
As you may be guessing, for this cdev_add(), you have to mention the driver’s file operation methods. With that, you have to register. We have to use cdev_init() and cdev_add().
Let’s first explore a what is cdev_init. cdev_init is used to initialize a cdev structure(Figure 2).
It has got two input arguments.
Look at the second argument. That is a file_operations.
By using this pointer, you have to mention your driver’s file operation methods or system call handler’s Like system call handlers for open, read, write, seek, etc. This is a cdev structure. Here you have to pass a pointer to a cdev structure. Since we are handling only one device, we should create a one variable of type struct cdev.
Here is an example, as shown in Figure 3.
You have to create two variables. Let’s say, eeprom_fops. This is a variable of type struct file operations.
After that, you have to initialize this file operation variable with your driver’s file operation methods. You have to populate the function pointers of your driver’s file operation methods. And after that, you have to create one more variable, that is, a cdev variable.
For example, here, I have created eeprom_cdev. This is a variable of type struct cdev. If you are managing a 10 devices, then you may have to create 10 cdev variables. But, in this case, we are handling only one device, I would create only the cdev variable. And then, by using cdev_init, you have to pass the pointers of those a variables.
So, now the cdev_init is a kernel API, which is implemented in char_dev.c.
And this is what it does. You can see here cdev has a field called ‘ops’, that is, operations. And cdev_init() function just initializes that field to this pointer what you send here.
This is a cdev structure, actually. You can see that the cdev structure has this field ‘ops’. This is a pointer to the file operation structure of the driver. So, the cdev_init() just initializes this field.
And after that, in this cdev structure, you have to initialize one more field, that is module owner field. This field should be initialized with a pointer to the module that owns this structure. It should usually be initialized to a macro called this module. This field is used to prevent a module from being unloaded while the structure is still in use.
Let’s see, what is this THIS_MODULE macro. So, THIS_MODULE is a macro which resolves into “pointer to a struct module variable, which corresponds to our current module”.
Basically, this *owner field in cdev structure is used to identify the current module, which owns this structure. You can find this macro in linux/export.h. Look at that macro. That macro is nothing but an address. So, an address to this variable of type struct module.
If you go back to our earlier project, that is hello_world. Here, let’s run the nm command to read all the symbols of the final kernel module, that is, main.ko. You see here __this_module, this is actually a variable of type struct module. We didn’t use this variable.
Who added it?
This gets added when we compile our kernel module. So, the build system adds this a variable.
If you want to see that, you can open main.mod.c. Just open main.mod.c as shown in Figure 8, and here it is.
The build system added this variable. Basically, what you are doing is, you are just assigning the address of this variable to the owner field. Whenever you use any kernel data structure such as cdev or file operations, and you see the owners field, then you should initialize that field to the macro, that is THIS_MODULE.
We actually explore the cdev structure. Let’s look at file operation structure(shown in Figure 9) . The file operation structure, this structure definition you can find in linux/fs.h.VFS file operation structure and it has various member elements.
Basically, all are function pointer variables. All these function pointer variables needs to be initialized with your drivers a file operation methods. We will not be using all the member elements here. So, we will be using open, read, write, and lseek for this exercise.
And look at the first member elements, that is, struct module owner. This again needs to be initialized with the macro THIS_MODULE. Now, let’s get back to the driver file.
We have to use cdev_init(). Let’s create two variables, as shown in Figure 10.
Let’s create cdev variable. struct cdev, let me call it as pcd pseudo character device cdev. After that, file operations of the driver. For this struct file_operations pcd_fops. We have to initialize this file operations a variable. Let’s do that in a moment, don’t worry.
And now, let’s include the appropriate header files. The header files we should be using is cdev.h. For the cdev we have to use #include <linux/cdev.h> and for the file operation it is fs.h.
After that, let’s finish our cdev_init(). So, it needs address of cdev structure. That is pcd_cdev, and address of our file operations, pcd_fops. So, this is our second step. And you also know that our cdev structure has owner field. right? that needs to be initialized. So, we forgot that.
Let’s do that. pcd_cdev.owner=THIS_MODULE as Figure 11.
You can do that here.
FastBit Embedded Brain Academy Courses
Click here: https://fastbitlab.com/course1