Linux Device Driver Programming Lecture 53| Pcd driver with multiple devices code implementation part-5

 

Pcd driver with multiple devices code implementation part-5

 

 

In this article, let’s implement the pcd_open method(Shown in Figure 1).

Figure 1. Pcd_open method
Figure 1. Pcd_open method

 

As you know, the open method of the driver gets called whenever the user space application tries to open our device file. In this open method, the virtual file system actually provides us the inode, a structure address of our device file, and also a pointer to the struct file. I think you already know this fact, that is, for every open attempt, the kernel creates the file kernel object.

In this open method, the driver must find out on which device file the open was attempted by the userspace because we have got four device files, isn’t it? You should find out on which device file the operation was attempted.

This we can easily find out by getting the information from the inode. Because for every device file, there will be one inode.

 

If you check the inode structure, here is an inode structure as shown in Figure 2. 

Pcd driver
Figure 2. Inode structure

 

Figure 3. i_rdev field in the inode structure
Figure 3. i_rdev field in the inode structure

 

And you already know that there are two important fields, that is i_rdev, as shown in Figure 3.

This represents the device number. So, by checking this number, we can come to know which device is being accessed.

 

That’s why let’s create one variable here called int minor number, as shown in Figure 4.

Pcd driver
Figure 4. Code to find out which device file open was attempted by the user space

 

First, the driver has to find out the minor number. The minor number is equal to you can use the MINOR macro inode, just dereference to get this field i_rdev. This macro gives you the minor number. As I explained before, the driver distinguishes between different devices using a minor number. So, this gives the minor number. Let’s print that information.

 

And after that, you can access the device private data structure here. Why accessing the device private data structure is important?

Because, in your driver, before handling any request from the userspace like read, write, open request. There are two things the driver must do. The first one is it should identify which device is being accessed, and it should be ready with its priority structure. Because the private data structure is a structure where all the device-specific information is kept. Like its buffer, address, size, and other things. That’s why it is very important driver extract the write device-specific information.

 

Our device-specific structure is struct  pcdev_private_data, isn’t it? Let’s create one pointer variable, as shown in Figure 5.

Figure 5. Creation of struct pcdev_private_data pointer
Figure 5. Creation of struct pcdev_private_data pointer

 

Now the question is, how do you access the device private data structure of this current device? For this, again, you can take the help of the inode structure. In the inode structure, you find another field called cdev. i_cdev. Let’s search for that. This actually holds the address of our per device cdev structure.

Figure 6.i_rdev field in the inode structure
Figure 6. i_rdev field in the inode structure

 

We can take the help of this pointer to access the device private data.

 

If you just take a look into our pcdev_private_data, there is a cdev structure, as shown in Figure 7.

Pcd driver
Figure 7. Pcdev_private_data structure

 

So, if I access this pointer(i_cdev from struct inode), I actually get the address of this member element(member element struct cdev cdev from struct pcdev_private_data). And by using this (member element struct cdev cdev), I can extract the container address. 

Who is a container of this member( struct cdev cdev) element? This(struct pcdev_private_data)  is a container of this(struct cdev cdev) member element. That’s why, by using the address of a this(struct cdev cdev) member element, we are going to extract it’s container.

 

For this, we are going to use something called container_of macro. Let’s explore the container_of_macro.

Figure 8. Container of macro
Figure 8. Container of macro

 

What’s a container_of_macro?

This container_of_macro is defined in /include/linux/kernel.h. This is used to cast a member of a structure out to the containing structure.

Let me explain what’s the use of this container_of. So, We have a separate article to explain how this container_of actually works. Maybe we will cover that once I finished this exercise.

But, let’s briefly see what is the use of this container_of macro.

  1. The container_of macro helps you to get the address of the containing structure by taking the address of its member element.
  2. As its name indicates, it gives you the “container” address of the member element of a structure.
  3. It takes three arguments. One is a pointer, the second one is type of the container, and the third one is name of the member the pointer refers to.

It actually takes three arguments, as shown in Figure 9. Let me explain those arguments.

Figure 9. Container of macro parameters
Figure 9. Container of macro parameters

 

Pointer, type, and member. The pointer is pointer to the member. And the second argument is type of the container struct in which the ‘member’ is embedded in. And the third argument is member; here, you should give the member name. Name of the member where the pointer refers to.

Let’s use this container_of. Remember that we are using container_of to extract this member elements parent structure. Let’s get back to the open method. Let me use the container_of().

As I said, inode gives me the address of the device-specific cdev. That’s why I’m going to use inode, and I’m going to dereference it, and I’m going to use i_cdev. This is a pointer. And the next argument is type of the container struct. What is a container struct? That is struct pcdev_private_data. And the third argument is member name. Member  name is cdev. This is a member name.

 

Figure 10. Container of macro implementation in our code
Figure 10. Container of macro implementation in our code

 

After that, that private data structure is really required for our other methods. For example, for read method and write method. Remember that, from the read method you cannot extract that. Why? Because there is no supply of inode. So, the VFS doesn’t supply inode to the read or write method.

That’s why it must be done in the open method. You have to save this for other methods. Because other methods also want to operate on that device private data, isn’t it? It has to manipulate other methods also manipulate the permission, or the buffer field, etc. That’s why you have to save this(pcdev_data). 

So, where do you save this? You save this data(pcdev_data) inside the file struct. Because, once a device is opened, a struct file kernel object is created, and the same kernel object is passed to different driver methods like read and write.

Read and write, even lseek, all these a driver methods receive the same kernel object(struct file *filp). That’s why, you save that device private data inside this struct file.

 

We can do that by using one interesting field in this structure called private_data. So, will see that. Let’s search for struct file. Now, if you just check this file structure, you can see that, there is a field called private_data.

Figure 11.*private_data field in struct file
Figure 11. *private_data field in struct file

 

You can use this private_data field in order to supply a device  private data to other methods of the driver such as read, write, lseek.

 

Let’s get back to the code. And what I’m going to do now is, I’m going to use this file pointer, dereference that. Let’s use the private_data field in order to store this pointer  pcdev_data.

Pcd driver
Figure 12. File pointer implementation in our code

 

And after that, the open method also should check the permission. Because, for example, in our case, the pcdev1 device is read-only. That’s why the user application should not try to open the device file of pcdev1 with read-write access. So, the driver should detect that, and it should then report whether the open was successful or not. That’s why the permission checking should be done in the open method.

I will implement the permission checking later. For a time being, let me implement this check_permission as shown in Figure 13. So, check_permission. Let’s for a time being, let’s keep it void. And let’s just return 0. The permission is granted,  something like that.

Pcd driver
Figure 13. Check_perminssion function

 

Once we finished coding for all the methods will come back to this open method and will finish this check_permission.

 

And after that, the final line is if the return is non-zero, then the open method of the driver concludes that the open was unsuccessful and return the error code as shown in Figure 14.

Pcd driver
Figure 14. Conclusion of open method driver(successful or unsuccessful)

 

Check_permission basically returns an error code or zero. So, we’ll implement that later.

I think we have come to the end of this open method and now let’s save this and try to build it. In the following article, let’s implement read and write. I will see you in the following article.

 

FastBit Embedded Brain Academy Courses

Click here: https://fastbitlab.com/course1

 

FastBitLab

The FastBit Embedded Brain Academy uses the power of internet to bring the online courses related to the field of embedded system programming, Real time operating system, Embedded Linux systems, etc at your finger tip with very low cost. Backed with strong experience of industry, we have produced lots of courses with the customer enrolment over 3000+ across 100+ countries.