Device Drivers PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 28
At a glance
Powered by AI
The key takeaways are that device drivers act as an interface between applications and hardware, and that drivers should be policy-free and implement a clear separation of concerns between mechanism and policy.

The different types of drivers in Linux kernel are char drivers, block drivers, and network drivers, which are differentiated based on their I/O characteristics.

The different ways for client-driver communication include a sequence of commands and responses, either blocking or non-blocking interfaces, and callbacks where the driver can wake up registered clients.

Device Drivers

Device drivers are interface between applications and hardware:

The interface:
simplifies the access
controls access from several clients
optimises the communication
ensures safety and error recovery
Policy vs. Mechanism
Mechanism: how to do it, possibly in a simple and robust manner. Lower
level task.
Policy: who can do it and to what extent, e.g. device access control and
exposure of capabilities. Implements restrictions on usage.
Drivers should be policy free: clear distinction of concerns. In some cases it
may not be the best way.
OS is responsible for policy decisions.
Clients of DDs
OS can be the main client to the device driver: driver interface is restricted
to common pattern followed by all such drivers.
OS provides another interface to clients to access the driver functionality.
Consider the network card driver and the socket interface.
Applications can be direct clients. Driver can provide a device specific
interface at a higher level of abstraction.
Firmware is a even lower level interface which resides on hardware itself.

control of device capability and its exposure


time-sensitive tasks
in-field changes to above two
typically implemented as state machines.
Client-Driver communication
A sequence of command and responses.
the command can be called directly or packaged in a generic interface
(command object).
These commands can block or provide non-blocking interfaces
Similar to interrupt servicing, NB interfaces are better: control is returned
immediately, commands can be pipelined (if drivers at different level
support it)
NB access requires either application polls the driver or the drivers can
wake the clients.
In latter case (called call-back), clients has to be regisetered to the driver.
Command objects are needed.
Client-Driver communications
The timeline for the communications:
Linux Kernel Overview
Divided into three types of drivers: char, block, network, depending on I/O
characteristics.
Char, Block, Network
Char devices: accessed as stream of bytes: to and fro movement in stream
are not easy to do.
should have open, close, read and write system calls at least.
minimal processing by OS
Block devices: accessed as a memory area
Network devices: used internally by the OS
communication is in term of packets, which are made in OS memory.
Major-Minor Numbers
Modules
Kernel includes only core drivers, rest loaded as needed: modules.
A simple module
Note the use of "printk"

}
Compiling
A "makefile" is used:

which generates a file hello-5.ko, which can be probed by "modprobe"


command ("lsmod", "insmod", "rmmod"). Also note module parameters can
be passed to insmod ("insmod hello-5 myshort=5"):
Working of a Char device module
Devices are listed in /dev
insmod calls the module initialisation function and rmmod calls the
module's exit function.
Major and minor numbers are used to create interfaces (major number
corresponding to the driver and minor number corresponding to the
client).
Structure file is used to represent an open file (i.e. attachment to a client):
mode information, blocking/non-blocking, directory entry, ..
Structure file_operations maps the file operations to functions inside the
device driver: read, write, ioctl, open, release..
Examples: Scull driver
Simple Character Utility for Loading Localities
Simply reads/writes to a memory area which it owns
Start with

Note the calls to _cdevinit, _cdevadd (and _cdevdel not shown here)
cdev structure is populated here.
scull_dev and scull_fops objects
scull_dev is allocated for each client, tracking its state

scull_fops lists the functions that scull driver provides to each client.
Implementing the functions
Function definition follow prototypes
Open function shown below initialises the devices and handle any errors
Release of the device
Deallocate all memory requested earlier
Shutdown device on last close: release is called only when all files are
closed.
Allocating memory
Scull memory is organised in small "pages" called quantum
Allocating memory
This releases all the memory associated with a device.

}
Read and Write
Prototypes

filp = file structure pointer


buff = buffer in user space where data should be read/written
count, offp = count of bytes and position in file.
Special way to copy to/from user space "buff", for safety checks
Read implementation
Return values specify what action was taken
<0 is error
=0 means end of file (EOF)
<= count means data transfer.

dptr = scull_follow(dev, item);


Write implementation is similar

if (!dptr->data) {
Vectored read and write
In many cases read and writes can be clubbed into batches using vector
version of read and write for improved efficiency.

Special data structure "iovec" is used for this:


Device control: IOCTL
user calls int ioctl (int fd, unsigned long cmd, ...);
number of arguments is not fixed
The prototype in driver is:
int (ioctl) (struct inode inode, struct file *filp, unsigned int cmd, unsigned
long arg);
This is a "packaged" command for implementing all kind of actions.
All IOCTL commands are unique in whole system and coded in following
manner:
Command is 32-bits: type(8), number(8), direction(2), size(14)
Field type is a driver-specific value.
Field number is the command selection.
Field direction is: _IOC_NONE, _IOC_READ, _IOC_WRITE, or _IOC_READ |
_IOC_WRITE (bi-directional)
Field size is the size of user data.
If command is invalid, the driver returns -ENOTTY
Waiting on events
Drivers can wait on many events when processing the requests
wait_event (queue, condition)
wait_event_interruptible (queue, condition)
wait_event_timeout (queue, condition, timeout)
wait_event_interruptible_timeout (queue, condition, timeout)
Typically wait_event_interruptible() is used
Then, another process or interrupt handler can wake up the driver:
void wake_up (wait_queue_head_t *queue);
void wake_up_interruptible (wait_queue_head_t *queue);
Accessing the hardware
Hardware registers are memory mapped and these memory address may
be treated somewhat differently than the normal memory access
No caching: disabled by Linux init code
No reordering by processor: memory barriers are used
a software barrier(): flushes all the GP registers in CPU to memory
a hardware barrier disables reordering by the processor.
Hardware barriers:
rmb() - Reads prior to rmb() will be completed before any subsequent
read is executed.
wmb() - Writes prior to wmb() will be completed before any subsequent
write is executed.
mb() - Same as above, for both reads and writes.
same functions can be used in all architectures (internally coded to use
processor specific features
Accessing IO registers
Always use request, access, release pattern to avoid conflicts
struct resource request_region (unsigned long first, unsigned long n,
const char name);
void release_region (unsigned long start, unsigned long n);
Access using:
unsigned inb (unsigned port)
void outb (unsigned char byte, unsigned port)
unsigned inw (unsigned port)
void outw (unsigned short word, unsigned port)
unsigned inl (unsigned port)
void outl (unsigned long longword, unsigned port)
64-bit access is not supported.
If the device needs memory, it is allocated/used/released using specific
kernel calls.
struct resource request_mem_region (unsigned long start, unsigned
long len, char name);
void release_mem_region (unsigned long start, unsigned long len);

You might also like