Linux Device Driver Programming Lecture 34- Character driver file operation methods contd.

  • Post author:
  • Post category:Blog

 

Character driver file operation methods contd.

 

 

In the previous article, we came to know about how VFS and it’s various data structures are involved in a file operation.

Open Method:

Whenever the user-level program executes a open system call, the open method( as shown in Figure 1) of your driver will get called, so the VFS passes these two information.

One is pointer of a inode associated with file name or device file, and the second one is pointer of file object. Why the VFS passes pointer to the inode? There is a reason for it.

Figure 1. Open method
Figure 1. Open method

 

But this should be the prototype of your open method in your character driver. We will use the name pcd_open, and this must be the prototype. And this method should return 0 if open is successful. That is, if your device open is successful. And any negative error code if open fails.

 

What do you do inside the open method? 

In your driver’s open method, you can initialize the device or make the device respond to subsequent system calls such as read-write. Or you can bring the device back to the active mode from the low power mode. Sometimes you need not to do anything in the open method, so you can keep it blank, or you need not to implement the open method at all. If you don’t implement the open method, then the open call will always be successful. Apart from that, we can detect device initialization errors.

Let’s say you try to do some device initialization, and if the device doesn’t respond, then you can return a negative error code saying open failed. You can check open permission. So, user should give some permission to open a device. The device could be read-only(RDONLY), or the device could be write-only(WRONLY), or the device could be read and write(RDWR). If device is read-only, that means there is a device from which you can only read, and if user opens your device file with read/write permission, then your open method can check the permission, and it can return a negative error code saying a write is not possible.

And you can identify the device being opened using minor number. This may be required when you have more than one minor numbers or more than one device files. Prepare device private data structure if required, and if required, you may also have to update the file position. I will  discuss more on this later. As I said, open method is optional actually. If not provided, open will always succeed, and your driver is not notified.

 

Close system call

Let’s discuss close system call. Generally, user program uses the close something like this. Close using the file descriptor.

Figure 2. Close system call
Figure 2. Close system call

 

Basically, what it says is,  release the file descriptor. When close is issued, the VFS releases the file object. And the release method is called when the last reference to an open file is closed. That is when the f_count field of the file object becomes 0. Whenever a close is issued on the fd, that may not trigger the release method immediately. The release method is triggered only when all the references to an open file is closed. The VFS tracks this by using f_count field of the file object. So, whenever close is issued, the f_count field of the file object decrements.

 

When that reaches to 0, then only the release method, as shown in Figure 3 gets called in your driver. That’s a reason why we call it as a release method, not close method. The VFS calls this driver method when all the references to an open file is closed, and the file object data structure is released from the memory. And the prototype is similar to the open method, it passes 2 arguments here. Pointer of Inode associated with the device file and pointer of file object. And we will be using this name pcd_release  in our character driver file.

Release Method:

Figure 3. Release method
Figure 3. Release method

 

Let’s see what we do inside the release method of the driver. In the release method, the driver can do reverse operation of what open had done.

For example, if the open method brings the device out of low power mode, then a release method may send the device back to the low power mode.

Basically, you should leave the device in its default state, the state which was before the open call. You can also free any data structures allocated by the open method. Basically, the release method returns 0 on success or negative error code if there is any errors.

For example, you try to de-initialize the device and the device it doesn’t respond, then you can return timeout, or IO error, something like that.

 

Read Method:

Let’s explore the read system call as shown in Figure 4. This is how read is issued from the user-level program.

Figure 4. Read system call
Figure 4. Read system call

 

It uses three arguments here. The file descriptor, the user buffer, and the number of a bytes to be read. And to handle this read system call, we will be implementing pcd_read in our character driver. 

 

And let’s look at the prototype of the read method as shown in Figure 5. 

Figure 5. Prototype of read method
Figure 5. Prototype of read method

 

The VFS passes all these information to our driver-read method. So, the first one is pointer of file object. And after that is a pointer to the user buffer, and this(size_t count) is a read count given by the user. And after that, loff_t *f_pos This(loff_t *f_pos) is actually pointer of current file position from which the read has to begin.

