2.5 Device Drivers

   


UNIX has its own way of handling physical devices. They are hidden from the user and accessible only over the file system, without limiting their functionality. For example, an application programmer can use the simple file operations read() and write() to access the driver's hardware, while the ioctl() command can be used to configure properties of a device.

Device drivers in the form of modules can be added or removed in Linux at any time. This offers you a comfortable tool to develop and test new functionalities. Figure 2-7 shows an excerpt from the /dev directory, where all devices are listed. In Linux, network adapters are not treated as normal devices and so they are not listed in the /dev directory. Linux has a separate interface for them. The reasons are described in [RuCo01]. Chapter 5 will discuss network devices in detail.

Figure 2-7. Excerpt from the /dev directory.
 brw-rw----       1 root       disk   3,       0 May 12 19:23 hda brw-rw----       1 root       disk   3,       1 May 12 19:23 hda1 brw-rw----       1 root       disk   3,       2 May 12 19:23 hda2 brw-rw----       1 root       disk   3,      64 May 12 19:23 hdb brw-rw----       1 root       disk   3,      65 May 12 19:23 hdb1 crw-rw----       1 root       uucp   4,      64 May 12 19:23 ttyS0 crw-rw----       1 root       uucp   4,      65 May 12 19:23 ttyS1 crw-rw-r--       1 root       root  10,       1 Sep 13 08:45 psaux 

We can see in Figure 2-7 that the entries for device drivers differ from regular directory entries. Each entry includes two numbers used to identify the device and its driver.

  • The major number identifies the driver of a device. For example, Figure 2-7 shows that the PS/2 driver has major number 10 and the hard disk driver (hdxx) has major number 3.

    The major number can be specified when you register a device driver, but it has to be unique. For drivers you think you will use less often, it is recommended that you let the kernel assign a major number. This ensures that the numbers are all unique. See details in [RuCo01].

  • The minor number is used to distinguish different devices used by the same driver. In Linux, a device driver can control more than one device, if the driver is designed as a reentrant driver. The minor number is then used as an additional number to distinguish the devices that driver controls. For example, the hard disk driver with major number 3 in Figure 2-7 controls three hard disks, distinguished by the minor numbers 1, 2, and 65.

Figure 2-7 also shows that the type of each driver is specified at the beginning of each row. Linux differs between two types of physical devices:

  • Block-oriented devices allow you optional access (i.e., an arbitrary set of blocks can be read or written consecutively without paying attention to the order in which you access them). To increase performance, Linux uses a cache memory to access block devices. File system can be accommodated only in block devices (hard disks, CD-ROMs, etc.), because they are required for optional or random access. Block devices are marked with a b in the /dev directory.

    A block-oriented driver can be registered with the kernel function register_blkdev(). If the function was completed successfully, then the driver can be addressed by the returned major number. Release_blkdev() is used to release the device.

  • Character-oriented devices are normally accessed in sequential order. They can be accessed only outside of a cache. Most devices in a computer are character-oriented (e.g., printer and sound card). Character-oriented devices are marked with a c in the /dev directory. You can use register_chrdev() to register and release_chrdev() to release character-oriented devices.

The virtual file /proc/devices lists all devices currently known to the kernel. This file is used to find the major number of a driver in the user address space, in case none has been specified during the registration.

To be able to use a device that has not been registered yet, you need to first select a driver and generate an entry in the /dev directory. To create this entry, you use the command mknod /dev/name typ major minor, which is passed the name, the type (b or c), the major number of the driver, and the selected minor number for that device. If the command is successful, then you can now use the usual file operations (read(), write(), ioctl(), ...) to access that device.

Figure 2-8 shows how the data structure is passed when you register a character-oriented driver. A driver is addressed over a virtual file in UNIX, so these are all regular file operations. We will briefly describe here only the most important functions, to give you an overview:

  • owner refers to the module implemented by the driver (for a module-based driver);

    Figure 2-8. File operations on a device driver.
     struct file_operations {        struct module     *owner;        int               (*lssek) (file, offset, origin);        int               (*read) (file, buffer, count, pos);        int               (*write) (file, buffer, count, pos);        int               (*readdir) (file, dir);        int               (*poll) (file, poll_table);        int               (*ioctl) (inode, file, cmd, unsigned arg);        ...        int               (*open) (inode, file);        int               (*release) (inode, file);        ... } 

  • lseek() sets the position pointer in a file. This function can be used for other purposes for non-file-oriented devices;

  • read() transfers data from the driver to the user address space. The driver has to have previously confirmed that the desired buffer is available in the user address space and whether this memory page is currently outsourced. Subsequently, the function copy_to_user() can be used to copy data to the user address space.

  • As with to read(), write() is used to transfer data, but, in this case, from the user address space to the kernel address space (with copy_from_user()). Here again, before you can copy, you have to check the validity of the data range in the user address space. The memory range in the kernel does not have to be verified, because the kernel segment is never outsourced.

  • ioctl() offers the most extensive functionality. It is used to set certain parameters of a driver or device. A constant that represents the desired command[3] and a pointer to the data to be passed with this command are passed to the ioctl() command. This can be arbitrary data. The power of this function is such that the ioctl() command could actually replace all other file operations of a driver.

    [3] The commands and their constants are specified arbitrarily by the programmer, but they should be unique within the kernel. For this reason, it is recommended to use a hierarchical coding. (See [RuCo01].)

  • open() and close() are used to prepare (or postedit) a driver for subsequent (or completed) commands. This function must not be confused with similar functions used to configure a driver. Such tasks are normally executed by the ioctl() command. open() is called by a process to inform the driver that it wants to use the device. If a process can be made available only exclusively, then this is policed by the open() function. For this purpose, open() checks on whether another process has already opened that device and, if so, denies access to it.

    close() releases the device. Whether exclusive use is desired depends on the type of device.

When a device driver is accessed, certain things can happen at the interface of the device driver. For example, if you use the C library function fopen() to open a device file from the /dev directory, then the open() function of the driver is called in the kernel. If you use fprintf() to write data to the device file, then the write() function of the driver will run in the kernel. Not all operations of a driver have to be supported; only those that the driver really needs.


       


    Linux Network Architecture
    Linux Network Architecture
    ISBN: 131777203
    EAN: N/A
    Year: 2004
    Pages: 187

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net