EmbLogic's Blog

Anatomy of a Device Driver

A device driver has three sides: one side talks to the rest of the kernel, one talks to the hardware, and one talks to the user.

In order to talk to the kernel, the driver registers with subsystems to respond to events. Such an event might be the opening of a file, a page fault, the plugging in of a new USB device, etc.

User Interface of a Device driver
Since Linux follows the UNIX model, and in UNIX everything is a file, users talk with device drivers through device files. Device files are a mechanism, supplied by the kernel,
precisely for this direct User-Driver interface.

Anatomy of scull device driver
The user talks with scull through the /dev/scull device file.When the user opens /dev/scull, the kernel calls scull’s open routine. When the user closes /dev/scull, the kernel calls scull’s release routine
Scull talks to the kernel through its initialization function . . . and through register_chrdev
. . . and through hooking into the timer interrupt .

Driver Initialization Code
static int __init scull_module _init ( void )
{
int ret ;
pr_debug ( ” Scull module init called \ n ” ) ;
i f ( ( ret = register_chrdev (SCULL_MAJOR_NUM, ” scull ” , & scull _ fops printk(KERN_ERR ” register_chrdev : %d \ n ” , ret ) ;
return ret ;
}

Driver Initialization
One function (init) is called on the driver’s initialization.
One function (exit) is called when the driver is removed from the system.

Question: what happens if the driver is compiled into the kernel, rather than as a module?

The init function will register hooks that will get the driver’s code called when the appropriate event happens.
Question: what if the init function doesn’t register any hooks?
There are various hooks that can be registered: file
operations, pci operations, USB operations, network operations – it all depends on what kind of device this is.

Registering Chardev Hooks
struct file_operation scull_fops = {
. owner = THIS_MODULE,
. open = scull_open ,
. release = scull _ release ,
. read = scull _read ,
. wr i t e = scull _write ,
.mmap = scull_mmap ,
. ioctl = scull _ ioctl
} ;
. . .
i f ( ( ret = register_chrdev (SCULL_MAJOR_NUM, ” scull ” , & scull _fops ) ) < 0 )
printk (KERN_ERR ” register_chrdev : %d \ n ” , ret ) ;

User Space Access to the Driver
The driver registers a character device tied to a given major number, but how does the user create such a file?
# mknod /dev/scull c 250 0

And how does the user open it?
if ((kfd = open(“/dev/scull”, O_RDWR)) < 0) {
perror(“open /dev/scull”);
exit(EXIT_FAILURE);
}

File Operations
. . . and then you start talking to the device. scull uses the following device file operations:
open for  allocating resources.
release for finishing releasing resources.
write for seting the starting positions
read for generating and then reading the next state .
mmap for potentially faster but more complex direct access .

The open and release Routines
open and release are where you perform any setup not done in initialization time and any cleanup not done in module unload time.

Scull_open
scull’s open routine allocates the scull structure which holds all of the state

static int scull_open ( struct inode *inode , struct file *filp )
{
struct scull *k ;
int ret ;
ret = alloc_scull (&k ) ;
if ( ret )
return ret ;
filp->private_data = k ;
return 0 ;
}

scull_release
scull’s release routine frees the resource allocated during open time.
static int scull_release ( struct inode *inode , struct file *filp )
{
struct scull *k = filp->pr i vate_data ;
…..
…..

free_scull ( k ) ;
return 0 ;
}

Open and Release
Beware of races if you have any global data . . . many a driver author stumble on this point.
Note also that release can fail, but almost no onechecks errors from close(), so it’s better if it doesn’t . . .
Question: what happens if the userspace program crashes while holding your device file open?

Use copy_from_user in case the user is passing a bad pointer.

Commentary on write
Note that even for such a simple function, care must be exercised when dealing with untrusted users.Users are always untrusted.
Always be prepared to handle errors!

memory mapping
The read-write mechanism, involves an overhead of a system call and related context switching and of memory copying. mmap maps pages of a file into memory, thus enabling
programs to directly access the memory directly and save the overhead, . . . but:
fast synchronization between kernel space and user space is a pain (why do we need it?),
and Linux read and write are really quite fast.

Referrence:-

Muli Ben-Yehuda IBM Haifa Research Labs and Haifux – Haifa Linux Club
Understanding the Linux Kernel, by Bovet and Cesati
Linux Device Drivers, 3rd edition, by Rubini et. al.
Linux Kernel Development, 2nd edition, by Robert Love
/usr/src/linux-xxx/

Comments are closed.