Lectures: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13.

Devices

This is the second of three self study modules that will look at devices and I/O. The goal of this module is to discuss the typical device driver architecture and to take a look at the interface common devices export.

At the end of this module, you should be able to:

  • explain the concept of delayed service for solving synchronization issues inside interrupt handlers,
  • implement a device driver with an interrupt driven communication and a software request queue,
  • propose a reasonable hardware-to-software interface for controlling a given device type,
  • propose a reasonable device-driver interface for controlling a given device type,
  • for common device types including keyboard, storage, and basic graphics cards.

This module is timed together with culminating work on the scheduler related assignments, it is therefore shorter in content and gives more space for coding. Do look at the self test questions, and, if you are still bored, there is also the remaining self study module on file systems.

Device Driver Architecture

In Arpaci-Dusseau Section 36 I/O Devices, recommended for the last self study module, you could see the code of a simple IDE (disk) driver with a single public facing function, ide_rw, which puts a disk read or write request into a queue that the driver maintains, and, if the disk is idle, tells the disk controler to go to work. The driver also has a single interrupt handling function, ide_intr, which is invoked when the disk controller finishes work.

[Q1] The public facing function and the interrupt handler both access a shared queue, which is protected by a lock. Think about the kernel implementation you are working on in your assignments. What would happen if a thread were holding a lock (such as is done in the ide_rw function above) and an interrupt handler attempted to acquire it (such as is done in the ide_intr function above) ?

Hint ...

The exact answer depends on the implementation details of your locks. But regardless of whether a lock attempt will pass or wait, is it good ?

Device Interfaces

In Arpaci-Dusseau Section 36 I/O Devices, recommended for the last self study module, you could see an example protocol for communicating with an IDE (disk) controller. The protocol went roughly as follows:

  • The controller exposes multiple registers

    • Sector number and count
    • Command (for writing)
    • Status (for reading)
    • Data
  • The main device functions are sector read and sector write

  • To perform a sector read the driver has to

    1. Wait until the status register says the controller is ready
    2. Write the sector number and count to the controller registers
    3. Write the sector read command code to the command register
    4. Wait until the status register says the command is done
    5. Read the sector data from the data register
  • Writing is performed analogously

[Q2] Pick some device type (other than disk) and, at roughly the same level of detail as above, describe how you think such device is controlled by the operating system - in particular, list the main device functions and how the controller would be told to perform them. There is no need to consider advanced functions, or describe existing hardware, just describe how you think your chosen device type could work.

Hint ...

Basic functions are usually straightforward - a keyboard will need to report key presses, a sound card will need to load or store sound samples, and so on. The nature of the interaction determines the interface - interrupts for notifications, direct memory access for large transfers. Additional techniques such as offloading will be used to cover particularly demanding cases.

[Q3] Do the same exercise for the interface between the driver and the kernel, that is, describe what functions you think the driver exports for the kernel to call.

Hint ...

The interface between the driver and the kernel usually strives to hide details of that particular device functionality - instead, it attempts to generalize for the same class of devices, so that the kernel can handle all devices of that class in the same way.