I have a question about Linux device file and drivers, with my current understanding as the following:
When a user calls open()
on some device file, at some point of time kernel will ask inode
of the device file for the read()
/write()
function.
The device file was created by udev
earlier, based on devices in /sys
folder, meaning the inode
of the device file should have i_fop
field pointed to functions that know how to talk to the device, such as read/write/mmap/ioctl/poll
. And that means each device file's inode->i_fop
field should point to a different file_operations
struct.
If so, the device driver will be providing these read()
/write()
functions, maybe the complete file_operations
struct, including read/write/mmap/ioctl
, etc.
Now, ULK says (in the description of open()
syscall for device files) "sets the i_fop
field of the inode
object to the address of either the def_blk_fops
or the def_chr_fops
file operation table, according to the type of device file." And that means all block device files have the same read()
/write()
function, but then how can a user talk to different devices?
I also checked the device_driver
struct, and indeed, there are no place to store file access functions, so how exactly an open()
syscall performs its job using a device specific driver then? And where do device specific operation function live, if not in device_driver
?
The following applies to opening character special devices.
When the file is opened, the i_fop
pointer from the inode object gets copied to the f_op
pointer of the file object. For character special devices, this points to def_chr_fops
. def_chr_fops.open
points to chrdev_open
, so chrdev_open(inode, filp)
is called when opening any character special device.
chrdev_open
looks through its set of registered struct cdev
objects which map the inode's major/minor numbers (combined into a dev_t
number) to a particular registered struct cdev
. If no matching struct cdev
is found, it returns -ENXIO
. Otherwise, it replaces the f_op
pointer in the file object with the ops
pointer from the struct cdev
, which was set by the driver for the character special device.
If the file object's f_op->open
is non-null, it is called and its return value is returned by chrdev_open
. Otherwise, no special "open" handling is required for this character special device and 0 is returned.
If chrdev_open
returns 0, the file object is in the "open" state and its f_op
pointer points to the driver-specific file operations. The open
syscall will eventually return a file descriptor. If chrdev_open
returns a negative errno value, the file object will be destroyed, the open
syscall will return -1 and errno
will be set according to the return value from chrdev_open
.