DEVATTACH(10.2) DEVATTACH(10.2)
NAME
devattach, devclone, devdir, devgen, devwalk, devdirread, devstat,
devopen, devbread, devbwrite, devcreate, devremove, devwstat, devreset,
devinit, devshutdown, openmode - common device driver support
SYNOPSIS
typedef int Devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i,
Dir *dp)
Chan* devattach(int tc, char *spec)
Chan* devclone(Chan *c)
void devdir(Chan *c, Qid qid, char *n, long length,
char *user, long perm, Dir *dp)
int devgen(Chan *c, char *name, Dirtab *tab, int ntab,
int i, Dir *dp)
Walkqid* devwalk(Chan *c, Chan *nc, char **name, int nname,
Dirtab *tab, int ntab, Devgen *gen)
void devstat(Chan *c, uchar *db, int n, Dirtab *tab,
int ntab, Devgen *gen)
long devdirread(Chan *c, char *d, long n, Dirtab *tab,
int ntab, Devgen *gen)
Chan* devopen(Chan *c, int omode, Dirtab *tab, int
ntab, Devgen *gen)
Block* devbread(Chan *c, long n, ulong offset)
long devbwrite(Chan *c, Block *bp, ulong offset)
void devcreate(Chan*, char*, int, ulong)
void devremove(Chan*)
void devwstat(Chan*, uchar*, int)
void devreset(void)
void devinit(void)
void devshutdown(void)
int openmode(ulong mode)
DESCRIPTION
Device drivers call these functions to carry out essential tasks and
default actions. They do most of the name space management for a
driver that serves a simple name space (eg, data and control files),
leaving the driver to concentrate on the device-specific details of the
I/O requests. More complex drivers also make good use of them at the
leaves of their name space, and to help manage the Chan structures cor‐
rectly.
A device has an associated type, represented as a Unicode character
(`rune') that identifies the device inside and outside the kernel. It
appears as the value of the type field in the Dir resulting from a sys-
stat(2) of any file provided by the device. A device is named outside
the kernel using a path name starting with # followed by the device
character (eg, c in #c for the console). Any subsequent characters
before the next '/' or end of string is the `device specifier', inter‐
preted solely by the device itself.
Devattach returns a new channel representing the root of the file tree
corresponding to device type tc, with device specifier spec. It is
normally called by a driver's attach function (see dev(10.2)). The qid
for the new channel is (Qid){0,0,QTDIR}, suitable for a root directory
for many devices, but a device driver is free to change it (provided
the QTDIR bit remains in the Qid.type).
Devclone returns a new channel that is a copy of c. An attempt to
clone an open channel causes a panic(10.2).
The structure is shown below:
typedef
struct Dir
{
/* system-modified data */
ushort type; /* server type */
uint dev; /* server subtype */
/* file data */
Qid qid; /* unique id from server */
ulong mode; /* permissions */
ulong atime; /* last read time */
ulong mtime; /* last write time */
vlong length; /* file length */
char *name; /* last element of path */
char *uid; /* owner name */
char *gid; /* group name */
char *muid; /* last modifier name */
} Dir;
This Dir structure corresponds directly to the Limbo Dir adt described
in sys-stat(2).
Given a channel and assorted other information, devdir initialises a
Dir structure at dp. Devdir supplies the following data itself:
atime last access time (set to current time)
mtime last modification time (set to kernel creation date)
gid group name (set to eve(10.2))
length length in bytes (set to zero, which is normal for most
devices)
Note that devdir assigns the values of name and user directly to fields
of *dp, and consequently those values must remain valid until the last
use of *dp. (Sometimes that requires the use of an auxiliary buffer,
such as up->genbuf.) If channel c corresponds to a file descriptor on
which 9P is served, devdir sets both the flag bit QTMOUNT in
dp->qid.type and the flag bit DMMOUNT in dp->mode (see sys-export(2)
and mount in sys-bind(2)).
A simple name space can be represented in a driver by an array of
Dirtab structures. The array is typically static when the names and
permissions are static, but can be dynamically allocated and ini‐
tialised if required. The structure of Dirtab is shown below:
typedef
struct Dirtab
{
char name[KNAMELEN];
Qid qid;
vlong length;
long perm;
} Dirtab;
The name `.' must appear as the first entry in a Dirtab if the default
devgen function is used. On the other hand, the name `..' must never
appear in a Dirtab table. Drivers that support a directory hierarchy
must walk up the hierarchy towards the root when their walk function
receives `..' as a file name component. The name `.' is never seen
by a driver.
The devdirread, devopen, devstat, and devwalk functions all take a gen
function argument, of type Devgen, which they invoke to retrieve the
items in a Chan that represents a directory. Gen takes a channel c (a
directory), a file name (which is nil except during devwalk), an array
of Dirtab structures tab of length ntab, and a table index i. The
functions calling gen expect it to place the i'th entry in the direc‐
tory into *dp. It should return 1 if the call was successful, -1 if i
is beyond the index of the last directory entry, or 0 if there is no
entry at i, but there are entries beyond it. When i has the special
value DEVDOTDOT then gen should set *dp to reflect the parent of c; if
c is a one-level device directory, then `..' is equivalent to `.'.
Custom implementations of gen often ignore devtab, and instead return
their own dynamically generated set of directory entries from some
other source. Exceptionally, during devwalk a non-nil name is pro‐
vided: it is the name being looked up, and a device-specific gen can
short-circuit the search by returning -1 if the name does not exist, or
filling in *dp and returning 1 if it does exist.
The function devgen is compatible with Devgen; it returns the i'th
entry in devtab, and can be used to provide a simple, static set of
directory entries.
Devwalk walks channel c to the file in the device named by the path
encoded in name, which is an array of strings of length nname. It pro‐
vides the interface to walk(5) within the kernel, and that specifica‐
tion must be well understood to appreciate all the nuances of its
interface. Fortunately, in nearly all device drivers, a device's walk
function typically passes its parameters on to devwalk (adding the
device's own Dirtab array as the the value of tab), and simply return‐
ing the result of devwalk.
Devwalk walks c using the given set of names, and if the walk is suc‐
cessful, the channel nc will refer to the result of the walk (specifi‐
cally, nc->qid is set to the Qid for the file). If nc is nil, devwalk
will allocate a new channel itself, that is initially a clone of c. As
in walk(5), devwalk can return a partial result, represented by a
dynamically allocated value of the following structure:
struct Walkqid
{
Chan *clone;
int nqid;
Qid qid[1];/* actually nname in length */
};
The value must be freed after use. For each element of name , devwalk
passes the tab parameter to gen together with the currently-sought ele‐
ment of name. If the first element is not found, devwalk returns nil;
otherwise, it returns a Walkqid value in which nqid elements of the
array qid are set to the qids (see intro(5)) of each valid element of
name. If all nname elements were successfully traversed, then nqid
will have the value nname, and clone will refer to the result of the
walk, which is either nc if given, or the new channel allocated by
devwalk. Otherwise, at least one element succeeded and nqid is less
than nname and clone is nil. On an error or incomplete walk, the error
string is set to the error that stopped the walk (eg, Enonexist or
Enotdir).
Devstat fills the array of bytes db with data in the format produced by
stat(5) that describes the file referenced by channel c, which must
have a corresponding entry returned by gen (ie, an entry with matching
Qid.path). If c is a communications channel connecting a 9P server to
a current mount point, the DMMOUNT bit is set in the resulting
Dir.mode, and QTMOUNT is set in Dir.qid.type. As in stat(5), the
length of the data written to db varies; if more than n bytes are
needed, devstat raises the error(10.2) Ebadarg. Otherwise, it returns
the number of bytes in db actually used.
If an entry with the desired qid is not found in the table, but c cor‐
responds to a directory (ie, QTDIR is set in c->qid.type), it is taken
to be a stat of a notional directory containing the files listed in
tab. Dirstat then builds the corresponding Dir structure: its Dir.name
is taken from c->path->elem; the length is DIRLEN*nelem(tab); and
Dir.perm is 0555 (read-execute for all).
Devdirread calls gen to obtain successive Dir structures representing
entries in the open directory c. These are converted to standard for‐
mat (see convD2M in styx(10.2)) and placed in the buffer b. It returns
the number of bytes in the result. At most n bytes will be returned,
in multiples of DIRLEN. Because the kernel maintains the current off‐
set in c, successive calls to devdirread return successive directory
components.
Devopen is called to check and complete a request to open channel c for
I/O according to omode (the open mode of sys-open(2)). It calls gen to
obtain successive directory entries which it searches for a Qid match‐
ing that of c, and ensures that the current user has permission to open
c with the given mode, omode, and that the mode itself is valid (see
openmode below). Permission is checked against the permission in the
matching entry. If no matching Qid is found, it is assumed that the
notional parent directory of the files represented in tab is to be
opened. Such a directory is deemed to have mode 0555, allowing access
by any user. A directory can only be opened for reading (OREAD).
Devopen returns the channel c on success. Last, it sets the bit COPEN
in Chan.flag to mark c as open. This convention can always be relied
upon by the driver's close function to tell if an open succeeded. On
the otherhand, if the open request was unsuccessful, devopen raises an
appropriate error(10.2) and does not return.
Devbread returns a Block (see allocb(10.2)) containing up to n bytes
read, using devtab[c->type]->read, from c starting at the given offset.
The read pointer in the returned Block points to the start of the data;
the write pointer points to the next available byte.
Devbwrite writes the data in Block bp to the file c at the given off‐
set, using the write function devtab[c->type]->write. It then frees
the block list bp before returning the number of bytes written.
Most built-in devices do not allow create, remove or wstat on their
files. Devcreate, devremove and devwstat are stubs that raise an
error(10.2), Eperm. They can be named directly in a device driver's
device switch (the Dev structure in /os/port/portdat.h: see dev(10.2)).
Devreset, devinit and devshutdown are also stubs; they do nothing. A
device driver puts them in its Dev structure when it need take no
action on device reset, initialisation, or shut down.
Openmode is used by a driver that does not use devopen, to check the
open mode it receives in its open routine. Openmode returns mode o,
the mode parameter to sys-open(2) or sys-create, shorn of OTRUNC and
similar options, and reduced to one of OREAD, OWRITE or ORDWR. In par‐
ticular, OEXEC becomes OREAD within the kernel. Openmode raises an
error(10.2) Ebadarg instead of returning, if o is an invalid mode (eg,
reserved bits set).
SOURCE
/emu/port/dev.c
/os/port/dev.c
SEE ALSO
allocb(10.2), eve(10.2), qio(10.2)
DEVATTACH(10.2)