The Importance of the container_of Macro in a Linux Character Device Driver
In this article, we explain the importance of the container_of macro in a linux character device driver.
The container_of macro is used especially used with linux device drivers with multiple character devices.
Typically, we use the container_of macro is access the device private data of each character device.
So how does this all work?
Let's say that we create a structure in C that represents the device private data for each character device.
Let's say below is the structure for the device private data that each character device
will use.
So let's say that for each character device that we create, we want these private data values included. This includes the buffer address, the size of the data, the serial number, the permission data, and the cdev structure of the device.
You see that one of the member elements of this pcdev_private_data structure is cdev.
cdev is a member element of the the pcdev_private_data structure, which serves as the container for the cdev member element. The pcdev_private_data is the container for this member element.
The container_of comes in good use for us because through the inode structure, we can get the cdev structure of a character device.
We can then use the container_of macro to get the container of the element, which then a allows us to access all the other member elements, such as the buffer address, the size of the data, serial mnumber, the permissions, etc.
The container_of macro allows us to get the address of the containing structure by taking an address of one of its member elements. As the name implies, the container_of macro gives the container address of the member element.
The container_of macro takes in 3 arguments, which are the pointer, the type of the container, and the name of the member the pointer refers to.
This is shown below.
container_of(pointer, the type of the container, the name of the member element)
The open file operation method will contain the container_of macro statement, because this is the file operation which is first run when a character device is used. A user has to first open it.
The code in our open file operation method is shown below.
So let's go through this code in the open file operation.
So you see that the open file operation takes in 2 parameters, the inode structure and the file structure.
The inode structure is important because each character device has a unique cdev structure; so that allow s us to single out each character device in a linux device driver where we create multiple character devices.
The inode structure has a property named i_cdev, which represents the pointer of the cdev structure for the particular character device which was opened.
This is the first parameter of the container_of macro; thus, for this parameter, we specify, inode->i_cdev
Next, we specify the type of the container structure. In our case, this would be, struct pcdev_private_data
Lastly, you specify the name of the member element of the containing structure that the first parameter points to. In this case, it would be, cdev.
Notice that before have this container_of statement, we create an instance of the private data structure with the line, struct pcdev_private_data *pcdev_data;
We then set this instance to the container_of statement.
So what we've done here is now we have all the private data of the character device within this variable.
This is how the container_of macro works.
We can create a structure and put a unique identifying member element within this structure, which is usually the cdev structure of a device, and we can access all the properties of this structure through the container_of macro. This is commonly done with device private data in a linux device driver. The device private data is essential in a linux device driver, because it gives us unique, needed information about each character device.
For example, let's look at the full program for a linux device driver with multiple character devices.