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,
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 correctly. 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 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', interpreted 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(9)). 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(9).
The Dir structure is shown below:
Given a channel and assorted other information, devdir initialises
a Dir structure at dp. Devdir supplies the following data itself:
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
initialised if required. The structure of Dirtab is shown below:
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 directory 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 provided: 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 provides the interface to walk(5) within the kernel, and that specification 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 returning the result of devwalk.
Devwalk walks c using the given set of names, and if the walk
is successful, the channel nc will refer to the result of the
walk (specifically, 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:
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 Styx 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(9) 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 corresponds 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 format (see convD2M in fcall(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 offset 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 open(2)). It calls gen to obtain successive directory entries which it searches for a Qid matching 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(9) and does not return. Devbread returns a Block (see allocb(9)) 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 offset, 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(9), Eperm. They can be named directly in a device driver's device switch (the Dev structure in /sys/src/9/port/portdat.h: see dev(9)). 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 open(2) or sys–create, shorn of OTRUNC
and similar options, and reduced to one of OREAD, OWRITE or ORDWR.
In particular, OEXEC becomes OREAD
within the kernel. Openmode raises an error(9) Ebadarg instead
of returning, if o is an invalid mode (eg, reserved bits set). |
SOURCE
/sys/src/9/port/dev.c |
SEE ALSO
allocb(9), eve(9), qio(9) |