clinuxlinux-kernellinux-device-driverdevice-driver

How does Linux link device file with device driver? Does open() syscall eventually call device driver code?


I have a question about Linux device file and drivers, with my current understanding as the following:

  1. 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.

  2. 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?


Solution

  • 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.