292 lines · 12.5 KB · Text
RawDownload
= devattach,  devclone,  devdir,  devgen,  devwalk, devdirread, devstat, devopen, devbread, devbwrite, devcreate,  devremove, devwstat,  devreset,  devinit, devshutdown, openmode: common device driver support     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) Device  drivers  call  these functions to carry out essentialtasks and default actions.  They do most of  the  name  spacemanagement  for a driver that serves a simple name space (eg,data and control files), leaving the driver to concentrate onthe  device-specific  details of the I/O requests.  More com‐plex drivers also make good use of  them  at  the  leaves  oftheir name space, and to help manage the Chan structures cor‐rectly. A device has an associated type,  represented  as  a  Unicodecharacter (`rune') that identifies the device inside and out‐side the kernel.  It appears as the value of the  type  fieldin  the Dir resulting from a sys-stat(2) of any file providedby the device.  A device is named outside the kernel using  apath  name  starting  with # followed by the device character(eg, c in #c for the  console).   Any  subsequent  charactersbefore  the  next  '/' or end of string is the `device speci‐fier', interpreted solely by the device itself. Devattach returns a new channel representing the root of  thefile tree corresponding to device type tc, with device speci‐fier spec.  It is normally called by a driver's attach  func‐tion  (see  dev(10.2)).   The  qid  for  the  new  channel is(Qid){0,0,QTDIR}, suitable for  a  root  directory  for  manydevices,  but  a device driver is free to change it (providedthe QTDIR bit remains in the Qid.type). Devclone returns a new channel that  is  a  copy  of  c.   Anattempt 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 adtdescribed in sys-stat(2). Given a channel and assorted other information,  devdir  ini‐tialises  a Dir structure at dp.  Devdir supplies the follow‐ing 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 directlyto  fields  of *dp, and consequently those values must remainvalid until the last use of *dp.   (Sometimes  that  requiresthe  use  of  an  auxiliary  buffer, such as up->genbuf.)  Ifchannel c corresponds to a file descriptor  on  which  9P  isserved, devdir sets both the flag bit QTMOUNT in dp->qid.typeand the flag bit DMMOUNT in dp->mode (see  sys-export(2)  andmount in sys-bind(2)). A  simple  name  space  can  be represented in a driver by anarray of Dirtab structures.  The array  is  typically  staticwhen the names and permissions are static, but can be dynami‐cally allocated and initialised if required.   The  structureof 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 ifthe default devgen function is used.  On the other hand,  thename `..'  must never appear in a Dirtab table.  Drivers thatsupport a directory hierarchy  must  walk  up  the  hierarchytowards the root when their walk function receives `..'  as afile name component.  The  name  `.'   is  never  seen  by  adriver. The  devdirread,  devopen, devstat, and devwalk functions alltake a gen function argument,  of  type  Devgen,  which  theyinvoke  to  retrieve  the  items  in a Chan that represents adirectory.  Gen takes a channel c (a directory), a file  name(which  is  nil  except  during  devwalk), an array of Dirtabstructures tab of length ntab, and  a  table  index  i.   Thefunctions  calling  gen  expect it to place the i'th entry inthe directory into *dp.  It should return 1 if the  call  wassuccessful, -1 if i is beyond the index of the last directoryentry, or 0 if there is no entry at i, but there are  entriesbeyond  it.   When i has the special value DEVDOTDOT then genshould set *dp to reflect the parent of c; if  c  is  a  one-level device directory, then `..' is equivalent to `.'.  Cus‐tom implementations of gen often ignore devtab,  and  insteadreturn  their  own  dynamically  generated  set  of directoryentries  from  some  other  source.   Exceptionally,   duringdevwalk  a  non-nil  name  is  provided: it is the name beinglooked up, and a device-specific gen  can  short-circuit  thesearch by returning -1 if the name does not exist, or fillingin *dp and returning 1 if it does exist. The function devgen is compatible with Devgen; it returns thei'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  bythe  path  encoded  in  name, which is an array of strings oflength nname.  It provides the interface  to  walk(5)  withinthe kernel, and that specification must be well understood toappreciate all the nuances of its interface.  Fortunately, innearly all device drivers, a device's walk function typicallypasses its parameters on to devwalk (adding the device's  ownDirtab  array  as the the value of tab), and simply returningthe result of devwalk. Devwalk walks c using the given set of names, and if the walkis successful, the channel nc will refer to the result of thewalk (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  canreturn  a  partial result, represented by a dynamically allo‐cated 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 thecurrently-sought element of name.  If the  first  element  isnot  found,  devwalk  returns  nil;  otherwise,  it returns aWalkqid value in which nqid elements of the array qid are setto the qids (see intro(5)) of each valid element of name.  Ifall nname elements were  successfully  traversed,  then  nqidwill have the value nname, and clone will refer to the resultof the walk, which is either nc if given, or the new  channelallocated  by  devwalk.  Otherwise, at least one element suc‐ceeded and nqid is less than nname and clone is nil.   On  anerror  or  incomplete  walk,  the  error string is set to theerror that stopped the walk (eg, Enonexist or Enotdir). Devstat fills the array of bytes db with data in  the  formatproduced  by  stat(5)  that  describes the file referenced bychannel c, which must have a corresponding entry returned  bygen  (ie, an entry with matching Qid.path).  If c is a commu‐nications channel connecting a 9P server to a  current  mountpoint,  the DMMOUNT bit is set in the resulting Dir.mode, andQTMOUNT is set in Dir.qid.type.  As in stat(5), the length ofthe  data  written  to  db  varies;  if more than n bytes areneeded, 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 corresponds  to  a  directory  (ie,  QTDIR  is  set  inc->qid.type),  it  is taken to be a stat of a notional direc‐tory containing the files listed in tab.  Dirstat then buildsthe  corresponding  Dir structure: its Dir.name is taken fromc->path->elem; the length is DIRLEN*nelem(tab); and  Dir.permis 0555 (read-execute for all). Devdirread calls gen to obtain successive Dir structures rep‐resenting entries in the open directory c.   These  are  con‐verted  to  standard  format  (see convD2M in styx(10.2)) andplaced in the buffer b.  It returns the number  of  bytes  inthe  result.   At most n bytes will be returned, in multiplesof DIRLEN.  Because the kernel maintains the  current  offsetin c, successive calls to devdirread return successive direc‐tory components. Devopen is called to check and complete  a  request  to  openchannel  c  for I/O according to omode (the open mode of sys-open(2)).   It  calls  gen  to  obtain  successive  directoryentries  which  it searches for a Qid matching that of c, andensures that the current user has permission to open  c  withthe given mode, omode, and that the mode itself is valid (seeopenmode below).  Permission is checked against  the  permis‐sion  in the matching entry.  If no matching Qid is found, itis assumed that the notional parent directory  of  the  filesrepresented  in  tab  is  to  be opened.  Such a directory isdeemed to have mode 0555, allowing access  by  any  user.   Adirectory  can  only  be opened for reading (OREAD).  Devopenreturns the channel c on success.   Last,  it  sets  the  bitCOPEN  in  Chan.flag  to mark c as open.  This convention canalways be relied upon by the driver's close function to  tellif  an open succeeded.  On the otherhand, if the open requestwas unsuccessful, devopen raises an  appropriate  error(10.2)and does not return. Devbread  returns a Block (see allocb(10.2)) containing up ton bytes read, using devtab[c->type]->read, from c starting atthe  given  offset.   The  read pointer in the returned Blockpoints to the start of the data; the write pointer points  tothe next available byte. Devbwrite  writes  the  data in Block bp to the file c at thegiven     offset,     using      the      write      functiondevtab[c->type]->write.   It  then  frees  the  block list bpbefore returning the number of bytes written. Most built-in devices do not allow create, remove or wstat ontheir  files.   Devcreate,  devremove  and devwstat are stubsthat raise an error(10.2), Eperm.  They can be named directlyin  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  donothing.  A device driver puts them in its Dev structure whenit need take no action on device  reset,  initialisation,  orshut down. Openmode  is  used  by a driver that does not use devopen, tocheck the open mode it receives in its open  routine.   Open‐mode  returns  mode  o,  the mode parameter to sys-open(2) orsys-create, shorn of OTRUNC and similar options, and  reducedto  one  of  OREAD,  OWRITE  or  ORDWR.  In particular, OEXECbecomes  OREAD  within  the  kernel.   Openmode   raises   anerror(10.2)  Ebadarg instead of returning, if o is an invalidmode (eg, reserved bits set). == source/emu/port/dev.c/os/port/dev.c == see ALSOallocb(10.2), eve(10.2), qio(10.2)