Linux Device Driver Programming Lecture 48- Pcd driver with multiple devices

 

Pcd driver with multiple devices

 

 

In the previous article, we actually wrote a pseudo character driver to handle a single device. In this article, let’s do one more exercise in which we are going to write a pseudo character driver with multiple devices.

This is an exercise as in Figure 1.

Figure 1. Exercise 1
Figure 1. Exercise 1

 

You have to modify the previous pseudo character device driver to support four pseudo character devices. So, you have to implement open, release, read, write, lseek driver methods, same as before, to handle user request. The pseudo character device, in our case, as you know, it is just a static memory area. You can use arrays here as well.

Figure 2. Detail of exercise
Figure 2. Detail of exercise

 

That means there will be one single driver or kernel module, I call  it as pcdrv pseudo character driver. And that driver is going to take care of four devices. Let’s call it as  Pcdev-1, Pcdev-2, Pcdev-3, and Pcdev-4. So, the driver, which we are about to write, should manage 4 devices  or it could be more. You can consider it as manage N devices. 

There will be a single driver managing all N devices, remember that.

You are not going to write 4 different kernel modules, there will be only one driver or one kernel module, which is going to manage N devices. There will be only one implementation of open, read, write, release, and lseek driver methods. That also means, the drivers should first determine which device is being accessed from the user’s space to fulfill the request. That logic you have to build. So, we will see that while we code for this exercise.

 

These are the device filename we are going to use for 4 different pseudo character devices, as shown in Figure 3.

Figure 3. Creating all these device
Figure 3. Creating all these device

 

So, in the /dev directory, you have to create all these device files, as shown in Figure 3.

 

Before implementing the driver, now let’s discuss some of the design aspect of the device driver. How we are going to manage the information? Remember that every device has its own private data, as shown in Figure 4.

Pcd driver with multiple devices, pseudo character driver
Figure 4. Private data of a device(Device-specific information)

 

In our case, a device is nothing but a memory area. We call it as a pseudo-device.

What are the attributes or device-specific information of our device?

The first thing could be a serial number. 

Every device may have its own serial number. A serial number could be a collection of characters or a string data. Since, our device is a memory area, so the starting address of the memory area is very important. That could be another device-specific information.

What is the size of that memory area, and the permission of that memory area also become device-specific information. That means, in our case, there are 4 device-specific information. The permission could be read-only, write-only, and read-write.

 

This is how your driver is going to manage a four different devices.

Pcd driver with multiple devices, pseudo character driver
Figure 5. Driver’s device management for 4 different devices

 

In this exercise, every device represents a unique memory area. You can create a the memory area either statically or by using dynamic memory allocation. But, let’s stick to static allocation that is array-based, because we have not yet discussed the dynamic memory allocation in the kernel space.

Let’s push that discussion to a later point in the article. So, we will discuss how to use dynamic memory allocation API later. For this exercise, let’s just tick to array-based memory.

The user space will see your device files Pcdev_1, Pcdev-2, Pcdev-3, and  four. And the driver is going to manage 4 memory areas. The Pcdev-1 will be read-only in our case, and Pcdev-2  will be write-only. Pcdev-3 and Pcdev-4 will be read-write memory. This is the configuration we are going to use for this exercise.

Let’s say there is a userspace application echo, which tries to write some data to pcdev-1. In that case, the driver should detect that, and it should return the error code EPERM. That means the operation is not permitted. Because this is read-only.

In the same way, if an application tries to read from this /dev/pcdev-2 device, then only the driver should detect that, and it should return the appropriate error code. And for these two devices, the userspace application can read and write.

Let’s say there is a userspace application echo tries to write some data to the pcdev-3, then the driver should appropriately it should copy that data to the write device. Drivers should not put that data in the memory areas of other devices.

That means the driver should detect to which device the request is coming in. That means the driver should detect, or it should understand which device is being accessed from the user space, and it should process that request appropriately. So, the driver should not corrupt the memory areas of other devices.

Before implementing this exercise, let me also discuss about the data structure we are going to use for this exercise.

Pcd driver with multiple devices, pseudo character driver
Figure 6. Device driver data structure

 

What will do for this driver is. In the driver, we will maintain 2 structures.

What are those structures?

The first structure is going to hold driver’s private data, and another structure is going to hold a device’s private data.

 

Let’s understand a structure to represent private device data. As I explained in the previous Figure 4, every device has its own private data.

Pcd driver with multiple devices, pseudo character driver
Figure 7. Structure to represent device’s private data

 

In our case, the pcdev device has couple of private data such as start  address, size, permission, serial number. We will put all these data in a structure format. That’s why I’m going to include, or I’m going to create, this structure.

Struct pcdev_private_data, which has a couple of member elements to capture the device’s private data.

The first member element will be a pointer to capture the start address, the next one is to capture the size, and this is a pointer to capture the serial number, which is nothing but a string of information, and this is to capture the permission.

This structure also embeds our cdev structure, that is, character device structure. Because, in this case, we are going to handle multiple character devices. Every device has to get registered with a virtual file system of the kernel.

Since we are using, let’s say, four devices, then we should be creating four cdev structure. So, that also means that the cdev structure is device-specific. That’s why it makes sense to keep that device-specific information in the device private data structure. That’s a reason why I embed the cdev variable in the device’s private data structure. I hope that makes sense. So, any private data if you think it is specific to device, then you are free to keep that in this structure.

We will later see when we use synchronization related a kernel object such as pin logs and other things. That also, if it is a device-specific, then it goes into this structure.

 

Now let’s explore the structure to represent the driver’s private data, as shown below.

/*This structure represents driver private data */
struct pcdrv_private_data
{
     int total_devices;
     struct pcdev_private_data pcdev_data[NO_OF_DEVICES];
};

Structure to represent driver’s private data

In our case, the driver is the one who is going to handle four or N  number of devices. Character devices. That’s why I include only two member elements in the driver’s private data structure.

The first one will be total devices. Because since the driver is handling  N number of devices, so this is it is just to track how many devices being handled in the driver. That’s a reason why I use a int total_devices variable. That becomes a driver’s info. The total devices will never become device information.

It is not a device-specific, it should be a driver-specific. Now, the next member element is nothing but device private data instances. So, this driver is handling N number of devices. That means, each device should have its own pcdev_private data struct variable.

That’s the reason I just created an array of private device data. Here,  number of devices macro represents a number of devices. The reason for embedding pcdev_ private_data instances. Inside the driver, structure is because driver is the one who is going to operate on the device-specific data.

For example,  driver is the one who is going to alter the memory address of a device to read or write data. That’s a reason why we can create this array of struct pcdev_private_data inside this driver structure.

I hope you understood about the structures a what we are going to use for this exercise. From the next article onwards, let’s start coding for this exercise.

 

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.