What is a device Driver?
- They are distinct "black boxes" that make a particular piece
of hardware respond to a well-defined internal programming
interface (taken from the onlin O'reilly device drivers book).
- Hide the detail of how a particular device works.
- User activities are performed by means of a set of
standardized calls that are independent of the specific driver.
- These calls are mapped to device-specific operations that act
on real hardware is then the role of the device driver.
- The drivers may be built separately from the rest of the kernel,
and "plugged in" at runtime when needed (as kernel modules).
- This modularity makes Linux drivers easy to write.
A good device driver should:
- be policy free.
- support synchronous and asynchronous operation.
- be able to be opened multiple times.
- exploit the full capabilities of the hardware.
- lack of software layers to "simplify things".
Classes of Devices and Modules
Character devices
- char driver can be accessed as a stream of bytes (like a file).
- implements at least the open, close, read, and write system calls.
- Examples: /dev/ttyS0, /dev/lp0, /dev/console.
Block devices
- block device is something that can host a filesystem, such as a disk
- it permits the transfer of any number of bytes at a time.
- block and char devices differ only in the way data is managed internally by the kernel,
- difference between char and block devices is transparent to the user.
- Examples: /dev/hda, /dev/sda, /dev/sg.
Network devices
- device that is able to exchange data with other hosts.
- usually hardware, but may be pure software, such as local loopback.
- in charge of sending and receiving data packets.
- communication between the kernel and a network device driver is completely different from that used with char and block drivers.
- instead of read and write, the kernel calls functions related to packet transmission
- Examples: /dev/eth0, /dev/wlan0.
Loading and removing device drivers
User space and Kernel space
- Modules run in kernel space
- Applications run in user space
- Most processors have different modes/user levels they will run in,
typical Pentium processors have 4. Only two are used in linux.
Module initialization and shutdown
- Kernel modules must have an init_module and cleanup_module routine.
- MOD_INC_USE_COUNT Increments the count for the current module
- MOD_DEC_USE_COUNT Decrements the count
Module parameter values
like options to a command line program
here's an example.
insmod my_module ival=92 sval="my driver"
To read these in the kernel code, the code would look like the following:
int ival=0;
char *sval;
MODULE_PARM (ival, "i");
MODULE_PARM (sval, "s");
The "i" indicates the value is an integer, the "s" indicates the value is
a string.
More appropriately for the lab:
int base_port = 0x300;
MODULE_PARM (base_port, "i");
MODULE_PARM_DESC (base_port, "The base I/O port (default 0x300)");
Major and Minor numbers
- can be seen using the ls -l command
- The major number identifies the driver associated with the device.
- The minor number is used only by the driver specified by the major number.
Here is an example from ls -l
crw-rw-rw- 1 root dialout 4, 64 Jun 30 11:19 ttyS0
crw-rw-rw- 1 root dialout 4, 65 Aug 16 00:00 ttyS1
The first letter in the permissions says that the device is
a char device. After the group and owner, we see the device uses
driver 4. ttyS0 hase minor 64 while ttyS1 has minor 65.
Adding a new driver to the system means assigning a major number to it.
The assignment should be made at driver (module) initialization
by calling the following function, defined in < linux/fs.h >:
int register_chrdev(unsigned int major, const char *name,
      struct file_operations *fops);
Once the driver has been registered in the kernel table,
its operations are associated with the given major number.
- In in filesystem, programs access the device through the /dev file system.
- You create an entry with mknod (specifying major and minor numbers).
For example:
mknod /dev/ibus0 c 40 1
You may use dynamic allocation of major numbers in your driver by using
0 as the major number of register_chrdev.
Use unregister_chrdev to unregister the driver.
File Operations
For next week's lab, your char device driver should have:
- an open method
- a release method
- a read method (to be written by you)
- a write method (to be written by you)
The open method should:
- Increment the usage count
- Check for device-specific errors
(such as device-not-ready or similar hardware problems)
- Initialize the device, if it is being opened for the first time
- Identify the minor number and update the f_op pointer, if necessary
- Allocate and fill any data structure to be put in filp->private_data
The release method should:
- Deallocate anything that open allocated in filp->private_data
- Shut down the device on last close
- Decrement the usage count
The read and write methods copy data from and to application code.
It is important to use (and understand)
unsigned long copy_to_user(void *to, const void *from,
      unsigned long count);
unsigned long copy_from_user(void *to, const void *from,
      unsigned long count);
In the driver code you need to write (in the read method), you may have a line
such as:
copy_to_user(buf, &portdata,retval);
Before you use this, you will want to read data from the port
portdata=inb(port);
Look at the online O'Reilly book to see what the return values of
read and write should be. Also read chapter 3 (and ideally the
first two chapters for more detailed information about writing
device drivers.