In read method as shown in Figure 6, what drivers should be doing is it should read a device file and return the data back to the user. That’s what read is, your driver should return the data back to the user. How many data bytes? So, count the number of data bytes. From which position in the device file, that is decided by this loff_t *f_pos field. Your driver code should read from this position onwards. This(size_t count) much amount of bytes and it should populate all that data bytes into this(char __user *buff) buffer given by the user. That’s all the driver needs to do.

And after that, there is a macro __user used with pointer of user buffer. What exactly is this macro? This is actually an optional macro which alerts the programmer that this is a user-level pointer. It cannot be trusted for direct dereferencing. That means, in your kernel-level programming, you should not trust this(*buff) pointer. 

You should not be doing any direct dereferencing to avoid the kernel faults. __user is a macro used with user-level pointers, which tells the programmer not to trust or assume it as a valid pointer to avoid kernel faults. Never try to dereference user given pointers directly in kernel-level programming. Instead, use dedicated kernel functions such as copy_to_user and copy_from_ user. GCC doesn’t care whether you use __user macro with user-level pointer or not. So, the GCC won’t trigger any warnings for this. Doesn’t matter whether you use it or not.

This is actually checked by sparse, a semantic checker tool of linux kernel to find possible coding faults. That’s why, this is actually optional but it’s highly recommended to use this macro with user-level pointers. And what you should be doing in read method is, read ‘count’ bytes from a device starting at position ‘f_pos.’

Update the position pointer by adding the number of bytes successfully read. Return number of bytes successfully read. Return 0 if there is no bytes to read. That is the situation of end of file. And after that, return appropriate error code if there is any error. A return value less than ‘count’ does not mean that an error has occurred.

Basically, you should read the amount requested here( size_t count). But, if that amount is not available, then you can simply return a number of bytes which was successfully read. That’s why, a return value less than ‘count’  does not mean that it’s an error.

 

Write Method:

How about write? Again, write is a system call( as shown in Figure 6) used by the user level program to write some data into the device. Write system call gets connected to your driver’s write method, and let’s call our write method as pcd_write.

Character driver file operation methods contd
Figure 6. Write system call

 

Let’s look at the prototype of pcd_write, as shown in Figure 7.

Character driver file operation methods contd
Figure 7. Write prototype

 

So, the input arguments are exactly same as read method. This(struct file *filp) is a pointer of file object, pointer of user buffer, and write count given by the user, and this is a current file position from which the write has to begin. In write method, you copy the data given by this(*buff) pointer into the device, that’s what write is. 

In write method, what you do is, write ‘count’ bytes into the device starting at position ‘f_pos’. And update the kernel file position by adding the number of bytes successfully written. Return number of bytes successfully written. Return appropriate error code if there is any error.

 

Llseek Method:

And after that, the last system call  is llseek (as shown in Figure 8).

Character driver file operation methods contd
Figure 8. Llseek method

 

Actually, llseek is used to alter the current file position. This has got nothing to do with a read or write of a device. This system call is used to alter the current file position. So, will come to know more about this a file position and all when we do a code exercise. And this(pcd_lseek) will be our driver method to handle lseek system call, and this is a prototype as shown in Figure 9.

Character driver file operation methods contd
Figure 9. llseek prototype

 

The VFS passes three arguments to this method. One is pointer of file object, and the second one is offset value, and the third one is the origin, that is whence information. Whence information can have three possible values.SEEK_SET, SEEK_CUR, SEEK_END.And so this(whence) decides how to use this (off). 

Let’s say if whence field is set to SEEK_SET. If whence is SEEK_SET, then the file offset is set to this value. The current file offset is just equated to this value. If whence is set to SEEK_CUR, then the current file offset is set to its current position plus offset bytes. This(off) is a offset bytes. And if the whence field is set to this value SEEK_END, then the current file offset is set to size of the file plus this offset. 

So, we’ll see more on this lseek method when we a code for this method in our character driver exercise. In the lseek method, you should do a these things. The driver should update the file pointer by using ‘offset’ and ‘whence’ information. The llseek  handler should return, newly updated file position, or the llseek method may also return error. We will see that during implementation .See you in upcoming article.

 

Get the Full Course on Linux Device Driver Here

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.