File Operations

In the next few sections, we’ll look at the various operations a driver can perform on the devices it manages. The device is identified internally by a file structure, and the kernel uses the file_operations structure to access the driver’s functions. This design is the first evidence we’ve seen of the object-oriented design of the Linux kernel. We’ll see more evidence of object-oriented design later. The structure file_operations is a table of function pointers, defined in <linux/fs.h>. The structure struct file is going to be described next.

The fops pointer, which we’ve already seen as an argument to the register_chrdev call, points to a table of operations (open, read, and so on). Each entry in the table points to the function defined by the driver to handle the requested operation. The table can contain NULL pointers for the operations you don’t support. The exact behavior of the kernel when a NULL pointer is specified is different for each function, as the list in the next section shows.

The file_operations structure has been slowly getting bigger as new functionality is added to the kernel (although no new fields were added between 1.2.0 and 2.0.x). There should be no side effects from this increase, because the C compiler takes care of any size mismatch by zero-filling uninitialized fields in global or static struct variables. New items are added at the end of the structure,[10] so a NULL value inserted at compile time will select the default behavior (remember that the module needs to be recompiled in any case for each new kernel version it will be loaded into).

A few of the function prototypes associated to fops fields, actually changed slightly during 2.1 development. These differences are covered in Section 17.2, in Chapter 17.

Overview of the Different Operations

The following list introduces all the operations that an application can invoke on a device. These operations are often called ``methods,'' using object-oriented programming terminology to denote actions declared by an object to act on itself.

I’ve tried to keep the list brief so it can be used as a reference, merely summarizing each operation and the default kernel behavior when a NULL pointer is used. You can skip over this list on your first reading and return to it later.

The rest of the chapter, after describing another important data structure (the file), explains the role of the most important operations and offers hints, caveats, and real code examples. We’ll defer discussion of the more complex operations to a later chapter, because we aren’t yet ready to dig into memory management and asynchronous notification.

The operations appear in struct file_operations in this order, and their return value is 0 for success or a negative error code to signal an error, unless otherwise noted:

int (*lseek) (struct inode *, struct file *, off_t, int);

The lseek method is used to change the current read/write position in a file, and the new position is returned as a (positive) return value. Errors are signalled by a negative return value. If the function is not specified for the driver, a seek relative to end-of-file fails, while other seeks succeed by modifying the position counter in the file structure (described in Section 3.4). The prototype of this function changed in 2.1.0, as explained in Section 17.2.1 in Chapter 17.

int (*read) (struct inode *, struct file *, char *, int);

Used to retrieve data from the device. A null pointer in this position causes the read system call to fail with -EINVAL (``Invalid argument''). A non-negative return value represents the number of bytes successfully read.

int (*write) (struct inode *, struct file *, const char *, ,               int);

Sends data to the device. If missing, -EINVAL is returned to the program calling the write system call. Note that the const specifier was missing in 1.2 headers. If you include const in your own write method, a warning is generated when compiling against older Linux headers. If you don’t include const, a warning is generated for newer versions; you can safely ignore the warning in either case. The return value, if non-negative, represents the number of bytes successfully written.

int (*readdir) (struct inode *, struct file *, void *, ,                 filldir_t);

This field should be NULL for device nodes; it is used only for directories.

int (*select) (struct inode *, struct file *, int, ,                select_table *);

select is used by programs to ask if the device is readable or writable, or if an ``exception'' condition has happened. If the pointer is NULL, the device is assumed to be both readable and writable, with no exceptions pending. The meaning of ``exception'' is device-dependent. The implementation of select is completely different in the current 2.1 development kernels. (See Section 17.2.2, in Chapter 17.) The return value tells whether condition is met (1) or not (0).

int (*ioctl) (struct inode *, struct file *, unsigned int, ,               unsigned long);

The ioctl system call offers a way to issue device-specific commands (like formatting a track of a floppy disk, which is neither reading nor writing). Additionally, a few ioctl commands are recognized by the kernel without referring to the fops table. If the device doesn’t offer an ioctl entry point, the system call returns -EINVAL for any request that isn’t predefined. A non-negative return value is passed back to the calling program to indicate a successful completion.

int (*mmap) (struct inode *, struct file *, ,              struct vm_area_struct *);

mmap is used to request a mapping of device memory to a process’s memory. If the device doesn’t provide this method, the mmap system call returns -ENODEV.

int (*open) (struct inode *, struct file *);

Though this is always the first operation performed on the device node, the driver is not required to declare a corresponding method. If this entry is NULL, opening the device always succeeds, but your driver isn’t notified.

void (*release) (struct inode *, struct file *);

This operation is invoked when the node is closed. Like open, release can be missing. In 2.0 and earlier kernel versions, the close system call never fails; this changed in 2.1.31 (see Chapter 17).

int (*fsync) (struct inode *, struct file *);

Flush the device. If not supported, the fsync system call returns -EINVAL.

int (*fasync) (struct inode *, struct file *, int);

This operation is used to notify the device of a change in its FASYNC flag. Asynchronous notification is an advanced topic and will be described in Section 5.4 in Chapter 5. The field can be NULL if the driver won’t support asynchronous notification.

int (*check_media_change) (kdev_t dev);

check_media_change is only used with block devices, especially removable media like floppies. The method is called by the kernel to determine if the physical medium (e.g., the floppy disk) in the device has changed since the last operation (return value is 1) or not (0). This function doesn’t need to be declared for char devices.

int (*revalidate) (kdev_t dev);

This is the last entry, which is meaningful only for block drivers. revalidate is related to buffer cache management, as is the previous method. We’ll discuss revalidate in Section 12.6 in Chapter 12.

The file_operations structure used in the scull driver is the following:

struct file_operations scull_fops = {
    scull_lseek,
    scull_read,
    scull_write,
    NULL,          /* scull_readdir */
    NULL,          /* scull_select */
    scull_ioctl,
    NULL,          /* scull_mmap */
    scull_open,
    scull_release,
                   /* nothing more, fill with NULLs */
};

A few of the prototypes above have changed slightly in the latest kernel development. The list was extracted from a 2.0.x header, and the prototypes as shown are correct for a wide range of kernels. The differences introduced in the 2.1 kernel (and the fix needed to make our modules portable) are detailed in the section pertaining to each specific operation and in Section 17.2 in Chapter 17.



[10] For example, version 2.1.31 added a new field called lock.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.104.238