8_character device drivers
TRANSCRIPT
-
7/31/2019 8_Character Device Drivers
1/44
Character Device Drivers
CSIE, NCKU
The information on the slides are fromLinux Device Drivers, Third Edition, by Jonathan Corbet, Alessandro Rubini, and GregKroah-Hartman. Copyright 2005 OReilly Media, Inc., 0-596-00590-3.
-
7/31/2019 8_Character Device Drivers
2/44
The Scull Char Driver
We present code fragments of a chardevice driver: scull
acts on a memory area, not traditional IO
devices Hardware independent
Portable
Can be a template
But, only shows the kernel interface
The device interface is not shown
-
7/31/2019 8_Character Device Drivers
3/44
The Scull Char Driver
Four devices implemented by scull scull0~3
each device
Contains a global and persistent memory area
Global
Data of an area can be shared by multiple users
Persistent Data remains even when the device is closed
-
7/31/2019 8_Character Device Drivers
4/44
Major and Minor Numbers
Char devices are accessed through devicefiles in the filesystem
Device files, Special files, or Nodes
Located in the/dev directory
Char devices Major Minor Device name
-
7/31/2019 8_Character Device Drivers
5/44
Major and Minor Numbers
Why we need major & minor numbers? For identifying a device
Major: the device type
Minor: the device number in that type
Traditionally, each major number is managed by
a driver
kernel seldom handles minor numbers But Linux allows multiple drivers share the same
major number
-
7/31/2019 8_Character Device Drivers
6/44
Internal Representation of Device
Numbers
dev_t is used to hold a device number Major (12bits) , minor (20bits) for kernel 2.6.0
The real representation can be changed.So,
Use the following macros
MAJOR(dev_t dev);
MINOR(dev_t dev);
MKDEV(int major, int minor);
-
7/31/2019 8_Character Device Drivers
7/44
Allocating and Freeing Device
Numbers
Allocating a range of device numbers to manage
If you know the major number
register_chrdev_region
Traditionally, major numbers are statically assigned
You can pick an unused number in
Documentation/devices.txt
Major number conflict may happen when you deploy your driver
If you want the kernel to give you a major numberdynamically
alloc_chrdev_region
-
7/31/2019 8_Character Device Drivers
8/44
Allocating and Freeing Device
Numbers
int register_chrdev_region(dev_t first,unsigned int count, char *name);
first: the first dev_t
Include major + first minor
count: total number of contiguous device
numbers you are requesting
name: the name of the device
-
7/31/2019 8_Character Device Drivers
9/44
Allocating and Freeing Device
Numbers
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor, unsigned int count,
char *name);
dev: output parameter
The dev_t indicating the (major, first minor) is
setup when the function successfully returns
Ch e ck / p r o c/ d e vice s o r s ys fs a ft e r t h e in vo ca t io n o f t h e
a b ove tw o fu n ct ion s
-
7/31/2019 8_Character Device Drivers
10/44
An Example of /proc/devices
Ch a r a c t e r d e vic e s :
1 m em
2 p t y
3 t t yp
4 t tyS6 lp
7 vcs
10 m is c
13 i n pu t
14 s o u n d
2 1 sg
18 0 u s b
Block d evices :
2 fd
8 sd
11 s r
65 sd6 6 s d
-
7/31/2019 8_Character Device Drivers
11/44
Allocating and Freeing Device
Numbers
Free the device numbers void unregister_chrdev_region(dev_t first,
unsigned int count);
-
7/31/2019 8_Character Device Drivers
12/44
Dynamic Allocation of Major
Numbers
More flexible, no conflict However, the device files can not be
created in advance
Create the device files after insmod
Creating ${device}0-3 after you got the $major
-
7/31/2019 8_Character Device Drivers
13/44
Where Are We?
Now, the driver have some devicenumbers
And, we have device files
mknod
Users can access these files
But, how these accesses finally go to thedriver ?
-
7/31/2019 8_Character Device Drivers
14/44
Some Important Data Structures
file_operations file
inode
-
7/31/2019 8_Character Device Drivers
15/44
File Operations
Let the accesses go to the driver Why? .. because device FILE
A collection of function pointers
read, write, Each driver should implement this set of functions
(some of them may be NULL)
Each open file is associated with its own set offunctions
File: object, file operation: method
-
7/31/2019 8_Character Device Drivers
16/44
File Operations: An Example
ssize_t (*read) (struct file *, char __user *,size_t, loff_t *);
corresponds to the read() system call
__user
user-space address
cannot be directly dereferenced
-
7/31/2019 8_Character Device Drivers
17/44
File Operations of the scull
Driver
struct file_operations scull_fops = {
.owner = THIS_MODULE,
.llseek = scull_llseek,
.read = scull_read,
.write = scull_write,
.ioctl = scull_ioctl,
.open = scull_open,
.release = scull_release,
};
O th e r s fu n ct io n s a r e n o t im p l em e n t e d b y t h e s c u ll d r ive r
H a n d le d b y th e k e r n e l d e fa u lt h a n d le r
The syntax is more portable acrosschanges in the definitions of thestructures
-
7/31/2019 8_Character Device Drivers
18/44
The file Structure
Represents an open instance of a file not specific to device drivers
created by the kernel on open
released when the last user close the file
Important fields
mode_t f_mode read/write permission
-
7/31/2019 8_Character Device Drivers
19/44
The file Structure
Important fields loff_t f_pos
current reading or writing position
unsigned int f_flags E.g., O_RDONLY, O_NONBLOCK, O_SYNC
A driver should check the O_NONBLOCK flag to
see if nonblocking operation has been requested
-
7/31/2019 8_Character Device Drivers
20/44
The file Structure
Important fields struct file_operations *f_op
operations associated with the file
assign the operations during open
you can change the file operations E.g., you can assign a different set of file operations for each
minor number during the open method
void *private_data
For your private use Usually be used to preserve state information across system
calls
-
7/31/2019 8_Character Device Drivers
21/44
The file Structure
Important fields struct dentry *f_dentry
The directory entry (dentry) structure
A directory is a file that records a set of directory entries
Usually be used to access inode filp->f_dentry->d_inode filp is a ptr to structfile
i_no some fields (name_len,) file/sudir name
Dentry format:
100 some fields (5,) file1
2037 some fields (7,) subdir1
80 some fields (7,) firefox
-
7/31/2019 8_Character Device Drivers
22/44
The inode Structure
Each file has an inode Record file information Data blocks, timing information, size.
If a file is opened by two users
Twofile structures and one inode structure How to find the file /usr/local/bin/foo ?
Important fields dev_t i_rdev
the actual device number ( for a device file )
struct cdev *i_cdev pointer to the cdev, which represents a char device
-
7/31/2019 8_Character Device Drivers
23/44
Char Device Registration
Allocating & Initializing cdev Method 1
struct cdev *my_cdev = cdev_alloc( );
my_cdev->ops = &my_fops; Method 2
void cdev_init(struct cdev *cdev, structfile_operations *fops);
In either way, my_cdev->owner = THIS_MODULE;
-
7/31/2019 8_Character Device Drivers
24/44
Char Device Registration
Registering to the kernel int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
num : first device number
count: number of device numbers for the device
Usually, 1
The registration may fail should check it
Once the registration succeeds, kernel will call youroperations prepare for that
Remove a char device void cdev_del(struct cdev *dev);
-
7/31/2019 8_Character Device Drivers
25/44
Device Registration in scull
scull represents each device with astructure of type struct scull_dev
struct scull_dev {
struct scull_qset *data; /* Pointer to first quantum set */int quantum; /* the current quantum size */
int qset; /* the current array size */
unsigned long size; /* amount of data stored here */
unsigned int access_key; /* used by sculluid and scullpriv */struct semaphore sem; /* mutual exclusion semaphore */
struct cdev cdev; /* Char device structure */
};
-
7/31/2019 8_Character Device Drivers
26/44
Device Registration in scullstatic void scull_setup_cdev(struct scull_dev *dev, int index)
{
int err, devno = MKDEV(scull_major, scull_minor + index);
cdev_init(&dev->cdev, &scull_fops); //init the cdev
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &scull_fops;err = cdev_add (&dev->cdev, devno, 1);// register to kernel
/* Fail gracefully if need be */
if (err)printk(KERN_NOTICE "Error %d adding scull%d", err, index);
}The caller :
for (i = 0; i < scull_nr_devs; i++) {
..
scull_setup_cdev(&scull_devices[i], i);
}
-
7/31/2019 8_Character Device Drivers
27/44
The Older Way
Registration int register_chrdev(unsigned int major, const
char *name, struct file_operations *fops);
registers minor numbers 0255 for the givenmajor number sets up a default cdev structure for each minor number
Unregistration int unregister_chrdev(unsigned int major,
const char *name);
-
7/31/2019 8_Character Device Drivers
28/44
Open Method
Initialization and preparation for lateroperations
Usually perform the following Jobs
Check for device-specific errors Initialize the device
Update the f_op pointer, if necessary
Allocate and fill any data structure to be putin filp->private_data
-
7/31/2019 8_Character Device Drivers
29/44
-
7/31/2019 8_Character Device Drivers
30/44
scull_open()
Introduced later
-
7/31/2019 8_Character Device Drivers
31/44
The release Method
Usually perform the following tasks Deallocate anything that open allocated in filp->
private_data
Shut down the device on last close
int scull_release(struct inode *inode, struct file *filp)
{
return 0;
}
Scull has no hardware to shut down
-
7/31/2019 8_Character Device Drivers
32/44
Memory Usage of the scull
Driver
In scull, the device is memory Variable sized
Dynamically expanded
Use kmalloc() and kfree()
void *kmalloc(size_t size, int flags);
Flags = = GFP_KERNEL void kfree(void *ptr);
-
7/31/2019 8_Character Device Drivers
33/44
Memory Usage of the scull
Driver
Each device is a linked list of pointers
Each pointer points to a scull_qset structure
scull_dev.qset
scull_dev.quantum
-
7/31/2019 8_Character Device Drivers
34/44
Scull_trim()int scull_trim(struct scull_dev *dev)
{
struct scull_qset *next, *dptr;int qset = dev->qset; /* "dev" is not-null */
int i;
for (dptr = dev->data; dptr; dptr = next) { /* all the list items */
if (dptr->data) {
for (i = 0; i < qset; i++) kfree(dptr->data[i]); // free the quantums in a qsetkfree(dptr->data); // free qsets data
dptr->data = NULL;
}
next = dptr->next;
kfree(dptr); // free the qset data structure
}dev->size = 0;
dev->quantum = scull_quantum;
dev->qset = scull_qset;
dev->data = NULL;
return 0;}
-
7/31/2019 8_Character Device Drivers
35/44
The read and write Methods
ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t
*offp);
ssize_t write(struct file *filp, const char __user *buff, size_t count,
loff_t *offp);
The buff argument is user-space pointer May not be directly accessible by kernel
May be paged out
Oops on directly access
May be a wrong/invalid pointer
Kernel does not trust the users
-
7/31/2019 8_Character Device Drivers
36/44
Accessing User Space Data
Transferring data with user space unsigned long copy_to_user(void __user *to, const void *from,unsigned long count);
unsigned long copy_from_user(void *to, const void __user *from,unsigned long count);
The above functions check whether the user space pointer is valid
Returns number of bytes that could NOT be copied perform data transfer
may sleep For paging in user pages
-
7/31/2019 8_Character Device Drivers
37/44
Accessing User Space Data
If you are sure the user addresses are fine Use __copy_to_user()/__copy_from_user()
No checks, faster
Be careful Kernel crashes
Security holes
-
7/31/2019 8_Character Device Drivers
38/44
Coming Back to
The read and write Methods
-
7/31/2019 8_Character Device Drivers
39/44
The Read Method
The return value R of read R = count ( > 0 )
The specified amount of data is read
0 < R < count Partial of the specified amount of data is read
R = 0 EOF
R < 0 Error
-
7/31/2019 8_Character Device Drivers
40/44
Scull_read()
// EOF, return 0
-
7/31/2019 8_Character Device Drivers
41/44
Scull_read()
// f_pos should reflect the current RW head
-
7/31/2019 8_Character Device Drivers
42/44
Scull_write()ssize_t scull_write (struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;struct scull_qset *dptr;
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM; /* value used in "goto out" statements */
find listitem, qset index and offset in the quantum skipped
dptr = scull_follow(dev, item);
if (dptr = = NULL) goto out;if (!dptr->data) {
dptr->data = kmalloc (qset * sizeof(char *), GFP_KERNEL);
if (!dptr->data) goto out; // no free memory!!!
memset (dptr->data, 0, qset * sizeof(char *));
}
-
7/31/2019 8_Character Device Drivers
43/44
Scull_write()
if (!dptr->data[s_pos]) {
dptr->data[s_pos] = kmalloc (quantum, GFP_KERNEL);//allocate the quantum
if (!dptr->data[s_pos]) goto out;
}
/* write only up to the end of this quantum */
if (count > quantum - q_pos) count = quantum - q_pos;
if (copy_from_user (dptr->data[s_pos]+q_pos, buf, count)) {retval = -EFAULT;
goto out;
}
*f_pos += count; retval = count; // f_pos should reflect the current RW head
/* update the size */
if (dev->size < *f_pos) dev->size = *f_pos;
out:
up(&dev->sem);
return retval;}
-
7/31/2019 8_Character Device Drivers
44/44
Scatter-Gather Read/Write
Read/write data from/to multiple buffers in asingle operation readv and writev
iovec structure Used to specify the address range of a buffer
An array of iovec structures is passed to readv()or writev()
A driver can implement readv() and writev()methods if it benefits from scatter-gather IO