Você está na página 1de 6

Device Drivers

Device drivers are the primary components of the


I/O subsystem.
Each device driver:

The Tempo Operating System

is responsible for managing input/output on a single


type of device at the lowest level by managing the
devices controller
is not (normally) called from user mode (usually cant
be called from user mode)
frequently presents a normalized interface to the
kernel (this depends on the particular OS design)

Part 8
System Call Implementation
Low-Level Input/Output Subsystem
1

Device Driver Basics

Devices and Device Controllers

A device driver usually has two parts: the top and the
bottom.
The top part is trigger by a call from the OS, and
manipulates data structures, returns results, and when
necessary triggers the bottom part (priming the pump).
The top part may block.
The bottom part of the device driver is usually interrupt
driven (and includes the interrupt handler). It handles
direct low-level interaction with the device and awakens the
top part when a request has been completed. The bottom
part will never block.

The distinction between devices and their


controllers is sometimes blurred
A serial port is usually a single chip (e.g. an 8250 or
16550)
A modern (IDE or SCSI) disk has a built-in controller
A display has a monitor and a video adapter that
usually has many components

A common practice is to allow one controller to


support multiple devices. For example, multiple old
ATA style disks were connected to one controller.

Controllers and Devices


CPU

Memory

Video Adapter

Buses: ISA, PCI, USB, AGP


Monitor

Although simple diagrams (as in the previous slide) show


only one bus with everything connected to it, modern
systems have many interface buses. For example:

System Bus

PPI: Programmable
Peripheral Interface
Controller

Controller

IDE Disk

IDE Disk

Serial Port

Keyboard (with
8042 chip)

Serial Device
(e.g. a modem)
The controller in this
drive is inactive.
5

ISA (Industry Standard Architecture): found in older PCs


PCI (Peripheral Component Interconnection): modern replacement
for ISA
PCI Express: an even more modern version of PCI!
USB (Universal Serial Bus): frequently used to connect external
devices
AGP (Accelerated Graphics Port): based on enhancement of PCI for
video adapters

Bridges

Host Adapters

How are all these buses interconnected in a computer


system?
Answer: with bridges!
A bridge is essentially a device (chip) that maps signals
between bus formats (synchronization, data, control, etc.)
A common PC architecture has a north bridge between
the host bus and the PCI, and DRAM (memory) buses; the
south bridge connects the PCI bus to the ISA, USB, IDE,
and AGP buses.
But many different organizations are possible!

So whats a host adapter (e.g. as used with IDE drives)?


