Title: Chapter Three' Char Driver
1Chapter Three. Char Driver
2Introduction
- Design and Write a compete char driver
- easier than block driver
- you can understand basic driver mechanism
- scull
- simple char utility for loading localities
- use memory as if it were a device
- usual operations for a device are read and write,
anyway - allocate memory using kmalloc
- you can download the source from www.oreilly.com
3Scull Overview
- types of devices implemented by scull
- global and persistent scull0-3
- data written can be shared
- data is not lost after close operation
- fifo devices scullpipe0-3
- one process reads what is being written by
another process - multiple reads content for the data
- blocking without interrupt
- drivers block usually with device interrupts
- scullsingle allow only one process
- scullpriv private to each virtual console
- sculluid, scullwid
- allow only one user at a time
- scullid generates error msg while the latter
blocks processes
4Major and Minor Number
- look at /dev using ls l command
- the first c means it is a char device
- while b means that it is a block device
- the numbers after the owner are them
- a major number identifies a device driver
- there can be several devices controlled by one
driver - upto 128 elements
- a minor number identifies a device
- since a driver(a major number) controls several
devices - used only by the driver
- 0-255
5Registering a new driver
- register_chrdev (major, name, fops)
- tells the kernel to remember the major number and
the name of the device driver associated with it.
- fops is a pointer to a table of operations (open,
read, ) - a user request (syscall) is mapped to one of
functions indexed in the jump table - when major 0, it returns a dynamically
allocated major number - unregister_chrdev(major, name)
- mknod /dev/name c 127 0
- the name should be the same
- now users can access the device
result register_chrdev(smajor, scull,
scull_fops) if (result lt 0) printk(w_level
scull cannot get a major d\n major)
return result if (smajor 0) smajor result
6File Operations
- file_operations structure defines a set of
operations for a device - refer page 50-51 for exact argument types for
each functions - ioctl defines commands specific to the device
- mmap memory mapped IO
- release invoked when the node is closed
- 4 NULLs
- fsync flush the device
- fasync for async operation
- check_media_change change diskettes
- revalidate validate the buffer
struct file_operations scull_fops
scull_lseek, scull_read, scull_write, NULL,
/ readdir / NULL, / select
/ scull_ioctl, NULL, /mmap
/ scull_open, scull_release, / insert 4
NULLs here /
7Open in General
- what is does
- initialize the device
- increment the usage count
- identify minor number
- inode-gti_rdev
- updates filp-gtf_op according to the minor number
- pointer to the actual open routine
- open routine varies depending on the device type
- filp? a pointer to the file structure (file
structure? next slide)
8file
- a data structure created by the kernel at open a
device - it is different from files used by applications
- a pointer to file is passed to any operations on
this device - released at close
- defines
- f_mode read or write
- f_pos position (64 bit)
- f_flags read only, nonblock, sync
- f_inode inode used by the kernel
- f_op operations allowed on this device like
scull_fops - private_data
- data area used by a driver
9scull_open( )
- there are 6 types of devices for scull
- scull0-3, scullpipe0-3, scullsingle, scullpriv,
sculluid, scullwid - set of operations for each of them are different
- how can they be differentiated? use some bits of
the minor - high 4bits type
- low 4 bits device number
- scull_fop_array
- array of pointers
- a pointer points to the set of operations allowed
for each device
struct file_operations scull_fop_array
scull_fops, / type 0 scull0-3
/ scull_priv_fops, / private
/ scull_pipe_fops, / pipe / scull_sngl_fops
, / single / scull_user_fops, /sculluid
/ scull_wuser_fops /scullwid /
10scull_open( ) code
int scull_open(struct inode inode, struct file
filp) int type (MINOR(inode-gti_rdev)
gtgt4) / high 4 bits / int num
(MINOR(inode-gti_rdev) 0xf) / low 4 bits /
Scull_Dev dev / explained later / if
(type) / all except type 0 / filp-gtf_op
scull_fop_arraytype return filp-gtf_op-gtopen(in
ode, filp) / device driver for the type
0 goes here /
- scull_open was defined at scull_fops that was
used by register_chedev( ) - you can use the same scull_open for all the
device types - MINOR is a macro to extract the minor number
11open for type 0 device
/ device driver for the type 0 goes here /
i f (num gt scull_nr_devs) return ENODEV
dev scull_devicesnum if ( (filp-gtf_flags
O_ACCMODE) O_WRONLY) scull_trim(dev)
filp-gtprivate_data dev / store the 1st node
of the linked list / MOD_INC_USE_COUNT
return 0
- no initialization because scull0-3 are memory
devices - when open for write, trim to zero this is a
scull decision - scull_nr_devs the number of available devices
- scull_devices array of scull memory
12Sculls Memory Usage for a device
Scull_Dev
Scull_Dev
Scull_Dev
next
next
next
. . . . .
data
data
data
quanta
quanta
quanta
quanta
quanta
quanta
quanta
quanta
quanta
. . . . .
. . . . .
. . . . .
quanta
quanta
quanta
qset
typedef struct Scull_Dev void data /
pointer to array(quantum set) / struct
Scull_Dev next int quantum / current
quantum size / int qset / current
quantum set size / unsigned long size
unsigned long access_key / for sculluid,
scullwid / unsigned int usage / lock
the device /
13scull_trim( )
int scull_trim(Scull_Dev dev)
Scull_Dev next, dptr int qset
dev-gtqset / qset size / int i if
(dev-gtusage) return EBUSY for (dptr
dev dptr dptr next) / for every node in
the linked list / if (dptr-gtdata) / this
node is not empty / for (i 0 i lt qset
i) / for every pointer in qset / if
(dptr-gtdatai) / this quanta is not empry
/ kfree(dptr-gtdatai)
kfree(dptr-gtdata) / free the whole array
/ dptr-gtdata NULL next
dptr-gtnext if (dptr ! dev) kfree(dptr) /
free nodes except the 1st one /
dev-gtsize 0
14Read and Write
- reading/writing a device means data transfer
between user and kernel space - pointer, memcpy cannot be used
- Linux has functions for cross-space copy
- defined in ltasm/segment.hgt
- void memcpy_from(void to, const void from,
unsigned long count) - void memcpy_tofs(void to, const void from,
unsigned long count) - what if the space data is not in memory?
- fault handler shows up ! your process sleeps!
- any functions that access use space should be
reentrant - Though you may request n bytes transfer, less
than n bytes may be transferred - the device returns the actual number of bytes
transferred - user code should check the return value and
reissue the request if necessary
15read( )
rw_t scull_read(struct inode inode, struct file
filp, char buf, count_t count) Scull_Dev
dev filp-gtprivate_data / the first node
stored at open / int qset dev-gtqsey
int quantum dev-gtquantum int itemsize
quantum qset / size of a node / unsigned
long f_pos filp-gtf_pos int item, s_pos,
q_pos, rest if (f_pos gt dev_size) return
0 / end of file / if (f_pos count gt
dev-gtsize) count dev-gtsize f_pos item
f_pos / itemsize / node number / rest
f_pos itemsize s_pos rest / quantum
/ quantum number / q_pos rest
quantum / location inside the quantum / dev
scull_follow(dev, item) / traverse the linked
list /
16read( ) cont
if (!dev-gtdata) return 0 / no qset / if
(!dev-gtdatas_pos return 0 / no quantum /
if (count gt quantum q_pos) / this read
routine reads / count quantum qpos / only
upto the end of the quantum) / dev-gtusage
memcpy(buf, dev-gtdatas_posq_pos,
count) dev_usage-- / , -- for entrant
code / filp-gtf_pos count return
count