The Importance of the container_of Macro in a Linux Character Device Driver

Embedded linux



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.





You don't have to go through all of this code.

But in the program above, we created a linux device driver with 4 character devices.

These 4 character devices have different features. They have different sizes. For example, character device 1 is 1024, character device 2 is 512, character device 3 is 1024, and character device 4 is 512 (bytes).

Being that this size data is placed within the private data of the device, we can retrieve all of this data and can know the data size for each character device.

Another type of private data that each device has are their own serial numbers. Character device 1 has a serial number of PCDEV1XYZ123, character device 2 has a serial number of PCDEV2XYZ123, character device 3 has a serial number of PCDEV3XYZ123, and character device 4 has a serial number of PCDEV4XYZ123.

Another type of private data that each device has are their own permissions, meaning the character device can be readonly, writeonly, or read/write. In our example, character device 1 is readonly, character device 2 is writeonly, character device 3 is read/write, and character device 4 is read/write.

All of this data can be obtained by creating an instance of the private data structure and access the elements of this structure through the container_of macro.

Again, the open file operation is the best place to put this container_of macro code, because it is the first file operation that is always run when accessing a character device. Once we are able to get this data, we save it to the file structure.

Since all file operation methods have the file structure as a parameter and the file structure stays open throughout a program once it is initially opened, we can access this device private data in any other file operation such as the read or write file operation.

So this illustrates the importance of the container_of macro in a linux character device driver and how it allows us to access device private data for each individual device.



Related Resources





HTML Comment Box is loading comments...