A host adapter (more completely called a host bus adapter
is the hardware that connects a bus from one or more
devices to a bus in the computer system.
For example, the cables from IDE drives are a bus which is
different from a PCI bus. The host adapter is a bridge-like
device that connects the IDE drive bus to the PCI bus.
There are even multiple kinds of IDE busses: parallel and
serial, requiring different kinds of host adapters.
SCSI is also another common disk drive bus type, and the
SCSI adapter/host adapter/host bus adapter is used to
connect SCSI drives (or more properly, a SCSI bus) to a
host bus (like the PCI bus).

Device Classes

Normalized Driver Interface (1)

Devices are commonly separated into two classes


for ease of organization:
Block devices: those which have addressable blocks of
data (e.g. disks, CDs, DVDs); device drivers must be
given a block number, a read/write command, and a
buffer in each request
Character devices: everything else; device drivers
transfer only one or a few bytes at a time, but must still
have a read/write command and know where to get/put
the data.

In most UNIX implementations each device in each


class has two numbers:
major: identifies the controller for all devices of the
same type (i.e. all IDE disks will have the same major
device number)
minor: identifies an instance of the device (i.e. each
individual IDE disk will have a unique minor device
number)

Two arrays of structures (bdevsw and cdevsw) are


indexed by the major device number to locate
entry points to device drivers.

Normalized Driver Interface (2)


Block device driver entry points:

10

Tempos Device Drivers


Tempo doesnt (yet) use a normalized device
driver format.
Each device driver is represented by a system call
for the most common actions.
Device-specific Tempo system calls:

d_open open a device


d_close close a device
d_strategy read or write a block
d_ioctl I/O actions other than read or write

Character device driver entry points

rdc read next keyboard character


conout write character to the display
diskio read/write a disk block and wait for completion
asynchio asynchronous disk I/O request (queue the
requests and continue executing)

d_open open a device


d_close close a device
d_read, d_write read or write a block
d_ioctl I/O actions other than read or write
11

12

The keyboard device driver

rdc: pseudocode

The top part of the keyboard device driver is in the


rdc system call; the bottom half is the interrupt
handler.
rdcs basic data structures include:
kbdata: array (one for each console) of circular queues;
each queue has at most NKEYS entries, each entry
containing one character code. frstkey is index of first
unused character code.
keysem: array of semaphores (one for each console) with
count equal to number of entries in consoles kbdata
array.

Do a down on the consoles semaphore


keysem[con] with the specified timeout.
Return TIMEOUT if the down operation timed out.
Copy kbdata[con][frstkey[con]] to result.
Increment frstkey[con] modulo NKEYS.
Return result.

13

Keyboard Interrupt Handling (1)

14

Keyboard Interrupt Handling (2)

Each key press or release generates an interrupt


(0x21, IRQ 1)
When a keyboard interrupt is handled:
The PIC is reenabled to allow additional interrupts
The keyboard status register is read to clear interrupt
request
The keyboard data register is read and used as
argument to kybdisr (the second-level interrupt handler)
When kybdisr returns, continue at isr30i (normal system
call return activity)

In kybdisr, the kbtoa function is called with the key code as


an argument. It returns NOCHAR if no character code
should be returned.
If the code indicates a virtual console switch should
happen, do it (by calling setvidpg) and return.
If the keyboard buffer is full, the character is ignored.
Add the character code to the circular queue.
If no processes are awaiting input from the console,
increment the keysem semaphores count.
Otherwise, awaken the first waiting process (remove from
the delta queue as appropriate); if its priority is higher than
that of the running process, arrange for a context switch.

15

Text-Mode Video Display (1)

16

Text-Mode Video Display (2)

Almost every display adapter in every PC ever


made supports text in the same way.
The generic chipset used has only a few significant
features:
Video RAM (usually at physical address 0xb8000 in color
display adapters) is treated as 16-bit values

High-order 8-bits control foreground/background color, blinking,


underling, etc.
Low-order 8-bits contain a character code
Standard 25 row, 80 column screen needs 4000 bytes

A pair of 8-bit wide adapter registers (numbered 12 and


13) contain the offset of the 16-bit item in video RAM
that is to be displayed in the upper-left corner of the
display. Following bytes used to fill remainder of display.
Another pair of registers (14 and 15) contain the offset
in video RAM of the position where the visible cursor is
located.

Most adapters have more than 4000 bytes available, so


multiple pages can be maintained, but only one can be
displayed on the monitor at a time.
17

18

int _setvidpg(void *pg)


{
unsigned int page = (unsigned int)pg;
unsigned int offset;

setvidpg: select a video page


Check requested page number; return error if
incorrect
Save current pages cursor location; restore
selected pages cursor location
Set adapter registers to begin displaying selected
page
Adjust position of the cursor

if (page >= NVPG) return NOSUCHVIDPG; /* parameter too large */


if (page == _vpage) return NOERROR; /* no change */
_vidrow[_vpage] = _vrow;
/* save current cursor position */
_vidcol[_vpage] = _vcol;
_vpage = page;
/* set new page number */
_vrow = _vidrow[_vpage];
/* restore old cursor position */
_vcol = _vidcol[_vpage];
offset = page * 80 * 25;
/* offset to new page */
outb(M6845_INDEX,12);
/* select high-order pg offset port */
outb(M6845_DATA,(offset>>8)&0xff); /* write high-order bits */
outb(M6845_INDEX,13);
/* select low-order pg offset port */
outb(M6845_DATA,offset & 0xff); /* write low-order bits */
_vbase = (unsigned char *)0xb8000 + 2 * offset;
_vsetcur();
/* set visible cursor position */
return NOERROR;
}

19

vsectur: set cursor position


Compute the offset of the 16-bit item in video RAM
to which the cursor should be positioned (using the
saved logical row/column information)
Set adapter registers to update the cursor location.

20

void _vsetcur(void)
{
unsigned int offset;

/* set visible cursor */


/* offset to cursor position */

if (_vcol >= 80) _vcol = 79;


/* guarantee valid column number */
if (_vrow >= 25) _vrow = 24;
/* guarantee valid row number */
offset = 2000 * _vpage
/* compute offset */
+ _vrow * 80 + _vcol;
outb(M6845_INDEX,14);
/* select high-order cursor port */
outb(M6845_DATA,(offset>>8)&0xff); /* write high-order bits */
outb(M6845_INDEX,15);
/* select low-order cursor port */
outb(M6845_DATA,offset & 0xff); /* write low-order bits */
}

21

conout: write to the display

22

Questions

One parameter (in current version): the character to be


displayed
If not a control character, determine correct location in
video RAM (2 (vrow 80 + vcol)), store the character,
and update the column number
Otherwise do control character function:
backspace: decrement column number
tab: adjust column number to next multiple of 8
line feed: move to next row; scroll if on row 25
form feed: clear screen; move cursor to row/col 0
carriage return: set column number to 0

Finally, update the visible cursor position and return


23

How is the display scrolled when a new line is


written on the last line of the page?
How is the display cleared?

The display could be scrolled by adjusting the


adapter registers that select where the first
displayed byte is located. Tempo just copies
lines 2-25 back to lines 1-24 and then blanks line
25 (in video RAM).
Copy blanks to all position in video RAM.
24

diskio: read/write a disk block

asynchio: asynchronous I/O request

Three arguments:

rw: 0 = read, 1 = write


blockno: 0, 1, = which disk block (LBN) to process
buffer: memory address of data

Purpose:

allocate and fill a request structure to store the disk


I/O request, mapping LBN to cyl/head/sector
add the request to the end of the request queue (at
IOhead)
if disk is idle, start the processing for the request
block (by calling _block) to await the I/O completion

This function is used to request an asynchronous


I/O operation (that is, one that does not
necessarily block the caller). These are used in
reading/writing blocks in buffers belonging to the
filesystem buffer cache.
Operation similar to diskio, but
requesting process info (in IOreq) is 0
address of buffer header in cache is provided

25

IOreq and bufhd Structures


struct IOreq {
int op;
int xc, xh, xs;
char *buffer;
struct bufhd *bh;
Process proc;

/*
/*
/*
/*
/*
/*
struct IOreq *next; /*

26

Disk Controller Basics

read=DSK_READ, write=DSK_WRITE */
disk block address (CHS) */
buffer address */
buffer header addr for asynch I/O */
request process (DISK_BLOCKED) */
or zero, for asynchronous I/O */
ptr to next request to process */

};
struct bufhd {
unsigned int blockno;
/* which block is represented? */
unsigned int status;
/* see sysparm.h */
char data[512];
/* the data */
struct bufhd *prev, *next; /* free list pointers */
};

Common interfaces:
ATA/IDE: frequently used in PCs
SCSI: more common in enterprise class machines

Basic operations are read/write (with variations)


OS uses logical block numbers, while disk uses
physical cylinder, track, sector numbers; driver
performs the mapping

27

ATA/IDE Interface

28

Controller Registers

A set of registers at various locations in the I/O


address space are used to specify information
about each I/O request.
When the command register is written, the
controller begins performing the requested
operation.
Data transfers (between memory and disk) take
place when the data request bit is set in the
status register.

For writes, this is shortly after the command is sent.


For reads, we wait for an interrupt to announce the data
availability.
29

0x1f0
0x1f1
0x1f2
0x1f3
0x1f4
0x1f6
0x1f7

data bytes read/written here


error register (yields error codes)
how many sectors to read/write
sector number
/ 0x1f5 cylinder number
drive/head number
command (write) or status (read)

30

static void startdiskcmd(void)


{
if (waitnotbusy()) code dealing with a controller timeout
outb(DSK_BASE + DSK_HEAD,0xa0 + IOhead->xh);
if (waitnotbusy()) more code dealing with a controller timeout
outb(DSK_BASE + DSK_CTL, 0x80); /* disable retries, enable interrupts */
outb(DSK_BASE + DSK_PRECOMP, 0);
outb(DSK_BASE + DSK_COUNT, 1);
outb(DSK_BASE + DSK_SECTOR, IOhead->xs);
outb(DSK_BASE + DSK_CYLLO, IOhead->xc & 0xff);
outb(DSK_BASE + DSK_CYLHI, IOhead->xc >> 8);
outb(DSK_BASE + DSK_CMD, IOhead->op);

First-level Disk Interrupt (IRQ14)


As usual, save user registers, establish kernel
segment registers
Read the controller status register to clear the
interrupt condition
Reenable priority interrupt controllers
Call the second level disk interrupt handler (which
sets _rs as appropriate)
Return from the interrupt

if (IOhead->op == DSK_WRITE) {
for(;;) {
/* for real drives, we'll need a timeout */
iostat = inb(DSK_BASE + DSK_STATUS) & DSK_DRQ;
if (iostat == DSK_DRQ) break;
}
_port_write(DSK_BASE + DSK_DATA, IOhead->buffer, 512);
}
}
31

32

Reasons for Blocking

Second-level Disk Interrupt Handler


If last request (still at top of request queue) was
read, copy data from the data register to the users
buffer
If I/O was synchronous (e.g. initiated by a user
process), make the process ready, and set _rs to 1
Otherwise (asynch request) set buffer status
appropriately; if read, wakeup all waiting
processes.
Remove request (struct IOreq) from the queue and
return it to the pool
If the I/O request queue is not empty, start
processing for the next request by calling
startdiskcmd.

The traditional reasons a process might block are


directly related to an action; examples:
down on a semaphore
sleep for a number of clock ticks
wait for a signal

But more complex requests (e.g. filesystem


operations) might cause a process to block
multiple times for a variety of reasons; examples:
wait for exclusive use of a disk block cache entry
wait for a read on a directory block to complete

33

34

Other Disk Driver Actions

Handling Arbitrary Block/Wakeup


A process (in kernel code) waits by:

setting eventaddr in the proctab entry to an address


(usually associated with the reason for waiting)
settin the process state to EVENT_WAIT
calling _block()

All processes waiting on address a are awakened


by calling wakeup with a as the parameter:
scan the process table for processes with a in the
eventaddr field
move these processes to the ready state
do the usual context switch test based on priority

35

Tempos low-level disk device driver is primitive, by


design. Real drivers add things such as:
support for multiple drives with different characteristics
(e.g. different sizes)
better I/O scheduling algorithms than FCFS (e.g. SSTF,
SCAN, LOOK, CSCAN)
handling of timeouts, I/O errors, retries, etc.
partitioning of large volumes into logical drives

36

Você também pode gostar