When you try to open() a device special file it will cause an open() function in the associated device driver to be called. The way that this works is that each device driver is just a collection of functions that you write to perform a set of specific tasks. Associated with each device driver is a data structure called struct file_operations, which contains a set of pointers to most of the main functions in your device driver.
When the system boots, an initialization function in each device driver is called by the kernel. One of the tasks performed by this function is to tell the kernel which major device number this device driver will use. At the same time, the initialization function will pass to the kernel a pointer to the structure of function addresses contained within the driver.
The file_operations structure contains many elements, only some of which will be required for most drivers. The full structure appears as follows, showing a collection of pointers to functions:
struct file_operations { int (*lseek)(); int (*read)(); int (*write)(); int (*readdir)(); int (*select)(); int (*ioctl)(); int (*mmap)(); int (*open)(); void (*release)(); int (*fsync)(); int (*fasync)(); int (*check_media_change)(); int (*revalidate)(); }
Here, there are obvious entry points for open(), read() and write(), though some others are less obvious. I'll describe the entry points in more detail when we need them.
Inside the kernel there are two tables, one for character device drivers and one for block device drivers. The tables are used to hold pointers to file_operations structures with the device driver function addresses stored in them. The major device numbers are used as an index to the appropriate table to access the file_operations structure associated with that major device number, and thus allow the kernel access to the addresses of the routines within the driver.