Linux Device Driver Programming Lecture 51- Pcd driver with multiple devices code implementation part-3

 

Pcd driver with multiple devices code implementation part-3

 

 

In this article, let’s implement the pcd_driver_init function. Let’s shift #if 0 to somewhere below.

First of all, we have to dynamically allocate device_numbers. We are going to use the alloc_chrdev region. Here, this device number you have to get from the driver’s data structure. This would be pcdrv_data. Its address you have to give &pcdrv_data.device_number. And this is a first minor. First minor we can start from 0 and next is NO_OF_DEVICES.

So, now we have four devices. That’s why we want four device_numbers whose base is stored in this device_number variable.

Figure 1. Dynamically allovate the device number for 4 devices
Figure 1. Dynamically allocate the device number for 4 devices

 

The alloc_chrdev_region now actually allocates device_numbers dynamically for four devices. Please note that all those four device_numbers will not get stored inside this device_number variable. Only the base is stored.

Let’s assume this function returns the major number dynamically. Let’s say it is 127. That’s why this device_number variable actually it has two parts. It has major number section and the minor number section. In the major number section, 127 will be stored. And by changing the minor section number, you can derive the next device. 

For example, if 127:0 is stored in this variable, then by changing this a minor number to next one, that is 127:1, you get the device number for the next device like that. That’s why the driver distinguishes between different devices using these minor numbers.

Then we are printing device_number information, but we have got four devices. Now, let’s use a loop to print it four times. So, now let me declare  ‘i’ above. device_number you can extract the major number by using MAJOR macro. For this, you have to pass device_number+i. Because to print the next device number, you have to increment the minor count. That’s why I’m using +i  device_number+i. And this device_number now it should be modified, you have to write pcdrv_data.

 

Our next task is to do cdev_init and cdev_add. This we have to do for every device. We have got four devices. So, we have to write all these codes in a loop, as shown below.

/*create device class under /sys/class/ */
pcdrv_data.class_pcd = class_create(THIS_MODULE,"pcd_class");
if(IS_ERR(pcdrv_data.class_pcd)){
       pr_err("Class creation failed\n");
       ret = PTR_ERR(pcdrv_data.class_pcd);
      goto unreg_chrdev;
}

for(i=0;i<NO_OF_DEVICES;i++){
               pr_info("Device number <major>:<minor> = %d:%d\n",MAJOR(pcdrv_data.device_number+i),MINOR(pcdrv_data.device_number+i));

      /*Initialize the cdev structure with fops*/
      cdev_init(&pcdrv_data.pcdev_data[i].cdev,&pcd_fops);

     /* Register a device (cdev structure) with VFS */
     pcdrv_data.pcdev_data[i].cdev.owner = THIS_MODULE;
     ret = cdev_add(&pcdrv_data.pcdev_data[i].cdev,pcdrv_data.device_number+i,1);
     if(ret < 0){
         pr_err("Cdev add failed\n");
         goto cdev_del;
      }

Cdev_init and cdev_add

And the class_create. The class_create need not be in a loop because only one time we create the class_create. 

And after that, device_create has to be in loop. Anyway, we have this loop for printing device number. So, now let’s open the loop here, and let’s put cdev_init and cdev_add  codes inside the loop , And let’s close the loop here. 

First, I do the alloc_chrdev_region, and after that, I do the class_create. So, the code must be pcdrv_data.class_pcd = class_create(THIS_MODULE, “pcd_class”);

And after that, error checking should be same. And after that, PTR_ERR macro should take pcdrv_data.class_pcd. 

Let’s check codes inside the for loop. Here, initialize the cdev structure with fops. Here, for the cdev_init, the first argument is address of the cdev structure, but the cdev is per device basis in this exercise. That’s why I’ll be writing this like you know pcdrv_data.pcdev_data [i].cdev. I hope this line is clear, and this will be the file operations. So, that means every cdev structure is initialized with file operation structure.

And after that, register a  device with VFS. Here pcdev_owner, so this will be  pcdrv_data.pcdev_data[i].cdev.owner.

After that, cdev_add. So, here also you should write something like this pcdrv_data.pcdev_data[i].cdev. And here, you have to mention the device_number. So, the device number is pcdrv_data.device_number. And we are adding one device.  After is a error checking.

Now let’s go to the next one  populate the sysfs with device information. So, device_pcd.  This will be pcdrv_data.device_pcd. device_create(class_pcd). class_pcd will be  pcdrv_data.NULL, device  number will be a pcdrv_data.device_number, NULL. Now, here you have to create different device file names for different devices. So, it should be PCDDEV1, 2,3,4.

Let’s look at device tree API once again. Let’s look at its implementation shown in Figure 2. So, here I have a reference device_create. And look at the last argument. This actually takes a formatted string. A string for the device_name, so this is a formatted string. That’s why you can a write something like this.

Figure 3. Device tree API
Figure 2. Device tree API

 

Let’s get back to the code. I will write this as “pcdev-%d”, and here you can mention i as shown in Figure 3.

Pcd driver with multiple devices code
Figure 3. device_create

 

When this loop runs for the first time,  this will be replaced by pcdev- this value i, that will be 0 like that. It creates a pcdev 0,1,2,3.

And after that, pcdrv_data.device_pcd. And in the error checking, this will be pcdrv_data.device_pcd. Our loop ends here.

Let’s remove if 0. We have almost reached the end. Remove #endif as well. Let me remove this return zero. If everything goes well here, then it would print “Module init was successful” and it returns zero. Otherwise, you have to do some cleanups.

Before that, let’s fix variables as shown below. So, this will be pcdrv_data.pcd (in class_destroy), and you have to delete the cdev structure. Actually, this you have to do it in a loop.

           pr_info("Module init was successful\n");

          return 0;

class_del:
          class_destroy(pcdrv_data.class_pcd);
cdev_del:
          cdev_del(&pcdrv_data.pcdev_data[i].cdev);
unreg_chrdev:
          unregister_chrdev_region(pcdrv_data.device_number,NO_OF_DEVICES);
out:
          pr_info("Module insertion failed\n");
          return ret;

Fixing code for class_del, cdev_del, unreg_chrdev, out

But before that,  This cdev should be &pcdrv_data.pcdev_data[i].cdev. Unregister_chrdev_region. You will be unregistering 4 devices. Here, you have to mention the number of devices, so let’s use the macro  NO_OF_DEVICES. And you print “model insertion failed,” and you just return the error code. And cleanup is incomplete actually, we will code this later. 

 

But let’s make host, and we have one error at 232, as shown in Figure 4.

Pcd driver with multiple devices code
Figure 4. Make host

 

Let’s check that this is pcdrv_data as shown in Figure 5.

Pcd driver with multiple devices code
Figure 5. Corrections in the code

 

So here, one more mistake. cdev_init is fine. In the cdev_add,  for the next pass, it has to be the next device number. That’s why you have to do +i.

And in the device_create, for the next pass, it has to be the next device. That’s why you have to do  +i here as well. Save and exit and compile the code.

In the next article, we will fix this cleanup procedure. Because we now have got multiple devices. So, that’s why here the cleanup procedure is a little different And we will cover that in the next 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.