Device Driver Implementation
description
Transcript of Device Driver Implementation
Digital UNIX Internals II Device Driver Implementation
Device Driver Implementation
Chapter 14
Digital UNIX Internals II Device Driver Implementation
Topics
• Interfaces• Autoconfiguration• Structures• Device Identification• Building and Loading Drivers
Digital UNIX Internals II Device Driver Implementation
When a Device Driver is Called (1)
• I/O Operation– kernel calls device driver on behalf of a user level
application to open, read, write, or close
• Interrupt Handling– kernel calls a device driver’s interrupt handler when a
hardware interrupt occurs
• Special Requests– special requests via ioctl calls
Digital UNIX Internals II Device Driver Implementation
When a Device Driver is Called (2)• Autoconfiguration
– kernel calls the driver’s probe, attach, and slave interfaces at system startup to determine what devices are available and to initialize them
• User-level requests to the sysconfig utility– kernel calls a device driver’s configure interface – to dynamically configure, unconfigure, query and reconfigure
a device– implicit during system boot or driver load
• Reinitialization– called by kernel when associated bus (path from CPU) is
reset
Digital UNIX Internals II Device Driver Implementation
When a Device Driver is Called (3)
drv_open()
drv_close()
....
drv_isr()
drv_probe()
drv_slave()
....
drv_configure()
bdevsw[]
cdevsw[]
_scb
System Calls
Interrupts
bus_list[]
controller_list[]
struct driver
AutoConfiguration
device_list[]
/etc/sysconfigtabkload server
Dynamic Loading
/var/subsys/driver.mod
/subsys/driver.mth
Digital UNIX Internals II Device Driver Implementation
Device Driver Design Issues
• There are many design issues that must be considered prior to writing a driver.
• Ideally, the driver will be able to run without recompilation in as many different environments as possible.
• Issues include:– System Configuration
• static vs. dynamic configuration• autoconfiguration
– block vs. character interfaces (or both)– Support for various Alpha buses
Digital UNIX Internals II Device Driver Implementation
Static vs. Dynamic Configuration
• It is important to write one device driver that a system manager can statically or dynamically configure into the kernel – static - kernel is rebuilt (doconfig)– dynamic - driver is loaded and controlled via sysconfig
• tools are provided to produce a single binary module (.mod)
Digital UNIX Internals II Device Driver Implementation
Autoconfiguration
• A process that determines what hardware actually exists during the current instance of the running kernel at static configuration time.
• The autoconfiguration software (the bus’s confl1 interface) calls the driver’s probe, attach, and slave interfaces to determine if the devices exist and are functional on a given system
• At boot time, system software: – Configures static drivers into a configuration tree – Configures loadable drivers into a configuration tree based
on /etc/sysconfigtab.
Digital UNIX Internals II Device Driver Implementation
Device Autoconfiguration
System Configuration File
bus tc0 at nexus
controller fb0 at tc0 vector fbintr
controller ipi0 at tc0 vector ipiintr
device disk ip1 at ipi0 unit 1
device disk ip2 at ipi0 unit 2
bus vba0 at tc0 slot 2 vector vbaerrors
controller sk0 at vba0 csr 0x8000 vector skintr 0xc8
bus vba1 at tc0 slot 1
controller cb0 at vba1 csr 0x80001000 vector cbintr 0x45
Digital UNIX Internals II Device Driver Implementation
Device Autoconfiguration (2)sysconfigtab driver fragment
none:
Module_Config_Name = none
Device_Dir = /dev
Device_Char_Major = ANY
Device_Char_Minor = 0
Device_Char_Files = none
Device_User = root
Device_Group = 0
Device_Mode = 666
Device_Major_Req = Same
TC_Option = Modname - 'NONE ', Driver_Name - none, Type - C,
Adpt_Config - N
Digital UNIX Internals II Device Driver Implementation
The driver structure (1)
struct driver {
int (*probe)(); /* called during autoconfig for each controller to */
/* register interrupt routine and device switch info */
int (*slave)(); /* optionally called for each device */
int (*cattach)(); /* optionally called for each controller to initialize */
int (*dattach)(); /* optionally called for each slave() returning success */
int (*go)(); /* not currently used */
caddr_t *addr_list; /* list of optional CSR addresses */
char *dev_name /* name of device connected to this controller */
struct device **dev_list; /* indexed by logunit, list of device structs currently connected */
char *ctlr_name; /* name of controller */
struct controller **ctlr_list; /* supports multiple controllers for single driver */
short xclu; /* not currently used */
Digital UNIX Internals II Device Driver Implementation
The driver structure (2)
int addr1_size; /* VMEbus only, size in bytes of first CSR area */
int addr1_atype; /* VMEbus only, addr space, transfer size, swap mode */
int addr2_size; /* VMEbus only, size in bytes of second CSR area */
int addr2_atype; /* VMEbus only, addr space, transfer size, swap mode */
int (*ctlr_unattach)(); /* dynamically configured drivers only, undo attach() allocations */
int (*dev_unattach)(); /* dynamically configured drivers only, undo dattach() allocations */
};
Digital UNIX Internals II Device Driver Implementation
Block vs. Character Devices
• Historically, there are two sets of interfaces to the kernel, one for character devices and another for block devices.
• Drivers defined their interface to the kernel which would load this information into either the character device (cdevsw) or block device (bdevsw) switch table.
• To reduce these differences, in V4.0 the dsent structure has been introduced. Thus the differences are less formalized and a driver, indeed, can support both.
• The kernel populates both character and block device switch tables with information from the dsent structure.
• The device major number is the index into these tables.• Both a character and a block device file may specify the same
entry (i.e. major number).
Digital UNIX Internals II Device Driver Implementation
The bdevsw Structure (1)
Entry Point Function(*d_open)() Opens the device and fills in structures
Verifies device that was identified during autoconfiguration
Locks nonshared devices
Sets up driver to buffer cache
Logs any errors
(*d_close)() Closes the device
Disks - null routine
(*d_strategy)() Takes address of buf structure as argument; Initiates read/write and returns immediately; I/O requests translated by system into block I/O routines
bread()/bwrite() calls device strategy with pointer to buffer containing parameters for I/O request
If synchronous request, caller sleeps on address of buf structure
Digital UNIX Internals II Device Driver Implementation
The bdevsw Structure (2)
Entry Point Function(*d_dump)() Writes all physical memory to the device
(*d_psize)() Returns the size of disk partitions
dflags Identifies device status
(*d_ioctl)() Gets or sets device control status or performs special operations
d_funnel Enables or disables on a per-file system basis
Enabled means uniprocessor driver support
Disabled means multiprocessor/parallelized driver support
Digital UNIX Internals II Device Driver Implementation
The cdevsw Structure (1)
Entry Point Function(*d_open)() Performs a function very similar to bdevsw
d_open()
(*d_close)() Closes the device
Disks - null routine
Tape - writes EOF marks to mark EOT and rewinds
(*d_read)() Reads data from a device; raw devices, calls physio() Terminals - reads request passed to driver
(*d_write)() Write data to device; parallels read() above
(*d_ioctl)() Performs an operation other than read/write such as get/set device parameters
(*d_stop)() Stops output on a device; only meaningful to terminal driver
(*d_reset)() Specifies a pointer to an entry point for the driver's reset interface, which stops all current work and places the device connected to the controller in a known state
*d_ttys Points to a table of tty structures, if a tty driver
Digital UNIX Internals II Device Driver Implementation
The cdevsw Structure (2)
Entry Point Function(*d_select)() Checks device to see if data is available for
reading/writing
(*d_mmap)() Maps device contents into memory; TURBOchannel only
d_funnel Enables or disables on a per-file system basis
Enables means uniprocessor driver support
Disabled means multiprocessor/parallelized driver support
(*d_segmap)() Specifies the segmap entry point
d_flags Set to C_DDIDKI if driver is SVR4 DDI/DKI-complaint
Digital UNIX Internals II Device Driver Implementation
The dsent Structure (1)
• Instead of separate block and character structures and pointers
• Drivers fill in the dsent structure and registers it during its probe() operation by calling devsw_add()
• dsent structure elements are the union of the elements in both character and block device switch tables.
Digital UNIX Internals II Device Driver Implementation
The dsent Structure (2)
struct dsent
{
int (*d_open)();
int (*d_close)();
int (*d_strategy)();
int (*d_read)();
int (*d_write)();
int (*d_ioctl)();
int (*d_dump)();
int (*d_psize)();
...
...
int (*d_stop)();
int (*d_reset)();
int (*d_select)();
int (*d_mmap)();
int (*d_segmap)();
struct tty *d_ttys;
int d_funnel;
int d_bflags;
int d_cflags;
};
Digital UNIX Internals II Device Driver Implementation
Supporting Multiple Buses
• To provide device driver binary compatibility across different bus architectures, different CPU architectures, and different CPU types within the same architecture,
• The kernel represents I/O bus address spaces through an I/O handle (io_handle_t).
• Device drivers use this handle to reference bus address space (which may be I/O space or Memory space, sparse or dense).
Digital UNIX Internals II Device Driver Implementation
Device Identification
• System administrators know devices as– Major number
• Identifies type of device– Disk, tape, terminal
– Minor number• Identifies instance of device
– Disk 5, Tape 2, TTY 3• Or identifies use of device
– Tape 2 in high density
• Kernel knows devices as a 32-bit dev_t– ( major_number >> 20 ) + minor_number
Digital UNIX Internals II Device Driver Implementation
Associating cluster and local devices
• Cluster device hash +----------+ hw_hash_entry
|----0-----|---> __________
|----------| | type |
|----------| |----------|
|----------| | count | hwc_devt_element (HWC_DEVT_ELE)
. |----------| ------------- -------------
. | listptr |----->| next |---->| next |-->...
. |----------| |-------------| |-------------|
|----------| | hashptr | | devt | | devt |
|---255----| ---------- |-------------| |-------------|
+----------+ +------------|l_devnodeinfo| |l_devnodeinfo|
| |-------------| |-------------|
| |c_devnodeinfo|-+ |c_devnodeinfo|
| ------------- | -------------
v devnode v devnode
------------- -------------
| HWC_DEVNODE | | HWC_DEVNODE |
|=============| |=============|
| hwc_devnode | | hwc_devnode |
| info | | info |
------------- -------------
(entry in local dbase) (entry in cluster dbase)
HWC_DEVNODE_INFO
Hash function: ((devt>>24) + (devt>>16) + (devt>>8) + devt) % 255
Digital UNIX Internals II Device Driver Implementation
Building a Driver
Digital UNIX Internals II Device Driver Implementation
Files Related to Static and Dynamic Configuration
NAME.list file - static drivers, define where the files are for this driver product (third parties)
BINARY.list file - identify the build rules associated with third-party device drivers built into the BINARY Makefile when the driver writer invokes the sourceconfig utility
files file fragment - for static drivers delivered in binary form, used by the config program
sysconfigtab file fragment - associated with drivers written to conform to the single binary module model so the driver can be statically or dynamically configured into the kernel. Specify the device major number requirements (if any), names and minor numbers of device special files.
Digital UNIX Internals II Device Driver Implementation
/dev/none Configuration Filessysconfigtab fragment
none:
Module_Config_Name = none
Device_Dir = /dev
Device_Char_Major = ANY
Device_Char_Minor = 0
Device_Char_Files = none
Device_User = root
Device_Group = 0
Device_Mode = 666
Device_Major_Req = Same
TC_Option = Modname - 'NONE ', Driver_Name - none, Type - C,
Adpt_Config - N
Digital UNIX Internals II Device Driver Implementation
/dev/none Configuration Filesfiles fragment
# This is the files file fragment for the /dev/none driver
# used to produce the single binary module.
#
MODULE/STATIC/none standard Binary
io/ESA100/none.c module none
Digital UNIX Internals II Device Driver Implementation
/dev/none Configuration FilesBINARY.list
/usr/sys/io/ESA100:
Digital UNIX Internals II Device Driver Implementation
System Management Tools (1)
doconfig creates or modifies an existing target configuration file, copies .product.list to NAME.list, creates
the device special file information for statically configured drivers, and builds a new
kernel (via calling the config program).
cfgmgr works with kloadsrv, the kernel load server, to manage device drivers that are
dynamically configured into the kernel.
device.mth works with driver products to produce the method file (.mth).
kmknod uses the information from the driver’s sysconfigtab file fragment and the driver’s attribute table to
dynamically create device special files at boot time for statically configured drivers.
Digital UNIX Internals II Device Driver Implementation
System Management Tools (2)
kreg maintains the /sys/conf/.product.list system file which registers device driver products that are statically configured into the kernel.
sysconfig configures a driver or handles a dynamically configured device driver. Provides a user interface to the cfgmgr framework.
osfboot boots a kernel. Contains support for boot link kernels that the doconfig -b command generates. Also supports dynamic booting and foreign devices.
sysconfigdb maintains the sysconfigtab database. The driver entries in the sysconfigtab file fragment are
appended to this database when the user installs a kernel kit.
Digital UNIX Internals II Device Driver Implementation
Building the Driver (1)
• Step 1: Create a directory to contain the driver product files #mkdir /usr/sys/io/ESA100
• Step 2: Copy driver product files into the above directory#cp none.c /usr/sys/io/ESA100
#cp nonereg.h /usr/sys/io/ESA100• Step 3: Create a none files file fragment to include in the
/usr/sys/conf/files file.• Step 4: Create none entry for the /sys/config/BINARY.list
file and add.• Step 5: Run the sourceconfig program from /usr/sys/conf
# cd /usr/sys/conf# ./sourceconfig BINARY
Digital UNIX Internals II Device Driver Implementation
Building the Driver (2)
• Step 6: Run the make program# cd /usr/sys/BINARY
# make none.mod
• Step 7: Create a sysconfigtab file fragment in /usr/sys/io/ESA100.
Run the sysconfigdb utility
# cd /usr/sys/io/ESA100# sysconfigdb -a -f
sysconfigtab none
Digital UNIX Internals II Device Driver Implementation
Statically adding the none driver to a kernel
• Step 1: Ensure that the none entries are in the /usr/sys/conf/NAME.list, and /usr/sys/conf/files
files• Step 2: Run the doconfig program
# cd /usr/sys/conf# doconfig -c NAME
• Step 3: copy the new kernel to the root directory# cp vmunix /vmunix
• Step 4: Shutdown and boot the system# shutdown -b now
Digital UNIX Internals II Device Driver Implementation
Dynamically loading the none driver
• Step 1: Ensure that the none sysconfigtab entry was added to the /etc/sysconfig file
• Step 2: Link to the single binary module# cd /var/subsys
# ln -s /usr/sys/BINARY/none.mod none.mod
• Step 3: Link to the method file# cd /var/subsys
# ln -s /subsys/device.mth none.mth• Step 4: Run the sysconfig utility
# sysconfig -c none