123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699 |
- .TH DEVATTACH 9
- .SH NAME
- devattach, devclone, devdir, devgen, devwalk, devdirread, devstat, devopen, devbread, devbwrite, devcreate, devremove, devwstat, devreset, devinit, devshutdown, openmode \- common device driver support
- .SH SYNOPSIS
- .nf
- .ta \w'\fLBlock* 'u +10n
- .B
- typedef int
- .B
- Devgen(Chan *c, char *name, Dirtab *tab, int ntab, int i, Dir *dp)
- .PP
- .B
- Chan* devattach(int tc, char *spec)
- .PP
- .B
- Chan* devclone(Chan *c)
- .PP
- .B
- void devdir(Chan *c, Qid qid, char *n, long length,
- .B
- char *user, long perm, Dir *dp)
- .PP
- .B
- int devgen(Chan *c, char *name, Dirtab *tab, int ntab,
- .B
- int i, Dir *dp)
- .PP
- .B
- Walkqid* devwalk(Chan *c, Chan *nc, char **name, int nname,
- .B
- Dirtab *tab, int ntab, Devgen *gen)
- .PP
- .B
- void devstat(Chan *c, uchar *db, int n, Dirtab *tab,
- .B
- int ntab, Devgen *gen)
- .PP
- .B
- long devdirread(Chan *c, char *d, long n, Dirtab *tab,
- .B
- int ntab, Devgen *gen)
- .PP
- .B
- Chan* devopen(Chan *c, int omode, Dirtab *tab,
- .B
- int ntab, Devgen *gen)
- .PP
- .B
- Block* devbread(Chan *c, long n, ulong offset)
- .PP
- .B
- long devbwrite(Chan *c, Block *bp, ulong offset)
- .PP
- .B
- void devcreate(Chan*, char*, int, ulong)
- .PP
- .B
- void devremove(Chan*)
- .PP
- .B
- void devwstat(Chan*, uchar*, int)
- .PP
- .B
- void devreset(void)
- .PP
- .B
- void devinit(void)
- .PP
- .B
- void devshutdown(void)
- .PP
- .B
- int openmode(ulong mode)
- .SH 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
- .B Chan
- structures correctly.
- .PP
- A device has an associated
- .IR type ,
- represented as a Unicode character (`rune') that identifies the device
- inside and outside the kernel.
- It appears as the value of the
- .B type
- field in the
- .B Dir
- resulting from a
- .IR sys-stat (2)
- of any file provided by the device.
- A device is named outside the kernel using
- a path name starting with
- .B #
- followed by the device character
- (eg,
- .B c
- in
- .B #c
- for the console).
- Any subsequent characters before
- the next '/' or end of string is the `device specifier',
- interpreted solely by the device itself.
- .PP
- .I Devattach
- returns a new channel representing
- the root of the file tree
- corresponding to device type
- .IR tc ,
- with device specifier
- .IR spec .
- It is normally called by a driver's
- .I attach
- function (see
- .IR dev (9)).
- The
- .B qid
- for the new channel is
- .BR "(Qid){0,0,QTDIR}" ,
- suitable for a root directory for many devices, but
- a device driver is free to change it (provided the
- .B QTDIR
- bit remains in the
- .BR Qid.type ).
- .PP
- .I Devclone
- returns a new channel that is a copy of
- .IR c .
- An attempt to clone an open channel causes a
- .IR panic (9).
- .PP
- The
- .L Dir
- structure is shown below:
- .IP
- .EX
- 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;
- .EE
- .PP
- This
- .B Dir
- structure corresponds directly to the Limbo
- .B Dir
- adt described in
- .IR sys-stat (2).
- .PP
- Given a channel and assorted other information,
- .I devdir
- initialises a Dir structure at
- .IR dp .
- .I Devdir
- supplies the following data itself:
- .RS
- .TF length
- .TP
- .B atime
- last access time (set to current time)
- .TP
- .B mtime
- last modification time (set to kernel creation date)
- .TP
- .B gid
- group name (set to
- .IR eve (9))
- .TP
- .B length
- length in bytes (set to zero, which
- is normal for most devices)
- .RE
- .PD
- .PP
- Note that
- .I devdir
- assigns the values of
- .I name
- and
- .I user
- directly to fields of
- .BI * dp,
- and consequently those values must remain valid until the last use of
- .BI * dp.
- (Sometimes that requires the use of an auxiliary buffer, such as
- .BR up->genbuf .)
- If channel
- .I c
- corresponds to a file descriptor on which Styx is served,
- .I devdir
- sets both the flag bit
- .B QTMOUNT
- in
- .IB dp ->qid.type
- and the flag bit
- .B DMMOUNT
- in
- .IB dp ->mode
- (see
- .I export
- in
- .IR sys-dial (2)
- and
- .I mount
- in
- .IR sys-bind (2)).
- .PP
- A simple name space can be represented in a driver by an array of
- .B 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
- .B Dirtab
- is shown below:
- .IP
- .EX
- typedef
- struct Dirtab
- {
- char name[KNAMELEN];
- Qid qid;
- vlong length;
- long perm;
- } Dirtab;
- .EE
- .PP
- The name
- .RB ` . '
- .I must
- appear as the first entry in a
- .B Dirtab
- if the default
- .I devgen
- function is used.
- On the other hand, the name
- .RB ` .. '
- must never appear in a
- .B Dirtab
- table.
- Drivers that support a directory hierarchy must walk up the hierarchy towards
- the root when their
- .I walk
- function receives
- .RB ` .. '
- as a file name component.
- The name
- .RB ` . '
- is never seen by a driver.
- .PP
- The
- .IR devdirread ,
- .IR devopen ,
- .IR devstat ,
- and
- .IR devwalk
- functions all take a
- .I gen
- function argument,
- of type
- .BR Devgen ,
- which they invoke to retrieve the items in
- a
- .B Chan
- that represents a directory.
- .I Gen
- takes a channel
- .I c
- (a directory),
- a file
- .I name
- (which is nil except during
- .IR devwalk ),
- an array of
- .B Dirtab
- structures
- .I tab
- of length
- .IR ntab ,
- and a table index
- .IR i .
- The functions calling
- .I gen
- expect it to place the
- .IR i 'th
- entry in the directory into
- .IR \f5*\fPdp .
- It should return 1
- if the call was successful,
- -1 if
- .I i
- is beyond the index of the last directory entry,
- or 0 if there is no entry at
- .IR i ,
- but there are entries beyond it.
- When
- .I i
- has the special value
- .B DEVDOTDOT
- then
- .I gen
- should set
- .IR \f5*\fPdp
- to reflect the parent of
- .IR c ;
- if
- .I c
- is a one-level device directory, then `..' is equivalent to `.'.
- Custom implementations of
- .I gen
- often ignore
- .IR devtab ,
- and instead return their own dynamically generated
- set of directory entries from some other source.
- Exceptionally, during
- .I devwalk
- a non-nil
- .I name
- is provided: it is the name being looked up, and a device-specific
- .I gen
- can short-circuit the search by returning -1 if the name does not exist,
- or filling in
- .IR \f5*\fPdp
- and returning 1 if it does exist.
- .PP
- The function
- .I devgen
- is compatible with
- .BR Devgen ;
- it returns the
- .IR i 'th
- entry in
- .IR devtab ,
- and can be used to provide a simple, static
- set of directory entries.
- .PP
- .I Devwalk
- walks channel
- .I c
- to the file in the device named by the path encoded in
- .IR name ,
- which is an array of strings of length
- .IR nname .
- It provides the interface to
- .IR 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
- .I walk
- function typically passes its parameters on to
- .I devwalk
- (adding the device's own
- .B Dirtab
- array as the the value of
- .IR tab ),
- and simply returning the result of
- .IR devwalk .
- .PP
- .I Devwalk
- walks
- .I c
- using the given set of names, and if the walk is successful, the
- channel
- .I nc
- will refer to the result of the walk
- (specifically,
- .IB nc ->qid
- is set to the Qid for the file).
- If
- .I nc
- is nil,
- .I devwalk
- will allocate a new channel itself, that is initially a clone of
- .IR c .
- As in
- .IR walk (5),
- .I devwalk
- can return a partial result,
- represented by
- a dynamically allocated value of the following structure:
- .IP
- .EX
- struct Walkqid
- {
- Chan *clone;
- int nqid;
- Qid qid[1]; /* actually nname in length */
- };
- .EE
- .PP
- The value must be freed after use.
- For each element of
- .I name ,
- .I devwalk
- passes
- the
- .I tab
- parameter to
- .I gen
- together with the currently-sought element of
- .IR name .
- If the first element is not found,
- .I devwalk
- returns nil; otherwise, it returns a
- .B Walkqid
- value in which
- .B nqid
- elements of the array
- .B qid
- are set to the qids (see
- .IR intro (5))
- of each valid element of
- .IR name .
- If all
- .I nname
- elements were successfully traversed, then
- .B nqid
- will have the value
- .IR nname ,
- and
- .B clone
- will refer to the result of the walk,
- which is either
- .I nc
- if given, or
- the new channel allocated by
- .IR devwalk .
- Otherwise, at least one element succeeded and
- .B nqid
- is less than
- .I nname
- and
- .B clone
- is nil.
- On an error or incomplete walk,
- the error string is set to the error that stopped the walk (eg,
- .B Enonexist
- or
- .BR Enotdir ).
- .PP
- .I Devstat
- fills the array of bytes
- .I db
- with data in the format produced by
- .IR stat (5)
- that describes the file
- referenced by channel
- .IR c ,
- which must have a corresponding entry
- returned by
- .IR gen
- (ie, an entry with matching
- .BR Qid.path ).
- If
- .I c
- is a communications channel connecting a Styx server to a current mount point,
- the
- .B DMMOUNT
- bit is set in the resulting
- .BR Dir.mode ,
- and
- .B QTMOUNT
- is set in
- .BR Dir.qid.type .
- As in
- .IR stat (5),
- the length of the data written to
- .I db
- varies; if more than
- .I n
- bytes are needed,
- .I devstat
- raises the
- .IR error (9)
- .BR Ebadarg .
- Otherwise, it returns the number of bytes in
- .I db
- actually used.
- .PP
- If an entry with the desired qid is not found in the table, but
- .I c
- corresponds to a directory
- (ie,
- .B QTDIR
- is set in
- .IR c\f5->qid.type\fP ),
- it is taken to be a
- .I stat
- of a notional directory containing the files listed in
- .IR tab .
- .I Dirstat
- then builds the corresponding Dir structure:
- its
- .B Dir.name
- is taken from
- .IR c\f5->path->elem\fP ;
- the length is
- .BI DIRLEN*nelem(tab) ;
- and
- .B Dir.perm
- is 0555 (read-execute for all).
- .PP
- .I Devdirread
- calls
- .I gen
- to obtain successive
- .B Dir
- structures representing entries in the open directory
- .IR c .
- These are converted to standard format (see
- .I convD2M
- in
- .IR fcall (2))
- and placed in the buffer
- .IR b .
- It returns the number of bytes in the result.
- At most
- .I n
- bytes will be returned, in multiples of
- .BR DIRLEN .
- Because the kernel maintains the current offset in
- .IR c ,
- successive calls to
- .I devdirread
- return successive directory components.
- .PP
- .I Devopen
- is called to check and complete a request to open channel
- .I c
- for I/O according to
- .IR omode
- (the open mode of
- .IR sys-open (2)).
- It calls
- .I gen
- to obtain successive directory entries
- which it searches
- for a Qid matching that of
- .IR c ,
- and ensures that the current user has permission to open
- .I c
- with the given mode,
- .IR omode ,
- and that the mode itself is valid
- (see
- .I 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
- .I 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
- .RB ( OREAD ).
- .I Devopen
- returns the channel
- .I c
- on success.
- Last, it sets the bit
- .B COPEN
- in
- .B Chan.flag
- to mark
- .I c
- as open.
- This convention can always be relied upon by the driver's
- .I close
- function to tell if an open succeeded.
- On the otherhand,
- if the open request was unsuccessful,
- .I devopen
- raises an appropriate
- .IR error (9)
- and does not return.
- .PP
- .I Devbread
- returns a
- .B Block
- (see
- .IR allocb (9))
- containing up to
- .I n
- bytes read,
- using
- .BI "devtab[" c "->type]->read" ,
- from
- .I c
- starting at the given
- .IR offset .
- The read pointer in the returned
- .B Block
- points to the start of the data;
- the write pointer points to the next available byte.
- .PP
- .I Devbwrite
- writes the data in
- .B Block
- .I bp
- to the file
- .I c
- at the given
- .IR offset ,
- using the write function
- .BI "devtab[" c "->type]->write" .
- It then frees the block list
- .I bp
- before
- returning the number of bytes written.
- .PP
- Most built-in devices do not allow
- .IR create ,
- .IR remove
- or
- .I wstat
- on their files.
- .IR Devcreate ,
- .I devremove
- and
- .I devwstat
- are stubs that raise an
- .IR error (9),
- .BR Eperm .
- They can be named directly in a device driver's device
- switch (the
- .B Dev
- structure in
- .BR /sys/src/9/port/portdat.h :
- see
- .IR dev (9)).
- .PP
- .IR Devreset ,
- .I devinit
- and
- .I devshutdown
- are also stubs;
- they do nothing.
- A device driver puts them in its
- .B Dev
- structure when it need take no action on device reset, initialisation, or shut down.
- .PP
- .I Openmode
- is used by a driver that does not use
- .IR devopen ,
- to check the open mode it receives in its open
- routine.
- .I Openmode
- returns mode
- .IR o ,
- the mode parameter to
- .IR sys-open (2)
- or
- .IR sys-create ,
- shorn of
- .BR OTRUNC
- and similar options,
- and reduced to one of
- .BR OREAD ,
- .BR OWRITE
- or
- .BR ORDWR .
- In particular,
- .B OEXEC
- becomes
- .B OREAD
- within the kernel.
- .I Openmode
- raises an
- .IR error (9)
- .B Ebadarg
- instead of returning, if
- .I o
- is an invalid mode (eg, reserved bits set).
- .SH SOURCE
- .B /sys/src/9/port/dev.c
- .SH SEE ALSO
- .IR allocb (9),
- .IR eve (9),
- .IR qio (9)
|