struct Dev
{
int dc;
char* name;
void (*reset)(void); /* native only */
void (*init)(void);
void (*shutdown)(void); /* native */
Chan* (*attach)(char *spec);
Walkqid* (*walk)(Chan *c, Chan *nc, char **name, int nname);
int (*stat)(Chan *c, uchar *db, int dbsize);
Chan* (*open)(Chan *c, int mode);
void (*create)(Chan *c, char *name, int mode, ulong perm);
void (*close)(Chan *c);
long (*read)(Chan *c, void *buf, long nbytes, vlong offset);
Block* (*bread)(Chan *c, long nbytes, ulong offset);
long (*write)(Chan *c, void*, long, vlong offset);
long (*bwrite)(Chan *c, Block *b, ulong offset);
void (*remove)(Chan *c);
int (*wstat)(Chan *c, uchar *db, int dbsize);
void (*power)(int on); /* native only */
int (*config)(int on, char *spec, DevConf *cf); /* native */
};
Every device driver serves a unique name space that represents to the corresponding device(s).
Applications act on the space using the operations of
sys-bind(2),
sys-open(2),
sys-read(2),
sys-stat(2),
and other system calls.
Within the kernel, the
Dev
structure defines the interface between the kernel and a device driver for
all operations on that driver's name space.
Dev
identifies the driver, and lists a set of C functions that are the driver's operations.
Most are operations on the
Chan
type that is the kernel representation of a file or directory active in a name space.
The kernel converts system calls acting on file descriptors into calls to a device's
Dev
operations acting on channel values.
All channel values presented through the
Dev
interface are associated with the corresponding device driver:
for channel
c,
c->type
specifies that driver.
Within the driver, the
c->qid.path
of a channel
c
identifies a file in the driver's name space, or even a client-specific instance of a file
(eg, for multiplexors such as
ip(3)).
The interpretation of the
path
is completely determined by the driver.
A device driver in the source file
devx.c
exports an initialised instance of
Dev xdevtab.
For instance,
devcons.c
contains the global initialiser:
-
Dev consdevtab = {
'c',
"cons",
devreset,
consinit,
devshutdown,
consattach,
conswalk,
consstat,
consopen,
devcreate,
consclose,
consread,
devbread,
conswrite,
devbwrite,
devremove,
devwstat,
};
The kernel accesses the driver only through its
Dev
structure, and consequently entry points such as
consinit,
consread,
etc. can (and should) be declared
static,
and thus local to the file.
The following elements of
Dev
identify the driver:
- dc
- The device's type, represented by a Unicode character (`rune') that must be unique
amongst those in a given kernel (and ideally for a given platform).
Its value is the value of
Dir.dtype
in the result of a
sys-stat(2)
applied to any file in the device.
- name
- The name that identifies the driver in a kernel configuration file and in
/dev/drivers
(see
cons(3)).
All the other entries are functions.
In many cases, the values given in a device's
Dev
will be the default operations provided by
devattach(10.2).
- reset()
- Called once during system initialisation by the native
kernel's
main
after initialising all supporting subsystems, including memory allocation, traps, screen, MMU (if used),
but with interrupts disabled, and before any kernel process environment has been established.
Typically used on some platforms to force some devices into a sane state
before interrupts are enabled.
- init()
- Called once during system initialisation in the context of the first kernel process,
with interrupts enabled, before the virtual machine has been started.
- shutdown()
- Called once in native kernels during system shut down.
Used on only a few platforms to force a device into a state that will allow it
to function correctly during and after a soft reboot (eg, without doing a full system hardware reset).
- attach(spec)
- Called on each new attach to the device (eg, a reference to
#c
by
sys-bind(2)).
Spec
is the string following the device character and before a subsequent
`/'
in the bind request.
It is the empty string for most devies.
If the attach is successful,
attach
should return a
Chan
the refers to the root of the tree served by the device driver.
Normally, it will suffice to return the value of
devattach(10.2).
- walk(c, nc, name, nname)
- Walks existing channel
c
from its current position in the device tree to that specified by the
path represented by
name[0],
...
name[nname-1].
The driver must interpret
`..'
as a walk from the current position one level up towards the root of the device tree.
The result is represented by a dynamically-allocated
Walkqid
value,
with contents as described in
devattach(10.2).
Most drivers simply pass parameters on to
devwalk
in
devattach(10.2)
and return its result.
- stat(c, db, nbytes)
- Fill
db
with
stat(5)
data describing the file referenced by
c.
Nbytes
gives the size of
db;
if the data will not fit, return the value specified for
convD2M
in
styx(10.2).
Most drivers simply pass parameters on to
devstat
in
devattach(10.2);
a few fill a local copy of a
Dir
structure, and call
convD2M
to store the machine-independent representation in
db.
- open(c, mode)
- Open the file represented by
Chan
c,
in the given
mode
(see
sys-open(2)),
and if successful, return a
Chan
value representing the result
(usually
c).
Many drivers simply apply
devopen
of
devattach(10.2).
Exclusive use drivers might check and increment a reference count.
- create(c, name, mode, perm)
- C
should be a directory.
Create a new file
name
in that directory, with permissions
perm,
opened with the given
mode.
If successful, make
c
refer to the newly created file.
Most drivers return an error on all creation attempts,
by specifying
devcreate
of
devattach(10.2)
in the
Dev
table.
- close(c)
- Close channel
c.
This must be implemented by all drivers; there is no default,
although the function often is a no-op.
Exclusive use drivers might decrement a reference count.
- read(c, buf, nbytes, offset)
- Implement a
sys-read(2)
of
nbytes
of data from the given
offset
in file
c,
and if successful, place the data in
buf,
and return the number of bytes read,
which must be no greater than
nbytes.
Devices sometimes ignore the
offset.
All device drivers must implement
read;
there is no default.
Note that if
c
is a directory, the data has an array of
stat(5)
data listing the directory contents, in the format prescribed by
read(5).
Most drivers have
devdirread
of
devattach(10.2)
do the work when
c
is the root directory of the device's tree.
- bread(c, nbytes, offset)
- Implement a
sys-read(2)
of
nbytes
of data from the given offset in file
c,
and if successful return the data in a
Block
(see
allocb(10.2)
and
qio(10.2)).
Most drivers use the default
devbread
provided by
devattach(10.2),
and nearly all ignore the
offset
in any case.
Drivers that manipulate Blocks internally, such as
ip(3),
ssl(3)
and similar protocol devices,
and drivers that are likely to provide data to those devices,
will provide a
devbread
implementation so as to reduce the number of times the data is copied.
- write(c, buf, nbytes, offset)
- Implement a write of
nbytes
of data from
buf
to file
c,
which must not be a directory,
starting at the given byte
offset.
Return the number of bytes actually written.
There is no default, but drivers that do not
implement writes to any of their files can simply call
error(Eperm)
to signal an error.
- bwrite(c, b, offset)
- Similar to the
write
entry point, but the data is contained in a
Block
b
(see
allocb(10.2)).
B
should be freed before return, whether the driver signals an error or not.
Most drivers use the default
devbwrite
from
devattach(10.2),
which calls the driver's
write
entry point using the data in
b.
Drivers that manipulate Blocks internally, such as
ip(3),
ssl(3)
and similar protocol devices,
will provide a
devbwrite
implementation so as to avoid copying the data needlessly.
- remove(c)
- Remove the file referenced by
c.
Most drivers raise an error by using the default
devremove
from
devattach(10.2).
- wstat(c, db, dbsize)
- Change the attributes of file
c,
using the
stat(5)
data in buffer
db,
which is
dbsize
bytes long.
Usually a driver will use
convM2D
of
styx(10.2)
to convert the data to a
Dir
structure, then apply the rules of
stat(5)
to decide which attributes are to be changed (and whether the change is allowed).
Most drivers simply return an error on all
wstat
requests by using the default
devwstat
from
devattach(10.2).
- power(on)
- Reserved for use in native kernels, to allow the kernel
to power the device on and off for power-saving;
on
is non-zero if the device is being powered up, and
zero if it is being powered down.
The device driver should save the device state if necessary.
Leave the
Dev
entry null for now.
- config(on, spec, cf)
- Reserved for use in native kernels to allow a device
to be configured on and off dynamically.
Leave the
Dev
entry null for now.
The elements
reset,
shutdown,
power,
and
config
are currently present only in the native kernels.