Linux Device Drivers
– Char Drivers
Agenda
● Introduction to Device Drivers
● Char Drivers
Introduction to Device Drivers
Role of a Device Driver
User Application
● Interaction with user apps
● Interaction with hardware
● Interaction with the kernel
● Logic Management
Kernel Device Driver ● Buffer Management
● Concurrency issues
Hardware
Classification of Device Drivers
● Classes of Device Drivers :
– Character Drivers
– Block Drivers
– Network Drivers
Char Drivers
Char Drivers
● Device Numbers
● Allocating and Freeing Device Numbers
● Char Device Registration
● Important Data Structures
● File Operations
Anatomy of Char Drivers
User Application
Accessed using the
name of the file
Device node in
/dev
Accessed using the
device number
Kernel Device Driver
Hardware
Device Numbers
● Device numbers :
– Major number : Identifies the driver associated with the device
– Minor number : Distinguishes amongst devices of the driver
● ls -l /dev
Device Number Representation
● 'dev_t' holds the device number
Major : a Major : a ● It is a 32-bit quatity :
Minor : x Minor : y
– Major : 12-bits
– Minor : 20-bits
/dev/cdev0 /dev/cdev1
● Device no. to :
– Major : MAJOR(dev_t dev);
– Minor : MINOR(dev_t dev);
Major:Minor to dev_t :
Device Driver
●
– MKDEV(int major, int minor);
Allocating and Freeing Device Numbers
● Obtain one or more devices to work with
:
– Include <linux/fs.h>
– Statically :
int register_chrdev_region(dev_t first,
unsigned int count,
const char *name);
– Dynamically :
int alloc_chrdev_region(dev_t *dev,
unsigned int firstminor,
unsigned int count,
Char Device Registration
● Kernel uses structures of type struct cdev
(linux/cdev.h) to represent char devices internally.
● Two step process :
– Allocate and initialise the char device :
void cdev_init(struct cdev *cdev, struct
file_operations *fops);
– Inform the kernel about the device :
int cdev_add(struct cdev *dev, dev_t num,
unsigned int count);
● Remove the char device from the system :
void cdev_del(struct cdev *dev);
Creation of device nodes
● Applications in the user-space can refer to a
device by creating a device file(or node)
in /dev of the filesystem
● # mknod /dev/node_name device_type
major minor --mode=perm
● Ex :
# mknod /dev/myDev c 249 0 --mode=0666
Important Data Structures
● struct file
● struct inode
● struct file_operations
struct file
● Represents an open file.
● Created by the kernel on open and is passed to
any function that operates on the file, until the last
close.
● For every instance of an open file, a struct file is
created.
● Each file can have multiple struct file, depending on
the number of times it has been opened.
struct file
<linux/fs.h>
struct file {
...
unsigned int f_flags;
struct file_operations *f_op;
void *private_data;
};
struct inode
● Used by the kernel to represent files, no matter
open or close.
● It basically holds the file's properties.
● Unlike struct file, it has got only one instance, even
though the file has been opened any number of
times.
struct inode
<linux/fs.h>
struct inode {
...
dev_t i_rdev;
struct cdev *i_cdev;
};
struct inode Vs struct file
struct file filp_1; struct file filp_2 struct file filp_3;
App-1 App-2 App-3
open()
open()
open()
struct inode ind;
struct file_operations
<linux/fs.h>
struct file_operations {
...
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);
};
struct file_operations : open()
● int (*open) (struct inode *, struct file *);
● 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
● Acquire the resources
● Allocate and fill any data structure to be put in filp-
>private_data
● Returns :
● 0 : Success
● Negative value, for any errors
struct file_operations : release()
● int (*release) (struct inode *, struct file *);
● Used to release access from the requested device.
● Deallocate anything that open() allocated
● Shutdown the device.
struct file_operations : read()
● ssize_t (*read) (struct file *, char __user *, size_t,
loff_t *);
● Interacts with the user-space application and
implements the read functionality for the user-space.
● Copies data from the kernel space to the user-space
by :
– <linux/uaccess.h>
unsigned long copy_to_user(void __user *to,
const void *from, unsigned long count);
● Returns :
– No. of bytes copied to the user
– Negative value on an error
struct file_operations : write()
● ssize_t (*write) (struct file *, const char __user *, size_t,
loff_t *);
● Interacts with the user-space application and
implements the write functionality for the user-space.
● Copies data from the user-space to the kernel space
by :
– <linux/uaccess.h>
unsigned long copy_from_user(void *to,
const void __user *from, unsigned
long count);
● Returns :
– No. of bytes copied from the user
– Negative value on an error
file_operations : read & write
User Application
write() read()
Device node in
/dev
write() read()
{ {
copy_from_user(); copy_to_user();
} }
Device Driver
References
● Jonathan Corbet, Alessandro Rubini and Greg
Kroah-Hartman,”Linux Device Drivers”,3rd Edition,
O'Reilly Publications
● www.google.com
Thank You :)