[xiph-commits] r12312 - in trunk: . fusd fusd/doc fusd/examples fusd/include fusd/kfusd fusd/libfusd

xiphmont at svn.xiph.org xiphmont at svn.xiph.org
Sat Jan 13 00:40:16 PST 2007


Author: xiphmont
Date: 2007-01-13 00:39:56 -0800 (Sat, 13 Jan 2007)
New Revision: 12312

Added:
   trunk/fusd/
   trunk/fusd/ChangeLog
   trunk/fusd/LICENSE
   trunk/fusd/Makefile
   trunk/fusd/README
   trunk/fusd/doc/
   trunk/fusd/doc/.cvsignore
   trunk/fusd/doc/Makefile
   trunk/fusd/doc/fusd-html-docs.tar.gz
   trunk/fusd/doc/fusd.pdf
   trunk/fusd/doc/fusd.ps
   trunk/fusd/doc/fusd.tex
   trunk/fusd/doc/html.sty
   trunk/fusd/doc/make-examples.pl
   trunk/fusd/examples/
   trunk/fusd/examples/console-read.c
   trunk/fusd/examples/drums.c
   trunk/fusd/examples/drums2.c
   trunk/fusd/examples/drums3.c
   trunk/fusd/examples/echo.c
   trunk/fusd/examples/helloworld
   trunk/fusd/examples/helloworld.c
   trunk/fusd/examples/ioctl.c
   trunk/fusd/examples/logring.c
   trunk/fusd/examples/pager.c
   trunk/fusd/examples/uid-filter.c
   trunk/fusd/include/
   trunk/fusd/include/fusd.h
   trunk/fusd/include/fusd_msg.h
   trunk/fusd/include/kfusd.h
   trunk/fusd/kfusd/
   trunk/fusd/kfusd/Makefile
   trunk/fusd/kfusd/Module.symvers
   trunk/fusd/kfusd/kfusd.c
   trunk/fusd/libfusd/
   trunk/fusd/libfusd/Makefile
   trunk/fusd/libfusd/libfusd.c
   trunk/fusd/make.include
Log:
Place personal working copy of fusd into revision control



Added: trunk/fusd/ChangeLog
===================================================================
--- trunk/fusd/ChangeLog	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/ChangeLog	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,98 @@
+v monty-1.11 January 11, 2007
+
+	Update for 2.6.recent (tested on 2.6.17 through 19)
+	Update module option code
+	Replace illegal type puns with unions
+	Eliminate all dead devfs code
+	Fix kernel panics caused by incorrect error handling
+	Build system updates
+	
+v kor-1.10-11 April 9, 2006
+
+	Added fixes for kernels 2.6.15 and 2.6.12 (Thanks to Joachim Forster)
+	Fixed some compiler warnings on amd64 architecture
+
+v kor-1.10-10 Sept 17, 2005
+
+	Added support for kernels 2.6.13 
+	Broke previous kernels
+
+v kor-1.10-9: Jun 18, 2005
+
+	Added support for mmap
+	Added support for simultaneous requests on the same file descriptor
+	from separate processes.
+
+v kor-1.10-8: Feb 10, 2005
+
+	Fixed some shutdown issues for non-devfs systems.
+
+v kor-1.10-7: Feb 9, 2005
+
+	Modified to work udev
+
+v kor-1.10-6: Feb 17, 2004.
+
+	Modified to work with the 2.6 kernel and devfs.
+
+v 1.10: August 19, 2003
+
+	First UCLA release -- no longer publically maintained or released
+	by Sensoria.  This version is the UCLA (public) fork.
+
+	Kernel module now, finally, has correct fine-grained locking that
+	does not assume atomicity of kernel code.  In other words, FUSD is
+	now safe for SMP machines, preemptible kernels, etc.
+
+	Python bindings have been contributed by Brian Warner.
+	
+	The old /dev/fusd control file has been moved into a subdirectory,
+	/dev/fusd/control.
+	
+	Human-readable status is now available in /dev/fusd/status.
+
+	By doing an ioctl() on /dev/fusd/status, it can also give you
+	binary status information -- reads will return an array of
+	fusd_status_t structures.
+	
+	Many, many subtle bugs have been fixed (e.g. rare race
+	conditions).
+
+	Lots of updates and bug-fixes to the documentation, which was
+	carefully read by a number of people who were actually trying to
+	use it.
+	
+	
+v 1.04: February 5, 2002
+
+	Change from the point of view of clients: Selecting on an FD being
+	provided by a FUSD driver will return as part of the exception set
+	if the driver disappears.  Useful for client programs that want to
+	gracefully handle driver crashes.
+
+	Some changes to get semantics of poll_diff (closer to) correct -
+	will require some more changes later
+	
+	Reduced max number of messages dispatched per call to
+	fusd_dispatch.  very slightly less efficient, but more fair.
+	
+	Protocol change - max name length now 47 instead of 31
+
+	Minor kernel module fixes: Fixed the malloc.h/slab.h confusion,
+	and added MODULE_LICENSE.
+
+	Minor API change: fusd_register now takes a const char *
+	name instead of a char *name.
+
+	
+v 1.03: October 3, 2001
+	Documentation fixs and a clarification of the license.
+	
+v 1.02,
+v 1.01: October 1, 2001
+	Various Makefile issues fixed that were keeping the package from
+	building.
+	
+v 1.00: September 28, 2001
+	Initial public release.
+	

Added: trunk/fusd/LICENSE
===================================================================
--- trunk/fusd/LICENSE	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/LICENSE	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,35 @@
+
+FUSD is free software, distributed under a GPL-compatible license (the
+``new'' BSD license, with the advertising clause removed).  The
+license is enumerated in its entirety below.
+
+-------------------------------------------------------------------------
+
+Copyright (c) 2001, Sensoria Corporation.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name of the Sensoria Corporation nor the names of
+  its contributors may be used to endorse or promote products derived
+  from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Added: trunk/fusd/Makefile
===================================================================
--- trunk/fusd/Makefile	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/Makefile	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,38 @@
+#####
+#
+#  Makefile for FUSD
+#
+PREFIX = /usr/local
+LIBDIR = $(PREFIX)/lib
+INCDIR = $(PREFIX)/include
+
+CC      = gcc 
+LD      = gcc
+INSTALL = install
+STRIP   = strip
+PREFIX  = /usr/local
+BINDIR  = $(PREFIX)/bin
+ETCDIR  = /etc/$(TARGET)
+MANDIR  = $(PREFIX)/man
+
+GCF  = -std=gnu99 -O2 -g -I../include
+SCF  = -fPIC
+SLF  = -shared -nostdlib
+
+export
+
+####################################################
+
+SUBDIRS = kfusd libfusd
+
+default: 
+	$(MAKE) -C libfusd 
+	$(MAKE) -C kfusd
+
+install:
+	$(MAKE) -C libfusd install
+	$(MAKE) -C kfusd install
+
+clean:
+	$(MAKE) -C kfusd clean
+	$(MAKE) -C libfusd clean

Added: trunk/fusd/README
===================================================================
--- trunk/fusd/README	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/README	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,139 @@
+
+FUSD: A Linux Framework for User-Space Devices
+----------------------------------------------
+
+Welcome to FUSD!
+
+This is FUSD snapshot 20070111, released 11 January 2007.  You can get
+the most recent source, along with online documentation, from xiph.org
+SVN:
+
+http://svn.xiph.org/trunk/fusd
+
+There is extensive documentation available in the 'doc' directory.
+The FUSD User Manual is available in PDF, Postscript, and HTML format.
+Most of this documentation dates from earlier versions of fusd; until
+it is fully updated, it may not cover all features that exist in the
+current version of fusd.
+
+FUSD is free and open source software, released under a BSD-style
+license.  See the file 'LICENSE' for details.
+
+
+QUICK START GUIDE
+=================
+
+Instructions for the impatient:
+
+1- Make sure you're using a system running Linux 2.6.x with udev; this
+version of fusd is incompatable with the now-deprecated devfs.  If the
+kernel is a packaged version from a distribution, also verify any
+optional packages needed for building new kernel modules are also
+installed.
+
+2- 'make ; make install' builds everything including examples, then
+installs the libraries, includes and kernel module.
+
+3- Update the udev configuration (usually in /etc/udev/rules.d/) to
+include the following rule:
+
+   # fusd device
+   SUBSYSTEM=="fusd",                      NAME="fusd/%k"
+
+After updating, restart udevd (skill udevd; udevd -d).
+
+4- Insert the FUSD kernel module (modprobe kfusd)
+
+5- Verify the fusd devices /dev/fusd/status and /dev/fusd/control
+exist.  If the modprobe succeeds but no fusd devices appear,
+doublecheck the udev rule config change and make sure udevd restarted
+successfully.  The kfusd kernel module must be inserted after udev has
+been correctly configured and restarted.
+
+6- Try  running the helloworld  example program (examples/helloworld).
+When  helloworld  is  running,  'cat  /dev/helloworld'  should  return
+'Hello, world!'.
+
+7- For more information, read the User's Manual in the 'doc' directory.
+
+WHAT IS FUSD?
+=============
+
+FUSD (pronounced "fused") is a Linux framework for proxying device
+file callbacks into user-space, allowing device files to be
+implemented by daemons instead of kernel code.  Despite being
+implemented in user-space, FUSD devices can look and act just like any
+other file under /dev which is implemented by kernel callbacks.
+
+A user-space device driver can do many of the things that kernel
+drivers can't, such as perform a long-running computation, block while
+waiting for an event, or read files from the file system.  Unlike
+kernel drivers, a user-space device driver can use other device
+drivers--that is, access the network, talk to a serial port, get
+interactive input from the user, pop up GUI windows, or read from
+disks.  User-space drivers implemented using FUSD can be much easier to
+debug; it is impossible for them to crash the machine, are easily
+traceable using tools such as gdb, and can be killed and restarted
+without rebooting.  FUSD drivers don't have to be in C--Perl, Python,
+or any other language that knows how to read from and write to a file
+descriptor can work with FUSD.  User-space drivers can be swapped out,
+whereas kernel drivers lock physical memory.
+
+FUSD drivers are conceptually similar to kernel drivers: a set of
+callback functions called in response to system calls made on file
+descriptors by user programs.  FUSD's C library provides a device
+registration function, similar to the kernel's devfs_register_chrdev()
+function, to create new devices.  fusd_register() accepts the device
+name and a structure full of pointers.  Those pointers are callback
+functions which are called in response to certain user system
+calls--for example, when a process tries to open, close, read from, or
+write to the device file.  The callback functions should conform to
+the standard definitions of POSIX system call behavior.  In many ways,
+the user-space FUSD callback functions are identical to their kernel
+counterparts.
+
+The proxying of kernel system calls that makes this kind of program
+possible is implemented by FUSD, using a combination of a kernel
+module and cooperating user-space library.  The kernel module
+implements a character device, /dev/fusd, which is used as a control
+channel between the two.  fusd_register() uses this channel to send a
+message to the FUSD kernel module, telling the name of the device the
+user wants to register.  The kernel module, in turn, registers that
+device with the kernel proper using devfs.  devfs and the kernel don't
+know anything unusual is happening; it appears from their point of
+view that the registered devices are simply being implemented by the
+FUSD module.
+
+Later, when kernel makes a callback due to a system call (e.g. when
+the character device file is opened or read), the FUSD kernel module's
+callback blocks the calling process, marshals the arguments of the
+callback into a message and sends it to user-space.  Once there, the
+library half of FUSD unmarshals it and calls whatever user-space
+callback the FUSD driver passed to fusd_register().  When that
+user-space callback returns a value, the process happens in reverse:
+the return value and its side-effects are marshaled by the library
+and sent to the kernel.  The FUSD kernel module unmarshals this
+message, matches it up with a corresponding outstanding request, and
+completes the system call.  The calling process is completely unaware
+of this trickery; it simply enters the kernel once, blocks, unblocks,
+and returns from the system call---just as it would for any other
+blocking call.
+
+One of the primary design goals of FUSD is stability.  It should
+not be possible for a FUSD driver to corrupt or crash the kernel,
+either due to error or malice.  Of course, a buggy driver itself may
+corrupt itself (e.g., due to a buffer overrun).  However, strict error
+checking is implemented at the user-kernel boundary which should
+prevent drivers from corrupting the kernel or any other user-space
+process---including the errant driver's own clients, and other FUSD
+drivers.
+
+For more information, please see the comprehensive documentation in
+the 'doc' directory.
+
+ Jeremy Elson <jelson at circlemud.org>
+ August 19, 2003
+
+ updated,
+ Monty <monty at xiph.org>
+ January 11, 2007

Added: trunk/fusd/doc/.cvsignore
===================================================================
--- trunk/fusd/doc/.cvsignore	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/.cvsignore	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,7 @@
+*.out
+*.log
+*.aux
+*.dvi
+*.toc
+*.lop
+*.[ch].example

Added: trunk/fusd/doc/Makefile
===================================================================
--- trunk/fusd/doc/Makefile	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/Makefile	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,24 @@
+default: docs
+
+clean:
+	@rm -f *.[ch].example
+
+examples:
+	@rm -f *.[ch].example
+	@./make-examples.pl ../examples/*.[ch]
+
+docs: examples
+	latex fusd
+	latex fusd
+
+html:
+	latex2html fusd
+	rm -f fusd/*.pl
+	rm -f fusd/images.*
+	rm -f fusd/WARNINGS
+	tar czvf fusd-html-docs.tar.gz fusd
+
+pdf: docs
+	dvips -t Letter -Ppdf -G0 fusd.dvi
+	ps2pdf fusd.ps
+

Added: trunk/fusd/doc/fusd-html-docs.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/fusd/doc/fusd-html-docs.tar.gz
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/fusd/doc/fusd.pdf
===================================================================
(Binary files differ)


Property changes on: trunk/fusd/doc/fusd.pdf
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/fusd/doc/fusd.ps
===================================================================
--- trunk/fusd/doc/fusd.ps	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/fusd.ps	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,3951 @@
+%!PS-Adobe-2.0
+%%Creator: dvips(k) 5.86 Copyright 1999 Radical Eye Software
+%%Title: fusd.dvi
+%%Pages: 37
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 612 792
+%%DocumentFonts: Times-Roman Times-Bold Courier Times-Italic
+%%+ Times-BoldItalic CMSY10 CMR10
+%%EndComments
+%DVIPSWebPage: (www.radicaleye.com)
+%DVIPSCommandLine: dvips -t Letter -Ppdf -G0 fusd.dvi
+%DVIPSParameters: dpi=8000, compressed
+%DVIPSSource:  TeX output 2003.08.20:1522
+%%BeginProcSet: tex.pro
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/CharBuilder{save 3 1 roll S A/base get 2 index get S
+/BitMaps get S get/Cd X pop/ctr 0 N Cdx 0 Cx Cy Ch sub Cx Cw add Cy
+setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx sub Cy .1 sub]{Ci}imagemask
+restore}B/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+%%BeginProcSet: alt-rule.pro
+%!
+% Patch by TVZ
+% Makes dvips files draw rules with stroke rather than fill.
+% Makes narrow rules more predictable at low resolutions
+% after distilling to PDF.
+% May have unknown consequences for very thick rules.
+% Tested only with dvips 5.85(k).
+TeXDict begin
+/QV {
+  gsave newpath /ruleY X /ruleX X
+  Rx Ry gt
+  { ruleX ruleY Ry 2 div sub moveto Rx 0 rlineto Ry }
+  { ruleX Rx 2 div add ruleY moveto 0 Ry neg rlineto Rx }
+  ifelse
+  setlinewidth 0 setlinecap stroke grestore
+} bind def
+end
+
+%%EndProcSet
+%%BeginProcSet: texc.pro
+%!
+/TeXDict 300 dict def TeXDict begin/N{def}def/B{bind def}N/S{exch}N/X{S
+N}B/A{dup}B/TR{translate}N/isls false N/vsize 11 72 mul N/hsize 8.5 72
+mul N/landplus90{false}def/@rigin{isls{[0 landplus90{1 -1}{-1 1}ifelse 0
+0 0]concat}if 72 Resolution div 72 VResolution div neg scale isls{
+landplus90{VResolution 72 div vsize mul 0 exch}{Resolution -72 div hsize
+mul 0}ifelse TR}if Resolution VResolution vsize -72 div 1 add mul TR[
+matrix currentmatrix{A A round sub abs 0.00001 lt{round}if}forall round
+exch round exch]setmatrix}N/@landscape{/isls true N}B/@manualfeed{
+statusdict/manualfeed true put}B/@copies{/#copies X}B/FMat[1 0 0 -1 0 0]
+N/FBB[0 0 0 0]N/nn 0 N/IEn 0 N/ctr 0 N/df-tail{/nn 8 dict N nn begin
+/FontType 3 N/FontMatrix fntrx N/FontBBox FBB N string/base X array
+/BitMaps X/BuildChar{CharBuilder}N/Encoding IEn N end A{/foo setfont}2
+array copy cvx N load 0 nn put/ctr 0 N[}B/sf 0 N/df{/sf 1 N/fntrx FMat N
+df-tail}B/dfs{div/sf X/fntrx[sf 0 0 sf neg 0 0]N df-tail}B/E{pop nn A
+definefont setfont}B/Cw{Cd A length 5 sub get}B/Ch{Cd A length 4 sub get
+}B/Cx{128 Cd A length 3 sub get sub}B/Cy{Cd A length 2 sub get 127 sub}
+B/Cdx{Cd A length 1 sub get}B/Ci{Cd A type/stringtype ne{ctr get/ctr ctr
+1 add N}if}B/id 0 N/rw 0 N/rc 0 N/gp 0 N/cp 0 N/G 0 N/CharBuilder{save 3
+1 roll S A/base get 2 index get S/BitMaps get S get/Cd X pop/ctr 0 N Cdx
+0 Cx Cy Ch sub Cx Cw add Cy setcachedevice Cw Ch true[1 0 0 -1 -.1 Cx
+sub Cy .1 sub]/id Ci N/rw Cw 7 add 8 idiv string N/rc 0 N/gp 0 N/cp 0 N{
+rc 0 ne{rc 1 sub/rc X rw}{G}ifelse}imagemask restore}B/G{{id gp get/gp
+gp 1 add N A 18 mod S 18 idiv pl S get exec}loop}B/adv{cp add/cp X}B
+/chg{rw cp id gp 4 index getinterval putinterval A gp add/gp X adv}B/nd{
+/cp 0 N rw exit}B/lsh{rw cp 2 copy get A 0 eq{pop 1}{A 255 eq{pop 254}{
+A A add 255 and S 1 and or}ifelse}ifelse put 1 adv}B/rsh{rw cp 2 copy
+get A 0 eq{pop 128}{A 255 eq{pop 127}{A 2 idiv S 128 and or}ifelse}
+ifelse put 1 adv}B/clr{rw cp 2 index string putinterval adv}B/set{rw cp
+fillstr 0 4 index getinterval putinterval adv}B/fillstr 18 string 0 1 17
+{2 copy 255 put pop}for N/pl[{adv 1 chg}{adv 1 chg nd}{1 add chg}{1 add
+chg nd}{adv lsh}{adv lsh nd}{adv rsh}{adv rsh nd}{1 add adv}{/rc X nd}{
+1 add set}{1 add clr}{adv 2 chg}{adv 2 chg nd}{pop nd}]A{bind pop}
+forall N/D{/cc X A type/stringtype ne{]}if nn/base get cc ctr put nn
+/BitMaps get S ctr S sf 1 ne{A A length 1 sub A 2 index S get sf div put
+}if put/ctr ctr 1 add N}B/I{cc 1 add D}B/bop{userdict/bop-hook known{
+bop-hook}if/SI save N @rigin 0 0 moveto/V matrix currentmatrix A 1 get A
+mul exch 0 get A mul add .99 lt{/QV}{/RV}ifelse load def pop pop}N/eop{
+SI restore userdict/eop-hook known{eop-hook}if showpage}N/@start{
+userdict/start-hook known{start-hook}if pop/VResolution X/Resolution X
+1000 div/DVImag X/IEn 256 array N 2 string 0 1 255{IEn S A 360 add 36 4
+index cvrs cvn put}for pop 65781.76 div/vsize X 65781.76 div/hsize X}N
+/p{show}N/RMat[1 0 0 -1 0 0]N/BDot 260 string N/Rx 0 N/Ry 0 N/V{}B/RV/v{
+/Ry X/Rx X V}B statusdict begin/product where{pop false[(Display)(NeXT)
+(LaserWriter 16/600)]{A length product length le{A length product exch 0
+exch getinterval eq{pop true exit}if}{pop}ifelse}forall}{false}ifelse
+end{{gsave TR -.1 .1 TR 1 1 scale Rx Ry false RMat{BDot}imagemask
+grestore}}{{gsave TR -.1 .1 TR Rx Ry scale 1 1 false RMat{BDot}
+imagemask grestore}}ifelse B/QV{gsave newpath transform round exch round
+exch itransform moveto Rx 0 rlineto 0 Ry neg rlineto Rx neg 0 rlineto
+fill grestore}B/a{moveto}B/delta 0 N/tail{A/delta X 0 rmoveto}B/M{S p
+delta add tail}B/b{S p tail}B/c{-4 M}B/d{-3 M}B/e{-2 M}B/f{-1 M}B/g{0 M}
+B/h{1 M}B/i{2 M}B/j{3 M}B/k{4 M}B/w{0 rmoveto}B/l{p -4 w}B/m{p -3 w}B/n{
+p -2 w}B/o{p -1 w}B/q{p 1 w}B/r{p 2 w}B/s{p 3 w}B/t{p 4 w}B/x{0 S
+rmoveto}B/y{3 2 roll p a}B/bos{/SS save N}B/eos{SS restore}B end
+
+%%EndProcSet
+%%BeginProcSet: 8r.enc
+% @@psencodingfile@{
+%   author = "S. Rahtz, P. MacKay, Alan Jeffrey, B. Horn, K. Berry",
+%   version = "0.6",
+%   date = "22 June 1996",
+%   filename = "8r.enc",
+%   email = "kb@@mail.tug.org",
+%   address = "135 Center Hill Rd. // Plymouth, MA 02360",
+%   codetable = "ISO/ASCII",
+%   checksum = "119     662    4424",
+%   docstring = "Encoding for TrueType or Type 1 fonts to be used with TeX."
+% @}
+% 
+% Idea is to have all the characters normally included in Type 1 fonts
+% available for typesetting. This is effectively the characters in Adobe
+% Standard Encoding + ISO Latin 1 + extra characters from Lucida.
+% 
+% Character code assignments were made as follows:
+% 
+% (1) the Windows ANSI characters are almost all in their Windows ANSI
+% positions, because some Windows users cannot easily reencode the
+% fonts, and it makes no difference on other systems. The only Windows
+% ANSI characters not available are those that make no sense for
+% typesetting -- rubout (127 decimal), nobreakspace (160), softhyphen
+% (173). quotesingle and grave are moved just because it's such an
+% irritation not having them in TeX positions.
+% 
+% (2) Remaining characters are assigned arbitrarily to the lower part
+% of the range, avoiding 0, 10 and 13 in case we meet dumb software.
+% 
+% (3) Y&Y Lucida Bright includes some extra text characters; in the
+% hopes that other PostScript fonts, perhaps created for public
+% consumption, will include them, they are included starting at 0x12.
+% 
+% (4) Remaining positions left undefined are for use in (hopefully)
+% upward-compatible revisions, if someday more characters are generally
+% available.
+% 
+% (5) hyphen appears twice for compatibility with both ASCII and Windows.
+% 
+/TeXBase1Encoding [
+% 0x00 (encoded characters from Adobe Standard not in Windows 3.1)
+  /.notdef /dotaccent /fi /fl
+  /fraction /hungarumlaut /Lslash /lslash
+  /ogonek /ring /.notdef
+  /breve /minus /.notdef 
+% These are the only two remaining unencoded characters, so may as
+% well include them.
+  /Zcaron /zcaron 
+% 0x10
+ /caron /dotlessi 
+% (unusual TeX characters available in, e.g., Lucida Bright)
+ /dotlessj /ff /ffi /ffl 
+ /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef
+ % very contentious; it's so painful not having quoteleft and quoteright
+ % at 96 and 145 that we move the things normally found there down to here.
+ /grave /quotesingle 
+% 0x20 (ASCII begins)
+ /space /exclam /quotedbl /numbersign
+ /dollar /percent /ampersand /quoteright
+ /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash
+% 0x30
+ /zero /one /two /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less /equal /greater /question
+% 0x40
+ /at /A /B /C /D /E /F /G /H /I /J /K /L /M /N /O
+% 0x50
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+% 0x60
+ /quoteleft /a /b /c /d /e /f /g /h /i /j /k /l /m /n /o
+% 0x70
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde
+ /.notdef % rubout; ASCII ends
+% 0x80
+ /.notdef /.notdef /quotesinglbase /florin
+ /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft
+ /OE /.notdef /.notdef /.notdef
+% 0x90
+ /.notdef /.notdef /.notdef /quotedblleft
+ /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright
+ /oe /.notdef /.notdef /Ydieresis
+% 0xA0
+ /.notdef % nobreakspace
+ /exclamdown /cent /sterling
+ /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft
+ /logicalnot
+ /hyphen % Y&Y (also at 45); Windows' softhyphen
+ /registered
+ /macron
+% 0xD0
+ /degree /plusminus /twosuperior /threesuperior
+ /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright
+ /onequarter /onehalf /threequarters /questiondown
+% 0xC0
+ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis
+ /Igrave /Iacute /Icircumflex /Idieresis
+% 0xD0
+ /Eth /Ntilde /Ograve /Oacute
+ /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex
+ /Udieresis /Yacute /Thorn /germandbls
+% 0xE0
+ /agrave /aacute /acircumflex /atilde
+ /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis
+ /igrave /iacute /icircumflex /idieresis
+% 0xF0
+ /eth /ntilde /ograve /oacute
+ /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex
+ /udieresis /yacute /thorn /ydieresis
+] def
+
+%%EndProcSet
+%%BeginProcSet: texps.pro
+%!
+TeXDict begin/rf{findfont dup length 1 add dict begin{1 index/FID ne 2
+index/UniqueID ne and{def}{pop pop}ifelse}forall[1 index 0 6 -1 roll
+exec 0 exch 5 -1 roll VResolution Resolution div mul neg 0 0]/Metrics
+exch def dict begin Encoding{exch dup type/integertype ne{pop pop 1 sub
+dup 0 le{pop}{[}ifelse}{FontMatrix 0 get div Metrics 0 get div def}
+ifelse}forall Metrics/Metrics currentdict end def[2 index currentdict
+end definefont 3 -1 roll makefont/setfont cvx]cvx def}def/ObliqueSlant{
+dup sin S cos div neg}B/SlantFont{4 index mul add}def/ExtendFont{3 -1
+roll mul exch}def/ReEncodeFont{CharStrings rcheck{/Encoding false def
+dup[exch{dup CharStrings exch known not{pop/.notdef/Encoding true def}
+if}forall Encoding{]exch pop}{cleartomark}ifelse}if/Encoding exch def}
+def end
+
+%%EndProcSet
+%%BeginFont: CMR10
+%!PS-AdobeFont-1.1: CMR10 1.00B
+%%CreationDate: 1992 Feb 19 19:54:52
+
+% Copyright (C) 1997 American Mathematical Society.  All Rights Reserved.
+
+11 dict begin
+/FontInfo 7 dict dup begin
+/version (1.00B) readonly def
+/Notice (Copyright (C) 1997 American Mathematical Society. All Rights Reserved) readonly def
+/FullName (CMR10) readonly def
+/FamilyName (Computer Modern) readonly def
+/Weight (Medium) readonly def
+/ItalicAngle 0 def
+/isFixedPitch false def
+end readonly def
+/FontName /CMR10 def
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/Encoding 256 array
+0 1 255 {1 index exch /.notdef put} for
+dup 48 /zero put
+readonly def
+/FontBBox{-251 -250 1009 969}readonly def
+/UniqueXX 5000793 def
+currentdict end
+currentfile eexec
+8053514d28ec28da1630165fab262882d3fca78881823c5537fe6c3dda8ee5b8
+97e17cb027f5c73fdbb56b0a7c25fc3512b55fe8f3acfbffcc7f4a382d8299cc
+8fd37d3cea49dabdca92847af0560b404ef71134b0f3d99934fc9d0b4e602011
+b9cfb856c23f958f3c5a2fbe0ef8587d1f5774879c324e51fcb22888b74f2415
+50d7401eb990d4f3a7af635198422283cac1b6cd446ddbcbd915db9bff88844e
+784c6bf7389803d9450b0c21756a017306457c7e62c1d269f306bd3402e266de
+fc3b5e7d8a8d2f5bf0fe6ddd40d07391df4fad4a6018dce29a2b8f692b29f202
+3a7c0e66de8ed85c14f1f8492167357f51a7e84cc5d92e0fee4d81cf7fbc8de5
+2d2e7bb57142033993f9c08c315abade8dbc4a732e84e142d3bee51557910e12
+cd8aa37c459a5e6b7f5269f59078aba3be4641a11ac48d0b625c8325b38ec08e
+4c9e5e7fed976a5650d99d82114f449b9ca14c4ec957702295a39a93ef93f618
+99b8ea06b092c3c1e503e6e436e0a9fa22576c8930ab3dc8c20f5d82b69cddf8
+ff4dacfa9c54bed5a3aa3ea5b129fe96be632843b9b6bc91b615581a985db56b
+1e01ca60ee69ca92cf5c0882ece62edad3e106d835348822400f0b66af658f2a
+e56ed08f8b0010571807009b73ab12a8cf14ca6c71f03c2a48c500f9d62266af
+154a6375ff600d9bac3f05ce34142d6867a79581c533176bb2f3117336671e2e
+44638a97167e2ea9644e31ea16c2ad2990ea33c54001e0c8156e6de8ab6a4d40
+a7137ba275f39589fea2e2db8256adc103d6f9cc038037a47e8fd469c5f98a5e
+3c15bd4ace40d340018b1cff7d1ed8abb0ac57b5b5a2c20a51957b96c453edb7
+dae5affd91a46d938fe0a13363001d844ded4323f1ee6d30012aea19b024a552
+315505535c85dc26bad31e09c50e6512802976d298c4e90d0044c362e6bf3ab3
+62a454ee93de25ce54411090c29e9d75c80ce26a84404bd9de3aee0e3f921ac5
+87f907572b8354a5c3165eea7e8b2ba4e4f834663063e9a307d8ff6f8b61acd8
+799bc105cddcf8f95f2160494fc01f7ec3effb95de571b8d7f27a2f9ad203c09
+cd4cffd98a119a507460e7fef5c910405e877aa1f8da68d1272e59e3adccef8d
+82e692b3229926fbe621080b7831a2ee248948dd3ae55082a939f02875a7a0eb
+7ae7d50270a576fbdfde7109c670f51be75b80b6fe3045ea50e2121024113974
+87d0093bc5c693c16b533a75364f7b986776dff23b9da648311e5807207d19ec
+ab067d8c2460245f75a171f077c74a36ecc1c8a5e4961a5cdf0ebdae91ff41c9
+32567a47d5b8d27de4e889c27bcbafaaf9c8435f714e81553aab7378dc00a75b
+610a34abcd06629ca15ade968ca0212e83afd16c2b285dac82ae1598fe7fca29
+3f2dbcb1ae9f13b16b7f7f116f491e898c05318fb243a1500d10bed3fc1ab9e2
+baaf89605bb4dae9507bdf2ced113151c8e78d0dd50dedffc8c3d82ed2eda703
+abc76d12fcd229a2072b8ff27d4b2254b2634ac81b6855757f47f95ea5f83532
+bd9ac8ae5fdd362dd687ec8469f57104aa0f52562719d8981502ad5de237ba75
+e61af35a6910efe2f726a06e7b8369739557675e4c4e5f6446b2ad1af93bd766
+c55d1db54c4ca76a850931e9abecbd3d52afd914c7bfc54d826a18df179585
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark
+
+%%EndFont 
+%%BeginFont: CMSY10
+%!PS-AdobeFont-1.1: CMSY10 1.0
+%%CreationDate: 1991 Aug 15 07:20:57
+
+% Copyright (C) 1997 American Mathematical Society.  All Rights Reserved.
+
+11 dict begin
+/FontInfo 7 dict dup begin
+/version (1.0) readonly def
+/Notice (Copyright (C) 1997 American Mathematical Society. All Rights Reserved) readonly def
+/FullName (CMSY10) readonly def
+/FamilyName (Computer Modern) readonly def
+/Weight (Medium) readonly def
+/ItalicAngle -14.035 def
+/isFixedPitch false def
+end readonly def
+/FontName /CMSY10 def
+/PaintType 0 def
+/FontType 1 def
+/FontMatrix [0.001 0 0 0.001 0 0] readonly def
+/Encoding 256 array
+0 1 255 {1 index exch /.notdef put} for
+dup 15 /bullet put
+dup 21 /greaterequal put
+readonly def
+/FontBBox{-29 -960 1116 775}readonly def
+/UniqueXX 5000820 def
+currentdict end
+currentfile eexec
+9b9c1569015f2c1d2bf560f4c0d52257bac8ced9b09a275ab231194ecf829352
+05826f4e975dcecec72b2cf3a18899ccde1fd935d09d813b096cc6b83cdf4f23
+b9a60db41f9976ac333263c908dcefcdbd4c8402ed00a36e7487634d089fd45a
+f4a38a56a4412c3b0baffaeb717bf0de9ffb7a8460bf475a6718b0c73c571145
+d026957276530530a2fbefc6c8f67052788e6703bb5ee49533870bca1f113ad8
+3750d597b842d8d96c423ba1273ddd32f3a54a912a443fcd44f7c3a6fe3956b0
+aa1e784aaec6fce08dae0c76da9d0a3eba57b98a6233d9e9f0c3f00fcc6b2c6a
+9ba23af389e6dfff4efec3de05d6276c6be417703ce508377f25960ef4ed83b4
+9b01b873f3a639ce00f356229b6477a081933fef3bb80e2b9dffa7f75567b1fa
+4d739b772f8d674e567534c6c5bbf1cf615372be20b18472f7aa58be8c216dbd
+df81cc0a86b6d8318ca68fe22c8af13b54d7576fe4ca5a7af9005ea5cc4edb79
+c0ab668e4fec4b7f5a9eb5f0e4c088cd818ecc4feb4b40ec8bd2981bf2336074
+b64c430035b7d4eb41c5714c319ae0c7f0df32ef5dcc37f92a96507d4eb9a5e5
+045913170d906e35107dafacb7af82cb8718807702f76fa29878a317eef3f7f4
+8284e3a42c2a9ed9c541585f0d34249ea150fce9634deee9ad9b375a1448ab5f
+04003aa5a88ae2e668c7807a0c9b7cc3746b783c0602a00361065126129730a4
+37c956a141a9455d4f353c8625c6bca2e508842eac84693ae8ba20053b3dec54
+7dadeecfb4913d243e65276c93bc007486705e24db3f8bbc554cc917e8b02ade
+fc9e37d370619b5d3417e0f32b594f763a21110da781e561bf3cef88359e333b
+493aaee20dfbbc6578c9b5ccd6cc24a5f992163ef8d85cc0755ff585c4baf230
+f063f036334661b3d10f73892f03060ed6a326472ef7659a8484dd8b316b6f68
+c1cf51b0cc356445a32714be670c950b7f947f9ccf84ada2ad79c32c0fda086c
+5328f8657d85da23df5bbeed5c0208bb940555acca206ae65a49ff540ea7b941
+8ff4c52cf2353ccfebd2657bc461d108b9
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000
+cleartomark
+
+%%EndFont 
+TeXDict begin 40258431 52099146 1000 8000 8000 (fusd.dvi)
+ at start /Fa 138[956 956 956 956 1[956 956 956 1[956 2[956
+956 1[956 956 956 956 1[956 55[956 956 40[{
+TeXBase1Encoding ReEncodeFont}17 1594.02 /Courier rf
+/Fb 207[553 48[{}1 1106.96 /CMR10 rf /Fc 136[797 797
+797 797 797 797 1[797 797 797 1[797 2[797 797 797 797
+797 797 797 1[797 7[797 3[797 1[797 797 1[797 797 797
+1[797 5[797 797 797 21[797 797 3[797 797 40[{
+TeXBase1Encoding ReEncodeFont}33 1328.35 /Courier rf
+/Fd 133[393 443 443 639 443 443 246 344 295 1[443 443
+443 689 246 443 246 246 443 443 295 393 443 393 443 393
+38[246 443 1[443 443 443 1[443 1[443 443 246 221 295
+45[{TeXBase1Encoding ReEncodeFont}36 885.568 /Times-Roman
+rf /Fe 198[332 332 332 332 332 332 332 332 332 332 48[{
+TeXBase1Encoding ReEncodeFont}10 664.176 /Times-Roman
+rf /Ff 198[387 387 387 387 387 387 387 387 387 387 48[{
+TeXBase1Encoding ReEncodeFont}10 774.872 /Times-Roman
+rf /Fg 234[861 5[553 15[{}2 1106.96 /CMSY10 rf /Fh 139[369
+517 4[739 36[517 33[442 39[{TeXBase1Encoding ReEncodeFont}5
+1328.35 /Times-BoldItalic rf /Fi 202[498 498 498 498
+498 498 48[{TeXBase1Encoding ReEncodeFont}6 996.264 /Times-Roman
+rf /Fj 134[664 664 959 664 739 442 517 590 739 739 664
+739 1107 369 739 1[369 739 664 442 590 739 590 739 664
+9[1328 1[959 886 739 959 1[812 2[1254 886 1033 1[517
+1033 1033 812 886 959 959 886 959 1[664 3[442 1[664 664
+664 664 664 664 664 664 664 2[332 442 332 4[442 36[739
+2[{TeXBase1Encoding ReEncodeFont}58 1328.35 /Times-Bold
+rf /Fk 134[491 491 738 491 553 308 431 431 1[553 553
+553 799 308 491 1[308 553 553 308 491 553 491 553 553
+14[676 8[369 799 25[277 369 277 4[369 36[553 2[{
+TeXBase1Encoding ReEncodeFont}31 1106.96 /Times-Italic
+rf /Fl 105[553 28[553 553 1[553 615 369 431 491 1[615
+553 615 922 308 615 1[308 615 553 369 491 615 491 615
+553 7[799 1[1107 1[799 1[615 2[676 1[799 1[738 861 1[431
+2[676 1[799 799 738 799 1[553 4[369 553 553 553 553 553
+553 553 553 553 553 1[277 369 277 4[369 39[{
+TeXBase1Encoding ReEncodeFont}53 1106.96 /Times-Bold
+rf /Fm 134[797 797 1[797 886 531 620 708 1[886 797 886
+1328 443 886 1[443 886 797 531 708 886 708 886 797 7[1151
+1[1594 1[1151 1[886 2[974 1[1151 1[1063 2[620 2[974 1063
+1151 1151 1063 1151 1[797 5[797 797 797 797 797 797 797
+797 797 797 2[531 45[{TeXBase1Encoding ReEncodeFont}48
+1594.02 /Times-Bold rf /Fn 104[1107 553 1[491 491 24[491
+553 553 799 553 553 308 431 369 553 553 553 553 861 308
+553 308 308 553 553 369 491 553 491 553 491 3[369 1[369
+1[799 799 1045 799 799 676 615 738 799 615 799 799 984
+676 799 431 369 799 799 615 676 799 738 738 799 1020
+491 3[308 308 553 553 553 553 553 553 553 553 553 553
+308 277 369 277 2[369 369 369 5[369 29[615 615 2[{
+TeXBase1Encoding ReEncodeFont}81 1106.96 /Times-Roman
+rf /Fo 103[664 26[664 664 664 664 664 664 664 664 664
+664 664 664 664 664 664 664 664 664 664 664 664 664 664
+664 664 664 664 664 664 1[664 1[664 664 664 664 664 664
+664 664 664 664 664 664 1[664 664 664 664 664 664 1[664
+664 664 664 664 664 664 664 664 664 664 664 664 664 664
+664 1[664 664 664 664 664 664 664 664 664 664 664 664
+664 664 664 664 664 664 664 664 1[664 664 664 33[{
+TeXBase1Encoding ReEncodeFont}88 1106.96 /Courier rf
+/Fp 134[664 5[517 442 2[664 664 1033 369 6[590 26[517
+4[812 69[{TeXBase1Encoding ReEncodeFont}10 1328.35 /Times-Roman
+rf /Fq 170[1658 1[1276 12[1403 1[1658 68[{TeXBase1Encoding ReEncodeFont}
+4 2295.84 /Times-Bold rf /Fr 135[1148 1658 1148 1148
+1[893 765 1[1148 1148 1148 1786 1[1148 1[638 2[765 1019
+1[1019 1[1019 20[1403 10[1658 19[765 45[{TeXBase1Encoding ReEncodeFont}
+19 2295.84 /Times-Roman rf /Fs 170[1988 1[1531 12[1531
+1[1988 9[766 58[{TeXBase1Encoding ReEncodeFont}5 2754.12
+/Times-Roman rf end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 8000dpi
+TeXDict begin
+%%BeginPaperSize: Letter
+letter
+%%EndPaperSize
+
+%%EndSetup
+%%Page: 1 1
+1 0 bop 22056 20963 a Fs(FUSD:)5362 25351 y Fr(A)574
+b(Linux)g Fq(F)p Fr(rame)-57 b(w)-23 b(ork)572 b(for)h
+Fq(U)p Fr(ser)-46 b(-)p Fq(S)p Fr(pace)575 b Fq(D)p Fr(e)-57
+b(vices)22361 59554 y Fp(Jeremy)332 b(Elson)19316 62985
+y Fo(jelson at circlemud.org)11014 64314 y
+(http://www.circlemud.org/jelson/software/fusd)22422
+68299 y Fn(19)278 b(August)f(2003)19102 69627 y(Documentation)j(for)c
+(FUSD)i(1.10)p eop
+%%Page: 1 2
+1 1 bop 863 2974 a Fm(Contents)863 5448 y Fl(1)1108 b(Intr)-20
+b(oduction)41971 b(1)2524 6776 y Fn(1.1)1163 b(What)277
+b(is)f(FUSD?)685 b(.)553 b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761
+b(1)2524 8105 y(1.2)1163 b(Ho)-28 b(w)278 b(does)f(FUSD)h(w)-11
+b(ork?)448 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(1)2524 9433
+y(1.3)1163 b(What)277 b(FUSD)h Fk(Isn')-33 b(t)746 b
+Fn(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(3)2524 10761
+y(1.4)1163 b(Related)278 b(W)-89 b(ork)774 b(.)553 b(.)g(.)g(.)g(.)h(.)
+f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1761 b(3)2524 12090 y(1.5)1163 b(Limitations)277
+b(and)h(Future)g(W)-89 b(ork)649 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(4)2524
+13418 y(1.6)1163 b(Author)277 b(Contact)i(Information)e(and)i(Ackno)-28
+b(wledgments)625 b(.)553 b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761
+b(5)2524 14746 y(1.7)1163 b(Licensing)278 b(Information)1085
+b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)1761 b(5)863 17182 y Fl(2)1108
+b(Wh)-17 b(y)278 b(use)f(FUSD?)40184 b(5)2524 18510 y
+Fn(2.1)1163 b(De)-28 b(vice)279 b(Dri)-28 b(v)-17 b(er)277
+b(Layering)515 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)
+g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(6)2524
+19838 y(2.2)1163 b(Use)277 b(of)f(User)-22 b(-Space)279
+b(Libraries)955 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(6)2524 21167 y(2.3)1163
+b(Dri)-28 b(v)-17 b(er)277 b(Memory)h(Protection)915
+b(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1761 b(6)2524 22495 y(2.4)1163 b(Gi)-28
+b(ving)278 b(libraries)e(language)k(independence)i(and)c(standard)g
+(noti\002cation)h(interf)-11 b(aces)882 b(.)554 b(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)1761 b(7)2524 23823 y(2.5)1163
+b(De)-28 b(v)-17 b(elopment)280 b(and)f(Deb)-22 b(ugging)279
+b(Con)-44 b(v)-17 b(enience)357 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1761 b(7)863 26259 y Fl(3)1108 b(Installing)276
+b(FUSD)40320 b(7)2524 27587 y Fn(3.1)1163 b(Prerequisites)314
+b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761
+b(7)2524 28915 y(3.2)1163 b(Compiling)278 b(FUSD)g(as)f(a)g(K)-28
+b(ernel)278 b(Module)866 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1761 b(7)2524 30244 y(3.3)1163 b(T)-77
+b(esting)277 b(and)h(T)-39 b(roubleshooting)833 b(.)553
+b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)1761 b(8)2524 31572 y(3.4)1163 b(Installation)1051
+b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761
+b(8)2524 32901 y(3.5)1163 b(Making)278 b(FUSD)g(P)-17
+b(art)278 b(of)e(the)i(K)-28 b(ernel)278 b(Proper)576
+b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761
+b(8)863 35336 y Fl(4)1108 b(Basic)277 b(De)-17 b(vice)279
+b(Cr)-20 b(eation)37624 b(9)2524 36664 y Fn(4.1)1163
+b(Using)277 b Fo(fusd)p 10713 36664 333 45 v 399 w(register)h
+Fn(to)f(create)h(a)f(ne)-28 b(w)279 b(de)-28 b(vice)462
+b(.)553 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1761 b(9)2524
+37993 y(4.2)1163 b(The)278 b Fo(open)f Fn(and)h Fo(close)g
+Fn(callbacks)644 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)
+f(.)g(.)g(.)g(.)h(.)1207 b(10)2524 39321 y(4.3)1163 b(The)278
+b Fo(read)f Fn(callback)736 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)
+f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(11)2524 40649 y(4.4)1163 b(The)278 b Fo(write)f Fn(callback)902
+b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(11)2524 41978 y(4.5)1163
+b(Unre)-17 b(gistering)278 b(a)f(de)-28 b(vice)279 b(with)e
+Fo(fusd)p 20290 41978 V 399 w(unregister\(\))776 b Fn(.)553
+b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)1207 b(13)863 44413 y Fl(5)1108
+b(Using)277 b(Inf)-28 b(ormation)278 b(in)f Fo(fusd)p
+15458 44413 V 400 w(file)p 18514 44413 V 399 w(info)28443
+b Fl(14)2524 45741 y Fn(5.1)1163 b(Re)-17 b(gistration)278
+b(of)e(Multiple)i(De)-28 b(vices,)278 b(and)g(P)-17 b(assing)278
+b(Data)g(to)f(Callbacks)839 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(14)2524 47070
+y(5.2)1163 b(The)278 b(dif)-28 b(ference)278 b(between)h
+Fo(device)p 19818 47070 V 400 w(info)e Fn(and)h Fo(private)p
+29674 47070 V 400 w(data)854 b Fn(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(17)863
+49505 y Fl(6)1108 b(Writing)277 b Fo(ioctl)g Fl(Callbacks)35124
+b(19)2524 50833 y Fn(6.1)1163 b(Using)277 b(macros)g(to)g(generate)i
+Fo(ioctl)f Fn(command)i(numbers)687 b(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(19)2524 52162 y(6.2)1163 b(Example)279 b(client)e(calls)g(and)h(dri)
+-28 b(v)-17 b(er)278 b(callbacks)363 b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)1207 b(20)863 54597 y Fl(7)1108
+b(Integrating)278 b(FUSD)g(W)-20 b(ith)277 b(Y)-123 b(our)278
+b(A)-28 b(pplication)279 b(Using)e Fo(fusd)p 28076 54597
+V 400 w(dispatch\(\))14896 b Fl(20)2524 55925 y Fn(7.1)1163
+b(Using)277 b Fo(fusd)p 10713 55925 V 399 w(dispatch\(\))888
+b Fn(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)1207 b(21)2524 57254 y(7.2)1163 b(Helper)277
+b(Functions)i(for)d(Constructing)j(an)e Fo(fd)p 23281
+57254 V 399 w(set)440 b Fn(.)553 b(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)
+1207 b(21)863 59689 y Fl(8)1108 b(Implementing)278 b(Blocking)h(System)
+e(Calls)29987 b(23)2524 61017 y Fn(8.1)1163 b(Blocking)279
+b(the)e(caller)g(by)h(blocking)h(the)e(dri)-28 b(v)-17
+b(er)854 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(24)2524 62346 y(8.2)1163 b(Blocking)279 b(the)e(caller)g(using)h
+Fo(-FUSD)p 19832 62346 V 399 w(NOREPLY)p Fn(;)g(unblocking)i(it)c
+(using)h Fo(fusd)p 36946 62346 V 400 w(return\(\))888
+b Fn(.)554 b(.)f(.)g(.)g(.)g(.)h(.)1207 b(24)5070 63674
+y(8.2.1)1329 b(K)-28 b(eeping)279 b(Per)-22 b(-Client)278
+b(State)605 b(.)553 b(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1207 b(25)5070 65002 y(8.2.2)1329 b(Blocking)279
+b(and)f(completing)h(reads)309 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)1207 b(26)5070 66331 y(8.2.3)1329
+b(Using)277 b Fo(fusd)p 14255 66331 V 399 w(destroy\(\))i
+Fn(to)e(clean)h(up)g(client)f(state)285 b(.)554 b(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(28)2524 67659 y(8.3)1163 b(Retrie)-28 b(ving)278 b(a)f(block)-11
+b(ed)280 b(system)d(call')-61 b(s)276 b(ar)-20 b(guments)278
+b(from)f(a)g Fo(fusd)p 31283 67659 V 399 w(file)p 34338
+67659 V 400 w(info)g Fn(pointer)1080 b(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g
+(.)h(.)1207 b(28)25804 74071 y(i)p eop
+%%Page: 2 3
+2 2 bop 863 2974 a Fl(9)1108 b(Implementing)278 b Fo(select)p
+Fl(able)h(De)-17 b(vices)30727 b(30)2524 4302 y Fn(9.1)1163
+b(Poll)277 b(state)f(and)j(the)e Fo(poll)p 15663 4302
+333 45 v 399 w(diff)h Fn(callback)939 b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)1207 b(30)2524 5631 y(9.2)1163
+b(Recei)-28 b(ving)279 b(a)f Fo(poll)p 13298 5631 V 399
+w(diff)f Fn(request)h(when)g(the)g(pre)-28 b(vious)278
+b(one)g(has)g(not)f(been)i(returned)f(yet)414 b(.)553
+b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(31)2524 6959 y(9.3)1163
+b(Adding)278 b Fo(select)g Fn(support)g(to)f Fo(pager.c)412
+b Fn(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)
+h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(31)863 9394 y Fl(10)555 b(P)-22 b(erf)-28 b(ormance)280
+b(of)d(User)-41 b(-Space)280 b(De)-17 b(vices)30718 b(33)863
+11830 y(11)555 b(FUSD)278 b(Implementation)g(Notes)33772
+b(33)2524 13158 y Fn(11.1)610 b(The)278 b(situation)f(with)g
+Fo(poll)p 16125 13158 V 399 w(diff)290 b Fn(.)554 b(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)
+g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(33)2524 14486 y(11.2)610 b(Restartable)277 b(System)h(Calls)775
+b(.)553 b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)
+f(.)g(.)g(.)g(.)h(.)1207 b(34)863 16922 y Fl(A)862 b(Using)277
+b Fo(strace)40455 b Fl(34)863 21986 y Fm(List)399 b(of)f(Example)h(Pr)
+-29 b(ograms)2524 24460 y Fn(1)1993 b(hello)-28 b(w)-11
+b(orld.c:)344 b(A)277 b(simple)g(program)h(using)g(FUSD)g(to)f(create)h
+Fo(/dev/hello-world)949 b Fn(.)553 b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)
+1761 b(2)2524 25788 y(2)1993 b(echo.c:)345 b(Using)277
+b(both)h Fo(read)f Fn(and)h Fo(write)g Fn(callbacks)608
+b(.)553 b(.)h(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h
+(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(13)2524
+27117 y(3)1993 b(uid-\002lter)-61 b(.c:)343 b(Inspecting)278
+b(data)g(in)f Fo(fusd)p 21171 27117 V 399 w(file)p 24226
+27117 V 400 w(info)g Fn(such)h(as)f(UID)g(and)h(PID)f(of)g(the)g
+(calling)h(process)1017 b(.)554 b(.)1207 b(15)2524 28445
+y(4)1993 b(drums.c:)343 b(P)-17 b(assing)278 b(pri)-28
+b(v)g(ate)278 b(data)g(to)f Fo(fusd)p 22175 28445 V 399
+w(register)i Fn(and)f(retrie)-28 b(ving)278 b(it)e(from)h
+Fo(device)p 41898 28445 V 399 w(info)1084 b Fn(.)553
+b(.)g(.)h(.)1207 b(16)2524 29773 y(5)1993 b(drums2.c:)344
+b(Using)277 b(both)h Fo(device)p 19026 29773 V 399 w(info)g
+Fn(and)g Fo(private)p 28882 29773 V 400 w(data)816 b
+Fn(.)553 b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)1207 b(18)2524 31102 y(6)1993 b(ioctl.h:)343
+b(Using)277 b(the)p 13136 31102 V 676 w Fo(IO)g Fn(macros)h(to)f
+(generate)i Fo(ioctl)e Fn(command)j(numbers)1077 b(.)553
+b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(20)2524 32430 y(7)1993 b(ioctl-client.c:)343 b(A)277
+b(program)h(that)f(mak)-11 b(es)278 b Fo(ioctl)g Fn(requests)f(on)h(a)f
+(\002le)g(descriptor)479 b(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)1207 b(21)2524 33758 y(8)1993 b(ioctl-serv)-17
+b(er)-61 b(.c:)343 b(A)277 b(dri)-28 b(v)-17 b(er)278
+b(that)f(handles)i Fo(ioctl)e Fn(requests)313 b(.)553
+b(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(22)2524 35087 y(9)1993
+b(drums3.c:)344 b(W)-89 b(aiting)277 b(for)g(both)h(FUSD)g(and)g
+(non-FUSD)h(e)-28 b(v)-17 b(ents)279 b(in)e(a)g Fo(select)h
+Fn(loop)640 b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(23)2524 36415 y(10)1440 b(console-read.c:)345 b(A)277
+b(simple)g(blocking)i(system)e(call)496 b(.)553 b(.)g(.)h(.)f(.)g(.)g
+(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)
+g(.)g(.)g(.)h(.)1207 b(24)2524 37743 y(11)1440 b(pager)-61
+b(.c)278 b(\(P)-17 b(art)277 b(1\):)343 b(Creating)278
+b(state)f(for)f(e)-28 b(v)-17 b(ery)280 b(client)d(using)g(the)h(dri)
+-28 b(v)-17 b(er)728 b(.)553 b(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g
+(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207 b(26)2524 39072 y(12)1440
+b(pager)-61 b(.c)278 b(\(P)-17 b(art)277 b(2\):)343 b(Block)278
+b(clients')f Fo(read)g Fn(requests,)g(and)i(later)d(completing)j(the)f
+(block)-11 b(ed)279 b(reads)300 b(.)554 b(.)f(.)g(.)g(.)g(.)h(.)1207
+b(27)2524 40400 y(13)1440 b(pager)-61 b(.c)278 b(\(P)-17
+b(art)277 b(3\):)343 b(Cleaning)279 b(up)f(when)g(a)g(client)f(lea)-22
+b(v)-17 b(es)646 b(.)554 b(.)f(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g
+(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(29)2524 41729 y(14)1440 b(pager)-61 b(.c)278 b(\(P)-17
+b(art)277 b(4\):)343 b(Supporting)279 b Fo(select\(2\))g
+Fn(by)e(implementing)i(a)f Fo(poll)p 35250 41729 V 399
+w(diff)f Fn(callback)448 b(.)553 b(.)h(.)f(.)g(.)g(.)g(.)h(.)1207
+b(32)25650 74071 y(ii)p eop
+%%Page: 1 4
+1 3 bop 863 2974 a Fm(1)1594 b(Intr)-29 b(oduction)863
+6333 y Fj(1.1)1329 b(What)332 b(is)h(FUSD?)863 9073 y
+Fn(FUSD)441 b(\(pronounced)i Fk(fused)p Fn(\))c(is)g(a)h(Linux)h(frame)
+-28 b(w)-11 b(ork)440 b(for)f(proxying)j(de)-28 b(vice)442
+b(\002le)e(callbacks)h(into)f(user)-22 b(-space,)481
+b(allo)-28 b(wing)863 10402 y(de)g(vice)394 b(\002les)e(to)f(be)h
+(implemented)i(by)f(daemons)g(instead)f(of)g(k)-11 b(ernel)392
+b(code.)689 b(Despite)392 b(being)h(implemented)h(in)d(user)-22
+b(-space,)863 11730 y(FUSD)278 b(de)-28 b(vices)279 b(can)f(look)g(and)
+g(act)g(just)e(lik)-11 b(e)277 b(an)-17 b(y)279 b(other)e(\002le)h
+(under)g(/de)-28 b(v)278 b(which)g(is)e(implemented)k(by)d(k)-11
+b(ernel)278 b(callbacks.)2524 13722 y(A)269 b(user)-22
+b(-space)271 b(de)-28 b(vice)271 b(dri)-28 b(v)-17 b(er)271
+b(can)g(do)f(man)-17 b(y)271 b(of)f(the)g(things)g(that)f(k)-11
+b(ernel)271 b(dri)-28 b(v)-17 b(ers)270 b(can')-20 b(t,)271
+b(such)g(as)e(perform)h(a)g(long-running)863 15051 y(computation,)367
+b(block)348 b(while)f(w)-11 b(aiting)347 b(for)f(an)i(e)-28
+b(v)-17 b(ent,)366 b(or)346 b(read)i(\002les)e(from)h(the)g(\002le)g
+(system.)552 b(Unlik)-11 b(e)348 b(k)-11 b(ernel)347
+b(dri)-28 b(v)-17 b(ers,)365 b(a)347 b(user)-22 b(-)863
+16379 y(space)316 b(de)-28 b(vice)316 b(dri)-28 b(v)-17
+b(er)315 b(can)h Fk(use)e(other)h(de)-17 b(vice)317 b(driver)-11
+b(s)p Fn(\227that)314 b(is,)322 b(access)315 b(the)g(netw)-11
+b(ork,)324 b(talk)315 b(to)f(a)h(serial)e(port,)323 b(get)315
+b(interacti)-28 b(v)-17 b(e)863 17707 y(input)308 b(from)e(the)h(user)
+-44 b(,)314 b(pop)308 b(up)f(GUI)g(windo)-28 b(ws,)315
+b(or)306 b(read)i(from)e(disks.)432 b(User)-22 b(-space)308
+b(dri)-28 b(v)-17 b(ers)307 b(implemented)i(using)e(FUSD)h(can)863
+19036 y(be)329 b(much)g(easier)f(to)f(deb)-22 b(ug;)355
+b(it)327 b(is)g(impossible)h(for)f(them)h(to)g(crash)g(the)g(machine,)
+343 b(are)328 b(easily)g(traceable)h(using)f(tools)g(such)g(as)863
+20364 y Fo(gdb)p Fn(,)317 b(and)310 b(can)g(be)g(killed)f(and)h
+(restarted)f(without)g(rebooting)i(e)-28 b(v)-17 b(en)311
+b(if)d(the)-17 b(y)310 b(become)h(corrupted.)440 b(FUSD)310
+b(dri)-28 b(v)-17 b(ers)310 b(don')-20 b(t)309 b(ha)-22
+b(v)-17 b(e)863 21693 y(to)351 b(be)h(in)f(C\227Perl,)370
+b(Python,)h(or)351 b(an)-17 b(y)353 b(other)e(language)j(that)e(kno)-28
+b(ws)352 b(ho)-28 b(w)353 b(to)e(read)h(from)e(and)j(write)d(to)h(a)h
+(\002le)f(descriptor)h(can)863 23021 y(w)-11 b(ork)278
+b(with)f(FUSD.)g(User)-22 b(-space)278 b(dri)-28 b(v)-17
+b(ers)277 b(can)i(be)e(sw)-11 b(apped)279 b(out,)e(whereas)i(k)-11
+b(ernel)277 b(dri)-28 b(v)-17 b(ers)278 b(lock)g(ph)-6
+b(ysical)278 b(memory)-72 b(.)2524 25013 y(Of)343 b(course,)360
+b(as)343 b(with)h(almost)f(e)-28 b(v)-17 b(erything,)363
+b(there)344 b(are)f(trade-of)-28 b(fs.)543 b(User)-22
+b(-space)344 b(dri)-28 b(v)-17 b(ers)344 b(are)f(slo)-28
+b(wer)344 b(than)g(k)-11 b(ernel)345 b(dri)-28 b(v)-17
+b(ers)863 26342 y(because)260 b(the)-17 b(y)258 b(require)g(three)g
+(times)f(as)g(man)-17 b(y)259 b(system)e(calls,)j(and)f(additional)g
+(memory)f(copies)g(\(see)g(section)f(10\).)337 b(User)-22
+b(-space)863 27670 y(dri)-28 b(v)-17 b(ers)226 b(can)h(not)e(recei)-28
+b(v)-17 b(e)228 b(interrupts,)235 b(and)226 b(do)h(not)e(ha)-22
+b(v)-17 b(e)228 b(the)d(full)g(po)-28 b(wer)227 b(to)e(modify)h
+(arbitrary)f(k)-11 b(ernel)227 b(data)f(structures)f(as)g(k)-11
+b(ernel)863 28998 y(dri)-28 b(v)-17 b(ers)395 b(do.)698
+b(Despite)395 b(these)g(limitations,)424 b(we)395 b(ha)-22
+b(v)-17 b(e)397 b(found)f(user)-22 b(-space)395 b(de)-28
+b(vice)397 b(dri)-28 b(v)-17 b(ers)396 b(to)f(be)g(a)g(po)-28
+b(werful)396 b(programming)863 30327 y(paradigm)279 b(with)e(a)g(wide)h
+(v)-28 b(ariety)278 b(of)e(uses)h(\(see)g(Section)i(2\).)2524
+32319 y(FUSD)323 b(is)e(free)h(softw)-11 b(are,)334 b(distrib)-22
+b(uted)322 b(under)i(a)e(GPL-compatible)j(license)e(\(the)f(\223ne)-28
+b(w\224)325 b(BSD)e(license,)334 b(with)322 b(the)h(adv)-17
+b(er)-22 b(-)863 33648 y(tising)277 b(clause)h(remo)-17
+b(v)g(ed\).)863 37481 y Fj(1.2)1329 b(Ho)-13 b(w)332
+b(does)g(FUSD)f(w)-13 b(ork?)863 40220 y Fn(FUSD)320
+b(dri)-28 b(v)-17 b(ers)320 b(are)f(conceptually)k(similar)317
+b(to)i(k)-11 b(ernel)320 b(dri)-28 b(v)-17 b(ers:)428
+b(a)319 b(set)g(of)g(callback)i(functions)f(called)g(in)f(response)h
+(to)f(system)863 41549 y(calls)277 b(made)i(on)f(\002le)g(descriptors)g
+(by)g(user)f(programs.)346 b(FUSD')-61 b(s)278 b(C)f(library)h(pro)-17
+b(vides)279 b(a)e(de)-28 b(vice)280 b(re)-17 b(gistration)278
+b(function,)g(similar)863 42877 y(to)224 b(the)h(k)-11
+b(ernel')-61 b(s)224 b Fo(devfs)p 10628 42877 333 45
+v 400 w(register)p 16340 42877 V 400 w(chrdev\(\))h Fn(function,)236
+b(to)224 b(create)h(ne)-28 b(w)226 b(de)-28 b(vices.)327
+b Fo(fusd)p 39020 42877 V 399 w(register\(\))226 b Fn(accepts)g(the)863
+44205 y(de)-28 b(vice)273 b(name)f(and)g(a)f(structure)f(full)g(of)g
+(pointers.)342 b(Those)272 b(pointers)f(are)f(callback)j(functions)f
+(which)f(are)g(called)h(in)f(response)g(to)863 45534
+y(certain)293 b(user)g(system)f(calls\227for)g(e)-17
+b(xample,)299 b(when)294 b(a)e(process)h(tries)f(to)g(open,)298
+b(close,)e(read)e(from,)h(or)e(write)f(to)g(the)h(de)-28
+b(vice)295 b(\002le.)863 46862 y(The)281 b(callback)i(functions)e
+(should)g(conform)g(to)g(the)f(standard)i(de\002nitions)f(of)f(POSIX)h
+(system)f(call)h(beha)-22 b(vior)-61 b(.)355 b(In)280
+b(man)-17 b(y)282 b(w)-11 b(ays,)863 48190 y(the)278
+b(user)-22 b(-space)277 b(FUSD)i(callback)g(functions)e(are)h
+(identical)g(to)f(their)g(k)-11 b(ernel)277 b(counterparts.)2524
+50183 y(Perhaps)356 b(the)g(best)f(w)-11 b(ay)356 b(to)f(sho)-28
+b(w)356 b(what)g(FUSD)g(does)g(is)e(by)i(e)-17 b(xample.)580
+b(Program)357 b(1)e(is)f(a)i(simple)f(FUSD)h(de)-28 b(vice)357
+b(dri)-28 b(v)-17 b(er)-61 b(.)863 51511 y(When)429 b(the)g(program)g
+(is)e(run,)465 b(a)428 b(de)-28 b(vice)431 b(called)e
+Fo(/dev/hello-world)h Fn(appears)f(under)g(the)g Fo(/dev)f
+Fn(directory)-72 b(.)797 b(If)427 b(that)863 52840 y(de)-28
+b(vice)256 b(is)d(read)i(\(e.g.,)j(using)d Fo(cat)p Fn(\),)j(the)c
+(read)h(returns)f Fo(Hello,)665 b(world!)337 b Fn(follo)-28
+b(wed)255 b(by)g(an)f(EOF)-89 b(.)255 b(Finally)-72 b(,)259
+b(when)d(the)e(dri)-28 b(v)-17 b(er)863 54168 y(is)276
+b(stopped)j(\(e.g.,)d(by)i(hitting)f(Control-C\),)g(the)g(de)-28
+b(vice)279 b(\002le)f(disappears.)2524 56160 y(On)325
+b(line)f(40)i(of)e(the)h(source,)337 b(we)325 b(use)g
+Fo(fusd)p 20179 56160 V 399 w(register\(\))h Fn(to)e(create)i(the)f
+Fo(/dev/hello-world)i Fn(de)-28 b(vice,)338 b(passing)863
+57489 y(pointers)312 b(to)g(callbacks)i(for)d(the)h(open\(\),)322
+b(close\(\))311 b(and)i(read\(\))f(system)g(calls.)447
+b(\(Lines)312 b(36\22639)i(use)e(the)h(GNU)f(C)g(e)-17
+b(xtension)314 b(that)863 58817 y(allo)-28 b(ws)302 b(initializer)e
+(\002eld)j(naming;)314 b(the)302 b(2.4)f(series)g(of)g(Linux)h(k)-11
+b(ernels)302 b(use)f(also)h(that)f(e)-17 b(xtension)303
+b(for)e(the)h(same)f(purpose.\))417 b(The)863 60146 y(\223Hello,)322
+b(W)-89 b(orld\224)313 b(read\(\))f(callback)i(itself)d(is)g(virtually)
+i(identical)g(to)f(what)h(a)f(k)-11 b(ernel)313 b(dri)-28
+b(v)-17 b(er)313 b(for)f(this)f(de)-28 b(vice)315 b(w)-11
+b(ould)313 b(look)g(lik)-11 b(e.)863 61474 y(It)337 b(can)i(inspect)g
+(and)g(modify)g(the)f(user')-61 b(s)337 b(\002le)h(pointer)-44
+b(,)354 b(cop)-11 b(y)339 b(data)g(into)f(the)g(user)-22
+b(-pro)-17 b(vided)340 b(b)-22 b(uf)-28 b(fer)-44 b(,)353
+b(control)339 b(the)f(system)g(call)863 62802 y(return)277
+b(v)-28 b(alue)279 b(\(either)e(positi)-28 b(v)-17 b(e,)278
+b(EOF)-89 b(,)278 b(or)f(error\),)e(and)k(so)d(forth.)2524
+64795 y(The)269 b(proxying)h(of)e(k)-11 b(ernel)269 b(system)f(calls)f
+(that)i(mak)-11 b(es)269 b(this)e(kind)i(of)f(program)h(possible)f(is)g
+(implemented)i(by)f(FUSD,)g(using)863 66123 y(a)296 b(combination)i(of)
+e(a)f(k)-11 b(ernel)297 b(module)g(and)g(cooperating)h(user)-22
+b(-space)297 b(library)-72 b(.)399 b(The)296 b(k)-11
+b(ernel)297 b(module)g(implements)g(a)e(character)863
+67451 y(de)-28 b(vice,)395 b Fo(/dev/fusd)p Fn(,)f(which)371
+b(is)e(used)i(as)e(a)h(control)h(channel)h(between)g(the)e(tw)-11
+b(o.)622 b(fusd)p 37393 67451 V 398 w(re)-17 b(gister\(\))369
+b(uses)h(this)f(channel)j(to)863 68780 y(send)304 b(a)f(message)i(to)e
+(the)g(FUSD)h(k)-11 b(ernel)304 b(module,)311 b(telling)304
+b(the)f(name)i(of)e(the)g(de)-28 b(vice)305 b(the)f(user)f(w)-11
+b(ants)303 b(to)g(re)-17 b(gister)-61 b(.)422 b(The)304
+b(k)-11 b(ernel)863 70108 y(module,)263 b(in)258 b(turn,)j(re)-17
+b(gisters)257 b(that)h(de)-28 b(vice)260 b(with)e(the)g(k)-11
+b(ernel)259 b(proper)g(using)f(de)-28 b(vfs.)337 b(de)-28
+b(vfs)259 b(and)g(the)f(k)-11 b(ernel)259 b(don')-20
+b(t)258 b(kno)-28 b(w)260 b(an)-17 b(ything)25681 74071
+y(1)p eop
+%%Page: 2 5
+2 4 bop 863 3831 50191 89 v 863 4815 a Fl(Pr)-20 b(ogram)280
+b(1)d Fn(hello)-28 b(w)-11 b(orld.c:)345 b(A)277 b(simple)g(program)h
+(using)f(FUSD)h(to)f(create)h Fo(/dev/hello-world)p 863
+5320 50191 45 v 365 6276 a Fi(1)1661 b Fo(#include)665
+b(<stdio.h>)2524 7604 y(#include)g(<string.h>)2524 8932
+y(#include)g(<errno.h>)2524 10261 y(#include)g("fusd.h")365
+11589 y Fi(5)2524 12917 y Fo(#define)g(GREETING)h("Hello,)f(world!\\n")
+2524 15574 y(int)f(do_open_or_close\(struct)669 b(fusd_file_info)d
+(*file\))2524 16902 y({)-133 18231 y Fi(10)2989 b Fo(return)665
+b(0;)g(/*)f(attempts)i(to)e(open)h(and)g(close)g(this)g(file)g(always)g
+(succeed)g(*/)2524 19559 y(})2524 22216 y(int)f(do_read\(struct)j
+(fusd_file_info)g(*file,)e(char)g(*user_buffer,)10494
+23544 y(size_t)g(user_length,)h(loff_t)f(*offset\))-133
+24873 y Fi(15)1661 b Fo({)3852 26201 y(int)665 b(retval)g(=)f(0;)3852
+28858 y(/*)g(The)h(first)g(read)g(to)g(the)f(device)i(returns)f(a)f
+(greeting.)1330 b(The)665 b(second)g(read)4516 30186
+y(*)f(returns)i(EOF.)f(*/)-133 31514 y Fi(20)2989 b Fo(if)664
+b(\(*offset)i(==)f(0\))f({)5180 32843 y(if)h(\(user_length)h(<)e
+(strlen\(GREETING\)\))6509 34171 y(retval)h(=)f(-EINVAL;)1330
+b(/*)665 b(the)f(user)h(must)g(supply)g(a)g(big)f(enough)i(buffer!)f
+(*/)5180 35499 y(else)g({)6509 36828 y(memcpy\(user_buffer,)i
+(GREETING,)f(strlen\(GREETING\)\);)h(/*)e(greet)g(user)g(*/)-133
+38156 y Fi(25)5646 b Fo(retval)665 b(=)f(strlen\(GREETING\);)j(/*)e
+(retval)g(=)g(number)g(of)f(bytes)h(returned)h(*/)6509
+39484 y(*offset)f(+=)g(retval;)g(/*)g(advance)g(user's)g(file)g
+(pointer)g(*/)5180 40813 y(})3852 42141 y(})-133 44798
+y Fi(30)2989 b Fo(return)665 b(retval;)2524 46126 y(})2524
+48783 y(int)f(main\(int)i(argc,)f(char)g(*argv[]\))2524
+50111 y({)-133 51440 y Fi(35)2989 b Fo(struct)665 b
+(fusd_file_operations)j(fops)d(=)f({)5180 52768 y(open:)h
+(do_open_or_close,)5180 54096 y(read:)g(do_read,)5180
+55425 y(close:)h(do_open_or_close)h(};)-133 58081 y Fi(40)2989
+b Fo(if)664 b(\(fusd_register\("/dev/hello-world",)671
+b(0666,)665 b(NULL,)g(&fops\))g(<)f(0\))5180 59410 y(perror\("Unable)j
+(to)d(register)i(device"\);)3852 60738 y(else)f({)5180
+62066 y(printf\("/dev/hello-world)k(should)c(now)g(exist)g(-)f(calling)
+h(fusd_run...\\n"\);)5180 63395 y(fusd_run\(\);)-133
+64723 y Fi(45)2989 b Fo(})3852 66051 y(return)665 b(0;)2524
+67380 y(})p 863 68874 V 25681 74071 a Fn(2)p eop
+%%Page: 3 6
+3 5 bop 863 2974 a Fn(unusual)256 b(is)e(happening;)265
+b(it)254 b(appears)h(from)g(their)f(point)h(of)g(vie)-28
+b(w)255 b(that)g(the)g(re)-17 b(gistered)255 b(de)-28
+b(vices)257 b(are)d(simply)h(being)h(implemented)863
+4302 y(by)278 b(the)f(FUSD)h(module.)2524 6295 y(Later)-44
+b(,)408 b(when)383 b(k)-11 b(ernel)383 b(mak)-11 b(es)383
+b(a)f(callback)j(due)e(to)f(a)g(system)g(call)g(\(e.g.)g(when)i(the)e
+(character)i(de)-28 b(vice)384 b(\002le)e(is)g(opened)i(or)863
+7623 y(read\),)286 b(the)e(FUSD)g(k)-11 b(ernel)285 b(module')-61
+b(s)285 b(callback)g(blocks)g(the)f(calling)g(process,)i(marshals)e
+(the)g(ar)-20 b(guments)285 b(of)e(the)h(callback)i(into)863
+8951 y(a)403 b(message)g(and)h(sends)f(it)e(to)i(user)-22
+b(-space.)720 b(Once)404 b(there,)434 b(the)403 b(library)g(half)f(of)g
+(FUSD)i(unmarshals)f(it)f(and)h(calls)g(whate)-28 b(v)-17
+b(er)863 10280 y(user)-22 b(-space)290 b(callback)i(the)e(FUSD)g(dri)
+-28 b(v)-17 b(er)290 b(passed)h(to)e(fusd)p 23790 10280
+333 45 v 399 w(re)-17 b(gister\(\).)379 b(When)291 b(that)e(user)-22
+b(-space)290 b(callback)i(returns)d(a)h(v)-28 b(alue,)294
+b(the)863 11608 y(process)255 b(happens)h(in)e(re)-28
+b(v)-17 b(erse:)333 b(the)254 b(return)g(v)-28 b(alue)256
+b(and)f(its)e(side-ef)-28 b(fects)254 b(are)g(marshaled)h(by)g(the)f
+(library)g(and)h(sent)f(to)g(the)g(k)-11 b(ernel.)863
+12936 y(The)302 b(FUSD)f(k)-11 b(ernel)301 b(module)i(unmarshals)e
+(this)e(message,)308 b(matches)301 b(it)f(up)h(with)f(a)h
+(corresponding)i(outstanding)f(request,)307 b(and)863
+14265 y(completes)g(the)f(system)f(call.)428 b(The)306
+b(calling)h(process)e(is)g(completely)i(una)-17 b(w)-11
+b(are)308 b(of)d(this)g(trick)-11 b(ery;)319 b(it)305
+b(simply)g(enters)g(the)h(k)-11 b(ernel)863 15593 y(once,)279
+b(blocks,)e(unblocks,)i(and)f(returns)f(from)g(the)g(system)g
+(call\227just)f(as)h(it)f(w)-11 b(ould)278 b(for)f(an)-17
+b(y)279 b(other)e(blocking)i(call.)2524 17586 y(One)312
+b(of)f(the)h(primary)g(design)g(goals)g(of)g(FUSD)g(is)f
+Fk(stability)p Fn(.)446 b(It)310 b(should)j(not)f(be)g(possible)g(for)e
+(a)i(FUSD)h(dri)-28 b(v)-17 b(er)312 b(to)f(corrupt)863
+18914 y(or)286 b(crash)g(the)g(k)-11 b(ernel,)289 b(either)d(due)h(to)e
+(error)h(or)f(malice.)371 b(Of)285 b(course,)j(a)e(b)-22
+b(uggy)288 b(dri)-28 b(v)-17 b(er)287 b(itself)d(may)j(corrupt)f
+(itself)f(\(e.g.,)i(due)g(to)f(a)863 20242 y(b)-22 b(uf)-28
+b(fer)292 b(o)-17 b(v)g(errun\).)387 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)297 b(strict)290 b(error)h(checking)j(is)c(implemented)k
+(at)d(the)g(user)-22 b(-k)-11 b(ernel)292 b(boundary)i(which)f(should)f
+(pre)-28 b(v)-17 b(ent)863 21571 y(dri)-28 b(v)-17 b(ers)290
+b(from)f(corrupting)h(the)g(k)-11 b(ernel)290 b(or)f(an)-17
+b(y)291 b(other)e(user)-22 b(-space)290 b(process\227including)i(the)e
+(errant)f(dri)-28 b(v)-17 b(er')-61 b(s)289 b(o)-28 b(wn)291
+b(clients,)h(and)863 22899 y(other)278 b(FUSD)g(dri)-28
+b(v)-17 b(ers.)863 26732 y Fj(1.3)1329 b(What)332 b(FUSD)g
+Fh(Isn')-49 b(t)863 29472 y Fn(FUSD)388 b(looks)g(similar)e(to)h
+(certain)h(other)g(Linux)h(f)-11 b(acilities)386 b(that)h(are)h
+(already)g(a)-22 b(v)-28 b(ailable.)676 b(It)386 b(also)i(skirts)d
+(near)j(a)g(fe)-28 b(w)388 b(of)f(the)863 30800 y(k)-11
+b(ernel')-61 b(s)277 b(hot-b)-22 b(utton)279 b(political)e(issues.)342
+b(So,)278 b(to)f(a)-22 b(v)g(oid)278 b(confusion,)g(we)g(present)f(a)g
+(list)f(of)h(things)g(that)g(FUSD)h(is)e Fk(not)p Fn(.)2524
+33900 y Fg(\017)554 b Fl(A)344 b(FUSD)i(dri)-11 b(v)g(er)346
+b(is)e(not)h(a)g(k)-11 b(er)-17 b(nel)347 b(module.)548
+b Fn(K)-28 b(ernel)346 b(modules)g(allo)-28 b(w\227well,)363
+b(modularity)345 b(of)g(k)-11 b(ernel)346 b(code.)548
+b(The)-17 b(y)3631 35228 y(let)325 b(you)j(insert)d(and)j(remo)-17
+b(v)g(e)328 b(k)-11 b(ernel)327 b(modules)h(dynamically)g(after)e(the)h
+(k)-11 b(ernel)327 b(boots.)491 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)341 b(once)328 b(inserted,)339 b(the)3631
+36556 y(k)-11 b(ernel)338 b(modules)h(are)g(actually)g(part)f(of)f(the)
+h(k)-11 b(ernel)339 b(proper)-61 b(.)527 b(The)-17 b(y)340
+b(run)e(in)g(the)g(k)-11 b(ernel')-61 b(s)338 b(address)g(space,)354
+b(with)338 b(all)g(the)3631 37885 y(same)351 b(pri)-28
+b(vile)-17 b(ges)352 b(and)g(restrictions)e(that)h(nati)-28
+b(v)-17 b(e)352 b(k)-11 b(ernel)352 b(code)g(does.)566
+b(A)350 b(FUSD)i(de)-28 b(vice)353 b(dri)-28 b(v)-17
+b(er)-44 b(,)370 b(in)350 b(contrast,)370 b(is)349 b(more)3631
+39213 y(similar)275 b(to)i(a)h(daemon\227a)h(program)f(that)g(runs)e
+(as)h(a)h(user)-22 b(-space)277 b(process,)h(with)f(a)g(process)g(ID.)
+2524 41427 y Fg(\017)554 b Fl(FUSD)301 b(is)f(not,)306
+b(and)c(doesn't)f(r)-20 b(eplace,)308 b(de)-17 b(vfs.)415
+b Fn(When)301 b(a)g(FUSD)g(dri)-28 b(v)-17 b(er)302 b(re)-17
+b(gisters)300 b(a)g(FUSD)i(de)-28 b(vice,)308 b(it)300
+b(automatically)3631 42755 y(creates)339 b(a)f(de)-28
+b(vice)341 b(\002le)e(in)f Fo(/dev)p Fn(.)528 b(Ho)-28
+b(we)g(v)-17 b(er)-44 b(,)356 b(FUSD)339 b(is)e(not)i(a)g(replacement)i
+(for)d(de)-28 b(vfs\227quite)339 b(the)g(contrary)-72
+b(,)355 b(FUSD)3631 44084 y(creates)424 b(those)g(de)-28
+b(vice)425 b(\002les)f(by)g Fk(using)g Fn(de)-28 b(vfs.)783
+b(In)424 b(a)f(normal)i(Linux)f(system,)460 b(only)425
+b(k)-11 b(ernel)424 b(modules)h(proper)-22 b(\227not)3631
+45412 y(user)g(-space)277 b(programs\227can)j(re)-17
+b(gister)276 b(with)h(de)-28 b(vfs)278 b(\(see)f(abo)-17
+b(v)g(e\).)2524 47626 y Fg(\017)554 b Fl(FUSD)214 b(is)f(not)i(UDI.)e
+Fn(UDI,)h(the)g(Uniform)g(Dri)-28 b(v)-17 b(er)215 b(Interf)-11
+b(ace)26987 47224 y Ff(1)27430 47626 y Fn(,)226 b(aims)214
+b(to)g(create)h(a)f(binary)h(API)f(for)f(dri)-28 b(v)-17
+b(ers)215 b(that)f(is)f(uniform)3631 48954 y(across)338
+b(operating)j(systems.)527 b(It')-61 b(s)338 b(true)g(that)h(FUSD)h
+(could)g(concei)-28 b(v)g(ably)343 b(be)c(used)h(for)e(a)h(similar)f
+(purpose)i(\(inasmuch)3631 50283 y(as)360 b(it)f(de\002nes)j(a)e
+(system)g(call)g(messaging)i(structure\).)592 b(Ho)-28
+b(we)g(v)-17 b(er)-44 b(,)383 b(this)359 b(w)-11 b(as)361
+b(not)f(the)h(goal)g(of)f(FUSD)h(as)f(much)h(as)f(an)3631
+51611 y(accidental)287 b(side)f(ef)-28 b(fect.)369 b(W)-89
+b(e)286 b(do)g(not)g(adv)-22 b(ocate)288 b(publishing)f(dri)-28
+b(v)-17 b(ers)286 b(in)f(binary-only)j(form,)e(e)-28
+b(v)-17 b(en)288 b(though)g(FUSD)e(does)3631 52939 y(mak)-11
+b(e)278 b(this)e(possible)h(in)g(some)h(cases.)2524 55153
+y Fg(\017)554 b Fl(FUSD)306 b(is)f(not)i(an)f(attempt)g(to)g(tur)-17
+b(n)307 b(Linux)g(into)f(a)g(micr)-20 b(ok)-11 b(er)-17
+b(nel.)433 b Fn(W)-89 b(e)307 b(aren')-20 b(t)306 b(trying)g(to)g(port)
+g(e)-17 b(xisting)307 b(dri)-28 b(v)-17 b(ers)306 b(into)3631
+56482 y(user)-22 b(-space)323 b(for)g(a)g(v)-28 b(ariety)324
+b(of)f(reasons)g(\(not)g(the)g(least)g(of)g(which)h(is)e
+(performance\).)482 b(W)-89 b(e')-55 b(v)-17 b(e)324
+b(used)g(FUSD)g(as)f(a)g(tool)g(to)3631 57810 y(write)314
+b(ne)-28 b(w)316 b(dri)-28 b(v)-17 b(ers)315 b(that)g(are)g(much)h
+(easier)f(from)g(user)-22 b(-space)316 b(than)f(the)-17
+b(y)316 b(w)-11 b(ould)316 b(be)g(in)e(the)i(k)-11 b(ernel;)334
+b(see)315 b(Section)i(2)e(for)3631 59138 y(use)277 b(cases.)863
+62971 y Fj(1.4)1329 b(Related)332 b(W)-100 b(ork)863
+65711 y Fn(FUSD)418 b(is)e(a)h(ne)-28 b(w)418 b(implementation,)453
+b(b)-22 b(ut)418 b(certainly)f(not)g(a)h(ne)-28 b(w)418
+b(idea\227the)g(theory)g(of)e(its)g(operation)i(is)e(the)h(same)h(as)e
+(an)-17 b(y)863 67039 y(microk)-11 b(ernel)280 b(operating)g(system.)
+346 b(A)279 b(microk)-11 b(ernel)279 b(\(roughly)h(speaking\))f(is)f
+(one)h(that)g(implements)g(only)g(v)-17 b(ery)279 b(basic)g(resource)p
+863 67987 20076 45 v 2070 68728 a Fe(1)2457 69041 y Fd(http://www)-58
+b(.projectudi.or)-16 b(g)25681 74071 y Fn(3)p eop
+%%Page: 4 7
+4 6 bop 863 2974 a Fn(protection)283 b(and)f(message)g(passing)g(in)f
+(the)h(k)-11 b(ernel.)356 b(Implementation)284 b(of)d(de)-28
+b(vice)283 b(dri)-28 b(v)-17 b(ers,)282 b(\002le)g(systems,)f(netw)-11
+b(ork)283 b(stacks,)f(and)863 4302 y(so)277 b(forth)g(are)g(rele)-17
+b(g)-6 b(ated)280 b(to)c(userspace.)345 b(P)-17 b(atrick)278
+b(Bridges)g(maintains)f(a)g(list)f(of)h(such)h(microk)-11
+b(ernel)278 b(operating)h(systems)47733 3900 y Ff(2)48175
+4302 y Fn(.)2524 6295 y(Also)243 b(related)i(is)e(the)i(idea)g(of)f(a)g
+(user)-22 b(-space)245 b(\002lesystem,)251 b(which)245
+b(has)f(been)i(implemented)g(in)e(a)g(number)i(of)e(conte)-17
+b(xts.)334 b(Some)863 7623 y(e)-17 b(xamples)332 b(include)f(Klaus)f
+(Schauser')-61 b(s)330 b(UFO)h(Project)22599 7221 y Ff(3)23372
+7623 y Fn(for)e(Solaris,)342 b(and)331 b(Jeremy)f(Fitzhardinge')-61
+b(s)331 b(\(no)f(longer)g(maintained\))863 8951 y(UserFS)4183
+8550 y Ff(4)5023 8951 y Fn(for)396 b(Linux)i(1.x.)702
+b(The)397 b(UFO)g(paper)19297 8550 y Ff(5)20138 8951
+y Fn(is)e(also)i(notable)h(because)h(it)c(has)i(a)g(good)h(surv)-17
+b(e)g(y)398 b(of)f(similar)e(projects)i(that)863 10280
+y(inte)-17 b(grate)278 b(user)-22 b(-space)278 b(code)h(with)e(system)g
+(calls.)863 14076 y Fj(1.5)1329 b(Limitations)333 b(and)e(Futur)-24
+b(e)331 b(W)-100 b(ork)863 16815 y Fn(In)282 b(its)e(current)i(form,)g
+(FUSD)h(is)e(useful)g(and)i(has)f(pro)-17 b(v)g(en)284
+b(to)e(be)g(quite)g(stable\227we)h(use)e(it)g(in)h(production)h
+(systems.)357 b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)863
+18144 y(it)312 b(does)i(ha)-22 b(v)-17 b(e)315 b(some)e(limitations)g
+(that)g(could)h(bene\002t)h(from)d(the)i(attention)g(of)f(de)-28
+b(v)-17 b(elopers.)453 b(Contrib)-22 b(utions)314 b(to)f(correct)h(an)
+-17 b(y)314 b(of)863 19472 y(these)h(de\002ciencies)i(are)d(welcomed!)
+457 b(\(Man)-17 b(y)316 b(of)e(these)h(limitations)e(will)h(not)g(mak)
+-11 b(e)316 b(sense)e(without)h(ha)-22 b(ving)316 b(read)f(the)g(rest)e
+(of)863 20801 y(the)278 b(documentation)i(\002rst.\))2524
+23695 y Fg(\017)554 b Fn(Currently)-72 b(,)373 b(FUSD)356
+b(only)f(supports)f(implementation)i(of)e(character)i(de)-28
+b(vices.)576 b(Block)356 b(de)-28 b(vices)356 b(and)f(netw)-11
+b(ork)355 b(de)-28 b(vices)3631 25023 y(are)277 b(not)g(supported)i
+(yet.)2524 27155 y Fg(\017)554 b Fn(The)335 b(k)-11 b(ernel)335
+b(has)g(15)g(dif)-28 b(ferent)334 b(callbacks)i(in)e(its)f
+Fo(file)p 25893 27155 333 45 v 400 w(operations)i Fn(structure.)515
+b(The)336 b(current)f(v)-17 b(ersion)335 b(of)f(FUSD)3631
+28484 y(does)277 b(not)h(proxy)g(some)g(of)e(the)i(more)f(obscure)i
+(ones)e(out)h(to)f(userspace.)2524 30616 y Fg(\017)554
+b Fn(Currently)-72 b(,)298 b(all)293 b(system)g(calls)g(that)h(FUSD)g
+(understands)i(are)d(proxied)i(from)e(the)h(FUSD)h(k)-11
+b(ernel)294 b(module)h(to)f(userspace.)3631 31944 y(Only)341
+b(the)g(userspace)h(library)e(kno)-28 b(ws)342 b(which)f(callbacks)h
+(ha)-22 b(v)-17 b(e)343 b(actually)f(been)g(re)-17 b(gistered)341
+b(by)g(the)g(FUSD)h(dri)-28 b(v)-17 b(er)-61 b(.)534
+b(F)-17 b(or)3631 33272 y(e)g(xample,)398 b(the)373 b(k)-11
+b(ernel)374 b(may)f(proxy)h(a)f(write\(\))e(system)h(call)h(to)f(user)
+-22 b(-space)374 b(e)-28 b(v)-17 b(en)375 b(if)c(the)i(dri)-28
+b(v)-17 b(er)374 b(has)e(not)h(re)-17 b(gistered)374
+b(a)3631 34601 y(write\(\))275 b(callback)k(with)e(fusd)p
+15101 34601 V 399 w(re)-17 b(gister\(\).)3631 36331 y(fusd)p
+5603 36331 V 398 w(re)g(gister\(\))238 b(should,)247
+b(b)-22 b(ut)239 b(currently)g(does)g(not,)247 b(tell)238
+b(the)h(k)-11 b(ernel)239 b(module)h(which)g(callbacks)g(it)e(w)-11
+b(ants)239 b(to)f(recei)-28 b(v)-17 b(e,)248 b(per)-22
+b(-)3631 37659 y(de)-28 b(vice.)424 b(This)303 b(will)g(be)h(more)g(ef)
+-28 b(\002cient)305 b(because)g(it)e(will)f(pre)-28 b(v)-17
+b(ent)306 b(useless)d(system)g(calls)g(for)g(unsupported)j(operations.)
+3631 38987 y(In)380 b(addition,)408 b(it)381 b(will)f(lead)i(to)f(more)
+g(logical)h(and)g(consistent)g(beha)-22 b(vior)383 b(by)e(allo)-28
+b(wing)383 b(the)e(k)-11 b(ernel)382 b(to)f(use)h(its)d(def)-11
+b(ault)3631 40316 y(implementations)336 b(of)f(certain)h(functions)g
+(such)g(as)f(write)-28 b(v\(\),)349 b(instead)335 b(of)g(being)i
+(fooled)f(into)f(thinking)h(the)g(dri)-28 b(v)-17 b(er)336
+b(has)3631 41644 y(an)277 b(implementation)i(of)e(it)f(in)h(cases)h
+(where)g(it)e(doesn')-20 b(t.)2524 43776 y Fg(\017)554
+b Fn(It)370 b(should)j(be)g(possible)f(to)f(write)g(a)h(FUSD)h(library)
+f(in)f(an)-17 b(y)374 b(language)g(that)e(supports)g(reads)g(and)h
+(writes)e(on)h(ra)-17 b(w)373 b(\002le)3631 45104 y(descriptors.)336
+b(In)257 b(the)h(future,)j(it)256 b(might)h(be)h(possible)f(to)g(write)
+g(FUSD)h(de)-28 b(vice)260 b(dri)-28 b(v)-17 b(ers)257
+b(in)g(a)h(v)-28 b(ariety)258 b(of)f(languages\227Perl,)3631
+46433 y(Python,)278 b(maybe)h(e)-28 b(v)-17 b(en)279
+b(Ja)-22 b(v)-28 b(a.)345 b(Ho)-28 b(we)g(v)-17 b(er)-44
+b(,)279 b(the)e(current)h(implementation)h(has)e(only)h(a)f(C)g
+(library)-72 b(.)2524 48565 y Fg(\017)554 b Fn(It')-61
+b(s)378 b(possible)i(for)f(dri)-28 b(v)-17 b(ers)380
+b(that)g(use)h(FUSD)f(to)g(deadlock\227for)j(e)-17 b(xample,)407
+b(if)379 b(a)h(dri)-28 b(v)-17 b(er)381 b(tries)e(to)g(open)j(itself.)
+650 b(In)380 b(this)3631 49893 y(one)270 b(case,)i(FUSD)e(returns)f
+Fo(-EDEADLOCK)p Fn(.)i(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)273
+b(deadlock)f(protection)f(should)g(be)f(e)-17 b(xpanded)273
+b(to)d(more)g(general)3631 51221 y(detection)278 b(of)f(c)-17
+b(ycles)279 b(of)d(arbitrary)h(length.)2524 53353 y Fg(\017)554
+b Fn(FUSD)324 b(should)g(pro)-17 b(vide)325 b(a)f(/proc)g(interf)-11
+b(ace)324 b(that)f(gi)-28 b(v)-17 b(es)325 b(deb)-22
+b(ugging)326 b(and)f(status)d(information,)336 b(and)324
+b(allo)-28 b(ws)324 b(parameter)3631 54682 y(tuning.)2524
+56814 y Fg(\017)554 b Fn(FUSD)412 b(w)-11 b(as)412 b(written)f(with)h
+(ef)-28 b(\002cienc)-17 b(y)415 b(in)c(mind,)446 b(b)-22
+b(ut)412 b(a)g(number)h(of)e(important)i(optimizations)g(ha)-22
+b(v)-17 b(e)413 b(not)f(yet)g(been)3631 58142 y(implemented.)340
+b(Speci\002cally)-72 b(,)266 b(we')-55 b(d)261 b(lik)-11
+b(e)261 b(to)g(try)f(to)h(reduce)h(the)g(number)g(of)e(memory)j(copies)
+e(by)h(using)f(a)g(b)-22 b(uf)-28 b(fer)261 b(shared)3631
+59470 y(between)279 b(user)e(and)h(k)-11 b(ernel)278
+b(space)g(to)f(pass)g(messages.)2524 61602 y Fg(\017)554
+b Fn(FUSD)272 b(currently)g(requires)f(de)-28 b(vfs,)272
+b(which)h(is)d(used)i(to)f(dynamically)i(create)f(de)-28
+b(vice)273 b(\002les)e(under)i Fo(/dev)e Fn(when)i(a)e(FUSD)3631
+62931 y(dri)-28 b(v)-17 b(er)273 b(re)-17 b(gisters)272
+b(itself.)341 b(This)272 b(is,)h(perhaps,)h(the)f(most)g(con)-44
+b(v)-17 b(enient)276 b(and)e(useful)f(paradigm)h(for)e(FUSD.)i(Ho)-28
+b(we)g(v)-17 b(er)-44 b(,)276 b(some)3631 64259 y(users)305
+b(ha)-22 b(v)-17 b(e)309 b(ask)-11 b(ed)307 b(if)f(it')-61
+b(s)305 b(possible)h(to)h(use)f(FUSD)i(without)f(de)-28
+b(vfs.)432 b(This)306 b(should)i(be)f(possible)f(if)g(FUSD)h(dri)-28
+b(v)-17 b(ers)307 b(bind)3631 65587 y(to)276 b(de)-28
+b(vice)280 b(major)d(numbers)h(instead)g(of)f(de)-28
+b(vice)279 b(\002le)e(names.)p 863 66453 20076 45 v 2070
+67194 a Fe(2)2457 67507 y Fd(http://www)-58 b
+(.cs.arizona.edu/people/bridges/os/microk)-9 b(ernel.html)2070
+68275 y Fe(3)2457 68588 y Fd(http://www)-58 b(.cs.ucsb)-35
+b(.edu/projects/ufo/inde)-13 b(x.html)2070 69356 y Fe(4)2457
+69669 y Fd(http://www)-58 b(.goop.or)-16 b(g/)223 b(jeremy/userfs/)2070
+70437 y Fe(5)2457 70750 y Fd(http://www)-58 b(.cs.ucsb)-35
+b(.edu/projects/ufo/97-usenix-ufo.ps)25681 74071 y Fn(4)p
+eop
+%%Page: 5 8
+5 7 bop 863 2974 a Fj(1.6)1329 b(A)-66 b(uthor)331 b(Contact)i(Inf)-33
+b(ormation)331 b(and)g(Ackno)-13 b(wledgments)863 5714
+y Fn(The)290 b(original)f(v)-17 b(ersion)290 b(of)f(FUSD)h(w)-11
+b(as)289 b(written)f(by)i(Jeremy)f(Elson)h(\(jelson at circlemud.or)-20
+b(g\))290 b(and)g(Le)-28 b(wis)289 b(Girod)g(at)g(Sensoria)863
+7042 y(Corporation.)492 b(Sensoria)327 b(no)g(longer)g(maintains)f
+(public)i(releases)e(of)g(FUSD,)g(b)-22 b(ut)327 b(the)f(same)h
+(authors)f(ha)-22 b(v)-17 b(e)328 b(since)f(fork)-11
+b(ed)327 b(the)863 8370 y(last)276 b(public)j(release)e(and)h(continue)
+h(to)e(maintain)h(FUSD)g(from)f(the)h(Uni)-28 b(v)-17
+b(ersity)278 b(of)e(California,)i(Los)f(Angeles.)2524
+10363 y(If)f(you)i(ha)-22 b(v)-17 b(e)279 b(b)-22 b(ug)278
+b(reports,)e(patches,)i(suggestions,)g(or)f(an)-17 b(y)278
+b(other)g(comments,)g(please)g(feel)f(free)g(to)g(contact)i(the)e
+(authors.)2524 12355 y(FUSD)263 b(has)f(tw)-11 b(o)262
+b(SourceF)-17 b(or)d(ge)14879 11954 y Ff(6)15326 12355
+y Fn(-host)261 b(mailing)i(lists:)334 b(a)262 b(lo)-28
+b(w-traf)g(\002c)263 b(list)d(for)i(announcements)k(\()p
+Fo(fusd-announce)p Fn(\))d(and)863 13684 y(a)346 b(list)e(for)h
+(general)i(discussion)f(\()p Fo(fusd-devel)p Fn(\).)549
+b(Subscription)347 b(information)f(for)f(both)i(lists)c(is)i(a)-22
+b(v)-28 b(ailable)347 b(at)f(the)g(Source-)863 15012
+y(F)-17 b(or)d(ge')-61 b(s)278 b(FUSD)g(mailing)g(list)d(page.)2524
+17005 y(F)-17 b(or)277 b(the)h(latest)e(releases)h(and)i(information)e
+(about)i(FUSD,)f(please)g(see)f(the)g(of)-28 b(\002cial)278
+b(FUSD)g(home)h(page)43521 16603 y Ff(7)43966 17005 y
+Fn(.)863 20837 y Fj(1.7)1329 b(Licensing)331 b(Inf)-33
+b(ormation)863 23577 y Fn(FUSD)294 b(is)e(free)h(softw)-11
+b(are,)297 b(distrib)-22 b(uted)293 b(under)h(a)f(GPL-compatible)j
+(license)e(\(the)f(\223ne)-28 b(w\224)295 b(BSD)f(license,)j(with)c
+(the)h(adv)-17 b(ertising)863 24905 y(clause)278 b(remo)-17
+b(v)g(ed\).)346 b(The)278 b(license)f(is)g(enumerated)i(in)e(its)f
+(entirety)h(belo)-28 b(w)-72 b(.)2524 26898 y(Cop)-11
+b(yright)250 b(\(c\))f(2001,)256 b(Sensoria)251 b(Corporation;)260
+b(\(c\))248 b(2003)j(Uni)-28 b(v)-17 b(ersity)251 b(of)e(California,)
+255 b(Los)249 b(Angeles.)335 b(All)249 b(rights)g(reserv)-17
+b(ed.)2524 28891 y(Redistrib)-22 b(ution)318 b(and)h(use)g(in)f(source)
+g(and)i(binary)f(forms,)327 b(with)318 b(or)g(without)g
+(modi\002cation,)331 b(are)318 b(permitted)h(pro)-17
+b(vided)320 b(that)863 30219 y(the)278 b(follo)-28 b(wing)278
+b(conditions)g(are)f(met:)2524 33097 y Fg(\017)554 b
+Fn(Redistrib)-22 b(utions)266 b(of)g(source)h(code)h(must)e(retain)g
+(the)h(abo)-17 b(v)g(e)269 b(cop)-11 b(yright)268 b(notice,)h(this)d
+(list)e(of)i(conditions)i(and)f(the)g(follo)-28 b(w-)3631
+34425 y(ing)277 b(disclaimer)-61 b(.)2524 36639 y Fg(\017)554
+b Fn(Redistrib)-22 b(utions)375 b(in)f(binary)i(form)f(must)f
+(reproduce)k(the)d(abo)-17 b(v)g(e)378 b(cop)-11 b(yright)376
+b(notice,)400 b(this)374 b(list)g(of)g(conditions)j(and)f(the)3631
+37968 y(follo)-28 b(wing)278 b(disclaimer)f(in)g(the)g(documentation)k
+(and/or)d(other)f(materials)g(pro)-17 b(vided)279 b(with)e(the)h
+(distrib)-22 b(ution.)2524 40182 y Fg(\017)554 b Fn(Neither)330
+b(the)g(names)h(of)f(Sensoria)h(Corporation)g(or)f(UCLA,)g(nor)h(the)f
+(names)h(of)e(other)i(contrib)-22 b(utors)330 b(may)h(be)f(used)h(to)
+3631 41510 y(endorse)278 b(or)f(promote)h(products)g(deri)-28
+b(v)-17 b(ed)279 b(from)e(this)f(softw)-11 b(are)277
+b(without)h(speci\002c)g(prior)f(written)f(permission.)2524
+44388 y(THIS)317 b(SOFTW)-133 b(ARE)319 b(IS)e(PR)-44
+b(O)-55 b(VIDED)317 b(BY)g(THE)h(A)-61 b(UTHORS)318 b(AND)g(CONTRIB)-11
+b(UT)-20 b(ORS)319 b(\223)-89 b(AS)318 b(IS\224)g(AND)f(ANY)g(EX-)863
+45716 y(PRESS)238 b(OR)f(IMPLIED)g(W)-133 b(ARRANTIES,)238
+b(INCLUDING,)g(B)-11 b(UT)237 b(NO)-44 b(T)236 b(LIMITED)h(T)-20
+b(O,)237 b(THE)g(IMPLIED)g(W)-133 b(ARRANTIES)863 47045
+y(OF)437 b(MERCHANT)-103 b(ABILITY)440 b(AND)d(FITNESS)h(FOR)f(A)g(P)
+-102 b(AR)-66 b(TICULAR)439 b(PURPOSE)g(ARE)e(DISCLAIMED.)h(IN)e(NO)863
+48373 y(EVENT)301 b(SHALL)f(THE)g(A)-61 b(UTHORS)301
+b(OR)f(CONTRIB)-11 b(UT)-20 b(ORS)302 b(BE)e(LIABLE)g(FOR)g(ANY)g
+(DIRECT)-82 b(,)300 b(INDIRECT)-82 b(,)300 b(INCI-)863
+49701 y(DENT)-103 b(AL,)234 b(SPECIAL,)f(EXEMPLAR)-72
+b(Y)-143 b(,)234 b(OR)f(CONSEQ)-11 b(UENTIAL)235 b(D)-44
+b(AMA)g(GES)233 b(\(INCLUDING,)f(B)-11 b(UT)232 b(NO)-44
+b(T)233 b(LIMITED)863 51030 y(T)-20 b(O,)314 b(PR)-44
+b(OCUREMENT)316 b(OF)d(SUBSTITUTE)j(GOODS)f(OR)e(SER)-89
+b(VICES;)316 b(LOSS)e(OF)g(USE,)g(D)-44 b(A)-123 b(T)-103
+b(A,)314 b(OR)f(PR)-44 b(OFITS;)315 b(OR)863 52358 y(B)-11
+b(USINESS)351 b(INTERR)-44 b(UPTION\))351 b(HO)-39 b(WEVER)351
+b(CA)-61 b(USED)351 b(AND)e(ON)h(ANY)f(THEOR)-72 b(Y)351
+b(OF)f(LIABILITY)-143 b(,)349 b(WHETHER)863 53686 y(IN)294
+b(CONTRA)-44 b(CT)-82 b(,)295 b(STRICT)g(LIABILITY)-143
+b(,)294 b(OR)g(T)-20 b(OR)-66 b(T)295 b(\(INCLUDING)f(NEGLIGENCE)i(OR)e
+(O)-44 b(THER)-61 b(WISE\))295 b(ARISING)863 55015 y(IN)371
+b(ANY)i(W)-133 b(A)-116 b(Y)371 b(OUT)i(OF)f(THE)g(USE)g(OF)h(THIS)f
+(SOFTW)-133 b(ARE,)373 b(EVEN)g(IF)f(AD)-44 b(VISED)372
+b(OF)g(THE)g(POSSIBILITY)i(OF)863 56343 y(SUCH)278 b(D)-44
+b(AMA)g(GE.)863 60743 y Fm(2)1594 b(Wh)-24 b(y)399 b(use)g(FUSD?)863
+63882 y Fn(One)240 b(basic)f(question)h(about)g(FUSD)g(that)f(one)g
+(might)g(ask)g(is:)323 b(what)240 b(is)e(it)f(good)k(for?)330
+b(Wh)-6 b(y)240 b(use)f(it?)331 b(In)238 b(this)g(section,)247
+b(we)239 b(describe)863 65210 y(some)278 b(of)e(the)i(situations)f(in)g
+(which)h(FUSD)g(has)f(been)i(the)e(solution)h(for)e(us.)p
+863 65934 20076 45 v 2070 66675 a Fe(6)2457 66987 y Fd(http://www)-58
+b(.sourcefor)-16 b(ge.net)2070 67746 y Fe(7)2457 68059
+y Fd(http://www)-58 b(.circlemud.or)-16 b(g/jelson/softw)-9
+b(are/fusd)25681 74071 y Fn(5)p eop
+%%Page: 6 9
+6 8 bop 863 2974 a Fj(2.1)1329 b(De)-20 b(vice)332 b(Dri)-13
+b(v)g(er)331 b(Lay)-13 b(ering)863 5714 y Fn(A)309 b(problem)g(that)g
+(comes)h(up)f(frequently)h(in)e(modern)i(operating)h(systems)d(is)f
+(contention)k(for)d(a)h(single)g(resource)g(by)h(multiple)863
+7042 y(competing)260 b(processes.)338 b(In)257 b(UNIX,)h(it')-61
+b(s)257 b(the)h(job)g(of)g(a)g(de)-28 b(vice)260 b(dri)-28
+b(v)-17 b(er)259 b(to)f(coordinate)i(access)f(to)f(such)h(resources.)
+337 b(By)258 b(accepting)863 8370 y(requests)327 b(from)f(user)h
+(processes)g(and)h(\(for)d(e)-17 b(xample\))329 b(queuing)g(and)f
+(serializing)e(them,)340 b(it)326 b(becomes)i(safe)f(for)f(processes)h
+(that)863 9699 y(kno)-28 b(w)273 b(nothing)g(about)f(each)h(other)e(to)
+g(mak)-11 b(e)273 b(requests)e(in)g(parallel)h(to)f(the)g(same)h
+(resource.)342 b(Of)271 b(course,)h(k)-11 b(ernel)272
+b(dri)-28 b(v)-17 b(ers)272 b(do)g(this)863 11027 y(job)g(already)-72
+b(,)274 b(b)-22 b(ut)271 b(the)-17 b(y)273 b(typically)f(operate)h(on)f
+(top)g(of)f(hardw)-11 b(are)273 b(directly)-72 b(.)342
+b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)275 b(k)-11 b(ernel)272
+b(dri)-28 b(v)-17 b(ers)272 b(can')-20 b(t)272 b(easily)f(be)i(layered)
+863 12355 y(on)278 b(top)f(of)g Fk(other)h(de)-17 b(vice)279
+b(driver)-11 b(s)p Fn(.)2524 14348 y(F)-17 b(or)252 b(e)-17
+b(xample,)259 b(consider)253 b(a)f(de)-28 b(vice)255
+b(such)d(as)g(a)g(modem)i(that)e(is)f(connected)k(to)d(a)g(host)g(via)g
+(a)g(serial)f(port.)335 b(Let')-61 b(s)252 b(say)g(we)g(w)-11
+b(ant)863 15676 y(to)271 b(implement)g(a)g(de)-28 b(vice)272
+b(dri)-28 b(v)-17 b(er)272 b(that)e(allo)-28 b(ws)271
+b(multiple)g(users)f(to)g(dial)h(the)f(telephone)j(\(e.g.,)f
+Fo(echo)664 b(1-310-555-1212)j(>)863 17005 y(/dev/phone-dialer)p
+Fn(\).)403 b(Such)298 b(a)e(dri)-28 b(v)-17 b(er)297
+b(should)g(be)g(layered)h Fk(on)f(top)f(of)g Fn(the)h(serial)e(port)i
+(dri)-28 b(v)-17 b(er)-22 b(\227that)297 b(is,)j(it)295
+b(most)h(lik)-11 b(ely)863 18333 y(w)g(ants)277 b(to)g(write)g(to)g
+Fo(/dev/ttyS0)p Fn(,)h(not)f(directly)h(to)f(the)g(U)-44
+b(AR)-66 b(T)277 b(hardw)-11 b(are)279 b(itself.)2524
+20325 y(While)272 b(it)g(is)g(possible)h(to)g(write)f(to)h(a)g(logical)
+g(\002le)h(from)e(within)h(a)g(k)-11 b(ernel)273 b(de)-28
+b(vice)275 b(dri)-28 b(v)-17 b(er)-44 b(,)274 b(it)e(is)g(both)i(trick)
+-17 b(y)273 b(and)h(considered)863 21654 y(bad)438 b(practice.)823
+b(In)437 b(the)g(w)-11 b(ords)436 b(of)h(k)-11 b(ernel)437
+b(hack)-11 b(er)439 b(Dick)e(Johnson)27919 21252 y Ff(8)28363
+21654 y Fn(,)476 b(\223Y)-122 b(ou)439 b(should)e(ne)-28
+b(v)-17 b(er)439 b(write)d(a)h([k)-11 b(ernel])437 b(module)h(that)863
+22982 y(requires)266 b(reading)i(or)d(writing)h(to)f(an)-17
+b(y)268 b(logical)f(de)-28 b(vice.)341 b(The)267 b(k)-11
+b(ernel)267 b(is)e(the)h(thing)g(that)g(translates)g(ph)-6
+b(ysical)267 b(I/O)f(to)f(logical)i(I/O.)863 24310 y(Attempting)278
+b(to)f(perform)g(logical)h(I/O)e(in)h(the)h(k)-11 b(ernel)278
+b(is)e(ef)-28 b(fecti)g(v)-17 b(ely)279 b(going)f(backw)-11
+b(ards.)-77 b(\224)2524 26303 y(W)-44 b(ith)227 b(FUSD,)j(it')-61
+b(s)227 b(possible)h(to)h(layer)g(de)-28 b(vice)230 b(dri)-28
+b(v)-17 b(ers)229 b(because)i(the)e(dri)-28 b(v)-17 b(er)229
+b(is)e(a)i(user)-22 b(-space)229 b(process,)239 b(not)229
+b(a)f(k)-11 b(ernel)230 b(module.)863 27631 y(A)289 b(FUSD)g
+(implementation)i(of)d(our)h(h)-6 b(ypothetical)292 b
+Fo(/dev/phone-dialer)f Fn(can)f(open)g Fo(/dev/ttyS0)g
+Fn(just)e(as)g(an)-17 b(y)290 b(other)863 28960 y(process)278
+b(w)-11 b(ould.)2524 30952 y(T)-89 b(ypically)-72 b(,)261
+b(such)256 b(layering)g(is)f(accomplished)j(by)d(system)g(daemons.)338
+b(F)-17 b(or)256 b(e)-17 b(xample,)262 b(the)255 b Fo(lpd)h
+Fn(daemon)h(manages)h(printers)863 32281 y(at)390 b(a)g(high)h(le)-28
+b(v)-17 b(el.)683 b(Since)391 b(it)e(is)g(a)h(user)-22
+b(-space)391 b(process,)418 b(it)389 b(can)i(access)g(the)f(ph)-6
+b(ysical)391 b(printer)f(de)-28 b(vices)392 b(using)e(k)-11
+b(ernel)391 b(de)-28 b(vice)863 33609 y(dri)g(v)-17 b(ers)278
+b(\(for)e(e)-17 b(xample,)279 b(using)e(printer)g(or)g(netw)-11
+b(ork)278 b(dri)-28 b(v)-17 b(ers\).)343 b(There)279
+b(a)e(number)h(of)f(adv)-28 b(antages)280 b(to)d(using)h(FUSD)g
+(instead:)2524 36487 y Fg(\017)554 b Fn(Using)378 b(FUSD,)g(a)g
+(daemon/dri)-28 b(v)-17 b(er)382 b(can)d(create)f(a)h(standard)f(de)-28
+b(vice)381 b(\002le)d(which)h(is)e(accessible)i(by)f(an)-17
+b(y)380 b(program)f(that)3631 37815 y(kno)-28 b(ws)247
+b(ho)-28 b(w)248 b(to)e(use)h(the)g(POSIX)g(system)f(call)h(interf)-11
+b(ace.)334 b(Some)247 b(trick)-11 b(ery)247 b(is)f(possible)g(using)h
+(named)i(pipes)e(and)g(FIFOs,)3631 39144 y(b)-22 b(ut)277
+b(quickly)h(becomes)h(dif)-28 b(\002cult)278 b(because)h(of)e(multiple)
+-17 b(x)g(ed)280 b(writes)c(from)g(multiple)i(processes.)2524
+41358 y Fg(\017)554 b Fn(FUSD)248 b(dri)-28 b(v)-17 b(ers)248
+b(recei)-28 b(v)-17 b(e)249 b(the)f(UID,)f(GID,)g(and)h(process)g(ID)f
+(along)i(with)e(e)-28 b(v)-17 b(ery)249 b(\002le)f(operation,)255
+b(allo)-28 b(wing)249 b(the)e(same)h(sorts)3631 42686
+y(of)301 b(security)i(policies)f(to)g(be)h(implemented)h(as)e(w)-11
+b(ould)303 b(be)g(possible)f(with)g(a)g(real)g(k)-11
+b(ernel)303 b(dri)-28 b(v)-17 b(er)-61 b(.)420 b(In)302
+b(contrast,)308 b(writes)301 b(to)3631 44014 y(a)277
+b(named)i(pipe,)e(UDP)-123 b(,)278 b(and)g(so)f(forth)f(are)i(\223anon)
+-17 b(ymous.)-77 b(\224)863 47847 y Fj(2.2)1329 b(Use)332
+b(of)g(User)-49 b(-Space)331 b(Libraries)863 50587 y
+Fn(Since)240 b(a)f(FUSD)h(dri)-28 b(v)-17 b(er)239 b(is)f(just)g(a)h
+(re)-17 b(gular)240 b(user)-22 b(-space)239 b(program,)248
+b(it)238 b(can)h(naturally)h(use)f(an)-17 b(y)240 b(of)f(the)g
+(enormous)h(body)h(of)d(e)-17 b(xisting)863 51915 y(libraries)314
+b(that)i(e)-17 b(xist)315 b(for)f(almost)h(an)-17 b(y)317
+b(task.)458 b(FUSD)316 b(dri)-28 b(v)-17 b(ers)315 b(can)h(easily)g
+(incorporate)h(user)e(interf)-11 b(aces,)324 b(encryption,)j(netw)-11
+b(ork)863 53244 y(protocols,)338 b(threads,)g(and)327
+b(almost)e(an)-17 b(ything)328 b(else.)488 b(In)326 b(contrast,)337
+b(porting)326 b(arbitrary)g(C)f(code)i(into)f(the)g(k)-11
+b(ernel)326 b(is)f(dif)-28 b(\002cult)326 b(and)863 54572
+y(usually)278 b(a)f(bad)h(idea.)863 58405 y Fj(2.3)1329
+b(Dri)-13 b(v)g(er)331 b(Memory)h(Pr)-24 b(otection)863
+61145 y Fn(Since)355 b(FUSD)g(dri)-28 b(v)-17 b(ers)354
+b(run)g(in)f(their)g(o)-28 b(wn)355 b(process)f(space,)374
+b(the)354 b(rest)f(of)g(the)h(system)g(is)e(protected)j(from)f(them.)
+573 b(A)354 b(b)-22 b(uggy)355 b(or)863 62473 y(malicious)252
+b(FUSD)h(dri)-28 b(v)-17 b(er)-44 b(,)257 b(at)251 b(the)h(v)-17
+b(ery)253 b(w)-11 b(orst,)255 b(can)e(only)f(corrupt)g(itself.)334
+b(It')-61 b(s)250 b(not)h(possible)h(for)f(it)g(to)g(corrupt)h(the)g(k)
+-11 b(ernel,)257 b(other)863 63801 y(FUSD)268 b(dri)-28
+b(v)-17 b(ers,)269 b(or)e(e)-28 b(v)-17 b(en)270 b(the)d(processes)h
+(that)f(are)g(using)h(its)d(de)-28 b(vices.)342 b(In)267
+b(contrast,)i(a)e(b)-22 b(uggy)269 b(k)-11 b(ernel)268
+b(module)h(can)f(bring)f(do)-28 b(wn)863 65130 y(an)-17
+b(y)279 b(process)e(in)g(the)h(system,)e(or)h(the)g(entire)h(k)-11
+b(ernel)278 b(itself.)p 863 66078 20076 45 v 2070 66819
+a Fe(8)2457 67131 y Fd(http://www)-58 b(.uwsg.indiana.edu/h)l
+(ypermail/linux/k)-9 b(ernel/0005.3/0061.html)25681 74071
+y Fn(6)p eop
+%%Page: 7 10
+7 9 bop 863 2974 a Fj(2.4)1329 b(Gi)-13 b(ving)333 b(libraries)e
+(language)h(independence)d(and)i(standard)g(noti\002cation)h
+(interfaces)863 5714 y Fn(One)396 b(particularly)g(interesting)f
+(application)i(of)e(FUSD)h(that)f(we')-55 b(v)-17 b(e)396
+b(found)h(v)-17 b(ery)396 b(useful)f(is)f(as)h(a)g(w)-11
+b(ay)396 b(to)f(let)g(re)-17 b(gular)396 b(user)-22 b(-)863
+7042 y(space)342 b(libraries)f(e)-17 b(xport)342 b(de)-28
+b(vice)343 b(\002le)f(APIs.)534 b(F)-17 b(or)342 b(e)-17
+b(xample,)359 b(imagine)343 b(you)f(had)g(a)f(library)g(which)h(f)-11
+b(actored)342 b(lar)-20 b(ge)342 b(composite)863 8370
+y(numbers.)596 b(T)-89 b(ypically)-72 b(,)384 b(it)360
+b(might)i(ha)-22 b(v)-17 b(e)362 b(a)g(C)f(interf)-11
+b(ace\227say)-72 b(,)383 b(a)361 b(function)h(called)g
+Fo(int)665 b(*factorize\(int)h(bignum\))p Fn(.)863 9699
+y(W)-44 b(ith)408 b(FUSD,)h(it')-61 b(s)406 b(possible)j(to)f(create)h
+(a)f(de)-28 b(vice)411 b(\002le)d(interf)-11 b(ace\227say)-72
+b(,)442 b(a)409 b(de)-28 b(vice)410 b(called)f Fo(/dev/factorize)i
+Fn(to)d(which)863 11027 y(clients)277 b(can)h Fo(write\(2\))g
+Fn(a)g(big)f(number)-44 b(,)278 b(then)g Fo(read\(2\))g
+Fn(back)h(its)c(f)-11 b(actors.)2524 13020 y(This)279
+b(may)h(sound)g(strange,)g(b)-22 b(ut)280 b(de)-28 b(vice)281
+b(\002le)f(APIs)e(ha)-22 b(v)-17 b(e)281 b(at)e(least)g(three)h(adv)-28
+b(antages)282 b(o)-17 b(v)g(er)281 b(a)f(typical)g(library)f(API.)g
+(First,)f(it)863 14348 y(becomes)253 b(much)f(more)f(language)i
+(independent\227an)-17 b(y)256 b(language)d(that)e(can)g(mak)-11
+b(e)252 b(system)f(calls)f(can)i(access)f(the)g(f)-11
+b(actorization)863 15676 y(library)-72 b(.)739 b(Second,)444
+b(the)409 b(f)-11 b(actorization)410 b(code)h(is)d(running)i(in)f(a)g
+(dif)-28 b(ferent)409 b(address)h(space;)476 b(if)408
+b(it)g(crashes,)442 b(it)408 b(w)-11 b(on')-20 b(t)409
+b(crash)g(or)863 17005 y(corrupt)364 b(the)g(caller)-61
+b(.)604 b(Third,)385 b(and)365 b(most)e(interestingly)-72
+b(,)385 b(it)363 b(is)g(possible)h(to)f(use)h Fo(select\(2\))h
+Fn(to)e(w)-11 b(ait)364 b(for)f(the)h(f)-11 b(actorization)863
+18333 y(to)337 b(complete.)526 b Fo(select\(2\))338 b
+Fn(w)-11 b(ould)338 b(mak)-11 b(e)339 b(it)d(easy)i(for)e(a)i(client)f
+(to)g(f)-11 b(actor)337 b(a)h(lar)-20 b(ge)338 b(number)g(while)g
+(remaining)g(responsi)-28 b(v)-17 b(e)863 19661 y(to)329
+b Fk(other)h Fn(e)-28 b(v)-17 b(ents)331 b(that)f(might)f(happen)j(in)d
+(the)h(meantime.)502 b(In)329 b(other)g(w)-11 b(ords,)343
+b(FUSD)330 b(allo)-28 b(ws)329 b(normal)h(user)-22 b(-space)331
+b(libraries)d(to)863 20990 y(inte)-17 b(grate)278 b(seamlessly)g(with)e
+(UNIX')-61 b(s)277 b(e)-17 b(xisting,)277 b(POSIX-standard)i(e)-28
+b(v)-17 b(ent)279 b(noti\002cation)g(interf)-11 b(ace:)344
+b Fo(select\(2\))p Fn(.)863 24743 y Fj(2.5)1329 b(De)-20
+b(v)-13 b(elopment)331 b(and)g(Deb)-27 b(ugging)332 b(Con)-53
+b(v)-13 b(enience)863 27483 y Fn(FUSD)286 b(processes)g(can)g(be)g(de)
+-28 b(v)-17 b(eloped)289 b(and)e(deb)-22 b(ugged)288
+b(with)d(all)g(the)g(normal)h(user)-22 b(-space)286 b(tools.)368
+b(Buggy)287 b(dri)-28 b(v)-17 b(ers)285 b(w)-11 b(on')-20
+b(t)286 b(crash)863 28812 y(the)318 b(system,)327 b(b)-22
+b(ut)318 b(instead)g(dump)g(cores)g(that)g(can)g(be)g(analyzed.)467
+b(All)317 b(of)g(your)h(f)-11 b(a)-22 b(v)g(orite)318
+b(visual)f(deb)-22 b(uggers,)330 b(memory)318 b(bounds)863
+30140 y(check)-11 b(ers,)420 b(leak)390 b(detectors,)419
+b(pro\002lers,)f(and)391 b(other)f(tools)g(can)h(be)f(applied)i(to)e
+(FUSD)g(dri)-28 b(v)-17 b(ers)391 b(as)e(the)-17 b(y)391
+b(w)-11 b(ould)391 b(to)f(an)-17 b(y)391 b(other)863
+31468 y(program.)863 35789 y Fm(3)1594 b(Installing)400
+b(FUSD)863 38928 y Fn(This)228 b(section)i(describes)f(the)g
+(installation)f(procedure)j(for)d(FUSD.)h(It)f(assumes)g(a)h(good)h(w)
+-11 b(orking)229 b(kno)-28 b(wledge)232 b(of)d(Linux)g(system)863
+40256 y(administration.)863 44010 y Fj(3.1)1329 b(Pr)-24
+b(er)g(equisites)863 46750 y Fn(Before)278 b(installing)f(FUSD,)h(mak)
+-11 b(e)278 b(sure)f(you)h(ha)-22 b(v)-17 b(e)279 b(all)e(of)f(the)i
+(follo)-28 b(wing)278 b(packages)i(installed)d(and)h(w)-11
+b(orking)278 b(correctly:)2524 49410 y Fg(\017)554 b
+Fl(Linux)293 b(k)-11 b(er)-17 b(nel)295 b(2.4.0)f(or)f(later)p
+Fn(.)391 b(FUSD)294 b(w)-11 b(as)292 b(de)-28 b(v)-17
+b(eloped)297 b(under)d(2.4.0)g(and)f(should)h(w)-11 b(ork)293
+b(with)g(an)-17 b(y)294 b(k)-11 b(ernel)294 b(in)e(the)i(2.4)3631
+50738 y(series.)2524 52777 y Fg(\017)554 b Fl(de)-17
+b(vfs)256 b(installed)f(and)h(running)-17 b(.)339 b Fn(FUSD)256
+b(dynamically)i(re)-17 b(gisters)255 b(de)-28 b(vices)257
+b(using)f(de)-28 b(vfs,)260 b(the)255 b(Linux)i(de)-28
+b(vice)258 b(\002lesystem)3631 54105 y(by)388 b(Richard)h(Gooch.)676
+b(F)-17 b(or)389 b(FUSD)f(to)g(w)-11 b(ork,)415 b(de)-28
+b(vfs)388 b(must)f(be)i(installed)e(and)i(running)g(on)f(your)h
+(system.)674 b(F)-17 b(or)389 b(more)3631 55434 y(information)277
+b(about)i(de)-28 b(vfs)277 b(installation,)g(see)h(the)f(de)-28
+b(vfs)278 b(home)g(page)30782 55032 y Ff(9)31227 55434
+y Fn(.)3631 57117 y(Note)295 b(that)g(some)h(distrib)-22
+b(utions)294 b(mak)-11 b(e)297 b(installation)e(de)-28
+b(vfs)295 b(easier)-61 b(.)398 b(RedHat)296 b(7.1,)k(for)295
+b(e)-17 b(xample,)301 b(already)c(has)e(all)g(of)g(the)3631
+58445 y(necessary)f(daemons)h(and)f(con\002guration)i(changes)g(inte)
+-17 b(grated.)393 b(de)-28 b(vfs)293 b(can)h(be)g(installed)f(simply)g
+(by)h(recompiling)h(the)3631 59774 y(k)-11 b(ernel)277
+b(with)g(de)-28 b(vfs)278 b(support)g(enabled)h(and)f(recon\002guring)i
+(LILO)e(to)e(pass)h Fo("devfs=mount")i Fn(to)e(the)h(k)-11
+b(ernel.)863 63527 y Fj(3.2)1329 b(Compiling)332 b(FUSD)f(as)h(a)h(K)
+-33 b(er)-20 b(nel)331 b(Module)863 66267 y Fn(Before)312
+b(compiling)h(an)-17 b(ything,)322 b(tak)-11 b(e)312
+b(a)f(look)h(at)g(the)f(Mak)-11 b(e\002le)313 b(in)e(FUSD')-61
+b(s)312 b(home)h(directory)-72 b(.)446 b(Adjust)312 b(an)-17
+b(y)312 b(constants)g(that)g(are)863 67596 y(not)368
+b(correct.)616 b(In)367 b(particular)-44 b(,)390 b(mak)-11
+b(e)369 b(sure)e Fo(KERNEL)p 21778 67596 333 45 v 400
+w(HOME)h Fn(correctly)g(re\003ects)g(the)g(place)h(where)g(your)f(k)-11
+b(ernel)369 b(sources)f(are)863 68924 y(installed,)277
+b(if)f(the)-17 b(y)278 b(aren')-20 b(t)278 b(in)f(the)g(def)-11
+b(ault)278 b(location)g(of)f Fo(/usr/src/linux)p Fn(.)p
+863 69696 20076 45 v 2070 70437 a Fe(9)2457 70750 y Fd(http://www)-58
+b(.atnf.csiro.au/)225 b(r)-16 b(gooch/linux/docs/de)-22
+b(vfs.html)25681 74071 y Fn(7)p eop
+%%Page: 8 11
+8 10 bop 2524 2974 a Fn(Then,)350 b(type)335 b Fo(make)p
+Fn(.)516 b(It)334 b(should)h(generate)i(a)d(directory)i(whose)f(name)h
+(looks)f(something)h(lik)-11 b(e)334 b Fo(obj.i686-linux)p
+Fn(,)351 b(or)863 4302 y(some)278 b(v)-28 b(ariation)278
+b(depending)i(on)e(your)g(architecture.)344 b(Inside)277
+b(of)g(that)g(directory)h(will)e(be)i(a)f(number)i(of)e(\002les,)f
+(including:)2524 7180 y Fg(\017)554 b Fn(kfusd.o)277
+b(\226)g(The)h(FUSD)h(k)-11 b(ernel)277 b(module)2524
+9394 y Fg(\017)554 b Fn(libfusd.a)277 b(\226)g(The)h(C)f(library)g
+(used)h(to)f(talk)g(to)g(the)g(k)-11 b(ernel)278 b(module)2524
+11608 y Fg(\017)554 b Fn(Example)278 b(programs)g(\226)f(link)-11
+b(ed)278 b(ag)-6 b(ainst)279 b(libfusd.a)2524 14486 y(Compilation)338
+b(of)f(the)g(k)-11 b(ernel)338 b(module)g(will)f(f)-11
+b(ail)336 b(if)g(the)h(dependencies)k(described)d(in)f(the)g(pre)-28
+b(vious)339 b(section)e(are)h(not)f(sat-)863 15815 y(is\002ed.)438
+b(The)310 b(module)g(must)f(be)g(compiled)h(ag)-6 b(ain)311
+b(Linux)e(k)-11 b(ernel)310 b(must)e(be)h(v2.4.0)h(or)f(later)-44
+b(,)315 b(and)310 b(the)f(k)-11 b(ernel)309 b(must)g(ha)-22
+b(v)-17 b(e)310 b(de)-28 b(vfs)863 17143 y(support)278
+b(enabled.)863 20976 y Fj(3.3)1329 b(T)-122 b(esting)332
+b(and)f(T)-98 b(r)-24 b(oubleshooting)863 23716 y Fn(Once)293
+b(e)-28 b(v)-17 b(erything)294 b(has)e(been)h(compiled,)k(gi)-28
+b(v)-17 b(e)293 b(it)d(a)i(try)f(to)g(see)h(if)e(it)h(actually)i(does)f
+(something.)388 b(First,)293 b(use)f Fo(insmod)g Fn(to)g(insert)863
+25044 y(the)216 b(FUSD)g(k)-11 b(ernel)217 b(module,)229
+b(e.g.)323 b Fo(insmod)665 b(obj.i686-linux/kfusd.o)p
+Fn(.)326 b(A)215 b(greeting)i(message)f(similar)f(to)g(\223)p
+Fo(fusd:)863 26372 y(starting,)666 b(Revision:)1330 b(1.50)p
+Fn(\224)328 b(should)f(appear)i(in)e(the)g(k)-11 b(ernel)327
+b(log)h(\(accessed)g(using)f(the)g Fo(dmesg)h Fn(command,)341
+b(or)863 27701 y(by)304 b(typing)h Fo(cat)665 b(/proc/kmsg)p
+Fn(\).)423 b(Y)-122 b(ou)305 b(can)f(v)-17 b(erify)304
+b(the)g(module)h(has)f(been)h(inserted)f(by)g(typing)g
+Fo(lsmod)p Fn(,)311 b(or)303 b(alternati)-28 b(v)-17
+b(ely)863 29029 y Fo(cat)665 b(/proc/modules)p Fn(.)2524
+31021 y(Once)323 b(the)f(module)i(has)e(been)h(inserted)f(successfully)
+-72 b(,)334 b(trying)322 b(running)h(the)f Fo(helloworld)i
+Fn(e)-17 b(xample)324 b(program.)479 b(When)863 32350
+y(run,)335 b(the)323 b(program)i(should)f(print)e(a)i(greeting)g
+(message)g(similar)e(to)h Fo(/dev/hello-world)667 b(should)e(now)g
+(exist)g(-)863 33678 y(calling)h(fusd)p 8899 33678 333
+45 v 399 w(run)p Fn(.)330 b(This)236 b(means)h(e)-28
+b(v)-17 b(erything)239 b(is)c(w)-11 b(orking;)251 b(the)237
+b(daemon)h(is)e(no)-28 b(w)237 b(block)-11 b(ed,)247
+b(w)-11 b(aiting)236 b(for)g(requests)g(to)h(the)863
+35006 y(ne)-28 b(w)308 b(de)-28 b(vice.)436 b(From)308
+b(another)g(shell,)314 b(type)308 b Fo(cat)665 b(/dev/hello-world)p
+Fn(.)436 b(Y)-122 b(ou)308 b(should)g(see)g Fo(Hello,)665
+b(world!)434 b Fn(printed)863 36335 y(in)277 b(response.)344
+b(T)-39 b(ry)278 b(killing)f(the)g(test)f(program;)i(the)g
+(corresponding)h(de)-28 b(vice)279 b(\002le)f(should)g(disappear)-61
+b(.)2524 38327 y(If)309 b(nothing)j(seems)f(to)f(be)i(w)-11
+b(orking,)320 b(try)310 b(looking)i(at)e(the)h(k)-11
+b(ernel)312 b(message)g(log)f(\(type)g Fo(dmesg)g Fn(or)f
+Fo(cat)665 b(/proc/kmsg)p Fn(\))863 39656 y(to)317 b(see)h(if)e(there)i
+(are)f(an)-17 b(y)319 b(errors.)463 b(If)316 b(nothing)j(seems)f(ob)-17
+b(viously)319 b(wrong,)328 b(try)317 b(turning)h(on)g(FUSD)g(k)-11
+b(ernel)318 b(module)h(deb)-22 b(ugging)863 40984 y(by)278
+b(de\002ning)h Fo(CONFIG)p 10202 40984 V 400 w(FUSD)p
+13258 40984 V 399 w(DEBUG)f Fn(in)f(kfusd.c,)g(then)h(recompiling)g
+(and)h(reinserting)e(the)g(module.)863 44817 y Fj(3.4)1329
+b(Installation)863 47557 y Fn(T)-89 b(yping)236 b Fo(make)665
+b(install)236 b Fn(will)d(cop)-11 b(y)236 b(the)e(FUSD)h(library)-72
+b(,)243 b(header)236 b(\002les,)242 b(and)236 b(man)f(pages)g(into)f
+Fo(/usr/local)p Fn(.)331 b(The)235 b(FUSD)863 48885 y(k)-11
+b(ernel)276 b(module)h(is)e Fk(not)g Fn(installed)h(automatically)h
+(because)h(of)d(v)-28 b(ariations)276 b(among)h(dif)-28
+b(ferent)276 b(Linux)g(distrib)-22 b(utions)275 b(in)g(ho)-28
+b(w)277 b(this)863 50213 y(is)219 b(accomplished.)327
+b(Y)-122 b(ou)221 b(may)g(w)-11 b(ant)221 b(to)f(arrange)h(to)f(ha)-22
+b(v)-17 b(e)222 b(the)e(module)i(start)d(automatically)i(on)g(boot)g
+(by)g(\(for)e(e)-17 b(xample\))222 b(cop)-11 b(ying)863
+51542 y(it)276 b(into)h Fo(/lib/modules/your-kernel-version)p
+Fn(,)283 b(and)278 b(adding)h(it)d(to)h Fo(/etc/modules.conf)p
+Fn(.)863 55375 y Fj(3.5)1329 b(Making)332 b(FUSD)f(P)-13
+b(art)332 b(of)g(the)g(K)-33 b(er)-20 b(nel)332 b(Pr)-24
+b(oper)863 58114 y Fn(The)335 b(earlier)f(instructions,)347
+b(by)335 b(def)-11 b(ault,)348 b(create)335 b(a)f(FUSD)h(k)-11
+b(ernel)335 b(module.)515 b(If)333 b(desired,)348 b(it')-61
+b(s)332 b(also)i(v)-17 b(ery)335 b(easy)g(to)f(b)-22
+b(uild)334 b(FUSD)863 59443 y(right)277 b(into)g(the)g(k)-11
+b(ernel,)278 b(instead:)2247 62321 y(1.)554 b(Unpack)233
+b(the)f(2.4)g(k)-11 b(ernel)233 b(sources)f(and)h(cop)-11
+b(y)233 b(all)e(the)h(\002les)g(in)f(the)h Fo(include)g
+Fn(and)h Fo(kfusd)f Fn(directories)g(into)g(your)g(k)-11
+b(ernel)3631 63649 y(source)262 b(tree,)i(under)e Fo(drivers/char)p
+Fn(.)340 b(F)-17 b(or)262 b(e)-17 b(xample,)266 b(if)260
+b(FUSD)j(is)d(in)h(your)h(home)h(directory)-72 b(,)264
+b(and)f(your)f(k)-11 b(ernel)262 b(is)e(in)3631 64977
+y Fo(/usr/src/linux)p Fn(:)8944 67634 y Fo(cp)664 b(\230/fusd/kfusd/*)j
+(\230/fusd/include/*)g(/usr/src/linux/drivers/char)2247
+70291 y Fn(2.)554 b(Apply)278 b(the)f(patch)h(found)h(in)e(FUSD')-61
+b(s)277 b Fo(patches)h Fn(directory)g(to)f(your)h(k)-11
+b(ernel)278 b(source)f(tree.)344 b(F)-17 b(or)277 b(e)-17
+b(xample:)25681 74071 y(8)p eop
+%%Page: 9 12
+9 11 bop 8944 2974 a Fo(cd)664 b(/usr/src/linux)8944
+4302 y(patch)h(-p0)g(<)f(\230/fusd/patches/fusd-inkernel.patch)3631
+6753 y Fn(The)401 b(FUSD)h(in-k)-11 b(ernel)402 b(patch)g(doesn')-20
+b(t)401 b(actually)h(change)i(an)-17 b(y)402 b(k)-11
+b(ernel)402 b(sources)f(proper;)463 b(it)400 b(just)h(adds)g(FUSD)h(to)
+f(the)3631 8082 y(k)-11 b(ernel)277 b(con\002guration)k(menu)d(and)g
+(Mak)-11 b(e\002le.)2247 10214 y(3.)554 b(Using)266 b(your)i(k)-11
+b(ernel)267 b(con\002gurator)j(of)c(choice)j(\(e.g.)339
+b Fo(make)665 b(menuconfig)p Fn(\),)270 b(turn)c(on)i(the)f(FUSD)g
+(options.)341 b(It)266 b(will)f(be)3631 11542 y(under)278
+b(the)f(\223Character)i(de)-28 b(vices\224)279 b(menu.)2247
+13674 y(4.)554 b(Build)277 b(and)h(install)e(the)i(k)-11
+b(ernel)278 b(as)f(usual.)863 18037 y Fm(4)1594 b(Basic)399
+b(De)-24 b(vice)398 b(Cr)-29 b(eation)863 21175 y Fn(Enough)280
+b(introduction\227it')-61 b(s)277 b(time)g(to)g(actually)h(create)g(a)f
+(basic)h(de)-28 b(vice)279 b(dri)-28 b(v)-17 b(er)278
+b(using)f(FUSD!)2524 23168 y(This)304 b(follo)-28 b(wing)306
+b(sections)f(will)f(illustrate)g(v)-28 b(arious)305 b(techniques)i
+(using)e(e)-17 b(xample)308 b(programs.)427 b(T)-89 b(o)305
+b(sa)-22 b(v)-17 b(e)306 b(space,)313 b(interesting)863
+24496 y(e)-17 b(xcerpts)418 b(are)e(sho)-28 b(wn)418
+b(instead)e(of)g(entire)h(programs.)761 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)453 b(the)416 b Fo(examples)i Fn(directory)f(of)f(the)g
+(FUSD)h(distrib)-22 b(ution)863 25824 y(contains)360
+b(all)e(the)h(e)-17 b(xamples)361 b(in)d(their)h(entirety)-72
+b(.)588 b(The)-17 b(y)361 b(can)f(actually)f(be)h(compiled)g(and)g(run)
+f(on)g(a)g(system)f(with)h(the)g(FUSD)863 27153 y(k)-11
+b(ernel)278 b(module)h(installed.)2524 29145 y(Where)362
+b(this)f(te)-17 b(xt)363 b(refers)e(to)h(e)-17 b(xample)364
+b(program)f(line)f(numbers,)384 b(it)361 b(refers)g(to)h(the)g(line)g
+(numbers)h(printed)g(alongside)h(the)863 30474 y(e)-17
+b(xcerpts)279 b(in)e(the)g(manual\227not)i(the)f(line)f(numbers)h(of)f
+(the)g(actual)h(programs)g(in)f(the)g Fo(examples)h Fn(directory)-72
+b(.)863 34269 y Fj(4.1)1329 b(Using)332 b Fc(fusd)p 10700
+34269 399 45 v 478 w(register)g Fj(to)h(cr)-24 b(eate)332
+b(a)g(new)f(de)-20 b(vice)863 37009 y Fn(W)-89 b(e)367
+b(sa)-17 b(w)366 b(an)h(e)-17 b(xample)368 b(of)e(a)g(simple)g(dri)-28
+b(v)-17 b(er)-44 b(,)388 b(hello)-28 b(w)-11 b(orld.c,)390
+b(in)365 b(Program)i(1)g(on)f(page)i(2.)610 b(Let')-61
+b(s)365 b(go)i(back)g(and)g(e)-17 b(xamine)369 b(that)863
+38337 y(program)278 b(no)-28 b(w)278 b(in)f(more)h(detail.)2524
+40330 y(The)228 b(FUSD)g(ball)g(starts)e(rolling)h(when)i(the)e
+Fo(fusd)p 22085 40330 333 45 v 400 w(register)h Fn(function)h(is)d
+(called,)238 b(as)227 b(sho)-28 b(wn)229 b(on)f(line)f(40.)328
+b(This)227 b(function)863 41658 y(tells)276 b(the)i(FUSD)g(k)-11
+b(ernel)278 b(module:)2524 44372 y Fg(\017)554 b Fo(char)664
+b(*name)p Fn(\227The)222 b(name)f(of)f(the)g(de)-28 b(vice)222
+b(being)f(created.)325 b(The)221 b(pre\002x)g(\(such)f(as)f
+Fo(/dev/)p Fn(\))h(must)g(match)h(the)f(location)3631
+45700 y(where)349 b(de)-28 b(vfs)350 b(has)f(been)i(mounted.)561
+b(Names)350 b(containing)h(slashes)d(\(e.g.,)367 b Fo
+(/dev/my-devices/dev1)p Fn(\))352 b(are)d(le)-17 b(g)-6
+b(al;)3631 47029 y(de)-28 b(vfs)277 b(creates)h(subdirectories)g
+(automatically)-72 b(.)2524 49161 y Fg(\017)554 b Fo(mode)p
+6353 49161 V 399 w(t)664 b(mode)p Fn(\227The)284 b(de)-28
+b(vice')-61 b(s)284 b(def)-11 b(ault)283 b(permissions.)358
+b(This)282 b(is)f(usually)i(speci\002ed)h(using)f(an)g(octal)f
+(constant)i(with)e(a)3631 50489 y(leading)c(0\227)p Fo(0666)g
+Fn(\(readable)h(and)f(writable)f(by)h(e)-28 b(v)-17 b(eryone\))280
+b(instead)d(of)g(the)h(incorrect)g(decimal)g(constant)g
+Fo(666)p Fn(.)2524 52621 y Fg(\017)554 b Fo(void)664
+b(*device)p 11665 52621 V 400 w(info)p Fn(\227Pri)-28
+b(v)g(ate)292 b(data)g(that)e(should)h(be)g(passed)g(to)f(callback)i
+(functions)f(for)f(this)g(de)-28 b(vice.)384 b(The)292
+b(use)3631 53949 y(of)276 b(this)h(\002eld)h(is)e(described)i(in)f
+(Section)i(5.1.)2524 56081 y Fg(\017)554 b Fo(struct)665
+b(fusd)p 11002 56081 V 399 w(file)p 14057 56081 V 399
+w(operations)h(*fops)p Fn(\227A)286 b(structure)f(containing)j
+(pointers)d(to)g(the)h(callback)h(functions)3631 57409
+y(that)277 b(should)h(be)f(called)i(by)e(FUSD)h(in)f(response)h(to)f
+(certain)h(e)-28 b(v)-17 b(ents.)2524 60123 y(If)228
+b(de)-28 b(vice)232 b(re)-17 b(gistration)230 b(is)f(successful,)239
+b Fo(fusd)p 20591 60123 V 399 w(register)231 b Fn(returns)e(a)h
+Fk(de)-17 b(vice)232 b(handle)p Fn(\227a)g(small)d(inte)-17
+b(ger)231 b Fg(\025)307 b Fb(0)p Fn(.)328 b(On)230 b(errors,)863
+61451 y(it)305 b(returns)h(-1)f(and)i(sets)e(the)h(global)h(v)-28
+b(ariable)308 b Fo(errno)e Fn(appropriately)-72 b(.)432
+b(In)305 b(reality)-72 b(,)313 b(the)306 b(de)-28 b(vice)308
+b(handle)g(you)f(get)f(is)f(a)h(plain)h(old)863 62780
+y(\002le)278 b(descriptor)-44 b(,)276 b(as)h(we')-11
+b(ll)277 b(see)g(in)g(Section)i(7.)2524 64772 y(Although)303
+b(Program)f(1)g(only)g(calls)f Fo(fusd)p 19296 64772
+V 399 w(register)i Fn(once,)308 b(it)301 b(can)h(be)g(called)h
+(multiple)e(times)g(if)g(the)g(FUSD)i(dri)-28 b(v)-17
+b(er)302 b(is)863 66100 y(handling)279 b(more)f(than)g(one)g(de)-28
+b(vice)279 b(as)e(we')-11 b(ll)276 b(see)i(in)f(Program)h(4.)2524
+68093 y(There)375 b(is)f(intentional)i(similarity)d(between)j
+Fo(fusd)p 23104 68093 V 400 w(register\(\))g Fn(and)f(the)g(k)-11
+b(ernel')-61 b(s)375 b(de)-28 b(vice)377 b(re)-17 b(gistration)374
+b(functions,)863 69421 y(such)265 b(as)f Fo(devfs)p 7728
+69421 V 400 w(register\(\))i Fn(and)f Fo(register)p 22208
+69421 V 400 w(chrdev\(\))p Fn(.)340 b(In)264 b(man)-17
+b(y)266 b(w)-11 b(ays,)267 b(FUSD')-61 b(s)265 b(interf)-11
+b(ace)265 b(is)e(meant)j(to)e(mirror)863 70750 y(the)278
+b(k)-11 b(ernel)278 b(interf)-11 b(ace)277 b(as)g(closely)h(as)f
+(possible.)25681 74071 y(9)p eop
+%%Page: 10 13
+10 12 bop 2524 2974 a Fn(The)358 b Fo(fusd)p 7324 2974
+333 45 v 400 w(file)p 10380 2974 V 399 w(operations)h
+Fn(structure,)378 b(de\002ned)360 b(in)e Fo(fusd.h)p
+Fn(,)379 b(contains)359 b(a)f(list)e(of)i(callbacks)h(that)f(are)g
+(used)h(in)863 4302 y(response)325 b(to)e(dif)-28 b(ferent)324
+b(system)g(calls)f(e)-17 b(x)g(ecuted)327 b(on)e(a)e(\002le.)484
+b(It)322 b(is)h(similar)g(to)g(the)h(k)-11 b(ernel')-61
+b(s)324 b Fo(file)p 39605 4302 V 399 w(operations)h Fn(structure,)863
+5631 y(accepting)g(callbacks)f(for)e(system)g(calls)g(such)h(as)f
+Fo(open\(\))p Fn(,)334 b Fo(close\(\))p Fn(,)g Fo(read\(\))p
+Fn(,)g Fo(write\(\))p Fn(,)g(and)324 b Fo(ioctl\(\))p
+Fn(.)480 b(F)-17 b(or)323 b(the)863 6959 y(most)243 b(part,)250
+b(the)244 b(prototypes)h(of)f(FUSD)g(\002le)g(operation)h(callbacks)h
+(are)d(the)h(same)h(as)e(their)g(k)-11 b(ernel)245 b(cousins,)250
+b(with)244 b(one)g(important)863 8287 y(e)-17 b(xception.)338
+b(The)254 b(\002rst)d(ar)-20 b(gument)255 b(of)d(FUSD)i(callbacks)g(is)
+e(al)-11 b(w)g(ays)253 b(a)g(pointer)g(to)f(a)h Fo(fusd)p
+35757 8287 V 399 w(file)p 38812 8287 V 400 w(info)g Fn(structure;)260
+b(it)252 b(contains)863 9616 y(information)322 b(that)g(can)g(be)g
+(used)g(to)f(identify)h(the)g(\002le.)476 b(This)321
+b(structure)g(is)g(used)h(instead)g(of)f(the)h(k)-11
+b(ernel')-61 b(s)321 b Fo(file)h Fn(and)g Fo(inode)863
+10944 y Fn(structures,)276 b(and)j(will)d(be)h(described)i(in)e(more)g
+(detail)h(later)-61 b(.)2524 12936 y(In)247 b(lines)h(35\22638)i(of)e
+(Program)h(1,)254 b(we)248 b(create)h(and)h(initialize)e(a)g
+Fo(fusd)p 29219 12936 V 399 w(file)p 32274 12936 V 399
+w(operations)i Fn(structure.)333 b(A)248 b(GCC-speci\002c)863
+14265 y(C)240 b(e)-17 b(xtension)241 b(allo)-28 b(ws)240
+b(us)f(to)h(name)g(structure)g(\002elds)g(e)-17 b(xplicitly)240
+b(in)g(the)g(initializer)-61 b(.)330 b(This)239 b(style)h(may)g(look)g
+(strange,)248 b(b)-22 b(ut)239 b(it)g(guards)863 15593
+y(ag)-6 b(ainst)302 b(errors)d(in)i(the)f(future)h(in)f(case)h(the)g
+(order)g(of)f(\002elds)h(in)f(the)h(structure)g(e)-28
+b(v)-17 b(er)302 b(changes.)415 b(The)302 b(2.4)e(k)-11
+b(ernel)302 b(series)d(uses)i(the)863 16922 y(same)278
+b(trick.)2524 18914 y(After)401 b(calling)i Fo(fusd)p
+11398 18914 V 399 w(register\(\))g Fn(on)g(line)f(40,)434
+b(the)402 b(e)-17 b(xample)405 b(program)e(calls)f Fo(fusd)p
+39357 18914 V 399 w(run\(\))g Fn(on)h(line)f(44.)719
+b(This)863 20242 y(function)235 b(turns)f(control)g(o)-17
+b(v)g(er)235 b(to)f(the)g(FUSD)h(frame)-28 b(w)-11 b(ork.)330
+b(fusd)p 25909 20242 V 399 w(run)234 b(blocks)h(the)f(dri)-28
+b(v)-17 b(er)234 b(until)g(one)h(of)e(the)i(de)-28 b(vices)235
+b(it)e(re)-17 b(gistered)863 21571 y(needs)278 b(to)f(be)h(serviced.)
+344 b(Then,)279 b(it)d(calls)h(the)g(appropriate)i(callback)g(and)f
+(blocks)g(ag)-6 b(ain)279 b(until)e(the)g(ne)-17 b(xt)278
+b(e)-28 b(v)-17 b(ent.)2524 23563 y(No)-28 b(w)-72 b(,)455
+b(imagine)420 b(that)f(a)g(user)f(types)i Fo(cat)664
+b(/dev/hello-world)p Fn(.)772 b(What)419 b(happens?)771
+b(Recall)420 b(\002rst)e(what)i(the)f Fo(cat)863 24892
+y Fn(program)283 b(itself)d(does:)353 b(opens)283 b(a)f(\002le,)h
+(reads)f(from)f(it)g(until)g(it)g(recei)-28 b(v)-17 b(es)283
+b(an)g(EOF)f(\(printing)f(whate)-28 b(v)-17 b(er)285
+b(it)280 b(reads)i(to)g(stdout\),)g(then)863 26220 y(closes)296
+b(it.)400 b Fo(cat)296 b Fn(w)-11 b(orks)296 b(the)g(same)h(w)-11
+b(ay)297 b(re)-17 b(g)-6 b(ardless)297 b(of)f(what)g(it')-61
+b(s)295 b(reading\227be)j(it)d(a)h(a)h(FUSD)g(de)-28
+b(vice,)302 b(a)297 b(re)-17 b(gular)297 b(\002le,)j(a)d(serial)863
+27548 y(port,)277 b(or)g(an)-17 b(ything)279 b(else.)343
+b(The)278 b Fo(strace)g Fn(program)g(is)e(a)i(great)f(w)-11
+b(ay)278 b(to)f(see)g(this)g(in)f(action;)i(see)g(Appendix)h(A)e(for)g
+(details.)863 31381 y Fj(4.2)1329 b(The)331 b Fc(open)h
+Fj(and)g Fc(close)g Fj(callbacks)863 34121 y Fn(The)298
+b(\002rst)d(tw)-11 b(o)297 b(callbacks)h(that)f(most)f(dri)-28
+b(v)-17 b(ers)297 b(typically)h(implement)f(are)g Fo(open)g
+Fn(and)h Fo(close)p Fn(.)402 b(Each)298 b(of)f(these)g(tw)-11
+b(o)296 b(functions)863 35449 y(are)222 b(passed)h(just)e(one)i(ar)-20
+b(gument\227the)224 b Fo(fusd)p 18814 35449 V 399 w(file)p
+21869 35449 V 400 w(info)e Fn(structure)g(that)f(describes)i(the)f
+(instance)h(of)f(the)g(\002le)g(being)h(opened)863 36778
+y(or)277 b(closed.)344 b(Use)277 b(of)g(the)g(information)h(in)f(that)g
+(structure)g(will)f(be)i(co)-17 b(v)g(ered)280 b(in)d(more)h(detail)f
+(in)g(Section)h(5.)2524 38770 y(The)g(semantics)f(of)g(an)h
+Fo(open)f Fn(callback')-61 b(s)279 b(return)e(v)-28 b(alue)278
+b(are)g(e)-17 b(xactly)279 b(the)e(same)h(as)e(inside)i(the)f(k)-11
+b(ernel:)2524 41648 y Fg(\017)554 b Fn(0)241 b(means)g(success,)249
+b(and)242 b(the)f(\002le)g(is)f(opened.)334 b(If)239
+b(the)j(\002le)f(is)f(allo)-28 b(wed)242 b(to)f(open,)249
+b(the)241 b(k)-11 b(ernel)242 b(returns)f(a)g(v)-28 b(alid)241
+b(\002le)h(descriptor)3631 42977 y(to)290 b(the)g(client.)383
+b(Using)291 b(that)f(descriptor)-44 b(,)293 b(other)e(callbacks)h(may)f
+(be)g(called)g(for)f(that)g(\002le,)k(including)e(\(at)d(least\))h(a)g
+Fo(close)3631 44305 y Fn(callback.)2524 46519 y Fg(\017)554
+b Fn(A)364 b(ne)-17 b(g)-6 b(ati)-28 b(v)-17 b(e)368
+b(number)e(indicates)f(a)g(f)-11 b(ailure,)386 b(and)366
+b(that)e(the)h(\002le)g(should)g(not)g(be)g(opened.)609
+b(Such)366 b(return)e(v)-28 b(alues)366 b(should)3631
+47847 y Fk(always)287 b Fn(be)h(the)g(speci\002ed)h(as)d(a)i(ne)-17
+b(g)-6 b(ati)-28 b(v)-17 b(e)290 b Fo(errno)e Fn(v)-28
+b(alue)289 b(such)e(as)g Fo(-EPERM)p Fn(,)h Fo(-EBUSY)p
+Fn(,)f Fo(-ENODEV)p Fn(,)h Fo(-ENOMEM)p Fn(,)g(and)3631
+49176 y(so)416 b(on.)764 b(F)-17 b(or)417 b(e)-17 b(xample,)454
+b(if)416 b(the)i(callback)h(returns)d Fo(-EPERM)p Fn(,)i(the)f(caller')
+-61 b(s)416 b Fo(open\(\))i Fn(will)e(return)h(-1,)452
+b(with)416 b Fo(errno)3631 50504 y Fn(set)367 b(to)g
+Fo(EPERM)p Fn(.)h(A)f(complete)j(list)c(of)h(possible)h(return)g(v)-28
+b(alues)369 b(can)f(be)h(found)g(in)e(the)h(Linux)h(k)-11
+b(ernel)368 b(sources,)391 b(under)3631 51832 y Fo(include/asm/errno.h)
+p Fn(.)2524 54710 y(If)267 b(an)j Fo(open)f Fn(callback)i(returns)d(0)h
+(\(success\),)h(a)f(dri)-28 b(v)-17 b(er)270 b(is)e Fk(guar)-17
+b(anteed)272 b Fn(to)d(recei)-28 b(v)-17 b(e)271 b(e)-17
+b(xactly)270 b(one)g Fo(close)g Fn(callback)h(for)d(that)863
+56039 y(\002le)d(later)-61 b(.)339 b(By)265 b(the)g(same)g(tok)-11
+b(en,)268 b(the)d(close)g(callback)h Fk(will)e(not)h
+Fn(be)g(called)g(if)f(the)h(open)h(f)-11 b(ails.)338
+b(Therefore,)268 b Fo(open)d Fn(callbacks)h(that)863
+57367 y(can)278 b(return)f(f)-11 b(ailure)277 b(must)g(be)h(sure)f(to)g
+(deallocate)i(an)-17 b(y)278 b(resources)g(the)-17 b(y)278
+b(might)g(ha)-22 b(v)-17 b(e)279 b(allocated)f(before)g(returning)g(a)f
+(f)-11 b(ailure.)2524 59360 y(Let')-61 b(s)347 b(return)i(to)f(our)g(e)
+-17 b(xample)351 b(in)d(Program)h(1,)366 b(which)349
+b(creates)g(the)g Fo(/dev/hello-world)h Fn(de)-28 b(vice.)559
+b(If)347 b(a)h(user)h(types)863 60688 y Fo(cat)665 b(/dev/hello-world)p
+Fn(,)250 b Fo(cat)241 b Fn(will)f(will)f(use)i(the)g
+Fo(open\(2\))h Fn(system)f(call)f(to)h(open)h(the)f(\002le.)332
+b(FUSD)241 b(will)f(then)i(proxy)863 62016 y(that)298
+b(system)g(call)g(to)f(the)h(dri)-28 b(v)-17 b(er)299
+b(and)g(acti)-28 b(v)g(ate)300 b(the)e(callback)i(that)e(w)-11
+b(as)298 b(re)-17 b(gistered)298 b(as)g(the)g Fo(open)g
+Fn(callback.)408 b(Recall)298 b(from)g(line)863 63345
+y(36)278 b(of)f(Program)h(1)f(that)g(we)h(re)-17 b(gistered)277
+b Fo(do)p 17888 63345 V 399 w(open)p 20943 63345 V 399
+w(or)p 22670 63345 V 399 w(close)p Fn(,)h(which)g(appears)g(on)g(line)f
+(8.)2524 65337 y(In)348 b Fo(helloworld.c)p Fn(,)369
+b(the)350 b Fo(open)f Fn(callback)i(al)-11 b(w)g(ays)350
+b(returns)f(0,)367 b(or)349 b(success.)559 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)370 b(in)349 b(a)g(real)g(dri)-28 b(v)-17
+b(er)-44 b(,)368 b(something)863 66666 y(more)346 b(interesting)g(will)
+f(probably)i(happen\227permissions)h(checks,)364 b(memory)347
+b(allocation)g(for)e(state-k)-11 b(eeping,)365 b(and)346
+b(so)g(forth.)863 67994 y(The)315 b(corresponding)i Fk(de)p
+Fn(-allocation)f(of)e(those)h(resources)g(should)g(occur)g(in)f(the)h
+Fo(close)f Fn(callback,)326 b(which)315 b(is)e(called)i(when)h(a)863
+69322 y(user)283 b(application)i(calls)e Fo(close)h Fn(on)g(their)f
+(\002le)h(descriptor)-61 b(.)362 b Fo(close)284 b Fn(callbacks)h(are)e
+(allo)-28 b(wed)285 b(to)e(return)h(error)e(v)-28 b(alues,)286
+b(b)-22 b(ut)284 b(this)863 70651 y(does)278 b(not)f(pre)-28
+b(v)-17 b(ent)280 b(the)d(\002le)g(from)g(actually)h(closing.)25405
+74071 y(10)p eop
+%%Page: 11 14
+11 13 bop 863 2974 a Fj(4.3)1329 b(The)331 b Fc(read)h
+Fj(callback)863 5714 y Fn(Returning)309 b(to)e(our)g
+Fo(cat)665 b(/dev/hello-world)310 b Fn(e)-17 b(xample,)317
+b(what)307 b(happens)j(after)c(the)i Fo(open)g Fn(is)e(successful?)434
+b(Ne)-17 b(xt,)316 b Fo(cat)863 7042 y Fn(will)345 b(try)g(to)g(use)h
+Fo(read\(2\))p Fn(,)364 b(which)346 b(will)f(get)h(proxied)h(by)f(FUSD)
+h(to)e(the)h(function)h Fo(do)p 36325 7042 333 45 v 399
+w(read)f Fn(on)g(line)g(13.)549 b(This)346 b(function)863
+8370 y(tak)-11 b(es)278 b(some)f(additional)i(ar)-20
+b(guments)278 b(that)f(we)h(didn')-20 b(t)277 b(see)g(in)g(the)h(open)g
+(and)g(close)g(callbacks:)2524 11225 y Fg(\017)554 b
+Fo(struct)665 b(fusd)p 11002 11225 V 399 w(file)p 14057
+11225 V 399 w(info)g(*file)p Fn(\227The)294 b(\002rst)e(ar)-20
+b(gument)294 b(to)e(all)g(callbacks,)297 b(containing)e(information)e
+(which)3631 12554 y(describes)277 b(the)h(\002le;)f(see)g(Section)i(5.)
+2524 14756 y Fg(\017)554 b Fo(char)664 b(*user)p 10337
+14756 V 400 w(buffer)p Fn(\227The)370 b(b)-22 b(uf)-28
+b(fer)369 b(that)f(the)g(callback)j(should)e(use)f(to)h(write)e(data)i
+(that)g(it)e(is)g(returning)i(to)g(the)3631 16085 y(user)-61
+b(.)2524 18287 y Fg(\017)554 b Fo(size)p 6353 18287 V
+399 w(t)664 b(user)p 10736 18287 V 399 w(length)p Fn(\227The)294
+b(maximum)f(number)g(of)e(bytes)h(requested)h(by)g(the)f(user)-61
+b(.)386 b(The)293 b(dri)-28 b(v)-17 b(er)292 b(is)f(allo)-28
+b(wed)293 b(to)3631 19616 y(return)277 b(fe)-28 b(wer)277
+b(bytes,)g(b)-22 b(ut)278 b(should)g(ne)-28 b(v)-17 b(er)279
+b(write)d(more)i(then)g Fo(user)p 29807 19616 V 399 w(length)g
+Fn(bytes)f(into)g Fo(user)p 41736 19616 V 399 w(buffer)p
+Fn(.)2524 21818 y Fg(\017)554 b Fo(loff)p 6353 21818
+V 399 w(t)664 b(*offset)p Fn(\227A)250 b(pointer)f(to)f(an)h(inte)-17
+b(ger)249 b(which)h(represents)e(the)h(caller')-61 b(s)248
+b(of)-28 b(fset)247 b(into)i(the)g(\002le)f(\(i.e.,)253
+b(the)c(user')-61 b(s)3631 23146 y(\002le)305 b(pointer\).)429
+b(This)306 b(v)-28 b(alue)307 b(can)g(be)f(modi\002ed)i(by)e(the)g
+(callback;)321 b(an)-17 b(y)307 b(change)h(will)d(be)h(propag)-6
+b(ated)309 b(back)e(to)f(the)g(user')-61 b(s)3631 24475
+y(\002le)277 b(pointer)h(inside)f(the)g(k)-11 b(ernel.)2524
+27330 y(The)278 b(semantics)f(of)g(the)g(return)h(v)-28
+b(alue)278 b(are)g(the)f(same)h(as)e(if)g(the)i(callback)h(were)e
+(being)i(written)e(inside)g(the)g(k)-11 b(ernel)278 b(itself:)2524
+30185 y Fg(\017)554 b Fn(Positi)-28 b(v)-17 b(e)248 b(return)g(v)-28
+b(alues)249 b(indicate)g(success.)334 b(If)246 b(the)i(call)g(is)e
+(successful,)254 b(and)249 b(the)f(dri)-28 b(v)-17 b(er)248
+b(has)g(copied)h(data)g(into)e Fo(buffer)p Fn(,)3631
+31513 y(the)416 b(return)g(v)-28 b(alue)417 b(indicates)g(ho)-28
+b(w)417 b(man)-17 b(y)417 b(bytes)f(were)h(copied.)761
+b(This)416 b(number)h(should)f(ne)-28 b(v)-17 b(er)418
+b(be)f(greater)f(than)h(the)3631 32842 y Fo(user)p 6353
+32842 V 399 w(length)278 b Fn(ar)-20 b(gument.)2524 35044
+y Fg(\017)554 b Fn(A)277 b(0)g(return)g(v)-28 b(alue)279
+b(indicates)f(EOF)f(has)h(been)g(reached)h(on)f(the)g(\002le.)2524
+37247 y Fg(\017)554 b Fn(As)278 b(in)i(the)f Fo(open)h
+Fn(and)h Fo(close)f Fn(callbacks,)h(ne)-17 b(g)-6 b(ati)-28
+b(v)-17 b(e)283 b(v)-28 b(alues)281 b(\(such)e(as)g(-EPERM,)h(-EPIPE,)g
+(or)f(-ENOMEM\))h(indicate)3631 38575 y(errors.)342 b(Such)278
+b(v)-28 b(alues)279 b(will)d(cause)i(the)g(user')-61
+b(s)276 b Fo(read\(\))i Fn(to)f(return)g(-1)g(with)f(errno)i(set)e
+(appropriately)-72 b(.)2524 41430 y(The)332 b(\002rst)f(time)h(a)g
+(read)h(is)e(done)i(on)f(a)g(de)-28 b(vice)334 b(\002le,)346
+b(the)332 b(user')-61 b(s)331 b(\002le)i(pointer)f(\()p
+Fo(*offset)p Fn(\))g(is)f(0.)508 b(In)331 b(the)i(case)f(of)g(this)f
+(\002rst)863 42759 y(read,)403 b(a)378 b(greeting)h(message)g(of)e
+Fo(Hello,)665 b(world!)646 b Fn(is)377 b(copied)i(back)g(to)f(the)g
+(user)-44 b(,)402 b(as)377 b(seen)i(on)f(line)g(24.)645
+b(The)379 b(user')-61 b(s)377 b(\002le)863 44087 y(pointer)279
+b(is)e(then)h(adv)-28 b(anced.)350 b(The)279 b(ne)-17
+b(xt)279 b(read)g(therefore)g(f)-11 b(ails)276 b(the)j(comparison)g(at)
+f(line)g(20,)h(f)-11 b(alling)277 b(straight)h(through)h(to)f(return)
+863 45415 y(0,)f(or)g(EOF)-89 b(.)2524 47408 y(In)369
+b(this)g(simple)g(program,)394 b(we)370 b(also)f(see)h(an)g(e)-17
+b(xample)372 b(of)e(an)g(error)f(return)h(on)g(line)f(22:)529
+b(if)369 b(the)h(user)f(tries)g(to)g(do)h(a)g(read)863
+48736 y(smaller)354 b(than)h(the)g(length)g(of)g(the)f(greeting)i
+(message,)374 b(the)355 b(read)g(will)f(f)-11 b(ail)353
+b(with)h(-EINV)-149 b(AL.)354 b(\(In)g(an)h(actual)g(dri)-28
+b(v)-17 b(er)-44 b(,)374 b(it)354 b(w)-11 b(ould)863
+50065 y(normally)323 b(not)g(be)g(an)g(error)f(for)f(a)i(user)f(to)g
+(pro)-17 b(vide)324 b(a)f(smaller)f(read)h(b)-22 b(uf)-28
+b(fer)322 b(than)h(the)g(size)f(of)g(the)h(a)-22 b(v)-28
+b(ailable)324 b(data.)480 b(The)323 b(right)863 51393
+y(w)-11 b(ay)246 b(for)f(dri)-28 b(v)-17 b(ers)246 b(to)f(handle)i
+(this)d(situation)i(is)e(to)h(return)g(partial)g(data,)253
+b(then)246 b(mo)-17 b(v)g(e)247 b Fo(*offset)f Fn(forw)-11
+b(ard)246 b(so)f(that)g(the)h(remainder)863 52721 y(is)276
+b(returned)i(on)g(the)f(ne)-17 b(xt)279 b Fo(read\(\))p
+Fn(.)344 b(W)-89 b(e)277 b(see)h(an)f(e)-17 b(xample)280
+b(of)d(this)f(in)h(Program)h(2.\))863 56549 y Fj(4.4)1329
+b(The)331 b Fc(write)i Fj(callback)863 59289 y Fn(Program)342
+b(1)g(illustrated)e(ho)-28 b(w)343 b(a)e(dri)-28 b(v)-17
+b(er)342 b(could)h(return)e(data)h Fk(to)f Fn(a)g(client)h(using)g(the)
+f Fo(read)h Fn(callback.)537 b(As)341 b(you)h(might)g(e)-17
+b(xpect,)863 60617 y(there)344 b(is)f(a)g(corresponding)j
+Fo(write)e Fn(callback)i(that)d(allo)-28 b(ws)344 b(the)g(dri)-28
+b(v)-17 b(er)344 b(to)f(recei)-28 b(v)-17 b(e)346 b(data)e
+Fk(fr)-50 b(om)344 b Fn(a)f(client.)543 b Fo(write)344
+b Fn(tak)-11 b(es)344 b(four)863 61945 y(ar)-20 b(guments,)278
+b(similar)e(to)h(the)g Fo(read)h Fn(callback:)2524 65016
+y Fg(\017)554 b Fo(struct)665 b(fusd)p 11002 65016 V
+399 w(file)p 14057 65016 V 399 w(info)g(*file)p Fn(\227The)294
+b(\002rst)e(ar)-20 b(gument)294 b(to)e(all)g(callbacks,)297
+b(containing)e(information)e(which)3631 66345 y(describes)277
+b(the)h(\002le;)f(see)g(Section)i(5.)2524 68547 y Fg(\017)554
+b Fo(const)665 b(char)f(*user)p 14322 68547 V 400 w(buffer)p
+Fn(\227Pointer)279 b(to)e(data)g(being)i(written)e(by)g(the)h(client)f
+(\(read-only\).)2524 70750 y Fg(\017)554 b Fo(size)p
+6353 70750 V 399 w(t)664 b(user)p 10736 70750 V 399 w(length)p
+Fn(\227The)279 b(number)g(of)e(bytes)g(pointed)i(to)e(by)g
+Fo(user)p 34471 70750 V 400 w(buffer)p Fn(.)25405 74071
+y(11)p eop
+%%Page: 12 15
+12 14 bop 2524 2974 a Fg(\017)554 b Fo(loff)p 6353 2974
+333 45 v 399 w(t)664 b(*offset)p Fn(\227A)250 b(pointer)f(to)f(an)h
+(inte)-17 b(ger)249 b(which)h(represents)e(the)h(caller')-61
+b(s)248 b(of)-28 b(fset)247 b(into)i(the)g(\002le)f(\(i.e.,)253
+b(the)c(user')-61 b(s)3631 4302 y(\002le)305 b(pointer\).)429
+b(This)306 b(v)-28 b(alue)307 b(can)g(be)f(modi\002ed)i(by)e(the)g
+(callback;)321 b(an)-17 b(y)307 b(change)h(will)d(be)h(propag)-6
+b(ated)309 b(back)e(to)f(the)g(user')-61 b(s)3631 5631
+y(\002le)277 b(pointer)h(inside)f(the)g(k)-11 b(ernel.)2524
+8730 y(The)278 b(semantics)f(of)g Fo(write)p Fn(')-61
+b(s)277 b(return)g(v)-28 b(alue)279 b(are)e(the)h(same)f(as)g(in)g(a)g
+(k)-11 b(ernel)278 b(callback:)2524 11608 y Fg(\017)554
+b Fn(Positi)-28 b(v)-17 b(e)321 b(return)f(v)-28 b(alues)321
+b(indicate)h(success)e(and)h(indicate)h(ho)-28 b(w)321
+b(man)-17 b(y)322 b(bytes)e(of)g(the)g(user')-61 b(s)320
+b(b)-22 b(uf)-28 b(fer)320 b(were)h(successfully)3631
+12936 y(written)284 b(\(i.e.,)i(successfully)f(processed)h(by)g(the)f
+(dri)-28 b(v)-17 b(er)286 b(in)e(some)i(w)-11 b(ay\).)367
+b(The)286 b(return)f(v)-28 b(alue)286 b(may)g(be)f(less)f(than)i(or)f
+(equal)3631 14265 y(to)276 b(the)i Fo(user)p 9120 14265
+V 399 w(length)g Fn(ar)-20 b(gument,)278 b(b)-22 b(ut)278
+b(should)g(ne)-28 b(v)-17 b(er)279 b(be)e(greater)-61
+b(.)2524 16479 y Fg(\017)554 b Fn(0)277 b(should)h(only)g(be)f
+(returned)h(in)f(response)h(to)f(a)h Fo(write)f Fn(of)g(length)h(0.)
+2524 18693 y Fg(\017)554 b Fn(Ne)-17 b(g)-6 b(ati)-28
+b(v)-17 b(e)406 b(v)-28 b(alues)403 b(\(such)g(as)g(-EPERM,)g(-EPIPE,)g
+(or)f(-ENOMEM\))h(indicate)h(errors.)719 b(Such)404 b(v)-28
+b(alues)404 b(will)e(cause)i(the)3631 20021 y(user')-61
+b(s)276 b Fo(write\(\))i Fn(to)f(return)g(-1)g(with)g(errno)g(set)g
+(appropriately)-72 b(.)2524 22899 y(Program)408 b(2,)441
+b(echo.c,)h(is)407 b(an)h(e)-17 b(xample)410 b(implementation)g(of)e(a)
+g(de)-28 b(vice)410 b(\()p Fo(/dev/echo)p Fn(\))e(that)g(uses)g(both)g
+Fo(read\(\))h Fn(and)863 24227 y Fo(write\(\))d Fn(callbacks.)730
+b(A)405 b(client)g(that)h(tries)e(to)h Fo(read\(\))h
+Fn(from)f(this)f(de)-28 b(vice)407 b(will)e(get)g(the)h(contents)g(of)f
+(the)h(most)f(recent)863 25556 y Fo(write\(\))p Fn(.)344
+b(F)-17 b(or)278 b(e)-17 b(xample:)863 27935 y Fo(\045)665
+b(echo)f(Hello)h(there)g(>)g(/dev/echo)863 29264 y(\045)g(cat)f
+(/dev/echo)863 30592 y(Hello)h(there)863 31920 y(\045)g(echo)f(Device)i
+(drivers)f(are)g(fun)f(>)h(/dev/echo)863 33249 y(\045)g(cat)f
+(/dev/echo)863 34577 y(Device)h(drivers)h(are)e(fun)2524
+37435 y Fn(The)260 b(implementation)i(of)d Fo(/dev/echo)i
+Fn(k)-11 b(eeps)261 b(a)e(global)i(v)-28 b(ariable,)264
+b Fo(data)p Fn(,)g(which)c(serv)-17 b(es)260 b(as)g(a)f(cache)j(for)d
+(the)h(data)g(most)863 38763 y(recently)255 b(written)e(to)g(the)h(dri)
+-28 b(v)-17 b(er)254 b(by)g(a)f(client)h(program.)336
+b(The)255 b(dri)-28 b(v)-17 b(er)254 b(does)g(not)f(assume)h(the)g
+(data)g(is)f(null-terminated,)258 b(so)c(it)e(also)863
+40092 y(k)-11 b(eeps)278 b(track)g(of)f(the)g(number)i(of)d(bytes)i(of)
+f(data)h(a)-22 b(v)-28 b(ailable.)345 b(\(These)278 b(tw)-11
+b(o)277 b(v)-28 b(ariables)278 b(appear)h(on)e(lines)g(1\2262.\))2524
+42084 y(The)313 b(dri)-28 b(v)-17 b(er')-61 b(s)313 b
+Fo(write)g Fn(callback)h(\002rst)e(frees)g(an)-17 b(y)314
+b(data)f(which)h(might)e(ha)-22 b(v)-17 b(e)315 b(been)f(allocated)g
+(by)f(a)g(pre)-28 b(vious)314 b(call)e(to)h(write)863
+43413 y(\(lines)348 b(26\22629\).)558 b(Ne)-17 b(xt,)367
+b(on)349 b(line)f(33,)367 b(it)347 b(attempts)i(to)f(allocate)h(ne)-28
+b(w)350 b(memory)f(for)f(the)h(ne)-28 b(w)349 b(data)g(arri)-28
+b(ving.)558 b(If)347 b(the)h(allocation)863 44741 y(f)-11
+b(ails,)377 b Fo(-ENOMEM)360 b Fn(is)d(returned)j(to)e(the)h(client.)
+588 b(If)357 b(the)i(allocation)h(is)d(successful,)379
+b(the)359 b(dri)-28 b(v)-17 b(er)359 b(copies)g(the)g(data)h(into)e
+(its)f(local)863 46069 y(b)-22 b(uf)-28 b(fer)302 b(and)g(stores)e(its)
+g(length)j(\(lines)d(37\22638\).)418 b(Finally)-72 b(,)308
+b(the)301 b(dri)-28 b(v)-17 b(er)303 b(tells)d(the)h(user)h(that)f(the)
+h(entire)f(b)-22 b(uf)-28 b(fer)301 b(w)-11 b(as)302
+b(consumed)h(by)863 47398 y(returning)278 b(a)f(v)-28
+b(alue)279 b(equal)f(to)f(the)h(number)g(of)f(bytes)g(the)h(user)f
+(tried)g(to)g(write)f(\()p Fo(user)p 34362 47398 V 399
+w(length)p Fn(\).)2524 49390 y(The)324 b Fo(read)f Fn(callback)i(has)e
+(some)h(e)-17 b(xtra)324 b(features)f(that)g(we)h(did)f(not)g(see)h(in)
+f(Program)h(1')-61 b(s)322 b Fo(read\(\))i Fn(callback.)483
+b(The)324 b(most)863 50719 y(important)387 b(is)e(that)h(it)g(allo)-28
+b(ws)386 b(the)h(dri)-28 b(v)-17 b(er)387 b(to)f(read)h(the)f(a)-22
+b(v)-28 b(ailable)388 b(data)f Fk(incr)-41 b(ementally)p
+Fn(,)415 b(instead)387 b(of)f(requiring)h(that)f(the)h(\002rst)863
+52047 y Fo(read\(\))288 b Fn(e)-17 b(x)g(ecuted)291 b(by)d(the)g
+(client)f(has)h(enough)h(space)g(for)d(all)h(the)h(data)g(the)g(dri)-28
+b(v)-17 b(er)288 b(has)f(a)-22 b(v)-28 b(ailable.)376
+b(In)287 b(other)h(w)-11 b(ords,)289 b(a)f(client)863
+53375 y(can)278 b(do)g(tw)-11 b(o)277 b(50-byte)i(reads,)e(and)h(e)-17
+b(xpect)279 b(the)f(same)f(ef)-28 b(fect)278 b(as)e(if)h(it)f(had)i
+(done)h(a)e(single)g(100-byte)i(read.)2524 55368 y(This)334
+b(is)g(implemented)j(using)f Fo(*offset)p Fn(,)350 b(the)335
+b(user')-61 b(s)334 b(\002le)h(pointer)-61 b(.)518 b(If)334
+b(the)h(user)g(is)f(trying)h(to)g(read)h(past)e(the)i(amount)g(of)863
+56696 y(data)362 b(we)f(ha)-22 b(v)-17 b(e)362 b(a)-22
+b(v)-28 b(ailable,)384 b(the)361 b(dri)-28 b(v)-17 b(er)361
+b(returns)g(EOF)g(\(lines)f(8\2269\).)595 b(Normally)-72
+b(,)382 b(this)360 b(happens)j(after)d(the)i(client)f(has)g(\002nished)
+863 58025 y(reading)g(data.)590 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)381 b(in)359 b(this)f(dri)-28 b(v)-17 b(er)-44
+b(,)380 b(it)358 b(might)i(happen)h(on)f(a)f(client')-61
+b(s)359 b(\002rst)f(read)i(if)e(nothing)i(has)f(been)i(written)e(to)g
+(the)863 59353 y(dri)-28 b(v)-17 b(er)278 b(yet)f(or)g(if)f(the)i(most)
+f(recent)h(write')-61 b(s)275 b(memory)k(allocation)f(f)-11
+b(ailed.)2524 61345 y(If)258 b(there)j(is)e(data)i(to)e(return,)264
+b(the)c(dri)-28 b(v)-17 b(er)261 b(computes)g(the)g(number)g(of)f
+(bytes)g(that)g(should)h(be)g(copied)g(back)h(to)e(the)g(client\227the)
+863 62674 y(minimum)e(of)f(the)h(number)g(of)f(bytes)h(the)g(user)f
+(ask)-11 b(ed)258 b(for)-44 b(,)260 b(and)f(the)e(number)i(of)e(bytes)h
+(of)f(data)h(that)f(this)g(client)g(hasn')-20 b(t)258
+b(seen)g(yet)863 64002 y(\(line)266 b(12\).)339 b(This)266
+b(data)g(is)f(copied)j(back)f(to)f(the)g(user')-61 b(s)265
+b(b)-22 b(uf)-28 b(fer)265 b(\(line)h(15\),)i(and)f(the)f(user')-61
+b(s)265 b(\002le)h(pointer)g(is)f(adv)-28 b(anced)269
+b(accordingly)863 65331 y(\(line)277 b(16\).)343 b(Finally)-72
+b(,)278 b(on)g(line)f(19,)g(the)h(client)f(is)f(told)h(ho)-28
+b(w)279 b(man)-17 b(y)278 b(bytes)g(were)f(copied)i(to)e(its)f(b)-22
+b(uf)-28 b(fer)-61 b(.)25405 74071 y(12)p eop
+%%Page: 13 16
+13 15 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(2)d Fn(echo.c:)345 b(Using)277 b(both)h Fo(read)g Fn(and)g
+Fo(write)f Fn(callbacks)p 863 3445 50191 45 v 365 4367
+a Fi(1)1661 b Fo(char)664 b(*data)i(=)e(NULL;)2524 5695
+y(int)g(data_length)i(=)f(0;)2524 8352 y(int)f(echo_read\(struct)j
+(fusd_file_info)g(*file,)e(char)g(*user_buffer,)365 9680
+y Fi(5)10959 b Fo(size_t)665 b(user_length,)i(loff_t)e(*offset\))2524
+11009 y({)3852 12337 y(/*)f(if)h(the)g(user)g(has)f(read)h(past)g(the)g
+(end)f(of)h(the)g(data,)g(return)g(EOF)g(*/)3852 13665
+y(if)f(\(*offset)i(>=)f(data_length\))5180 14994 y(return)h(0;)-133
+16322 y Fi(10)3852 17650 y Fo(/*)e(only)h(return)h(as)e(much)h(data)g
+(as)f(we)h(have)g(*/)3852 18979 y(user_length)h(=)e(MIN\(user_length,)j
+(data_length)g(-)d(*offset\);)3852 21636 y(/*)g(copy)h(data)g(to)g
+(user)g(starting)g(from)g(the)g(first)g(byte)g(they)g(haven't)g(seen)g
+(*/)-133 22964 y Fi(15)2989 b Fo(memcpy\(user_buffer,)667
+b(data)e(+)g(*offset,)g(user_length\);)3852 24292 y(*offset)g(+=)g
+(user_length;)3852 26949 y(/*)f(tell)h(them)g(how)g(much)g(data)g(they)
+g(got)f(*/)3852 28277 y(return)h(user_length;)-133 29606
+y Fi(20)1661 b Fo(})2524 32262 y(ssize_t)665 b(echo_write\(struct)i
+(fusd_file_info)g(*file,)e(const)g(char)g(*user_buffer,)15143
+33591 y(size_t)g(user_length,)h(loff_t)g(*offset\))2524
+34919 y({)-133 36247 y Fi(25)2989 b Fo(/*)664 b(free)h(the)g(old)g
+(data,)g(if)f(any)h(*/)3852 37576 y(if)f(\(data)i(!=)e(NULL\))h({)5180
+38904 y(free\(data\);)5180 40232 y(data)g(=)g(NULL;)5180
+41561 y(data_length)h(=)f(0;)-133 42889 y Fi(30)2989
+b Fo(})3852 45546 y(/*)664 b(allocate)i(space)f(for)g(new)g(data;)g
+(return)g(error)g(if)f(that)h(fails)g(*/)3852 46874 y(if)f(\(\(data)i
+(=)e(malloc\(user_length\)\))k(==)c(NULL\))5180 48203
+y(return)i(-ENOMEM;)-133 49531 y Fi(35)3852 50859 y Fo(/*)e(make)h(a)g
+(copy)g(of)f(user's)h(data;)g(tell)g(the)g(user)g(we)f(copied)i
+(everything)g(*/)3852 52188 y(memcpy\(data,)g(user_buffer,)h
+(user_length\);)3852 53516 y(data_length)f(=)e(user_length;)3852
+54844 y(return)h(user_length;)-133 56173 y Fi(40)1661
+b Fo(})p 863 57667 V 863 60988 a Fj(4.5)1329 b(Unr)-24
+b(egistering)331 b(a)i(de)-20 b(vice)331 b(with)h Fc(fusd)p
+22940 60988 399 45 v 479 w(unregister\(\))863 63728 y
+Fn(All)e(de)-28 b(vices)331 b(re)-17 b(gistered)331 b(by)g(a)f(dri)-28
+b(v)-17 b(er)331 b(are)f(unre)-17 b(gistered)332 b(automatically)g
+(when)f(the)g(program)g(e)-17 b(xits)330 b(\(or)g(crashes\).)502
+b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)863 65056 y(the)409
+b Fo(fusd)p 5346 65056 333 45 v 399 w(unregister\(\))h
+Fn(function)g(can)f(be)g(used)g(to)g(unre)-17 b(gister)409
+b(a)f(de)-28 b(vice)411 b(without)e(terminating)g(the)g(entire)f(dri)
+-28 b(v)-17 b(er)-61 b(.)863 66384 y Fo(fusd)p 3585 66384
+V 399 w(unregister)279 b Fn(tak)-11 b(es)277 b(one)h(ar)-20
+b(gument:)345 b(a)278 b(de)-28 b(vice)279 b(handle)g(\(i.e.,)c(the)j
+(return)f(v)-28 b(alue)279 b(from)d Fo(fusd)p 41820 66384
+V 400 w(register\(\))p Fn(\).)2524 68377 y(A)217 b(de)-28
+b(vice)219 b(can)g(be)f(unre)-17 b(gistered)219 b(at)e(an)-17
+b(y)219 b(time.)323 b(An)-17 b(y)219 b(client)f(system)f(calls)g(that)h
+(are)f(pending)j(when)f(a)e(de)-28 b(vice)220 b(is)c(unre)-17
+b(gistered)863 69705 y(will)276 b(return)h(immediately)i(with)e(an)g
+(error)-61 b(.)343 b(In)277 b(this)f(case,)i Fo(errno)g
+Fn(will)e(be)h(set)g(to)g Fo(-EPIPE)p Fn(.)25405 74071
+y(13)p eop
+%%Page: 14 17
+14 16 bop 863 2974 a Fm(5)1594 b(Using)399 b(Inf)-40
+b(ormation)399 b(in)f Fa(fusd)p 21880 2974 479 45 v 576
+w(file)p 26280 2974 V 575 w(info)863 6112 y Fn(W)-89
+b(e)232 b(mentioned)i(in)d(the)g(pre)-28 b(vious)233
+b(sections)f(that)f(the)h(\002rst)e(ar)-20 b(gument)233
+b(to)e(e)-28 b(v)-17 b(ery)233 b(callback)h(is)c(a)i(pointer)f(to)h(a)f
+Fo(fusd)p 45008 6112 333 45 v 399 w(file)p 48063 6112
+V 400 w(info)863 7440 y Fn(structure.)331 b(This)240
+b(structure)g(contains)i(information)f(that)f(can)i(be)f(useful)f(to)g
+(dri)-28 b(v)-17 b(er)241 b(implementers)g(in)g(deciding)h(ho)-28
+b(w)241 b(to)g(respond)863 8769 y(to)277 b(a)g(system)g(call)g
+(request.)2524 10761 y(The)h(\002elds)f(of)g Fo(fusd)p
+11118 10761 V 399 w(file)p 14173 10761 V 399 w(info)h
+Fn(structures)e(f)-11 b(all)277 b(into)g(se)-28 b(v)-17
+b(eral)278 b(cate)-17 b(gories:)2524 13639 y Fg(\017)554
+b Fk(Read-only)-61 b(.)345 b Fn(The)278 b(dri)-28 b(v)-17
+b(er)278 b(can)g(inspect)g(the)f(v)-28 b(alue,)279 b(b)-22
+b(ut)277 b(changing)j(it)c(will)g(ha)-22 b(v)-17 b(e)279
+b(no)f(ef)-28 b(fect.)4959 15853 y Fl(\226)554 b Fo(pid)p
+8124 15853 V 399 w(t)664 b(pid)p Fn(:)344 b(The)278 b(process)f(ID)g
+(of)g(the)g(process)g(making)i(the)f(request)4959 17624
+y Fl(\226)554 b Fo(uid)p 8124 17624 V 399 w(t)664 b(uid)p
+Fn(:)344 b(The)278 b(user)f(ID)f(of)h(the)g(o)-28 b(wner)279
+b(of)e(the)g(process)h(making)g(the)g(request)4959 19396
+y Fl(\226)554 b Fo(gid)p 8124 19396 V 399 w(t)664 b(gid)p
+Fn(:)344 b(The)278 b(group)g(ID)f(of)f(the)i(o)-28 b(wner)278
+b(of)f(the)g(process)h(making)g(the)g(request)2524 21610
+y Fg(\017)554 b Fk(Read-write)-17 b(.)332 b Fn(An)-17
+b(y)243 b(changes)h(to)d(the)h(v)-28 b(alue)243 b(will)e(be)h(propag)-6
+b(ated)245 b(back)e(to)e(the)h(k)-11 b(ernel)242 b(and)h(be)f(written)f
+(to)g(the)h(appropriate)3631 22938 y(in-k)-11 b(ernel)277
+b(structure.)4959 25152 y Fl(\226)554 b Fo(unsigned)665
+b(int)g(flags)p Fn(:)482 b(A)345 b(cop)-11 b(y)348 b(of)d(the)i
+Fo(f)p 26137 25152 V 398 w(flags)g Fn(\002eld)f(in)g(the)g(k)-11
+b(ernel')-61 b(s)347 b Fo(file)f Fn(structure.)549 b(The)347
+b(\003ags)6066 26480 y(are)277 b(an)h(or')-55 b(d-together)278
+b(set)e(of)h(the)g(k)-11 b(ernel')-61 b(s)278 b Fo(O)p
+23913 26480 V 675 w Fn(series)e(of)h(\003ags:)344 b Fo(O)p
+31990 26480 V 399 w(NONBLOCK)p Fn(,)278 b Fo(O)p 38920
+26480 V 398 w(APPEND)p Fn(,)g Fo(O)p 44521 26480 V 398
+w(SYNC)p Fn(,)g(etc.)4959 28251 y Fl(\226)554 b Fo(void)665
+b(*device)p 14101 28251 V 399 w(info)p Fn(:)409 b(The)311
+b(data)f(passed)g(to)g Fo(fusd)p 29144 28251 V 399 w(register)h
+Fn(when)f(the)g(de)-28 b(vice)312 b(w)-11 b(as)309 b(re)-17
+b(gistered;)327 b(see)6066 29580 y(Section)278 b(5.1)g(for)e(details)
+4959 31351 y Fl(\226)554 b Fo(void)665 b(*private)p 14765
+31351 V 400 w(data)p Fn(:)423 b(A)316 b(generic)i(per)-22
+b(-\002le-descriptor)318 b(pointer)f(usable)h(by)f(the)g(dri)-28
+b(v)-17 b(er)318 b(for)e(its)f(o)-28 b(wn)318 b(pur)-22
+b(-)6066 32679 y(poses,)383 b(such)363 b(as)f(to)g(k)-11
+b(eep)363 b(state)f(\(or)f(a)h(pointer)h(to)f(state\))f(that)h(should)h
+(be)g(maintained)h(between)g(operations)f(on)6066 34007
+y(the)395 b(same)g(instance)h(of)e(an)i(open)g(\002le.)696
+b(It)394 b(is)g(guaranteed)j(to)e(be)g(NULL)h(when)g(the)f(\002le)g(is)
+e(\002rst)h(opened.)699 b(See)6066 35336 y(Section)278
+b(5.2)g(for)e(more)i(details.)2524 37550 y Fg(\017)554
+b Fk(Hidden)299 b(\002elds.)406 b Fn(The)299 b(dri)-28
+b(v)-17 b(er)299 b(should)f(not)h(touch)g(these)f(\002elds)h(\(such)f
+(as)f Fo(fd)p Fn(\).)406 b(The)-17 b(y)300 b(contain)f(state)f(used)g
+(by)h(the)f(FUSD)3631 38878 y(library)276 b(to)h(generate)i(the)f
+(reply)f(sent)g(to)g(the)h(k)-11 b(ernel.)2524 41756
+y Fl(Important)273 b(note:)342 b Fn(the)272 b(v)-28 b(alue)275
+b(of)d(the)h Fo(fusd)p 20262 41756 V 399 w(file)p 23317
+41756 V 399 w(info)g Fn(pointer)g(itself)e(has)i Fk(no)g(meaning)p
+Fn(.)344 b(Repeated)275 b(requests)d(on)i(the)863 43085
+y(same)287 b(\002le)g(descriptor)f Fk(will)g(not)h Fn(generate)h
+(callbacks)g(with)e(identical)h Fo(fusd)p 31000 43085
+V 400 w(file)p 34056 43085 V 399 w(info)f Fn(pointer)h(v)-28
+b(alues,)290 b(as)c(w)-11 b(ould)288 b(be)f(the)863 44413
+y(case)281 b(with)e(an)h(in-k)-11 b(ernel)281 b(dri)-28
+b(v)-17 b(er)-61 b(.)352 b(In)279 b(other)h(w)-11 b(ords,)280
+b(if)f(a)h(dri)-28 b(v)-17 b(er)280 b(needs)h(to)e(k)-11
+b(eep)282 b(state)d(in)g(between)j(successi)-28 b(v)-17
+b(e)282 b(system)d(calls)g(on)863 45741 y(a)333 b(user')-61
+b(s)331 b(\002le)i(descriptor)-44 b(,)345 b(it)332 b
+Fk(must)g Fn(store)g(that)g(state)g(using)g(the)h Fo(private)p
+30689 45741 V 400 w(data)f Fn(\002eld.)510 b(The)333
+b Fo(fusd)p 41540 45741 V 399 w(file)p 44595 45741 V
+400 w(info)f Fn(pointer)863 47070 y(itself)276 b(is)g(ephemeral;)j(the)
+e(data)h(to)f(which)h(it)e(points)i(is)e(persistent.)2524
+49062 y(Program)282 b(3)f(sho)-28 b(ws)282 b(an)g(e)-17
+b(xample)284 b(of)d(ho)-28 b(w)283 b(a)e(dri)-28 b(v)-17
+b(er)282 b(might)g(mak)-11 b(e)283 b(use)e(of)g(the)h(data)g(in)f(the)h
+Fo(fusd)p 40577 49062 V 399 w(file)p 43632 49062 V 400
+w(info)f Fn(structure.)863 50390 y(Much)333 b(of)f(the)g(dri)-28
+b(v)-17 b(er)333 b(is)d(identical)j(to)f(hello)-28 b(w)-11
+b(orld.c.)509 b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)348
+b(instead)332 b(of)g(printing)g(a)g(static)f(greeting,)347
+b(this)331 b(ne)-28 b(w)333 b(program)863 51719 y(generates)273
+b(a)f(custom)g(message)h(each)g(time)e(the)h(de)-28 b(vice)273
+b(\002le)f(is)f(read,)i(as)e(seen)h(on)h(line)e(25.)342
+b(The)273 b(message)f(contains)h(the)f(PID)f(of)863 53047
+y(the)278 b(user)e(process)i(that)f(requested)i(the)e(read)h(\()p
+Fo(file->pid)p Fn(\).)2524 55040 y(In)360 b(addition,)383
+b(Program)362 b(3')-61 b(s)360 b Fo(open)i Fn(callback)g(does)g(not)f
+(return)g(0)g(\(success\))g(unconditionally)j(as)c(it)g(did)h(in)g
+(Program)h(1.)863 56368 y(Instead,)339 b(it)326 b(checks)i(\(on)e(line)
+h(7\))f(to)g(mak)-11 b(e)328 b(sure)e(the)h(UID)f(of)g(the)h(process)f
+(trying)h(to)f(read)h(from)f(the)h(de)-28 b(vice)328
+b(\()p Fo(file->uid)p Fn(\))863 57696 y(matches)271 b(the)f(UID)g
+(under)h(which)g(the)f(dri)-28 b(v)-17 b(er)270 b(itself)f(is)f
+(running)k(\()p Fo(getuid\(\))p Fn(\).)341 b(If)268 b(the)-17
+b(y)271 b(don')-20 b(t)271 b(match,)h(-EPERM)e(is)f(returned.)863
+59025 y(In)354 b(other)g(w)-11 b(ords,)373 b(only)355
+b(the)f(user)g(who)h(ran)f(the)g(dri)-28 b(v)-17 b(er)355
+b(is)e(allo)-28 b(wed)355 b(to)f(read)h(from)e(the)i(de)-28
+b(vice)356 b(that)e(it)f(creates.)574 b(If)353 b(an)-17
+b(y)355 b(other)863 60353 y(user)-22 b(\227including)279
+b(root!\227tries)d(to)h(open)h(it,)e(a)h(\223Permission)h(denied\224)h
+(error)e(will)f(be)i(generated.)863 64186 y Fj(5.1)1329
+b(Registration)333 b(of)f(Multiple)g(De)-20 b(vices,)332
+b(and)g(P)-13 b(assing)331 b(Data)h(to)h(Callbacks)863
+66926 y Fn(De)-28 b(vice)264 b(dri)-28 b(v)-17 b(ers)263
+b(frequently)g(e)-17 b(xpose)264 b(se)-28 b(v)-17 b(eral)264
+b(dif)-28 b(ferent)262 b(\223\003a)-22 b(v)g(ors\224)264
+b(of)e(a)g(de)-28 b(vice.)340 b(F)-17 b(or)263 b(e)-17
+b(xample,)267 b(a)c(single)f(magnetic)i(tape)f(dri)-28
+b(v)-17 b(e)863 68254 y(will)256 b(often)i(ha)-22 b(v)-17
+b(e)259 b(man)-17 b(y)259 b(dif)-28 b(ferent)257 b(de)-28
+b(vice)259 b(\002les)f(in)f Fo(/dev)p Fn(.)336 b(Each)259
+b(de)-28 b(vice)259 b(\002le)f(represents)f(a)g(dif)-28
+b(ferent)258 b(combination)h(of)e(options)863 69582 y(such)278
+b(as)f(re)-28 b(wind/no-re)g(wind,)279 b(or)e(compressed/uncompressed.)
+347 b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)280 b(the)-17
+b(y)278 b(access)g(the)f(same)h(ph)-6 b(ysical)278 b(tape)g(dri)-28
+b(v)-17 b(e.)25405 74071 y(14)p eop
+%%Page: 15 18
+15 17 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(3)d Fn(uid-\002lter)-61 b(.c:)344 b(Inspecting)278
+b(data)g(in)f Fo(fusd)p 22294 2940 333 45 v 399 w(file)p
+25349 2940 V 399 w(info)h Fn(such)f(as)g(UID)g(and)h(PID)f(of)g(the)g
+(calling)h(process)p 863 3445 50191 45 v 365 4400 a Fi(1)1661
+b Fo(int)664 b(do_open\(struct)j(fusd_file_info)g(*file\))2524
+5728 y({)3852 7057 y(/*)d(If)h(the)g(UID)f(of)h(the)g(process)g(trying)
+g(to)g(do)f(the)h(read)g(doesn't)g(match)g(the)4516 8385
+y(*)f(UID)h(of)g(the)f(owner)h(of)g(the)g(driver,)g(return)g(-EPERM.)
+1330 b(If)664 b(you)h(run)g(this)365 9714 y Fi(5)3653
+b Fo(*)664 b(driver)i(as)e(a)h(normal)g(user,)g(even)g(root)g(won't)g
+(be)f(able)h(to)g(read)g(from)f(the)4516 11042 y(*)g(device)i(file)f
+(created!)g(*/)3852 12370 y(if)f(\(file->uid)i(!=)f(getuid\(\)\))5180
+13699 y(return)h(-EPERM;)-133 16355 y Fi(10)2989 b Fo(return)665
+b(0;)2524 17684 y(})2524 20340 y(int)f(do_read\(struct)j
+(fusd_file_info)g(*file,)e(char)g(*user_buffer,)10494
+21669 y(size_t)g(user_length,)h(loff_t)f(*offset\))-133
+22997 y Fi(15)1661 b Fo({)3852 24325 y(char)665 b(buf[128];)3852
+25654 y(int)g(len;)3852 28310 y(/*)f(The)h(first)g(read)g(to)g(the)f
+(device)i(returns)f(a)f(greeting.)1330 b(The)665 b(second)g(read)-133
+29639 y Fi(20)3653 b Fo(*)664 b(returns)i(EOF.)f(*/)3852
+30967 y(if)f(\(*offset)i(!=)f(0\))5180 32296 y(return)h(0;)3852
+34952 y(/*)e(len)h(gets)g(set)g(to)f(the)h(number)g(of)g(characters)h
+(written)f(to)g(buf)f(*/)-133 36281 y Fi(25)2989 b Fo(len)665
+b(=)f(sprintf\(buf,)i("Your)f(PID)g(is)g(\045d.)1328
+b(Have)665 b(a)g(nice)g(day.\\n",)g(file->pid\);)3852
+38937 y(/*)f(NEVER)i(return)f(more)g(data)g(than)f(the)h(user)g(asked)g
+(for)g(*/)3852 40266 y(if)f(\(user_length)j(<)d(len\))5180
+41594 y(len)h(=)f(user_length;)-133 42922 y Fi(30)3852
+44251 y Fo(memcpy\(user_buffer,)j(buf,)e(len\);)3852
+45579 y(*offset)g(+=)g(len;)3852 46907 y(return)g(len;)2524
+48236 y(})p 863 49730 V 2524 53051 a Fn(T)-39 b(raditionally)-72
+b(,)252 b(the)246 b(de)-28 b(vice)247 b(\002le')-61 b(s)245
+b Fk(minor)h(number)g Fn(w)-11 b(as)245 b(used)h(to)f(communicate)j
+(the)e(desired)g(options)f(with)g(de)-28 b(vice)248 b(dri)-28
+b(v)-17 b(ers.)863 54379 y(But,)367 b(since)350 b(de)-28
+b(vfs)350 b(dynamically)i(\(and)e(unpredictably\))i(generates)e(both)g
+(major)g(and)g(minor)g(numbers)g(e)-28 b(v)-17 b(ery)351
+b(time)e(a)h(de)-28 b(vice)863 55708 y(is)298 b(re)-17
+b(gistered,)305 b(a)299 b(dif)-28 b(ferent)300 b(technique)h(w)-11
+b(as)299 b(de)-28 b(v)-17 b(eloped.)413 b(When)300 b(using)g(de)-28
+b(vfs,)305 b(dri)-28 b(v)-17 b(ers)299 b(are)g(allo)-28
+b(wed)301 b(to)e(associate)h(a)f(v)-28 b(alue)301 b(\(of)863
+57036 y(type)278 b Fo(void)665 b(*)p Fn(\))276 b(with)h(each)i(de)-28
+b(vice)279 b(the)-17 b(y)279 b(re)-17 b(gister)-61 b(.)343
+b(This)277 b(f)-11 b(acility)276 b(tak)-11 b(es)278 b(the)f(place)h(of)
+f(the)h(minor)f(number)-61 b(.)2524 59029 y(The)302 b(de)-28
+b(vfs)302 b(solution)g(is)f(also)g(used)i(by)f(FUSD.)g(The)h
+(mysterious)e(third)g(ar)-20 b(gument)304 b(to)d Fo(fusd)p
+39277 59029 333 45 v 399 w(register)i Fn(that)f(we)f(men-)863
+60357 y(tioned)309 b(in)g(Section)g(4.1)g(is)e(an)i(arbitrary)f(piece)i
+(of)e(data)h(that)f(can)i(be)f(passed)g(to)f(FUSD)h(when)h(a)e(de)-28
+b(vice)310 b(is)e(re)-17 b(gistered.)437 b(Later)-44
+b(,)863 61685 y(when)378 b(a)e(callback)j(is)c(acti)-28
+b(v)g(ated,)403 b(the)377 b(contents)g(of)g(that)f(ar)-20
+b(gument)378 b(are)f(a)-22 b(v)-28 b(ailable)378 b(in)e(the)h
+Fo(device)p 41033 61685 V 399 w(info)g Fn(member)g(of)g(the)863
+63014 y Fo(fusd)p 3585 63014 V 399 w(file)p 6640 63014
+V 400 w(info)277 b Fn(structure.)2524 65006 y(Program)213
+b(4)f(sho)-28 b(ws)212 b(an)h(e)-17 b(xample)215 b(of)c(this)h
+(technique,)227 b(inspired)212 b(by)h(Alessandro)g(Rubini')-61
+b(s)212 b(similar)f(de)-28 b(vfs)213 b(tutorial)e(published)863
+66335 y(in)321 b(Linux)h(Mag)-6 b(azine)9366 65933 y
+Ff(10)10199 66335 y Fn(.)475 b(It)320 b(creates)h(a)g(number)i(of)d(de)
+-28 b(vices)323 b(in)e(the)g Fo(/dev/drums)h Fn(directory)-72
+b(,)333 b(each)322 b(of)f(which)h(is)e(useful)h(for)863
+67663 y(generating)345 b(a)e(dif)-28 b(ferent)343 b(kind)g(of)f
+(\223sound\224\227)p Fo(/dev/drums/bam)p Fn(,)364 b Fo(/dev/drums/boom)
+p Fn(,)d(and)344 b(so)e(on.)541 b(Reading)344 b(from)p
+863 68611 20076 45 v 1738 69352 a Fe(10)2457 69664 y
+Fd(http://www)-58 b(.linux.it/k)-9 b(erneldocs/de)-22
+b(vfs/)25405 74071 y Fn(15)p eop
+%%Page: 16 19
+16 18 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(4)d Fn(drums.c:)344 b(P)-17 b(assing)277 b(pri)-28
+b(v)g(ate)279 b(data)f(to)f Fo(fusd)p 23298 2940 333
+45 v 399 w(register)h Fn(and)g(retrie)-28 b(ving)278
+b(it)e(from)h Fo(device)p 43020 2940 V 400 w(info)p 863
+3445 50191 45 v 365 4400 a Fi(1)1661 b Fo(static)665
+b(char)g(*drums_strings[])i(=)d({"bam",)h("bum",)h("beat",)f("boom",)
+23777 5728 y("bang",)h("crash",)f(NULL};)2524 8385 y(int)f
+(drums_read\(struct)j(fusd_file_info)g(*file,)e(char)g(*user_buffer,)
+365 9714 y Fi(5)11623 b Fo(size_t)665 b(user_length,)i(loff_t)e
+(*offset\))2524 11042 y({)3852 12370 y(int)g(len;)3852
+13699 y(char)g(sound[128];)-133 16355 y Fi(10)2989 b
+Fo(/*)664 b(file->device_info)k(is)c(what)h(we)g(passed)g(to)f
+(fusd_register)j(when)e(we)4516 17684 y(*)f(registered)i(the)f(device)g
+(*/)3852 19012 y(strcpy\(sound,)h(\(char)f(*\))g(file->device_info\);)
+3852 20340 y(strcat\(sound,)h("\\n"\);)-133 22997 y Fi(15)2989
+b Fo(/*)664 b(1st)h(read)g(returns)g(the)g(sound;)g(2nd)g(returns)h
+(EOF)e(*/)3852 24325 y(if)g(\(*offset)i(!=)f(0\))5180
+25654 y(return)h(0;)3852 28310 y(/*)e(NEVER)i(return)f(more)g(data)g
+(than)f(the)h(user)g(asked)g(for)g(*/)-133 29639 y Fi(20)2989
+b Fo(len)665 b(=)f(MIN\(user_length,)j(strlen\(sound\)\);)3852
+30967 y(memcpy\(user_buffer,)g(sound,)f(len\);)3852 32296
+y(*offset)f(+=)g(len;)3852 33624 y(return)g(len;)2524
+34952 y(})-133 36281 y Fi(25)2524 37609 y Fo(int)f(main\(int)i(argc,)f
+(char)g(*argv[]\))2524 38937 y({)3852 40266 y(int)g(i;)3852
+41594 y(char)g(buf[128];)-133 42922 y Fi(30)3852 44251
+y Fo(for)g(\(i)f(=)h(0;)f(drums_strings[i])j(!=)e(NULL;)g(i++\))g({)
+5180 45579 y(sprintf\(buf,)i("/dev/drums/\045s",)g(drums_strings[i]\);)
+5180 46907 y(if)e(\(fusd_register\(buf,)i(0666,)e(drums_strings[i],)j
+(&drums_fops\))e(<)e(0\))6509 48236 y(fprintf\(stderr,)i("\045s)f
+(register)h(failed:)f(\045m\\n",)g(drums_strings[i]\);)-133
+49564 y Fi(35)2989 b Fo(})3852 52221 y(fprintf\(stderr,)667
+b("calling)e(fusd_run...\\n"\);)3852 53549 y(fusd_run\(\);)3852
+54878 y(return)g(0;)-133 56206 y Fi(40)1661 b Fo(})p
+863 57700 V 863 61021 a Fn(an)-17 b(y)279 b(of)e(these)g(de)-28
+b(vices)279 b(will)d(return)h(a)g(string)g(equal)h(to)f(the)g(de)-28
+b(vice')-61 b(s)279 b(name.)2524 63014 y(The)313 b(\002rst)f(thing)i
+(to)f(notice)g(about)i Fo(drums.c)e Fn(is)f(that)h(it)f(re)-17
+b(gisters)312 b(more)i(than)f(one)h(FUSD)g(de)-28 b(vice.)453
+b(In)313 b(the)g(loop)g(starting)863 64342 y(in)407 b(line)g(31,)440
+b(it)406 b(calls)h Fo(fusd)p 12201 64342 333 45 v 399
+w(register\(\))h Fn(once)h(for)d(e)-28 b(v)-17 b(ery)409
+b(de)-28 b(vice)409 b(named)g(in)e Fo(drums)p 37877 64342
+V 399 w(strings)h Fn(on)g(line)f(1.)733 b(When)863 65670
+y Fo(fusd)p 3585 65670 V 399 w(run\(\))371 b Fn(is)e(called,)393
+b(it)369 b(automatically)j(w)-11 b(atches)371 b(e)-28
+b(v)-17 b(ery)372 b(de)-28 b(vice)372 b(the)e(dri)-28
+b(v)-17 b(er)370 b(re)-17 b(gistered,)394 b(and)371 b(acti)-28
+b(v)g(ates)371 b(the)f(callbacks)863 66999 y(associated)376
+b(with)f(each)i(de)-28 b(vice)377 b(as)e(needed.)639
+b(Although)377 b Fo(drums.c)f Fn(uses)f(the)g(same)h(set)e(of)h
+(callbacks)h(for)f(e)-28 b(v)-17 b(ery)377 b(de)-28 b(vice)377
+b(it)863 68327 y(re)-17 b(gisters)387 b(\(as)f(can)i(be)g(seen)g(on)g
+(line)f(33\),)414 b(each)389 b(de)-28 b(vice)389 b(could)g(ha)-22
+b(v)-17 b(e)389 b(dif)-28 b(ferent)387 b(callbacks)h(if)f(desired.)673
+b(\(Not)387 b(sho)-28 b(wn)389 b(is)d(the)863 69655 y(initialization)
+278 b(of)e Fo(drums)p 11321 69655 V 400 w(fops)p Fn(,)h(which)h
+(assigns)f Fo(drums)p 24708 69655 V 399 w(read)h Fn(to)f(be)g(the)h
+Fo(read)f Fn(callback.\))25405 74071 y(16)p eop
+%%Page: 17 20
+17 19 bop 2524 2974 a Fn(If)239 b Fo(drums)p 6887 2974
+333 45 v 400 w(read)i Fn(is)f(called)i(for)f(all)f(6)h(types)h(of)f
+(drums,)248 b(ho)-28 b(w)242 b(does)g(it)e(kno)-28 b(w)242
+b(which)g(de)-28 b(vice)243 b(it')-61 b(s)240 b(supposed)j(to)e(be)g
+(servicing)863 4302 y(when)220 b(it)e(gets)g(called?)326
+b(The)219 b(answer)g(is)f(in)g(the)h(third)f(ar)-20 b(gument)221
+b(of)d Fo(fusd)p 29223 4302 V 399 w(register\(\))p Fn(,)231
+b(which)220 b(we)f(were)g(pre)-28 b(viously)220 b(ignor)-22
+b(-)863 5631 y(ing.)371 b(Whate)-28 b(v)-17 b(er)288
+b(v)-28 b(alue)288 b(is)d(passed)i(to)f Fo(fusd)p 18255
+5631 V 399 w(register\(\))i Fn(will)d(be)i(passed)g(back)h(to)e(the)g
+(callback)i(in)e(the)h Fo(device)p 48063 5631 V 400 w(info)863
+6959 y Fn(\002eld)267 b(of)e(the)h Fo(fusd)p 8624 6959
+V 399 w(file)p 11679 6959 V 400 w(info)g Fn(structure.)339
+b(The)267 b(name)f(of)g(the)g(drum)g(sound)h(is)e(passed)h(to)g
+Fo(fusd)p 40460 6959 V 399 w(register)h Fn(on)f(line)g(33,)863
+8287 y(and)278 b(later)f(retrie)-28 b(v)-17 b(ed)279
+b(by)e(the)h(dri)-28 b(v)-17 b(er)278 b(on)f(line)g(12.)2524
+10280 y(Although)406 b(this)e(e)-17 b(xample)407 b(uses)e(a)g(string)f
+(as)h(its)e Fo(device)p 26212 10280 V 400 w(info)p Fn(,)437
+b(the)405 b(pointer)h(can)g(be)f(used)h(for)e(an)-17
+b(ything\227a)408 b(mode)863 11608 y(number)-44 b(,)278
+b(a)f(pointer)h(to)f(a)g(con\002guration)j(structure,)d(and)h(so)f(on.)
+863 15441 y Fj(5.2)1329 b(The)331 b(differ)-24 b(ence)332
+b(between)e Fc(device)p 22228 15441 399 45 v 479 w(info)i
+Fj(and)f Fc(private)p 34279 15441 V 479 w(data)863 18181
+y Fn(As)310 b(we)g(mentioned)i(in)e(Section)h(5,)318
+b(the)310 b Fo(fusd)p 19319 18181 333 45 v 399 w(file)p
+22374 18181 V 400 w(info)g Fn(structure)g(has)g(tw)-11
+b(o)310 b(seemingly)h(similar)d(\002elds,)319 b(both)310
+b(of)g(which)863 19509 y(can)433 b(be)f(used)h(by)f(dri)-28
+b(v)-17 b(ers)432 b(to)g(store)f(their)g(o)-28 b(wn)433
+b(data:)653 b Fo(device)p 27267 19509 V 400 w(info)432
+b Fn(and)h Fo(private)p 37433 19509 V 400 w(data)p Fn(.)807
+b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)472 b(there)433 b(is)d(an)863
+20837 y(important)278 b(dif)-28 b(ference)278 b(between)h(them:)2524
+23937 y Fg(\017)554 b Fo(private)p 8345 23937 V 399 w(data)315
+b Fn(is)e(stored)i Fk(per)f(\002le)h(descriptor)p Fn(.)456
+b(If)313 b(20)i(processes)g(open)h(a)e(FUSD)h(de)-28
+b(vice)317 b(\(or)-44 b(,)323 b(one)315 b(process)g(opens)3631
+25265 y(a)350 b(FUSD)i(de)-28 b(vice)353 b(20)e(times\),)368
+b(each)353 b(of)d(those)h(20)h(\002le)f(descriptors)f(will)g(ha)-22
+b(v)-17 b(e)353 b(their)d(o)-28 b(wn)352 b(cop)-11 b(y)352
+b(of)f Fo(private)p 48063 25265 V 400 w(data)3631 26594
+y Fn(associated)260 b(with)g(them.)338 b(This)259 b(\002eld)h(is)f
+(therefore)h(useful)f(to)h(dri)-28 b(v)-17 b(ers)260
+b(that)f(need)i(to)f(dif)-28 b(ferentiate)260 b(multiple)g(requests)f
+(to)h(a)3631 27922 y(single)303 b(de)-28 b(vice)306 b(that)e(might)g
+(be)g(serviced)h(in)e(parallel.)423 b(\(Note)304 b(that)g(most)f(UNIX)h
+(v)-28 b(ariants,)310 b(including)c(Linux,)311 b(do)304
+b(allo)-28 b(w)3631 29250 y(multiple)306 b(processes)h(to)f(share)h(a)f
+(single)g(\002le)h(descriptor)-22 b(\227speci\002cally)-72
+b(,)316 b(if)305 b(a)h(process)h Fo(open)p Fn(s)f(a)h(\002le,)313
+b(then)307 b Fo(fork)p Fn(s.)431 b(In)3631 30579 y(this)276
+b(case,)h(processes)h(will)e(also)h(share)g(a)h(single)f(cop)-11
+b(y)278 b(of)f Fo(private)p 31175 30579 V 400 w(data)p
+Fn(.\))3631 32350 y(The)331 b(\002rst)f(time)g(a)g(FUSD)i(dri)-28
+b(v)-17 b(er)331 b(sees)f Fo(private)p 23831 32350 V
+400 w(data)h Fn(\(in)f(the)h Fo(open)f Fn(callback\),)346
+b(it)329 b(is)h(guaranteed)j(to)d(be)h(NULL.)3631 33678
+y(An)-17 b(y)278 b(changes)h(to)e(it)f(by)i(a)f(dri)-28
+b(v)-17 b(er)278 b(callback)h(will)d(only)i(af)-28 b(fect)278
+b(the)f(state)g(associated)h(with)f(that)g(single)g(\002le)h
+(descriptor)-61 b(.)2524 35892 y Fg(\017)554 b Fo(device)p
+7681 35892 V 399 w(info)339 b Fn(is)f(k)-11 b(ept)339
+b Fk(per)g(de)-17 b(vice)p Fn(.)530 b(That)340 b(is,)352
+b Fk(all)339 b Fn(clients)f(of)h(a)f(de)-28 b(vice)341
+b(share)e(a)g Fk(single)g Fn(cop)-11 b(y)340 b(of)e Fo(device)p
+47786 35892 V 400 w(info)p Fn(.)3631 37220 y(Unlik)-11
+b(e)440 b Fo(private)p 11786 37220 V 399 w(data)p Fn(,)480
+b(which)441 b(is)d(al)-11 b(w)g(ays)440 b(initialized)f(to)g(NULL,)h
+Fo(device)p 37228 37220 V 400 w(info)f Fn(is)g(al)-11
+b(w)g(ays)439 b(initialized)h(to)3631 38549 y(whate)-28
+b(v)-17 b(er)349 b(v)-28 b(alue)350 b(the)d(dri)-28 b(v)-17
+b(er)349 b(passed)f(to)f Fo(fusd)p 22582 38549 V 400
+w(register)h Fn(as)g(described)h(in)e(the)h(pre)-28 b(vious)349
+b(section.)555 b(If)346 b(a)i(callback)3631 39877 y(changes)251
+b(the)e(cop)-11 b(y)251 b(of)e Fo(device)p 16657 39877
+V 399 w(info)h Fn(in)f(the)g Fo(fusd)p 25329 39877 V
+399 w(file)p 28384 39877 V 399 w(info)h Fn(structure,)k(this)248
+b(has)i(no)f(ef)-28 b(fect;)259 b Fo(device)p 48063 39877
+V 400 w(info)3631 41205 y Fn(can)278 b(only)g(be)f(set)g(at)g(re)-17
+b(gistration)277 b(time,)g(with)g Fo(fusd)p 24384 41205
+V 399 w(register)p Fn(.)2524 44305 y(In)381 b(short,)408
+b Fo(device)p 10776 44305 V 400 w(info)382 b Fn(is)f(used)i(to)f(dif)
+-28 b(ferentiate)382 b Fk(de)-17 b(vices)p Fn(.)660 b
+Fo(private)p 33575 44305 V 400 w(data)382 b Fn(is)f(used)i(to)f(dif)-28
+b(ferentiate)383 b Fk(user)-11 b(s)381 b(of)863 45633
+y(those)278 b(de)-17 b(vices)p Fn(.)2524 47626 y(Program)267
+b(5,)h(drums2.c,)g(illustrates)d(the)h(dif)-28 b(ference)268
+b(between)g Fo(device)p 31251 47626 V 399 w(info)f Fn(and)g
+Fo(private)p 41085 47626 V 400 w(data)p Fn(.)340 b(Lik)-11
+b(e)266 b(the)h(origi-)863 48954 y(nal)295 b(drums.c,)k(it)294
+b(creates)h(a)g(b)-22 b(unch)297 b(of)d(de)-28 b(vices)297
+b(in)d Fo(/dev/drums/)p Fn(,)301 b(each)296 b(of)f(which)g
+(\223plays\224)i(a)d(dif)-28 b(ferent)295 b(sound.)398
+b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)863 50283 y(it)263
+b(also)h(does)h(something)g(ne)-28 b(w:)338 b(k)-11 b(eeps)265
+b(track)f(of)g(ho)-28 b(w)265 b(man)-17 b(y)266 b(times)d(each)j(de)-28
+b(vice)266 b(has)e(been)i(opened.)341 b(Ev)-17 b(ery)266
+b(read)e(to)g(an)-17 b(y)266 b(drum)863 51611 y(gi)-28
+b(v)-17 b(es)279 b(you)h(the)e(name)h(of)f(its)f(sound)i(as)f(well)g
+(as)g(your)h(unique)g(\223user)g(number\224.)348 b(And,)279
+b(instead)g(of)e(returning)i(just)e(a)i(single)f(line)863
+52939 y(\(as)f(drums.c)g(did\),)g(it)f(will)g(k)-11 b(eep)279
+b(generating)g(more)e(\223sound\224)j(e)-28 b(v)-17 b(ery)279
+b(time)d(a)i Fo(read\(\))f Fn(system)g(call)g(arri)-28
+b(v)-17 b(es.)2524 54932 y(The)278 b(trick)e(is)g(that)i(we)f(w)-11
+b(ant)278 b(to)f(k)-11 b(eep)278 b(users)f(separate)h(from)e(each)j
+(other)-61 b(.)344 b(F)-17 b(or)278 b(e)-17 b(xample,)279
+b(user)e(one)h(might)f(type:)863 57311 y Fo(\045)665
+b(more)f(/dev/drums/bam)863 58640 y(You)h(are)g(user)f(1)h(to)f(hear)h
+(a)g(drum)f(go)h('bam'!)863 59968 y(You)g(are)g(user)f(1)h(to)f(hear)h
+(a)g(drum)f(go)h('bam'!)863 61296 y(You)g(are)g(user)f(1)h(to)f(hear)h
+(a)g(drum)f(go)h('bam'!)863 62625 y(...)2524 65483 y
+Fn(Meanwhile,)279 b(another)g(user)f(in)f(a)h(dif)-28
+b(ferent)278 b(shell)f(might)h(type)h(the)f(same)g(command)i(at)d(the)h
+(same)h(time,)e(and)i(get)f(dif)-28 b(ferent)863 66811
+y(results:)25405 74071 y(17)p eop
+%%Page: 18 21
+18 20 bop 863 3187 50191 89 v 863 4164 a Fl(Pr)-20 b(ogram)280
+b(5)d Fn(drums2.c:)344 b(Using)277 b(both)h Fo(device)p
+20148 4164 333 45 v 400 w(info)f Fn(and)h Fo(private)p
+30004 4164 V 400 w(data)p 863 4669 50191 45 v 365 5624
+a Fi(1)1661 b Fo(struct)665 b(drum_info)h({)3852 6953
+y(char)f(*name;)3852 8281 y(int)g(num_users;)2524 9609
+y(})f(drums[])h(=)g({)365 10938 y Fi(5)2989 b Fo({)664
+b("bam",)i(0)e(},)3852 12266 y({)g("bum",)i(0)e(},)3852
+13594 y(/*)g(...)h(*/)3852 14923 y({)f(NULL,)h(0)g(})2524
+16251 y(};)-133 17579 y Fi(10)2524 18908 y Fo(int)f(drums_open\(struct)
+j(fusd_file_info)g(*file\))2524 20236 y({)3852 21564
+y(/*)d(file->device_info)k(is)c(what)h(we)g(passed)g(to)f
+(fusd_register)j(when)e(we)4516 22893 y(*)f(registered)i(the)f(device.)
+1330 b(It's)665 b(a)f(pointer)h(into)g(the)g("drums")g(struct.)h(*/)
+-133 24221 y Fi(15)2989 b Fo(struct)665 b(drum_info)h(*d)e(=)h
+(\(struct)g(drum_info)h(*\))f(file->device_info;)3852
+26878 y(/*)f(Store)i(this)e(user's)i(unique)f(user)g(number)g(in)g
+(their)g(private_data)h(*/)3852 28206 y(file->private_data)h(=)e
+(\(void)g(*\))f(++\(d->num_users\);)-133 30863 y Fi(20)2989
+b Fo(return)665 b(0;)g(/*)f(return)h(success)h(*/)2524
+32191 y(})2524 34848 y(int)e(drums_read\(struct)j(fusd_file_info)g
+(*file,)e(char)g(*user_buffer,)12486 36176 y(size_t)g(user_length,)i
+(loff_t)e(*offset\))-133 37505 y Fi(25)1661 b Fo({)3852
+38833 y(struct)665 b(drum_info)h(*d)e(=)h(\(struct)g(drum_info)h(*\))f
+(file->device_info;)3852 40161 y(int)g(len;)3852 41490
+y(char)g(sound[128];)-133 44146 y Fi(30)2989 b Fo(sprintf\(sound,)667
+b("You)d(are)h(user)g(\045d)g(to)f(hear)h(a)f(drum)h(go)g
+('\045s'!\\n",)9165 45475 y(\(int\))g(file->private_data,)j(d->name\);)
+3852 48131 y(len)d(=)f(MIN\(user_length,)j(strlen\(sound\)\);)3852
+49460 y(memcpy\(user_buffer,)g(sound,)f(len\);)-133 50788
+y Fi(35)2989 b Fo(return)665 b(len;)2524 52116 y(})2524
+54773 y(int)f(main\(int)i(argc,)f(char)g(*argv[]\))2524
+56102 y({)-133 57430 y Fi(40)2989 b Fo(struct)665 b(drum_info)h(*d;)
+3852 58758 y(char)f(buf[128];)3852 61415 y(for)g(\(d)f(=)h(drums;)g
+(d->name)g(!=)g(NULL;)g(d++\))g({)5180 62743 y(sprintf\(buf,)i
+("/dev/drums/\045s",)g(d->name\);)-133 64072 y Fi(45)4317
+b Fo(if)665 b(\(fusd_register\(buf,)i(0666,)e(d,)g(&drums_fops\))h(<)f
+(0\))6509 65400 y(fprintf\(stderr,)h("\045s)f(register)h(failed:)f
+(\045m\\n",)g(d->name\);)3852 66728 y(})3852 68057 y(/*)f(...)h(*/)p
+863 69518 V 25405 74071 a Fn(18)p eop
+%%Page: 19 22
+19 21 bop 863 3896 a Fo(\045)665 b(more)f(/dev/drums/bam)863
+5224 y(You)h(are)g(user)f(2)h(to)f(hear)h(a)g(drum)f(go)h('bam'!)863
+6553 y(You)g(are)g(user)f(2)h(to)f(hear)h(a)g(drum)f(go)h('bam'!)863
+7881 y(You)g(are)g(user)f(2)h(to)f(hear)h(a)g(drum)f(go)h('bam'!)863
+9209 y(...)2524 12068 y Fn(The)287 b(idea)f(is)f(that)i(no)f(matter)g
+(ho)-28 b(w)287 b(long)g(those)g(tw)-11 b(o)286 b(users)f(go)i(on)g
+(reading)g(their)f(de)-28 b(vices,)289 b(the)e(dri)-28
+b(v)-17 b(er)287 b(al)-11 b(w)g(ays)286 b(generates)i(a)863
+13396 y(message)278 b(that)f(is)f(speci\002c)j(to)e(that)g(user)-61
+b(.)343 b(The)278 b(tw)-11 b(o)277 b(users')f(data)i(are)g(not)f
+(intermingled.)2524 15388 y(T)-89 b(o)313 b(implement)g(this,)320
+b(Program)313 b(5)g(introduces)g(a)f(ne)-28 b(w)314 b
+Fo(drum)p 26788 15388 333 45 v 399 w(info)f Fn(structure)f(\(lines)f
+(1-4\),)321 b(which)313 b(k)-11 b(eeps)313 b(track)g(of)f(both)863
+16717 y(the)421 b(drum')-61 b(s)420 b(name,)457 b(and)421
+b(the)g(number)h(of)e(time)g(each)i(drum)f(de)-28 b(vice)422
+b(has)e(been)i(opened.)776 b(An)420 b(instance)i(of)e(this)f
+(structure,)863 18045 y Fo(drums)p Fn(,)393 b(is)369
+b(initialized)h(on)g(lines)f(4-8.)622 b(Note)370 b(that)g(the)g(call)f
+(to)h Fo(fusd)p 29261 18045 V 399 w(register)h Fn(\(line)e(45\))h(no)
+-28 b(w)371 b(passes)e(a)h(pointer)g(to)g(a)863 19373
+y Fo(drum)p 3585 19373 V 399 w(info)258 b Fn(structure.)336
+b(\(This)257 b Fo(drum)p 16634 19373 V 399 w(info)665
+b(*)257 b Fn(pointer)h(is)e(shared)i(by)g(e)-28 b(v)-17
+b(ery)259 b(instance)g(of)e(a)g(client)g(that)h(opens)g(a)f(particular)
+863 20702 y(type)278 b(of)f(drum.\))2524 22694 y(Each)413
+b(time)g(a)f(drum)h(de)-28 b(vice)414 b(is)e(opened,)448
+b(its)411 b Fo(drum)p 23704 22694 V 400 w(info)h Fn(structure)g(is)g
+(retrie)-28 b(v)-17 b(ed)414 b(from)e Fo(device)p 43458
+22694 V 399 w(info)h Fn(\(line)f(15\).)863 24023 y(Then,)266
+b(on)c(line)f(18,)k(the)c Fo(num)p 12287 24023 V 399
+w(users)h Fn(\002eld)g(is)e(incremented)k(and)e(the)f(ne)-28
+b(w)263 b(user)e(number)h(is)f(stored)g(in)g Fo(fusd)p
+44270 24023 V 399 w(file)p 47325 24023 V 399 w(info)p
+Fn(')-61 b(s)863 25351 y Fo(private)p 5577 25351 V 400
+w(data)308 b Fn(\002eld.)434 b(T)-89 b(o)308 b(reiterate)g(our)f
+(earlier)g(point:)404 b Fo(device)p 28888 25351 V 400
+w(info)308 b Fk(contains)g(information)g(global)g(to)g(all)e(user)-11
+b(s)307 b(of)863 26679 y(a)277 b(de)-17 b(vice)-11 b(,)279
+b(while)f Fo(private)p 12436 26679 V 400 w(data)f Fk(has)g(information)
+h(speci\002c)h(to)e(a)g(particular)g(user)g(of)g(the)h(de)-17
+b(vice)g(.)2524 28672 y Fn(It')-61 b(s)413 b(also)i(w)-11
+b(orthwhile)416 b(to)f(note)h(that)g(when)g(we)g(increment)g
+Fo(num)p 29087 28672 V 399 w(users)g Fn(on)g(line)f(18,)450
+b(a)416 b(simple)f Fo(num)p 44918 28672 V 399 w(users++)h
+Fn(is)863 30000 y(correct.)478 b(If)321 b(this)g(w)-11
+b(as)321 b(a)h(dri)-28 b(v)-17 b(er)323 b(inside)f(the)g(k)-11
+b(ernel,)334 b(we')-55 b(d)321 b(ha)-22 b(v)-17 b(e)324
+b(to)e(use)g(something)h(lik)-11 b(e)322 b Fo(atomic)p
+40226 30000 V 399 w(inc\(\))h Fn(because)g(a)f(plain)863
+31329 y Fo(i++)301 b Fn(is)f(not)h(atomic.)415 b(Such)303
+b(a)e(non-atomic)h(statement)g(will)e(result)g(in)h(a)g(race)g
+(condition)i(on)e(SMP)h(platforms,)k(if)300 b(an)h(interrupt)863
+32657 y(handler)328 b(also)e(touches)h Fo(num)p 12383
+32657 V 399 w(users)p Fn(,)339 b(or)326 b(in)g(some)h(future)f(Linux)h
+(k)-11 b(ernel)327 b(that)g(is)e(preempti)-28 b(v)-17
+b(e.)493 b(Since)327 b(this)f(FUSD)h(dri)-28 b(v)-17
+b(er)327 b(is)863 33985 y(just)276 b(a)i(plain,)f(single-threaded)i
+(user)-22 b(-space)278 b(application,)h(good)f(old)g
+Fo(++)f Fn(still)e(w)-11 b(orks.)863 38385 y Fm(6)1594
+b(Writing)399 b Fa(ioctl)h Fm(Callbacks)863 41524 y Fn(The)302
+b(POSIX)f(API)g(pro)-17 b(vides)302 b(for)e(a)h(function)h(called)f
+Fo(ioctl)p Fn(,)307 b(which)302 b(allo)-28 b(ws)301 b
+(\223out-of-band\224)i(con\002guration)h(information)d(to)863
+42852 y(be)e(passed)g(to)f(a)g(de)-28 b(vice)301 b(dri)-28
+b(v)-17 b(er)299 b(through)g(a)g(\002le)f(descriptor)-61
+b(.)407 b(Using)299 b(FUSD,)g(you)g(can)g(write)f(a)g(de)-28
+b(vice)301 b(dri)-28 b(v)-17 b(er)299 b(with)f(a)g(callback)863
+44180 y(to)282 b(handle)i Fo(ioctl)f Fn(requests)f(from)g(clients.)358
+b(F)-17 b(or)283 b(the)g(most)f(part,)h(it')-61 b(s)280
+b(just)i(lik)-11 b(e)282 b(writing)g(a)g(callback)i(for)e
+Fo(read)g Fn(or)g Fo(write)p Fn(,)i(as)863 45509 y(we')-55
+b(v)-17 b(e)264 b(seen)f(in)f(pre)-28 b(vious)264 b(sections.)339
+b(From)263 b(the)g(client')-61 b(s)262 b(point)h(of)f(vie)-28
+b(w)-72 b(,)267 b Fo(ioctl)c Fn(traditionally)g(tak)-11
+b(es)263 b(three)g(ar)-20 b(guments:)337 b(a)263 b(\002le)863
+46837 y(descriptor)-44 b(,)277 b(a)g(command)j(number)-44
+b(,)278 b(and)g(a)f(pointer)h(to)f(an)-17 b(y)278 b(additional)h(data)f
+(that)f(might)g(be)h(required)g(for)f(the)g(command.)863
+50670 y Fj(6.1)1329 b(Using)332 b(macr)-24 b(os)331 b(to)i(generate)e
+Fc(ioctl)h Fj(command)f(numbers)863 53410 y Fn(The)300
+b(Linux)h(header)g(\002le)e Fo(/usr/include/asm/ioctl.h)k
+Fn(de\002nes)e(macros)f(that)f Fk(must)g Fn(be)h(used)g(to)f(create)h
+(the)f Fo(ioctl)863 54738 y Fn(command)280 b(number)-61
+b(.)344 b(These)279 b(macros)e(tak)-11 b(e)278 b(v)-28
+b(arious)278 b(combinations)h(of)e(three)g(ar)-20 b(guments:)2524
+57838 y Fg(\017)554 b Fo(type)p Fn(\227an)431 b(8-bit)e(inte)-17
+b(ger)431 b(selected)g(to)f(be)g(speci\002c)h(to)f(the)g(de)-28
+b(vice)432 b(dri)-28 b(v)-17 b(er)-61 b(.)803 b Fo(type)430
+b Fn(should)h(be)f(chosen)h(so)f(as)g(not)3631 59166
+y(to)371 b(con\003ict)i(with)e(other)h(dri)-28 b(v)-17
+b(ers)372 b(that)f(might)h(be)g(\223listening\224)h(to)e(the)h(same)g
+(\002le)f(descriptor)-61 b(.)627 b(\(Inside)371 b(the)h(k)-11
+b(ernel,)396 b(for)3631 60494 y(e)-17 b(xample,)292 b(the)c(TCP)h(and)f
+(IP)g(stacks)g(use)f(distinct)h(numbers)g(since)h(an)f
+Fo(ioctl)g Fn(sent)g(to)f(a)h(sock)-11 b(et)289 b(\002le)f(descriptor)g
+(might)3631 61823 y(be)277 b(e)-17 b(xamined)280 b(by)e(both)g
+(stacks.\))2524 64036 y Fg(\017)554 b Fo(number)p Fn(\227an)241
+b(8-bit)f(inte)-17 b(ger)241 b(\223command)h(number)-61
+b(.)-77 b(\224)333 b(W)-44 b(ithin)239 b(a)h(dri)-28
+b(v)-17 b(er)-44 b(,)248 b(distinct)239 b(numbers)i(should)g(be)g
+(chosen)g(for)e(each)3631 65365 y(dif)-28 b(ferent)277
+b(kind)h(of)f Fo(ioctl)g Fn(command)j(that)d(the)g(dri)-28
+b(v)-17 b(er)278 b(services.)2524 67579 y Fg(\017)554
+b Fo(data)p 6353 67579 V 399 w(type)p Fn(\227The)292
+b(name)f(of)f(a)g(type)h(used)g(to)f(compute)i(ho)-28
+b(w)291 b(man)-17 b(y)292 b(bytes)f(are)f(e)-17 b(xchanged)295
+b(between)d(the)e(client)h(and)3631 68907 y(the)277 b(dri)-28
+b(v)-17 b(er)-61 b(.)344 b(This)277 b(ar)-20 b(gument)279
+b(is,)d(for)g(e)-17 b(xample,)279 b(the)f(name)g(of)f(a)g(structure.)
+25405 74071 y(19)p eop
+%%Page: 20 23
+20 22 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(6)d Fn(ioctl.h:)343 b(Using)278 b(the)p 14258 2940
+333 45 v 675 w Fo(IO)g Fn(macros)f(to)g(generate)i Fo(ioctl)f
+Fn(command)h(numbers)p 863 3445 50191 45 v 365 4400 a
+Fi(1)1661 b Fo(/*)664 b(definition)i(of)f(the)f(structure)i(exchanged)g
+(between)f(client)g(and)g(server)g(*/)2524 5728 y(struct)g
+(ioctl_data_t)h({)3852 7057 y(char)f(string1[60];)3852
+8385 y(char)g(string2[60];)365 9714 y Fi(5)1661 b Fo(};)2524
+12370 y(#define)665 b(IOCTL_APP_TYPE)i(71)d(/*)h(arbitrary)g(number)h
+(unique)f(to)f(this)h(app)g(*/)2524 15027 y(#define)g(IOCTL_TEST2)h
+(_IO\(IOCTL_APP_TYPE,)i(2\))c(/*)h(int)f(argument)i(*/)-133
+16355 y Fi(10)1661 b Fo(#define)665 b(IOCTL_TEST3)h
+(_IOR\(IOCTL_APP_TYPE,)i(3,)c(struct)i(ioctl_data_t\))2524
+17684 y(#define)f(IOCTL_TEST4)h(_IOW\(IOCTL_APP_TYPE,)i(4,)c(struct)i
+(ioctl_data_t\))2524 19012 y(#define)f(IOCTL_TEST5)h
+(_IOWR\(IOCTL_APP_TYPE,)i(5,)d(struct)g(ioctl_data_t\))p
+863 20524 V 2524 23606 a Fn(The)278 b(macros)f(used)h(to)f(generate)i
+(command)g(numbers)g(are:)2524 26407 y Fg(\017)p 3697
+26407 333 45 v 952 w Fo(IO\(int)665 b(type,)g(int)g(number\))318
+b Fn(\226)f(used)h(for)e(a)h(simple)g(ioctl)f(that)h(sends)h(nothing)g
+(b)-22 b(ut)317 b(the)g(type)h(and)g(number)-44 b(,)3631
+27736 y(and)278 b(recei)-28 b(v)-17 b(es)279 b(back)f(nothing)h(b)-22
+b(ut)277 b(an)h(\(inte)-17 b(ger\))277 b(retv)-28 b(al.)2524
+29830 y Fg(\017)p 3697 29830 V 952 w Fo(IOR\(int)665
+b(type,)h(int)e(number,)i(data)p 24020 29830 V 399 w(type\))401
+b Fn(\226)f(used)h(for)e(an)i(ioctl)f(that)g(reads)g(data)h
+Fk(fr)-50 b(om)400 b Fn(the)h(de)-28 b(vice)3631 31159
+y(dri)g(v)-17 b(er)-61 b(.)344 b(The)278 b(dri)-28 b(v)-17
+b(er)278 b(will)e(be)h(allo)-28 b(wed)279 b(to)e(return)g
+Fo(sizeof\(data)p 30165 31159 V 401 w(type\))g Fn(bytes)h(to)f(the)g
+(user)-61 b(.)2524 33253 y Fg(\017)p 3697 33253 V 952
+w Fo(IOW\(int)665 b(type,)h(int)e(number,)i(data)p 24020
+33253 V 399 w(type\))241 b Fn(\226)f(similar)f(to)p 33189
+33253 V 638 w(IOR,)h(b)-22 b(ut)240 b(used)h(to)f(write)g(data)h
+Fk(to)f Fn(the)g(dri)-28 b(v)-17 b(er)-61 b(.)2524 35348
+y Fg(\017)p 3697 35348 V 952 w Fo(IORW\(int)666 b(type,)f(int)f
+(number,)i(data)p 24684 35348 V 399 w(type\))267 b Fn(\226)g(a)f
+(combination)j(of)p 37237 35348 V 665 w Fo(IOR)e Fn(and)p
+41759 35348 V 666 w Fo(IOW)p Fn(.)f(That)h(is,)h(data)f(is)3631
+36676 y(both)277 b(written)g(to)g(the)h(dri)-28 b(v)-17
+b(er)277 b(and)i(then)e(read)h(back)h(from)e(the)g(dri)-28
+b(v)-17 b(er)278 b(by)g(the)f(client.)2524 39478 y(Program)322
+b(6)f(is)f(an)h(e)-17 b(xample)324 b(header)f(\002le)e(sho)-28
+b(wing)322 b(the)g(use)f(of)g(these)g(macros.)476 b(In)321
+b(real)g(programs,)332 b(the)321 b(client)h(e)-17 b(x)g(ecuting)863
+40806 y(an)278 b(ioctl)f(and)h(the)f(dri)-28 b(v)-17
+b(er)278 b(that)f(services)h(it)e(must)g(share)i(the)f(same)h(header)h
+(\002le.)863 44585 y Fj(6.2)1329 b(Example)332 b(client)g(calls)g(and)f
+(dri)-13 b(v)g(er)331 b(callbacks)863 47325 y Fn(Program)268
+b(7)f(sho)-28 b(ws)268 b(a)f(client)g(program)h(that)f(e)-17
+b(x)g(ecutes)270 b Fo(ioctl)p Fn(s)d(using)g(the)h(ioctl)e(command)k
+(numbers)e(de\002ned)h(in)e(Program)h(6.)863 48653 y(The)236
+b Fo(ioctl)p 6205 48653 V 399 w(data)p 9260 48653 V 400
+w(t)e Fn(is)g(application-speci\002c;)252 b(our)236 b(simple)e(test)h
+(program)h(de\002nes)g(it)e(as)h(a)g(structure)g(containing)i(tw)-11
+b(o)235 b(arrays)863 49981 y(of)266 b(characters.)340
+b(The)267 b(\002rst)e Fo(ioctl)h Fn(call)g(\(line)g(10\))g(sends)g(the)
+g(command)i Fo(IOCTL)p 32974 49981 V 400 w(TEST3)p Fn(,)g(which)f
+(retrie)-28 b(v)-17 b(es)266 b(strings)f Fk(fr)-50 b(om)266
+b Fn(the)863 51310 y(dri)-28 b(v)-17 b(er)-61 b(.)344
+b(The)278 b(second)h Fo(ioctl)f Fn(uses)f(the)g(command)j
+Fo(IOCTL)p 24809 51310 V 399 w(TEST4)e Fn(\(line)e(18\),)h(which)i
+(sends)e(strings)f Fk(to)h Fn(the)g(dri)-28 b(v)-17 b(er)-61
+b(.)2524 53302 y(The)278 b(portion)f(of)g(the)h(FUSD)g(dri)-28
+b(v)-17 b(er)278 b(that)f(services)g(these)g(calls)g(is)f(sho)-28
+b(wn)279 b(in)e(Program)h(8.)2524 55295 y(The)337 b(ioctl)g(e)-17
+b(xample)339 b(header)g(\002le)e(and)h(test)e(programs)h(sho)-28
+b(wn)339 b(in)d(this)g(document)k(\(Programs)d(6,)351
+b(7,)h(and)338 b(8\))f(are)g(actually)863 56623 y(contained)f(in)d(a)g
+(lar)-20 b(ger)-44 b(,)347 b(single)333 b(e)-17 b(xample)336
+b(program)e(included)h(in)e(the)h(FUSD)g(distrib)-22
+b(ution)333 b(called)h Fo(ioctl.c)p Fn(.)512 b(That)334
+b(source)863 57952 y(code)279 b(sho)-28 b(ws)277 b(other)h(v)-28
+b(ariations)278 b(on)f(calling)h(and)g(servicing)g Fo(ioctl)g
+Fn(commands.)863 62298 y Fm(7)1594 b(Integrating)399
+b(FUSD)f(W)-29 b(ith)399 b(Y)-177 b(our)398 b(A)-40 b(pplication)400
+b(Using)f Fa(fusd)p 40051 62298 479 45 v 575 w(dispatch\(\))863
+65436 y Fn(The)228 b(e)-17 b(xample)229 b(applications)g(we')-55
+b(v)-17 b(e)228 b(seen)f(so)g(f)-11 b(ar)226 b(ha)-22
+b(v)-17 b(e)229 b(something)f(in)f(common:)320 b(after)227
+b(initialization)g(and)h(de)-28 b(vice)229 b(re)-17 b(gistration,)863
+66765 y(the)g(y)300 b(call)f Fo(fusd)p 7670 66765 333
+45 v 399 w(run\(\))p Fn(.)408 b(This)298 b(gi)-28 b(v)-17
+b(es)300 b(up)f(control)g(of)g(the)g(program')-61 b(s)299
+b(\003o)-28 b(w)-72 b(,)304 b(turning)c(it)d(o)-17 b(v)g(er)300
+b(to)f(the)g(FUSD)g(library)g(instead.)863 68093 y(This)234
+b(w)-11 b(ork)g(ed)236 b(\002ne)f(for)e(our)i(simple)f(e)-17
+b(xample)236 b(programs,)243 b(b)-22 b(ut)235 b(doesn')-20
+b(t)234 b(w)-11 b(ork)235 b(in)f(a)g(real)g(program)h(that)g(needs)g
+(to)f(w)-11 b(ait)234 b(for)f(e)-28 b(v)-17 b(ents)863
+69421 y(other)316 b(than)h(FUSD)g(callbacks.)460 b(F)-17
+b(or)317 b(this)e(reason,)325 b(our)316 b(frame)-28 b(w)-11
+b(ork)317 b(pro)-17 b(vides)317 b(another)g(w)-11 b(ay)317
+b(to)f(acti)-28 b(v)g(ate)317 b(callbacks)g(that)f(does)863
+70750 y(not)278 b(require)f(the)h(dri)-28 b(v)-17 b(er)277
+b(to)g(gi)-28 b(v)-17 b(e)279 b(up)f(control)f(of)g(its)f
+Fo(main\(\))p Fn(.)25405 74071 y(20)p eop
+%%Page: 21 24
+21 23 bop 863 1955 50191 89 v 863 2932 a Fl(Pr)-20 b(ogram)280
+b(7)d Fn(ioctl-client.c:)344 b(A)276 b(program)j(that)e(mak)-11
+b(es)278 b Fo(ioctl)f Fn(requests)g(on)h(a)f(\002le)h(descriptor)p
+863 3437 50191 45 v 365 4392 a Fi(1)4317 b Fo(int)665
+b(fd,)g(ret;)5180 5721 y(struct)h(ioctl_data_t)g(d;)5180
+8377 y(if)f(\(\(fd)g(=)f(open\("/dev/ioctltest",)k(O_RDWR\)\))e(<)e
+(0\))h({)365 9706 y Fi(5)5646 b Fo(perror\("client:)666
+b(can't)g(open)e(ioctltest"\);)6509 11034 y(exit\(1\);)5180
+12363 y(})5180 15019 y(/*)h(test3:)g(make)g(sure)g(we)f(can)h(get)g
+(data)g(FROM)g(a)f(driver)h(using)g(ioctl)g(*/)-133 16348
+y Fi(10)4317 b Fo(ret)665 b(=)f(ioctl\(fd,)i(IOCTL_TEST3,)g(&d\);)5180
+17676 y(printf\("ioctl)h(test3:)e(got)g(retval=\045d\\n",)h(ret\);)5180
+19004 y(printf\("ioctl)h(test3:)e(got)g(string1='\045s'\\n",)i
+(d.string1\);)5180 20333 y(printf\("ioctl)g(test3:)e(got)g
+(string2='\045s'\\n",)i(d.string2\);)-133 22989 y Fi(15)4317
+b Fo(/*)665 b(test4:)g(make)g(sure)g(we)f(can)h(send)g(data)g(TO)f(a)h
+(driver)g(using)g(an)g(ioctl)g(*/)5180 24318 y(sprintf\(d.string1,)j
+(TEST4_STRING1\);)5180 25646 y(sprintf\(d.string2,)g(TEST4_STRING2\);)
+5180 26974 y(ret)d(=)f(ioctl\(fd,)i(IOCTL_TEST4,)g(&d\);)p
+863 28486 V 863 31807 a Fj(7.1)1329 b(Using)332 b Fc(fusd)p
+10700 31807 399 45 v 478 w(dispatch\(\))863 34547 y Fn(Recall)443
+b(from)g(Section)g(4.1)g(that)g Fo(fusd)p 17141 34547
+333 45 v 399 w(register)h Fn(returns)e(a)g Fk(\002le)h(descriptor)g
+Fn(for)f(e)-28 b(v)-17 b(ery)444 b(de)-28 b(vice)445
+b(that)d(is)g(successfully)863 35875 y(re)-17 b(gistered.)357
+b(This)281 b(\002le)h(descriptor)f(can)i(be)f(used)g(to)f(acti)-28
+b(v)g(ate)283 b(de)-28 b(vice)284 b(callbacks)f(\223manually)-72
+b(,)-77 b(\224)284 b(without)e(passing)g(control)g(of)f(the)863
+37203 y(application)256 b(to)f Fo(fusd)p 9874 37203 V
+399 w(run\(\))p Fn(.)336 b(Whene)-28 b(v)-17 b(er)257
+b(the)e(\002le)g(descriptor)f(becomes)j(readable)f(according)g(to)f
+Fo(select\(2\))p Fn(,)260 b(it)253 b(should)863 38532
+y(be)258 b(passed)g(to)g Fo(fusd)p 9214 38532 V 399 w(dispatch\(\))p
+Fn(,)k(which)d(in)e(turn)g(will)g(acti)-28 b(v)g(ate)259
+b(callbacks)g(in)e(the)h(same)g(w)-11 b(ay)258 b(that)f
+Fo(fusd)p 44836 38532 V 400 w(run\(\))g Fn(does.)863
+39860 y(In)277 b(other)h(w)-11 b(ords,)276 b(an)i(application)h(can:)
+2247 42738 y(1.)554 b(Sa)-22 b(v)-17 b(e)278 b(the)g(\002le)f
+(descriptors)g(returned)h(by)g Fo(fusd)p 22546 42738
+V 399 w(register\(\))p Fn(;)2247 44952 y(2.)554 b(Add)263
+b(those)f(FUSD)h(\002le)g(descriptors)f(to)g(an)h Fo(fd)p
+22108 44952 V 398 w(set)g Fn(that)f(is)f(passed)i(to)f
+Fo(select)p Fn(,)k(along)d(with)f(an)-17 b(y)264 b(other)e(\002le)h
+(descrip-)3631 46280 y(tors)276 b(that)h(might)g(be)h(interesting)f(to)
+g(the)h(application;)g(and)2247 48494 y(3.)554 b(P)-17
+b(ass)277 b(e)-28 b(v)-17 b(ery)279 b(FUSD)f(\002le)f(descriptor)h
+(that)f Fo(select)h Fn(indicates)g(is)e(readable)j(to)e
+Fo(fusd)p 37363 48494 V 399 w(dispatch)p Fn(.)2524 51372
+y Fo(fusd)p 5246 51372 V 399 w(dispatch\(\))247 b Fn(returns)f(0)g(if)f
+(at)g(least)h(one)h(callback)h(w)-11 b(as)246 b(successfully)g(acti)-28
+b(v)g(ated.)335 b(On)247 b(error)-44 b(,)251 b(-1)245
+b(is)g(returned)i(with)863 52701 y Fo(errno)261 b Fn(set)e
+(appropriately)-72 b(.)340 b Fo(fusd)p 15100 52701 V
+399 w(dispatch\(\))262 b Fn(will)d(ne)-28 b(v)-17 b(er)262
+b(block\227if)f(no)f(messages)h(are)g(a)-22 b(v)-28 b(ailable)262
+b(from)d(the)i(k)-11 b(ernel,)264 b(it)863 54029 y(will)276
+b(return)h(-1)g(with)g Fo(errno)h Fn(set)e(to)h Fo(EAGAIN)p
+Fn(.)863 57862 y Fj(7.2)1329 b(Helper)332 b(Functions)e(f)-33
+b(or)332 b(Constructing)g(an)g Fc(fd)p 27226 57862 399
+45 v 478 w(set)863 60602 y Fn(The)305 b(FUSD)f(library)g(pro)-17
+b(vides)305 b(tw)-11 b(o)304 b(\(optional\))g(utility)f(functions)h
+(that)g(can)g(mak)-11 b(e)305 b(it)e(easier)h(to)f(write)g
+(applications)j(that)d(inte-)863 61930 y(grate)278 b(FUSD)g(into)f
+(their)g(o)-28 b(wn)278 b Fo(select\(\))g Fn(loops.)344
+b(Speci\002cally:)2524 64808 y Fg(\017)554 b Fo(void)664
+b(fusd)p 9673 64808 333 45 v 400 w(fdset)p 13393 64808
+V 399 w(add\(fd)p 17776 64808 V 400 w(set)g(*set,)h(int)g(*max\))p
+Fn(\227is)361 b(meant)h(to)e(help)i(construct)g(an)f
+Fo(fd)p 46707 64808 V 399 w(set)g Fn(that)3631 66136
+y(will)347 b(be)i(passed)g(as)f(the)g(\223readable)j(fds\224)d(set)f
+(to)h(select.)557 b(This)348 b(function)i(adds)e(the)h(\002le)g
+(descriptors)f(of)g(all)f(pre)-28 b(viously)3631 67465
+y(re)-17 b(gistered)317 b(FUSD)g(de)-28 b(vices)318 b(to)e(the)h(fd)p
+18882 67465 V 398 w(set)f Fo(set)p Fn(.)461 b(It)315
+b(assumes)i(that)f Fo(set)h Fn(has)f(already)i(been)g(initialized)e(by)
+h(the)g(caller)-61 b(.)3631 68793 y(The)282 b(inte)-17
+b(ger)282 b Fo(max)f Fn(is)f(updated)k(to)d(re\003ect)h(the)f(lar)-20
+b(gest)281 b(\002le)h(descriptor)f(number)i(in)e(the)g(set.)355
+b Fo(max)282 b Fn(is)e(not)h(changed)j(if)d(the)3631
+70121 y(v)-28 b(alue)278 b(passed)g(to)f Fo(fusd)p 13365
+70121 V 399 w(fdset)p 17084 70121 V 400 w(add)g Fn(is)f(already)i(lar)
+-20 b(ger)278 b(than)g(the)f(lar)-20 b(gest)277 b(FUSD)h(\002le)f
+(descriptor)h(added)h(to)e(the)g(set.)25405 74071 y(21)p
+eop
+%%Page: 22 25
+22 24 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(8)d Fn(ioctl-serv)-17 b(er)-61 b(.c:)344 b(A)277 b(dri)-28
+b(v)-17 b(er)278 b(that)f(handles)h Fo(ioctl)g Fn(requests)p
+863 3445 50191 45 v 365 4400 a Fi(1)1661 b Fo(/*)664
+b(This)h(function)h(is)e(run)h(by)f(the)h(driver)g(*/)2524
+5728 y(int)f(do_ioctl\(struct)j(fusd_file_info)g(*file,)e(int)g(cmd,)g
+(void)f(*arg\))2524 7057 y({)3852 8385 y(struct)h(ioctl_data_t)h(*d;)
+365 9714 y Fi(5)3852 11042 y Fo(if)e(\(_IOC_TYPE\(cmd\))j(!=)e
+(IOCTL_APP_TYPE\))5180 12370 y(return)h(0;)3852 15027
+y(switch)f(\(cmd\))g({)-133 16355 y Fi(10)2989 b Fo(case)665
+b(IOCTL_TEST3:)h(/*)f(returns)g(data)g(to)f(the)h(client)g(*/)5180
+17684 y(d)g(=)f(arg;)5180 19012 y(strcpy\(d->string1,)k
+(TEST3_STRING1\);)5180 20340 y(strcpy\(d->string2,)g(TEST3_STRING2\);)
+5180 21669 y(return)e(0;)-133 22997 y Fi(15)4317 b Fo(break;)3852
+25654 y(case)665 b(IOCTL_TEST4:)h(/*)f(gets)g(data)f(from)h(the)g
+(client)g(*/)5180 26982 y(d)g(=)f(arg;)5180 28310 y(printf\("ioctl)j
+(server:)e(test4,)g(string1:)h(got)f('\045s'\\n",)g(d->string1\);)-133
+29639 y Fi(20)4317 b Fo(printf\("ioctl)667 b(server:)e(test4,)g
+(string2:)h(got)f('\045s'\\n",)g(d->string2\);)5180 30967
+y(return)h(0;)5180 32296 y(break;)3852 33624 y(default:)5180
+34952 y(printf\("ioctl)h(server:)e(got)g(unknown)g(cmd,)g(sigh,)g(this)
+g(is)g(broken\\n"\);)-133 36281 y Fi(25)4317 b Fo(return)666
+b(-EINVAL;)5180 37609 y(break;)3852 38937 y(})3852 41594
+y(return)f(0;)-133 42922 y Fi(30)1661 b Fo(})p 863 44417
+V 2524 47738 a Fg(\017)554 b Fo(void)664 b(fusd)p 9673
+47738 333 45 v 400 w(dispatch)p 15385 47738 V 400 w(fdset\(fd)p
+21097 47738 V 399 w(set)h(*set\))p Fn(\227is)220 b(meant)h(to)f(be)g
+(called)h(on)g(the)f Fo(fd)p 41899 47738 V 399 w(set)g
+Fn(that)g(is)f Fk(r)-41 b(eturned)3631 49066 y Fn(by)357
+b(select.)583 b(It)356 b(assumes)h(that)g Fo(set)g Fn(contains)h(a)g
+(set)e(\002le)h(descriptors)g(that)g Fo(select\(\))h
+Fn(has)f(indicated)i(are)e(readable.)3631 50394 y Fo(fusd)p
+6353 50394 V 399 w(dispatch)p 12064 50394 V 400 w(fdset\(\))409
+b Fn(calls)g Fo(fusd)p 22615 50394 V 399 w(dispatch)h
+Fn(on)f(e)-28 b(v)-17 b(ery)411 b(descriptor)e(in)g Fo(set)g
+Fn(that)g(is)e(a)i(v)-28 b(alid)410 b(FUSD)3631 51723
+y(descriptor)-61 b(.)343 b(Non-FUSD)279 b(descriptors)e(in)g
+Fo(set)g Fn(are)h(ignored.)2524 54601 y(The)g(e)-17 b(xcerpt)279
+b(of)d Fo(drums3.c)i Fn(sho)-28 b(wn)279 b(in)e(Program)h(9)f
+(demonstrates)h(the)f(use)h(of)f(these)g(helper)h(functions.)344
+b(This)277 b(program)863 55929 y(is)271 b(similar)g(to)h(the)g(earlier)
+g(drums.c)g(e)-17 b(xample:)343 b(it)271 b(creates)i(a)f(number)h(of)f
+(musical)h(instruments)e(such)i(as)f Fo(/dev/drums/bam)863
+57257 y Fn(and)469 b Fo(/dev/drums/boom)p Fn(.)919 b(Ho)-28
+b(we)g(v)-17 b(er)-44 b(,)518 b(in)468 b(addition)h(to)f(servicing)h
+(its)e(musical)h(callbacks,)517 b(the)469 b(dri)-28 b(v)-17
+b(er)468 b(also)g(prints)g(a)863 58586 y(prompt)379 b(to)g(standard)g
+(input)g(asking)g(ho)-28 b(w)380 b(\223loud\224)g(the)f(drums)f(should)
+i(be.)648 b(Instead)379 b(of)f(turning)h(control)g(of)f
+Fo(main\(\))h Fn(o)-17 b(v)g(er)863 59914 y(to)326 b
+Fo(fusd)p 4772 59914 V 399 w(run\(\))h Fn(as)f(in)g(the)g(pre)-28
+b(vious)328 b(e)-17 b(xamples,)340 b Fo(drums3)327 b
+Fn(uses)f Fo(select\(\))h Fn(to)f(simultaneously)i(w)-11
+b(atch)327 b(its)d(FUSD)k(\002le)863 61243 y(descriptors)277
+b(and)h(standard)g(input.)344 b(It)276 b(responds)i(to)f(input)h(from)e
+(both)i(sources.)2524 63235 y(On)327 b(lines)f(2\2265,)341
+b(an)327 b Fo(fd)p 11662 63235 V 399 w(set)g Fn(and)h(its)e(associated)
+i(\223max\224)h(v)-28 b(alue)328 b(are)g(initialized)f(to)g(contain)h
+(stdin')-61 b(s)326 b(\002le)h(descriptor)-61 b(.)494
+b(On)863 64563 y(line)304 b(9,)310 b(we)304 b(use)g Fo(fusd)p
+10062 64563 V 399 w(fdset)p 13781 64563 V 399 w(add)g
+Fn(to)g(add)h(the)f(FUSD)g(\002le)g(descriptors)g(for)f(all)g(re)-17
+b(gistered)304 b(de)-28 b(vices.)425 b(\(Not)303 b(sho)-28
+b(wn)305 b(in)f(this)863 65892 y(e)-17 b(xcerpt)356 b(is)d(the)i(de)-28
+b(vice)356 b(re)-17 b(gistration,)373 b(which)355 b(is)e(the)i(same)f
+(as)g(the)h(re)-17 b(gistration)354 b(code)i(we)e(sa)-17
+b(w)355 b(in)f Fo(drums.c)p Fn(.\))575 b(On)354 b(line)g(13)863
+67220 y(we)i(call)f(select,)375 b(which)356 b(blocks)g(until)f(one)i
+(of)e(the)g(fd')-61 b(s)355 b(in)g(the)h(set)e(is)h(readable.)579
+b(On)356 b(lines)f(17)h(and)g(18,)375 b(we)356 b(check)h(to)e(see)h(if)
+863 68548 y(standard)249 b(input)g(is)e(readable;)259
+b(if)247 b(so,)253 b(a)248 b(function)i(is)d(called)i(which)g(reads)f
+(the)g(user')-61 b(s)248 b(response)g(from)g(standard)h(input)g(and)g
+(prints)863 69877 y(a)256 b(ne)-28 b(w)257 b(prompt.)337
+b(Finally)-72 b(,)261 b(on)c(line)f(21,)k(we)c(call)g
+Fo(fusd)p 22139 69877 V 400 w(dispatch)p 27851 69877
+V 400 w(fdset)p Fn(,)k(which)d(in)f(turn)g(will)f(acti)-28
+b(v)g(ate)258 b(the)e(callbacks)i(for)25405 74071 y(22)p
+eop
+%%Page: 23 26
+23 25 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(9)d Fn(drums3.c:)344 b(W)-89 b(aiting)278 b(for)e(both)i(FUSD)g(and)g
+(non-FUSD)i(e)-28 b(v)-17 b(ents)278 b(in)f(a)g Fo(select)h
+Fn(loop)p 863 3445 50191 45 v 365 4400 a Fi(1)2989 b
+Fo(/*)664 b(initialize)i(the)f(set)g(*/)3852 5728 y(FD_ZERO\(&fds\);)
+3852 8385 y(/*)f(add)h(stdin)g(to)g(the)f(set)h(*/)365
+9714 y Fi(5)2989 b Fo(FD_SET\(STDIN_FILENO,)668 b(&fds\);)3852
+11042 y(max)d(=)f(STDIN_FILENO;)3852 13699 y(/*)g(add)h(all)g(FUSD)g
+(fds)f(to)h(the)g(set)f(*/)3852 15027 y(fusd_fdset_add\(&fds,)k
+(&max\);)-133 16355 y Fi(10)3852 17684 y Fo(while)d(\(1\))g({)5180
+19012 y(tmp)g(=)f(fds;)5180 20340 y(if)h(\(select\(max+1,)h(&tmp,)g
+(NULL,)f(NULL,)g(NULL\))g(<)f(0\))6509 21669 y(perror\("selecting"\);)
+-133 22997 y Fi(15)4317 b Fo(else)665 b({)6509 24325
+y(/*)f(if)h(stdin)g(is)f(readable,)i(read)f(the)g(user's)g(response)g
+(*/)6509 25654 y(if)f(\(FD_ISSET\(STDIN_FILENO,)k(&tmp\)\))7837
+26982 y(read_volume\(STDIN_FILENO\);)-133 29639 y Fi(20)5646
+b Fo(/*)664 b(call)h(any)g(FUSD)g(callbacks)g(that)g(have)g(messages)h
+(waiting)f(*/)6509 30967 y(fusd_dispatch_fdset\(&tmp\);)5180
+32296 y(})3852 33624 y(})p 863 35118 V 863 38439 a Fn(de)-28
+b(vices)279 b(that)e(ha)-22 b(v)-17 b(e)279 b(pending)g(system)e(calls)
+g(w)-11 b(aiting)277 b(to)g(be)h(serviced.)2524 40432
+y(It')-61 b(s)337 b(w)-11 b(orth)339 b(reiterating)g(that)g(dri)-28
+b(v)-17 b(ers)339 b(are)g(not)h(required)g(to)e(use)i(the)f(FUSD)h
+(helper)f(functions)h Fo(fusd)p 43071 40432 333 45 v
+399 w(fdset)p 46790 40432 V 400 w(add)f Fn(and)863 41760
+y Fo(fusd)p 3585 41760 V 399 w(dispatch)p 9296 41760
+V 400 w(fdset)p Fn(.)798 b(If)427 b(it')-61 b(s)427 b(more)i(con)-44
+b(v)-17 b(enient,)469 b(a)429 b(dri)-28 b(v)-17 b(er)429
+b(can)h(manually)g(sa)-22 b(v)-17 b(e)429 b(all)f(of)h(the)f(\002le)h
+(descriptors)g(re-)863 43088 y(turned)282 b(by)h Fo(fusd)p
+8083 43088 V 399 w(register)p Fn(,)g(construct)f(its)e(o)-28
+b(wn)283 b Fo(fd)p 23508 43088 V 399 w(set)p Fn(,)f(and)h(then)f(call)f
+Fo(fusd)p 35060 43088 V 400 w(dispatch)h Fn(on)g(each)h(descriptor)f
+(that)863 44417 y(is)415 b(readable.)760 b(This)415 b(method)i(is)d
+(sometimes)i(required)g(for)f(inte)-17 b(gration)417
+b(with)e(other)h(frame)-28 b(w)-11 b(orks)416 b(that)f(w)-11
+b(ant)416 b(to)f(tak)-11 b(e)416 b(o)-17 b(v)g(er)863
+45745 y(your)425 b Fo(main\(\))p Fn(.)785 b(F)-17 b(or)424
+b(e)-17 b(xample,)463 b(the)425 b(GTK)f(user)g(interf)-11
+b(ace)425 b(frame)-28 b(w)-11 b(ork)30621 45343 y Ff(11)31876
+45745 y Fn(is)423 b(e)-28 b(v)-17 b(ent-dri)-28 b(v)-17
+b(en)428 b(and)d(requires)f(that)g(you)h(pass)863 47073
+y(control)371 b(of)g(your)g Fo(main)g Fn(to)f(it.)623
+b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)396 b(it)370 b(does)h(allo)-28
+b(w)371 b(you)h(to)f(gi)-28 b(v)-17 b(e)372 b(it)d(a)i(\002le)g
+(descriptor)g(and)h(a)e(function)i(pointer)-44 b(,)394
+b(say-)863 48402 y(ing)385 b(\223Call)g(this)e(callback)j(when)g
+Fo(select)f Fn(indicates)g(this)f(\002le)g(descriptor)h(has)g(become)h
+(readable.)-77 b(\224)667 b(A)384 b(GTK)h(application)863
+49730 y(that)347 b(implements)g(FUSD)h(de)-28 b(vices)348
+b(can)g(w)-11 b(ork)347 b(by)g(gi)-28 b(ving)348 b(GTK)f(all)g(the)f
+(FUSD)i(\002le)f(descriptors)g(indi)-28 b(vidually)-72
+b(,)365 b(and)348 b(calling)863 51059 y Fo(fusd)p 3585
+51059 V 399 w(dispatch\(\))279 b Fn(when)f(GTK)g(calls)f(the)g
+(associated)h(callbacks.)863 55459 y Fm(8)1594 b(Implementing)399
+b(Blocking)g(System)f(Calls)863 58597 y Fn(All)359 b(of)h(the)g(e)-17
+b(xample)362 b(dri)-28 b(v)-17 b(ers)361 b(that)f(we')-55
+b(v)-17 b(e)361 b(seen)f(until)g(no)-28 b(w)361 b(ha)-22
+b(v)-17 b(e)362 b(had)f(an)f(important)g(feature)h(missing:)508
+b(the)-17 b(y)361 b(ne)-28 b(v)-17 b(er)362 b(had)f(to)863
+59925 y Fk(wait)330 b Fn(for)f(an)-17 b(ything.)504 b(So)331
+b(f)-11 b(ar)-44 b(,)342 b(a)330 b(dri)-28 b(v)-17 b(er')-61
+b(s)330 b(response)h(to)e(a)h(system)g(call)g(has)g(al)-11
+b(w)g(ays)331 b(been)g(immediately)g(a)-22 b(v)-28 b(ailable\227allo)g
+(wing)863 61254 y(the)317 b(dri)-28 b(v)-17 b(er)318
+b(to)e(response)i(immediately)-72 b(.)464 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)329 b(real)316 b(de)-28 b(vices)319 b(are)e(often)g(not)g
+(that)g(luck)-17 b(y:)424 b(the)-17 b(y)318 b(usually)g(ha)-22
+b(v)-17 b(e)318 b(to)f(w)-11 b(ait)317 b(for)863 62582
+y(something)302 b(to)e(happen)i(before)f(completing)h(a)f(client')-61
+b(s)299 b(system)h(call.)413 b(F)-17 b(or)301 b(e)-17
+b(xample,)308 b(a)300 b(dri)-28 b(v)-17 b(er)301 b(might)f(be)h(w)-11
+b(aiting)301 b(for)e(data)i(to)863 63910 y(arri)-28 b(v)-17
+b(e)278 b(from)f(the)g(serial)f(port)h(or)g(o)-17 b(v)g(er)279
+b(the)e(netw)-11 b(ork,)278 b(or)f(e)-28 b(v)-17 b(en)279
+b(w)-11 b(aiting)278 b(for)e(a)i(user)f(action.)2524
+65903 y(In)215 b(situations)h(lik)-11 b(e)216 b(this,)227
+b(a)216 b(basic)g(capability)h(most)f(de)-28 b(vice)218
+b(dri)-28 b(v)-17 b(ers)216 b(must)f(ha)-22 b(v)-17 b(e)218
+b(is)d(the)h(ability)g(to)g Fk(bloc)-22 b(k)217 b Fn(the)f(caller)-61
+b(.)323 b(Blocking)863 67231 y(operations)335 b(are)f(important)g
+(because)i(the)-17 b(y)335 b(pro)-17 b(vide)336 b(a)e(simple)f(interf)
+-11 b(ace)335 b(to)e(user)h(programs)g(that)g(does)h(\003o)-28
+b(w)335 b(control,)348 b(rather)863 68560 y(than)290
+b(something)h(more)f(e)-17 b(xpensi)-28 b(v)-17 b(e)293
+b(lik)-11 b(e)290 b(continuous)h(polling.)382 b(F)-17
+b(or)290 b(e)-17 b(xample,)294 b(user)c(programs)g(e)-17
+b(xpect)292 b(to)d(be)h(able)g(to)g(e)-17 b(x)g(ecute)p
+863 69508 20076 45 v 1738 70248 a Fe(11)2457 70561 y
+Fd(http://www)-58 b(.gtk.or)-16 b(g)25405 74071 y Fn(23)p
+eop
+%%Page: 24 27
+24 26 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(10)e Fn(console-read.c:)345 b(A)277 b(simple)g(blocking)i(system)e
+(call)p 863 3445 50191 45 v 365 4400 a Fi(1)1661 b Fo(int)664
+b(do_read\(struct)j(fusd_file_info)g(*file,)e(char)g(*user_buffer,)
+10494 5728 y(size_t)g(user_length,)h(loff_t)f(*offset\))2524
+7057 y({)3852 8385 y(char)g(buf[128];)365 9714 y Fi(5)3852
+11042 y Fo(if)f(\(*offset)i(>)e(0\))5180 12370 y(return)i(0;)3852
+15027 y(/*)e(print)i(a)e(prompt)h(*/)-133 16355 y Fi(10)2989
+b Fo(printf\("Got)666 b(a)e(read)h(from)g(pid)g(\045d.)1329
+b(What)665 b(do)f(we)h(say?\\n>)g(",)g(file->pid\);)3852
+17684 y(fflush\(stdout\);)3852 20340 y(/*)f(get)h(a)g(response)g(from)g
+(the)g(console)g(*/)3852 21669 y(if)f(\(fgets\(buf,)j(sizeof\(buf\))f
+(-)e(1,)h(stdin\))g(==)f(NULL\))-133 22997 y Fi(15)4317
+b Fo(return)666 b(0;)3852 25654 y(/*)e(compute)i(length)f(of)g(the)f
+(response,)i(and)f(return)g(*/)3852 26982 y(user_length)h(=)e
+(MIN\(user_length,)j(strlen\(buf\)\);)3852 28310 y
+(memcpy\(user_buffer,)g(buf,)e(user_length\);)-133 29639
+y Fi(20)2989 b Fo(*offset)665 b(+=)g(user_length;)3852
+30967 y(return)g(user_length;)2524 32296 y(})p 863 33790
+V 863 37111 a Fn(a)388 b(statement)f(lik)-11 b(e)388
+b Fo(read\(fd,)665 b(buf,)g(sizeof\(buf\)\))p Fn(,)416
+b(and)389 b(e)-17 b(xpect)389 b(the)f(read)g(call)f(to)g(block)i
+(\(stop)e(the)g(\003o)-28 b(w)389 b(of)e(the)863 38439
+y(calling)278 b(program\))g(until)f(data)g(is)g(a)-22
+b(v)-28 b(ailable.)345 b(This)277 b(is)f(much)i(simpler)f(and)h(more)f
+(ef)-28 b(\002cient)279 b(than)f(polling)g(repeatedly)-72
+b(.)2524 40432 y(In)276 b(the)i(follo)-28 b(wing)278
+b(sections,)f(we')-11 b(ll)276 b(describe)i(ho)-28 b(w)279
+b(to)e(block)h(and)g(unblock)i(system)c(calls)h(for)g(de)-28
+b(vices)278 b(that)f(use)h(FUSD.)863 44265 y Fj(8.1)1329
+b(Blocking)332 b(the)g(caller)g(by)g(blocking)f(the)h(dri)-13
+b(v)g(er)863 47004 y Fn(The)326 b(easiest)e(\(b)-22 b(ut)324
+b(least)g(useful\))g(w)-11 b(ay)326 b(to)e(block)i(a)f(client')-61
+b(s)324 b(system)g(call)g(is)g(simply)g(to)h(block)g(the)g(dri)-28
+b(v)-17 b(er)-44 b(,)337 b(too.)486 b(F)-17 b(or)325
+b(e)-17 b(xample,)863 48333 y(consider)348 b(Program)g(10,)364
+b(which)348 b(implements)f(a)g(de)-28 b(vice)349 b(called)e
+Fo(/dev/console-read)p Fn(.)555 b(Whene)-28 b(v)-17 b(er)350
+b(a)d(process)g(tries)e(to)863 49661 y(read)297 b(from)f(this)g(de)-28
+b(vice,)303 b(the)296 b(dri)-28 b(v)-17 b(er)297 b(prints)f(a)g(prompt)
+h(to)f(standard)i(input,)j(asking)c(for)f(a)h(reply)-72
+b(.)401 b(\(The)297 b(prompt)g(appears)g(in)g(the)863
+50989 y(shell)286 b(the)h(dri)-28 b(v)-17 b(er)287 b(w)-11
+b(as)286 b(run)h(in,)h(not)f(the)g(shell)f(that')-61
+b(s)285 b(trying)i(to)f(read)h(from)f(the)h(de)-28 b(vice.\))373
+b(When)287 b(the)g(user)f(enters)h(a)f(line)h(of)f(te)-17
+b(xt,)863 52318 y(the)318 b(response)h(is)e(returned)i(to)e(the)i
+(client)f(that)g(did)g(the)g(original)g Fo(read\(\))p
+Fn(.)466 b(By)318 b(blocking)i(the)e(dri)-28 b(v)-17
+b(er)319 b(w)-11 b(aiting)318 b(for)g(the)g(reply)-72
+b(,)863 53646 y(the)278 b(client)f(that)g(issued)g(the)h(system)e(call)
+i(is)e(block)-11 b(ed)279 b(as)e(well.)2524 55639 y(Blocking)341
+b(the)e(dri)-28 b(v)-17 b(er)340 b(this)e(w)-11 b(ay)340
+b(is)e(safe\227unlik)-11 b(e)341 b(programming)g(in)e(the)g(k)-11
+b(ernel)340 b(proper)-44 b(,)355 b(where)340 b(doing)g(something)h(lik)
+-11 b(e)863 56967 y(this)372 b(w)-11 b(ould)374 b(block)f(the)g(entire)
+g(system.)630 b(It')-61 b(s)371 b(also)h(easy)i(to)e(implement,)398
+b(as)372 b(seen)h(from)f(the)h(e)-17 b(xample)375 b(abo)-17
+b(v)g(e.)633 b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)399 b(it)863
+58295 y(mak)-11 b(es)284 b(the)f(dri)-28 b(v)-17 b(er)284
+b(unresponsi)-28 b(v)-17 b(e)286 b(to)c(system)h(call)g(requests)g
+(that)g(might)g(be)h(coming)g(from)f(other)g(clients.)360
+b(If)282 b(another)j(process)863 59624 y(tries)310 b(to)g(do)i(an)-17
+b(ything)313 b(at)d(all)h(with)f(a)h(block)-11 b(ed)313
+b(dri)-28 b(v)-17 b(er')-61 b(s)311 b(de)-28 b(vice\227e)g(v)-17
+b(en)315 b(an)c Fo(open\(\))p Fn(\227it)g(will)f(block)i(until)f(the)g
+(dri)-28 b(v)-17 b(er)311 b(w)-11 b(ak)g(es)863 60952
+y(up)303 b(ag)-6 b(ain.)421 b(This)302 b(limitation)g(mak)-11
+b(es)303 b(blocking)i(dri)-28 b(v)-17 b(ers)302 b(inappropriate)j(for)c
+(an)-17 b(y)304 b(de)-28 b(vice)305 b(dri)-28 b(v)-17
+b(er)303 b(that)f(e)-17 b(xpects)304 b(to)f(service)f(more)863
+62280 y(than)278 b(one)g(client)g(at)f(a)g(time.)863
+66113 y Fj(8.2)1329 b(Blocking)332 b(the)g(caller)g(using)f
+Fc(-FUSD)p 22235 66113 399 45 v 478 w(NOREPLY)p Fj(;)i(unblocking)d(it)
+j(using)e Fc(fusd)p 43439 66113 V 479 w(return\(\))863
+68853 y Fn(If)314 b(a)i(de)-28 b(vice)317 b(dri)-28 b(v)-17
+b(er)316 b(e)-17 b(xpects)318 b(more)d(than)h(one)h(client)f(at)f(a)g
+(time\227as)g(is)g(often)g(the)h(case\227a)h(slightly)e(dif)-28
+b(ferent)315 b(programming)863 70181 y(model)376 b(is)d(needed)k(for)d
+(system)g(calls)g(that)g(can)i(potentially)f(block.)637
+b(Instead)374 b(of)h(blocking,)400 b(the)375 b(dri)-28
+b(v)-17 b(er)375 b(immediately)h(sends)25405 74071 y(24)p
+eop
+%%Page: 25 28
+25 27 bop 863 2974 a Fn(a)349 b(message)i(to)e(the)g(FUSD)h(frame)-28
+b(w)-11 b(ork)351 b(that)e(says,)367 b(in)349 b(essence,)368
+b(\223Don')-20 b(t)350 b(unblock)h(the)f(client)f(that)h(issued)f(this)
+f(system)h(call,)863 4302 y(b)-22 b(ut)337 b(continue)i(sending)f
+(additional)g(system)f(call)f(requests)h(that)g(might)g(be)g(coming)i
+(from)d(other)h(clients.)-77 b(\224)522 b(Dri)-28 b(v)-17
+b(er)337 b(callbacks)863 5631 y(can)290 b(send)f(this)e(message)i(to)f
+(FUSD)i(by)f(returning)g(the)f(special)h(v)-28 b(alue)290
+b Fo(-FUSD)p 31984 5631 333 45 v 399 w(NOREPLY)g Fn(instead)e(of)h(a)f
+(normal)h(system)f(call)863 6959 y(return)277 b(v)-28
+b(alue.)2524 8951 y(Before)463 b(a)g(callback)h(blocks)g(the)f(caller)f
+(by)i(returning)f Fo(-FUSD)p 28666 8951 V 399 w(NOREPLY)p
+Fn(,)h(it)d(must)i(sa)-22 b(v)-17 b(e)463 b(the)g Fo(fusd)p
+45008 8951 V 399 w(file)p 48063 8951 V 400 w(info)863
+10280 y Fn(pointer)402 b(that)f(w)-11 b(as)401 b(pro)-17
+b(vided)404 b(to)d(the)g(callback)i(as)e(its)f(\002rst)g(ar)-20
+b(gument.)718 b(Later)-44 b(,)432 b(when)402 b(an)g(e)-28
+b(v)-17 b(ent)403 b(occurs)f(which)g(allo)-28 b(ws)402
+b(the)863 11608 y(client')-61 b(s)275 b(block)-11 b(ed)277
+b(system)e(call)g(to)g(complete,)i(the)e(dri)-28 b(v)-17
+b(er)276 b(should)g(call)f Fo(fusd)p 31396 11608 V 399
+w(return\(\))p Fn(,)h(which)g(will)f(unblock)i(the)e(calling)863
+12936 y(process)j(and)g(complete)h(its)c(system)i(call.)344
+b Fo(fusd)p 20285 12936 V 399 w(return\(\))278 b Fn(tak)-11
+b(es)277 b(tw)-11 b(o)277 b(ar)-20 b(guments:)2524 15815
+y Fg(\017)554 b Fn(The)278 b Fo(fusd)p 8351 15815 V 399
+w(file)p 11406 15815 V 399 w(info)f Fn(pointer)h(that)f(the)h(callback)
+h(sa)-22 b(v)-17 b(ed)278 b(earlier;)f(and)2524 18029
+y Fg(\017)554 b Fn(The)392 b(system)g(call')-61 b(s)391
+b(return)h(v)-28 b(alue)393 b(\(in)f(other)g(w)-11 b(ords,)420
+b(the)392 b(v)-28 b(alue)394 b(that)e(w)-11 b(ould)392
+b(ha)-22 b(v)-17 b(e)394 b(been)g(returned)e(by)h(the)f(callback)3631
+19357 y(function)331 b(had)f(it)f(not)i(returned)f Fo(-FUSD)p
+19670 19357 V 400 w(NOREPLY)p Fn(\).)g(FUSD)h(itself)d
+Fk(does)j(not)f Fn(e)-17 b(xamine)332 b(the)e(return)g(v)-28
+b(alue)332 b(passed)e(as)3631 20685 y(the)384 b(second)i(ar)-20
+b(gument)386 b(to)f Fo(fusd)p 17338 20685 V 399 w(return)p
+Fn(;)438 b(it)384 b(simply)g(propag)-6 b(ates)387 b(that)e(v)-28
+b(alue)386 b(back)g(to)e(the)h(k)-11 b(ernel)385 b(as)f(the)h(return)
+3631 22014 y(v)-28 b(alue)278 b(of)f(the)h(block)-11
+b(ed)279 b(system)e(call.)2524 24892 y(Dri)-28 b(v)-17
+b(ers)335 b(should)g(ne)-28 b(v)-17 b(er)337 b(call)e
+Fo(fusd)p 16824 24892 V 399 w(return)h Fn(more)f(than)h(once)g(on)g(a)e
+(single)h Fo(fusd)p 36719 24892 V 400 w(file)p 39775
+24892 V 399 w(info)g Fn(pointer)-61 b(.)517 b(Doing)336
+b(so)863 26220 y(will)276 b(ha)-22 b(v)-17 b(e)279 b(unde\002ned)i
+(results,)275 b(similar)h(to)h(calling)h Fo(free\(\))g
+Fn(twice)f(on)h(the)f(same)h(pointer)-61 b(.)2524 28213
+y(It)240 b(also)h(bears)h(repeating)h(that)f(a)g(callback)h(can)g(call)
+e Fk(either)h Fn(call)f(fusd)p 29218 28213 V 399 w(return\(\))g(e)-17
+b(xplicitly)242 b Fk(or)g Fn(return)f(a)h(normal)g(return)f(v)-28
+b(alue)863 29541 y(\(i.e.,)276 b(not)h Fo(-FUSD)p 8215
+29541 V 400 w(NOREPLY)p Fn(\),)g(b)-22 b(ut)277 b(not)h(both.)2524
+31533 y Fo(-FUSD)p 5910 31533 V 399 w(NOREPLY)336 b Fn(and)h
+Fo(fusd)p 15883 31533 V 399 w(return\(\))g Fn(mak)-11
+b(e)337 b(it)e(easy)h(for)f(a)h(dri)-28 b(v)-17 b(er)336
+b(to)g(block)h(a)f(process,)350 b(then)337 b(unblock)g(it)e(later)863
+32862 y(when)235 b(data)f(becomes)h(a)-22 b(v)-28 b(ailable.)331
+b(When)234 b(the)g(callback)h(returns)e Fo(-FUSD)p 29305
+32862 V 399 w(NOREPLY)p Fn(,)h(the)g(dri)-28 b(v)-17
+b(er)234 b(is)e(freed)i(up)g(to)f(w)-11 b(ait)233 b(for)f(other)863
+34190 y(e)-28 b(v)-17 b(ents,)283 b(e)-28 b(v)-17 b(en)283
+b(though)f(the)f(process)g(making)h(the)e(system)h(call)f(is)g(still)e
+(block)-11 b(ed.)356 b(The)281 b(dri)-28 b(v)-17 b(er)281
+b(can)h(then)f(w)-11 b(ait)280 b(for)g(something)i(to)863
+35518 y(happen)299 b(that)e(unblocks)i(the)e(original)g(caller)-22
+b(\227for)296 b(e)-17 b(xample,)304 b(another)298 b(FUSD)f(e)-28
+b(v)-17 b(ent,)304 b(data)297 b(from)f(a)h(serial)f(port,)301
+b(or)c(data)g(from)863 36847 y(the)314 b(netw)-11 b(ork.)453
+b(\(Recall)314 b(from)f(Section)i(7)e(that)h(a)f(FUSD)i(dri)-28
+b(v)-17 b(er)314 b(can)g(simultaneously)h(w)-11 b(ait)313
+b(for)g(both)h(FUSD)g(and)h(non-FUSD)863 38175 y(e)-28
+b(v)-17 b(ents.\))2524 40168 y(FUSD)435 b(includes)g(an)g(e)-17
+b(xample)437 b(program,)474 b Fo(pager.c)p Fn(,)h(which)435
+b(demonstrates)g(these)g(techniques.)817 b(The)436 b(pager)f(dri)-28
+b(v)-17 b(er)863 41496 y(implements)301 b(a)f(simple)g(noti\002cation)i
+(interf)-11 b(ace)301 b(which)g(lets)e(an)-17 b(y)301
+b(number)h(of)d(\223w)-11 b(aiters\224)301 b(w)-11 b(ait)300
+b(for)f(a)h(signal)g(from)g(a)g(\223noti\002er)-61 b(.)-77
+b(\224)863 42824 y(All)257 b(the)h(w)-11 b(aiters)258
+b(w)-11 b(ait)257 b(by)i(trying)f(to)f(read)i(from)e
+Fo(/dev/pager/notify)p Fn(.)340 b(Those)259 b(reads)f(will)f(block)i
+(until)e(a)h(noti\002er)h(writes)863 44153 y(the)377
+b(string)f Fo(page)h Fn(to)g Fo(/dev/pager/input)p Fn(.)644
+b(It')-61 b(s)375 b(easy)i(to)g(try)f(the)h(application)i(out\227run)e
+(the)g(dri)-28 b(v)-17 b(er)-44 b(,)402 b(and)378 b(then)f(open)863
+45481 y(three)313 b(other)f(shells.)448 b(In)312 b(tw)-11
+b(o)312 b(of)g(them,)322 b(type)313 b Fo(cat)664 b(/dev/pager/notify)p
+Fn(.)452 b(The)313 b(reads)f(will)g(block.)449 b(Then,)323
+b(in)312 b(the)g(third)863 46809 y(shell,)277 b(type)h
+Fo(echo)664 b(page)h(>)g(/dev/pager/input)p Fn(\227the)280
+b(other)d(tw)-11 b(o)277 b(shells)g(should)h(become)h(unblock)-11
+b(ed.)2524 48802 y(Let')-61 b(s)276 b(tak)-11 b(e)278
+b(a)f(look)h(at)f(ho)-28 b(w)278 b(this)f(application)i(is)d
+(implemented,)j(step)e(by)g(step.)863 52413 y Fl(8.2.1)1108
+b(K)-28 b(eeping)279 b(P)-22 b(er)-41 b(-Client)278 b(State)863
+55153 y Fn(The)351 b(\002rst)f(thing)h(to)f(notice)h(about)h
+Fo(pager.c)f Fn(is)e(that)h(it)g(k)-11 b(eeps)351 b Fk(per)-22
+b(-client)351 b(state)p Fn(.)562 b(That)351 b(is,)367
+b(for)350 b(e)-28 b(v)-17 b(ery)352 b(\002le)f(descriptor)f(open)863
+56482 y(to)295 b(the)g(dri)-28 b(v)-17 b(er)-44 b(,)299
+b(a)c(structure)g(is)f(allocated)i(that)f(has)g(information)h(relating)
+f(to)f(that)h(\002le)g(descriptor)-61 b(.)397 b(Pre)-28
+b(vious)296 b(dri)-28 b(v)-17 b(er)296 b(e)-17 b(xamples)863
+57810 y(were,)315 b(for)306 b(the)h(most)f(part,)314
+b Fk(r)-41 b(eactive)p Fn(\227the)-17 b(y)310 b(recei)-28
+b(v)-17 b(ed)309 b(requests,)314 b(and)308 b(immediately)h(generated)g
+(responses.)433 b(Since)308 b(there)f(w)-11 b(as)863
+59138 y(ne)-28 b(v)-17 b(er)335 b(more)f(than)g(one)g(request)g
+(outstanding,)348 b(there)334 b(w)-11 b(as)333 b(no)h(need)h(to)e(k)-11
+b(eep)334 b(a)f(list)f(of)h(them.)512 b(The)334 b(pager)g(application)h
+(is)e(the)863 60467 y(\002rst)304 b(one)i(that)e(must)g(k)-11
+b(eep)307 b(track)e(of)f(an)h(arbitrary)g(number)h(of)e(requests)h
+(that)f(might)h(be)h(outstanding)g(at)e(the)h(same)g(time.)426
+b(The)863 61795 y(\002rst)271 b(e)-17 b(xcerpt)274 b(of)e
+Fo(pager.c)p Fn(,)h(which)g(appears)h(in)e(Program)h(11,)g(sho)-28
+b(ws)272 b(the)h(code)g(which)g(creates)g(this)e(per)-22
+b(-client)272 b(state.)342 b(Lines)863 63123 y(1\2266)240
+b(de\002ne)h(a)e(structure,)246 b Fo(pager)p 14218 63123
+V 400 w(client)p Fn(,)g(which)240 b(k)-11 b(eeps)240
+b(all)f(the)g(information)h(we)f(need)h(about)g(each)h(client)e
+(attached)i(to)e(the)863 64452 y(dri)-28 b(v)-17 b(er)-61
+b(.)341 b(The)269 b Fo(open)f Fn(callback)i(for)d Fo(/dev/pager/notify)
+p Fn(,)272 b(sho)-28 b(wn)269 b(on)f(lines)g(12\22631,)j(allocates)e
+(memory)f(for)f(an)i(instance)863 65780 y(of)342 b(this)f(structure)g
+(and)i(adds)g(it)e(to)g(a)h(link)-11 b(ed)343 b(list.)536
+b(\(If)340 b(the)i(memory)h(allocation)h(f)-11 b(ails,)356
+b(an)343 b(error)e(is)g(returned)h(to)g(the)g(client)g(on)863
+67108 y(line)300 b(18;)311 b(this)298 b(will)h(pre)-28
+b(v)-17 b(ent)301 b(the)f(\002le)g(from)f(opening.\))412
+b(Note)300 b(on)g(line)g(25)g(that)f(we)h(use)g(the)g
+Fo(private)p 41394 67108 V 399 w(data)g Fn(\002eld)g(to)g(store)f(a)863
+68437 y(pointer)249 b(to)f(the)g(client)g(state;)257
+b(this)247 b(allo)-28 b(ws)249 b(the)f(structure)g(to)g(be)g(retrie)-28
+b(v)-17 b(ed)250 b(when)f(later)f(callbacks)h(on)g(this)e(\002le)h
+(descriptor)g(arri)-28 b(v)-17 b(e.)863 69765 y(The)278
+b(memory)g(is)e(deallocated)k(when)f(the)e(\002le)g(is)g(closed;)g(we')
+-11 b(ll)277 b(see)g(that)g(in)g(a)g(later)g(section.)25405
+74071 y(25)p eop
+%%Page: 26 29
+26 28 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(11)e Fn(pager)-61 b(.c)278 b(\(P)-17 b(art)277 b(1\):)343
+b(Creating)278 b(state)f(for)f(e)-28 b(v)-17 b(ery)279
+b(client)f(using)f(the)h(dri)-28 b(v)-17 b(er)p 863 3445
+50191 45 v 365 4400 a Fi(1)1661 b Fo(/*)664 b(per-client)i(structure)g
+(to)e(keep)h(track)g(of)g(who)g(has)f(an)h(open)g(FD)f(to)h(us)f(*/)
+2524 5728 y(struct)h(pager_client)h({)3852 7057 y(int)f
+(last_page_seen;)1995 b(/*)664 b(seq.)h(no.)g(of)f(last)h(page)g(this)g
+(client)g(has)g(seen)g(*/)3852 8385 y(struct)g(fusd_file_info)i(*read;)
+e(/*)g(outstanding)h(read)f(request,)g(if)g(any)f(*/)365
+9714 y Fi(5)2989 b Fo(struct)665 b(fusd_file_info)i(*polldiff;)f(/*)e
+(outstanding)i(polldiff)g(request)f(*/)3852 11042 y(struct)g
+(pager_client)h(*next;)1994 b(/*)665 b(to)f(construct)i(the)f(linked)g
+(list)g(*/)2524 12370 y(};)2524 15027 y(struct)g(pager_client)h
+(*client_list)g(=)f(NULL;)g(/*)f(list)h(of)g(clients)g(\(open)g(FDs\))g
+(*/)-133 16355 y Fi(10)1661 b Fo(int)664 b(last_page)i(=)e(0;)h(/*)f
+(seq.)h(no.)g(of)g(the)f(most)h(recent)g(page)g(to)g(arrive)g(*/)2524
+19012 y(/*)f(open)h(on)g(/dev/pager/notify:)i(create)e(state)g(for)g
+(this)g(client)g(*/)2524 20340 y(static)g(int)g
+(pager_notify_open\(struct)j(fusd_file_info)f(*file\))2524
+21669 y({)-133 22997 y Fi(15)2989 b Fo(/*)664 b(create)i(state)f(for)f
+(this)h(client)h(*/)3852 24325 y(struct)f(pager_client)h(*c)f(=)f
+(malloc\(sizeof\(struct)k(pager_client\)\);)3852 26982
+y(if)c(\(c)h(==)g(NULL\))5180 28310 y(return)h(-ENOBUFS;)-133
+29639 y Fi(20)3852 30967 y Fo(/*)e(initialize)i(fields)g(of)e(this)h
+(client)g(state)g(*/)3852 32296 y(memset\(c,)h(0,)e(sizeof\(struct)j
+(pager_client\)\);)3852 33624 y(c->last_page_seen)g(=)d(last_page;)-133
+36281 y Fi(25)2989 b Fo(/*)664 b(save)h(the)g(pointer)g(to)g(this)g
+(state)g(so)f(it)h(gets)g(returned)g(to)g(us)g(later)g(*/)3852
+37609 y(file->private_data)i(=)e(c;)3852 40266 y(/*)f(add)h(this)g
+(client)g(to)g(the)g(client)g(list)g(*/)3852 41594 y(c->next)g(=)g
+(client_list;)-133 42922 y Fi(30)2989 b Fo(client_list)666
+b(=)e(c;)3852 45579 y(return)h(0;)2524 46907 y(})p 863
+48402 V 2524 51723 a Fn(Another)253 b(thing)g(to)g(notice)h(about)f
+(the)g(open)i(callback)f(is)e(the)h(use)g(of)f(the)h
+Fo(last)p 33489 51723 333 45 v 399 w(page)p 36544 51723
+V 399 w(seen)g Fn(v)-28 b(ariable.)337 b(The)253 b(dri)-28
+b(v)-17 b(er)254 b(gi)-28 b(v)-17 b(es)863 53051 y(a)301
+b(sequence)i(number)f(to)f(e)-28 b(v)-17 b(ery)302 b(page)g(it)e(recei)
+-28 b(v)-17 b(es;)314 b Fo(last)p 23800 53051 V 399 w(page)p
+26855 53051 V 399 w(seen)301 b Fn(stores)f(the)h(number)h(of)e(the)h
+(most)g(recent)g(page)h(seen)863 54379 y(by)366 b(a)g(client.)608
+b(When)367 b(a)e(ne)-28 b(w)367 b(client)f(arri)-28 b(v)-17
+b(es)366 b(\(i.e.,)386 b(it)364 b(opens)j Fo(/dev/pager/notify)p
+Fn(\),)390 b(its)364 b Fo(last)p 41511 54379 V 399 w(page)p
+44566 54379 V 399 w(seen)i Fn(state)f(is)863 55708 y(set)311
+b(equal)h(to)f(the)g(page)h(that)f(has)h(most)e(recently)i(arri)-28
+b(v)-17 b(ed;)329 b(this)310 b(forces)h(a)g(ne)-28 b(w)312
+b(client)g(to)e(w)-11 b(ait)311 b(for)f(the)i Fk(ne)-22
+b(xt)311 b Fn(page,)321 b(rather)311 b(than)863 57036
+y(immediately)279 b(being)f(noti\002ed)h(of)d(a)i(page)g(that)f(has)h
+(arri)-28 b(v)-17 b(ed)278 b(in)f(the)g(past.)863 60648
+y Fl(8.2.2)1108 b(Blocking)279 b(and)f(completing)h(r)-20
+b(eads)863 63387 y Fn(The)418 b(ne)-17 b(xt)419 b(part)e(of)g
+Fo(pager.c)h Fn(is)e(sho)-28 b(wn)418 b(in)f(Program)h(12.)764
+b(The)418 b Fo(pager)p 31463 63387 V 400 w(notify)p 35847
+63387 V 399 w(read)g Fn(function)g(seen)g(on)f(line)h(1)f(is)863
+64716 y(re)-17 b(gistered)405 b(as)f(the)g Fo(read)h
+Fn(callback)h(for)d(the)i Fo(/dev/pager/notify)i Fn(de)-28
+b(vice.)726 b(It)403 b(blocks)i(the)g(read)f(request)h(using)g(the)863
+66044 y(technique)278 b(we)e(described)h(earlier:)342
+b(it)275 b(stores)f(the)i Fo(fusd)p 23100 66044 V 399
+w(file)p 26155 66044 V 399 w(info)g Fn(pointer)g(in)g(that)f(client')
+-61 b(s)275 b(state)g(structure,)h(and)g(returns)863
+67372 y Fo(-FUSD)p 4249 67372 V 400 w(NOREPLY)p Fn(.)309
+b(\(Note)h(that)f(the)h(pointer)g(to)f(the)g(client')-61
+b(s)309 b(state)g(structure)g(comes)h(from)f(the)h Fo(private)p
+44555 67372 V 400 w(data)f Fn(\002eld)h(of)863 68701
+y Fo(fusd)p 3585 68701 V 399 w(file)p 6640 68701 V 400
+w(info)p Fn(,)277 b(where)h(the)f(open)i(callback)g(stored)e(it.\))
+25405 74071 y(26)p eop
+%%Page: 27 30
+27 29 bop 863 3167 50191 89 v 863 4151 a Fl(Pr)-20 b(ogram)280
+b(12)e Fn(pager)-61 b(.c)278 b(\(P)-17 b(art)277 b(2\):)343
+b(Block)278 b(clients')f Fo(read)g Fn(requests,)g(and)i(later)d
+(completing)j(the)f(block)-11 b(ed)279 b(reads)p 863
+4656 50191 45 v 365 5611 a Fi(1)1661 b Fo(ssize_t)665
+b(pager_notify_read\(struct)j(fusd_file_info)f(*file,)e(char)g
+(*buffer,)19792 6940 y(size_t)g(len,)g(loff_t)h(*offset\))2524
+8268 y({)3852 9597 y(struct)f(pager_client)h(*c)f(=)f(\(struct)i
+(pager_client)g(*\))f(file->private_data;)365 10925 y
+Fi(5)3852 12253 y Fo(if)f(\(c)h(==)g(NULL)f(||)h(c->read)g(!=)g(NULL\))
+g({)5180 13582 y(fprintf\(stderr,)i("pager_read's)g(arguments)e(are)g
+(confusd,)h(alas"\);)5180 14910 y(return)g(-EINVAL;)3852
+16238 y(})-133 17567 y Fi(10)3852 18895 y Fo(c->read)f(=)g(file;)3852
+20223 y(pager_notify_complete_read\(c\);)3852 21552 y(return)g
+(-FUSD_NOREPLY;)2524 22880 y(})-133 24208 y Fi(15)2524
+25537 y Fo(void)f(pager_notify_complete_read\(struct)670
+b(pager_client)d(*c\))2524 26865 y({)3852 28193 y(/*)d(if)h(there)g(is)
+g(no)f(outstanding)i(read,)f(do)g(nothing)g(*/)3852 29522
+y(if)f(\(c)h(==)g(NULL)f(||)h(c->read)g(==)g(NULL\))-133
+30850 y Fi(20)4317 b Fo(return;)3852 33507 y(/*)664 b(if)h(there)g(are)
+g(no)f(outstanding)i(pages,)g(do)e(nothing)i(*/)3852
+34835 y(if)e(\(c->last_page_seen)k(>=)c(last_page\))5180
+36164 y(return;)-133 37492 y Fi(25)3852 38820 y Fo(/*)g(bring)i(this)e
+(client)i(up)e(to)h(date)g(with)f(the)h(most)g(recent)g(page)g(*/)3852
+40149 y(c->last_page_seen)i(=)d(last_page;)3852 42805
+y(/*)g(and)h(notify)g(the)g(client)g(by)g(unblocking)h(the)f(read)f
+(\(read)h(returns)h(0\))e(*/)-133 44134 y Fi(30)2989
+b Fo(fusd_return\(c->read,)668 b(0\);)3852 45462 y(c->read)d(=)g(NULL;)
+2524 46790 y(})2524 49447 y(ssize_t)g(pager_input_write\(struct)j
+(fusd_file_info)f(*file,)-133 50775 y Fi(35)18929 b Fo(const)665
+b(char)g(*buffer,)h(size_t)f(len,)g(loff_t)g(*offset\))2524
+52104 y({)3852 53432 y(struct)g(pager_client)h(*c;)3852
+56089 y(/*)e(...)h(*/)-133 57417 y Fi(40)3852 58746 y
+Fo(CASE\("page"\))h({)5180 60074 y(last_page++;)5180
+62731 y(for)f(\(c)g(=)f(client_list;)i(c)f(!=)f(NULL;)h(c)f(=)h
+(c->next\))g({)-133 64059 y Fi(45)5646 b Fo
+(pager_notify_complete_polldiff\(c\);)6509 65387 y
+(pager_notify_complete_read\(c\);)5180 66716 y(})3852
+68044 y(})p 863 69538 V 25405 74071 a Fn(27)p eop
+%%Page: 28 31
+28 30 bop 2524 2974 a Fo(pager)p 5910 2974 333 45 v 399
+w(notify)p 10293 2974 V 400 w(complete)p 16005 2974 V
+399 w(read)274 b Fk(unbloc)-22 b(ks)274 b Fn(pre)-28
+b(viously)274 b(block)-11 b(ed)275 b(reads.)342 b(This)273
+b(function)h(\002rst)e(checks)i(to)f(see)g(that)863 4302
+y(there)301 b(is,)306 b(in)300 b(f)-11 b(act,)306 b(a)301
+b(block)-11 b(ed)303 b(read)e(\(line)g(19\).)414 b(It)300
+b(then)h(checks)i(to)d(see)h(if)f(a)h(page)h(has)f(arri)-28
+b(v)-17 b(ed)302 b(that)e(the)h(client)g(hasn')-20 b(t)301
+b(seen)h(yet)863 5631 y(\(line)329 b(23\).)499 b(Finally)-72
+b(,)342 b(it)328 b(updates)j(the)e(client)g(state)g(and)h(unblocks)h
+(the)e(block)-11 b(ed)331 b(read)f(by)g(calling)f Fo(fusd)p
+42126 5631 V 399 w(return)p Fn(.)500 b(Note)330 b(the)863
+6959 y(second)258 b(ar)-20 b(gument)257 b(to)f Fo(fusd)p
+12448 6959 V 399 w(return)h Fn(is)e(a)h(0;)263 b(as)255
+b(we)h(sa)-17 b(w)257 b(in)e(Section)j(4.3,)i(a)c(0)g(return)g(v)-28
+b(alue)257 b(to)f(a)g Fo(read)g Fn(system)g(call)g(means)863
+8287 y(EOF)-89 b(.)278 b(\(The)g(system)f(call)g(will)f(be)i(unblock)
+-11 b(ed)280 b(re)-17 b(g)-6 b(ardless)278 b(of)f(the)g(return)g(v)-28
+b(alue.\))2524 10280 y Fo(pager)p 5910 10280 V 399 w(notify)p
+10293 10280 V 400 w(complete)p 16005 10280 V 399 w(read)383
+b Fn(is)f(called)h(e)-28 b(v)-17 b(ery)385 b(time)d(a)h(ne)-28
+b(w)384 b(page)g(arri)-28 b(v)-17 b(es.)660 b(Ne)-28
+b(w)383 b(pages)h(are)e(processed)i(by)863 11608 y Fo(pager)p
+4249 11608 V 400 w(input)p 7969 11608 V 399 w(write)301
+b Fn(\(line)f(34\),)306 b(which)301 b(is)f(the)g Fo(write)h
+Fn(callback)i(for)c Fo(/dev/pager/input)p Fn(.)416 b(After)300
+b(recording)i(the)863 12936 y(f)-11 b(act)294 b(that)g(a)h(ne)-28
+b(w)295 b(page)h(has)e(arri)-28 b(v)-17 b(ed,)299 b(it)294
+b(calls)g Fo(pager)p 22092 12936 V 399 w(notify)p 26475
+12936 V 399 w(complete)p 32186 12936 V 400 w(read)h Fn(for)e(each)j
+(client)f(that)f(has)g(an)h(open)h(\002le)863 14265 y(descriptor)-61
+b(.)377 b(This)288 b(will)f(complete)i(the)g(reads)f(of)g(an)-17
+b(y)289 b(clients)f(who)h(ha)-22 b(v)-17 b(e)290 b(not)e(yet)h(seen)g
+(this)e(ne)-28 b(w)289 b(data,)i(and)e(ha)-22 b(v)-17
+b(e)290 b(no)f(ef)-28 b(fect)288 b(on)863 15593 y(clients)277
+b(that)g(don')-20 b(t)278 b(ha)-22 b(v)-17 b(e)279 b(outstanding)g
+(reads.)2524 17586 y(There)433 b(is)f(another)i(interesting)f(point)h
+(to)e(notice)i(about)g Fo(pager)p 28984 17586 V 399 w(notify)p
+33367 17586 V 400 w(read)p Fn(.)811 b(On)433 b(line)f(12,)472
+b(after)433 b(it)f(stores)g(the)863 18914 y(block)-11
+b(ed)309 b(system)e(call')-61 b(s)306 b(pointer)-44 b(,)314
+b(b)-22 b(ut)307 b(before)g(we)g(return)g Fo(-FUSD)p
+27135 18914 V 400 w(NOREPLY)p Fn(,)g(it)f(calls)g(the)h(completion)i
+(function.)433 b(This)307 b(has)863 20242 y(the)357 b(ef)-28
+b(fect)358 b(of)e(returning)i(an)-17 b(y)358 b(data)g(that)f(might)g
+(already)h(be)f(a)-22 b(v)-28 b(ailable)359 b(back)g(to)d(the)h(caller)
+g(immediately)-72 b(.)584 b(If)356 b(that)h(happens,)863
+21571 y(we)282 b(will)f(end)i(up)f(calling)h Fo(fusd)p
+13724 21571 V 399 w(return)g Fk(befor)-41 b(e)282 b Fn(we)h(return)e
+Fo(-FUSD)p 29275 21571 V 400 w(NOREPLY)p Fn(.)h(This)g(probably)h
+(seems)f(strange,)i(b)-22 b(ut)281 b(it')-61 b(s)863
+22899 y(le)-17 b(g)-6 b(al.)608 b(Recall)366 b(that)f(a)g(callback)i
+(can)f(call)f(fusd)p 19839 22899 V 399 w(return\(\))f(e)-17
+b(xplicitly)366 b Fk(or)f Fn(return)g(a)g(normal)g(\(not)g
+Fo(-FUSD)p 42694 22899 V 400 w(NOREPLY)p Fn(\))g(return)863
+24227 y(v)-28 b(alue,)279 b(b)-22 b(ut)277 b(not)g(both;)h(the)f(order)
+h(doesn')-20 b(t)277 b(matter)-61 b(.)863 27839 y Fl(8.2.3)1108
+b(Using)277 b Fo(fusd)p 9889 27839 V 399 w(destroy\(\))i
+Fl(to)e(clean)h(up)g(client)f(state)863 30579 y Fn(Finally)-72
+b(,)268 b(let')-61 b(s)264 b(tak)-11 b(e)266 b(a)f(look)h(at)f(one)i
+(last)d(aspect)i(of)f(the)g(pager)i(program:)338 b(ho)-28
+b(w)266 b(it)e(cleans)i(up)g(the)g(per)-22 b(-client)265
+b(state)g(when)h(a)g(client)863 31907 y(lea)-22 b(v)-17
+b(es.)331 b(This)234 b(is)g(mostly)h(straightforw)-11
+b(ard,)243 b(with)235 b(one)h(e)-17 b(xception:)325 b(a)236
+b(client)f(may)h(ha)-22 b(v)-17 b(e)237 b(an)e(outstanding)i(read)f
+(request)f(out)h(when)863 33235 y(a)309 b(close)g(request)g(comes)g
+(in.)437 b(Normally)-72 b(,)317 b(a)309 b(client)f(can')-20
+b(t)309 b(mak)-11 b(e)310 b(another)g(system)e(call)h(request)g(while)f
+(a)h(pre)-28 b(vious)310 b(system)e(call)863 34564 y(is)314
+b(still)f(block)-11 b(ed.)459 b(Ho)-28 b(we)g(v)-17 b(er)-44
+b(,)327 b(the)315 b Fo(close)h Fn(system)e(call)h(is)f(an)i(e)-17
+b(xception:)422 b(it)314 b(gets)g(called)i(when)h(a)e(client)g(dies)g
+(\(for)f(e)-17 b(xample,)863 35892 y(if)367 b(it)g(recei)-28
+b(v)-17 b(es)369 b(an)g(interrupt)f(signal\).)615 b(If)367
+b(a)h Fo(close)g Fn(comes)h(in)e(while)i(another)g(system)e(call)h(is)f
+(still)f(outstanding,)392 b(the)368 b(state)863 37220
+y(associated)263 b(with)e(the)h(outstanding)h(request)f(should)g(be)g
+(freed)g(to)f(a)-22 b(v)g(oid)263 b(a)e(memory)i(leak.)339
+b(The)262 b Fo(fusd)p 41121 37220 V 399 w(destroy)g Fn(function)h(is)
+863 38549 y(used)278 b(to)f(do)h(this,)e(seen)h(on)h(linen)g(12-14)g
+(of)f(Program)h(13.)863 42382 y Fj(8.3)1329 b(Retrie)-20
+b(ving)332 b(a)h(block)-13 b(ed)330 b(system)h(call')-49
+b(s)333 b(ar)-13 b(guments)330 b(fr)-24 b(om)332 b(a)g
+Fc(fusd)p 37031 42382 399 45 v 479 w(file)p 40698 42382
+V 478 w(info)g Fj(pointer)863 45121 y Fn(In)341 b(the)h(pre)-28
+b(vious)343 b(section,)358 b(we)342 b(sho)-28 b(wed)343
+b(ho)-28 b(w)342 b(the)g Fo(fusd)p 23678 45121 333 45
+v 399 w(return)g Fn(function)h(can)f(be)h(used)f(to)f(specify)h(the)g
+(return)f(v)-28 b(alue)343 b(of)863 46450 y(a)305 b(system)g(call)g
+(that)g(w)-11 b(as)305 b(pre)-28 b(viously)306 b(block)-11
+b(ed.)430 b(Ho)-28 b(we)g(v)-17 b(er)-44 b(,)314 b(man)-17
+b(y)306 b(system)f(calls)g(ha)-22 b(v)-17 b(e)306 b(side)f(ef)-28
+b(fects)305 b(in)g(addition)h(to)f(returning)863 47778
+y(a)316 b(v)-28 b(alue\227for)316 b(e)-17 b(xample,)327
+b(in)316 b(a)f Fo(read\(\))h Fn(request,)326 b(the)315
+b(data)h(being)h(returned)g(has)e(to)g(be)h(copied)i(into)d(the)h
+(caller')-61 b(s)315 b(b)-22 b(uf)-28 b(fer)-61 b(.)458
+b(T)-89 b(o)863 49106 y(f)-11 b(acilitate)356 b(this,)376
+b(FUSD)357 b(pro)-17 b(vides)358 b(accessor)f(functions)h(that)e(let)g
+(dri)-28 b(v)-17 b(ers)357 b(retrie)-28 b(v)-17 b(e)358
+b(the)f(ar)-20 b(guments)357 b(that)g(had)h(been)g(passed)f(to)863
+50435 y(its)311 b(callbacks)i(at)f(the)g(time)g(the)g(call)g(w)-11
+b(as)312 b(originally)g(issued.)448 b(F)-17 b(or)312
+b(e)-17 b(xample,)323 b(the)312 b Fo(fusd)p 35959 50435
+V 400 w(get)p 38351 50435 V 399 w(read)p 41406 50435
+V 399 w(buffer\(\))h Fn(function)863 51763 y(will)349
+b(return)h(a)g(pointer)h(to)f(the)g(data)h(b)-22 b(uf)-28
+b(fer)350 b(that)h(is)e(pro)-17 b(vided)352 b(with)e
+Fo(read\(\))g Fn(callbacks.)564 b(Dri)-28 b(v)-17 b(ers)350
+b(can)h(use)g(these)f(accessor)863 53091 y(functions)278
+b(to)f(af)-28 b(fect)277 b(change)j(to)d(a)g(client)h
+Fk(befor)-41 b(e)278 b Fn(calling)f Fo(fusd)p 26296 53091
+V 400 w(return\(\))p Fn(.)2524 55084 y(The)h(follo)-28
+b(wing)278 b(accessor)g(functions)f(are)h(a)-22 b(v)-28
+b(ailable,)278 b(all)f(of)g(which)h(tak)-11 b(e)278 b(a)f(single)g
+Fo(fusd)p 37712 55084 V 400 w(file)p 40768 55084 V 399
+w(info)665 b(*)277 b Fn(ar)-20 b(gument:)2524 57962 y
+Fg(\017)554 b Fo(int)664 b(char)h(*fusd)p 12994 57962
+V 400 w(get)p 15386 57962 V 399 w(read)p 18441 57962
+V 399 w(buffer)p Fn(\227The)358 b(destination)g(b)-22
+b(uf)-28 b(fer)357 b(for)f(data)h(that)g(a)f(dri)-28
+b(v)-17 b(er)358 b(is)d(returning)i(to)g(a)3631 59290
+y(process)277 b(doing)h(a)g Fo(read\(\))p Fn(.)2524 61504
+y Fg(\017)554 b Fo(const)665 b(char)f(*fusd)p 14322 61504
+V 400 w(get)p 16714 61504 V 399 w(write)p 20433 61504
+V 399 w(buffer)p Fn(\227The)324 b(source)f(b)-22 b(uf)-28
+b(fer)323 b(containing)h(data)f(sent)f(to)g(the)g(dri)-28
+b(v)-17 b(er)323 b(by)g(a)3631 62833 y(process)277 b(doing)h(a)g
+Fo(write\(\))p Fn(.)2524 65047 y Fg(\017)554 b Fo(fusd)p
+6353 65047 V 399 w(get)p 8744 65047 V 399 w(length)p
+Fn(\227The)279 b(length)f(\(in)f(bytes\))g(of)f(the)i(b)-22
+b(uf)-28 b(fer)277 b(for)g(either)g(a)g Fo(read\(\))h
+Fn(or)f(a)g Fo(write\(\))p Fn(.)2524 67261 y Fg(\017)554
+b Fo(loff)p 6353 67261 V 399 w(t)664 b(fusd)p 10736 67261
+V 399 w(get)p 13127 67261 V 399 w(offset)p Fn(\227The)255
+b(\002le)e(descriptor')-61 b(s)252 b(byte)h(of)-28 b(fset,)257
+b(typically)d(used)f(in)f Fo(read\(\))h Fn(and)h Fo(write\(\))3631
+68589 y Fn(callbacks.)25405 74071 y(28)p eop
+%%Page: 29 32
+29 31 bop 863 1955 50191 89 v 863 2940 a Fl(Pr)-20 b(ogram)280
+b(13)e Fn(pager)-61 b(.c)278 b(\(P)-17 b(art)277 b(3\):)343
+b(Cleaning)279 b(up)f(when)g(a)f(client)h(lea)-22 b(v)-17
+b(es)p 863 3445 50191 45 v 365 4400 a Fi(1)1661 b Fo(/*)664
+b(close)h(on)g(/dev/pager/notify:)i(destroy)e(state)h(for)e(this)h
+(client)g(*/)2524 5728 y(static)g(int)g(pager_notify_close\(struct)j
+(fusd_file_info)f(*file\))2524 7057 y({)3852 8385 y(struct)e
+(pager_client)h(*c;)365 9714 y Fi(5)3852 11042 y Fo(if)e(\(\(c)h(=)g
+(\(struct)g(pager_client)h(*\))f(file->private_data\))i(!=)e(NULL\))g
+({)5180 13699 y(/*)g(take)g(this)g(client)g(off)g(our)f(client)h(list)g
+(*/)5180 15027 y(client_list_remove\(c\);)-133 16355
+y Fi(10)5180 17684 y Fo(/*)g(if)f(there)h(is)g(a)f(read)h(outstanding,)
+h(free)f(the)g(state)g(*/)5180 19012 y(if)g(\(c->read)g(!=)g(NULL\))g
+({)6509 20340 y(fusd_destroy\(c->read\);)6509 21669 y(c->read)g(=)f
+(NULL;)-133 22997 y Fi(15)4317 b Fo(})5180 24325 y(/*)665
+b(destroy)g(any)g(outstanding)h(polldiffs)g(*/)5180 25654
+y(if)f(\(c->polldiff)h(!=)f(NULL\))g({)6509 26982 y
+(fusd_destroy\(c->polldiff\);)6509 28310 y(c->polldiff)h(=)e(NULL;)-133
+29639 y Fi(20)4317 b Fo(})5180 32296 y(/*)665 b(get)g(rid)f(of)h(the)f
+(struct)i(*/)5180 33624 y(free\(c\);)5180 34952 y(file->private_data)i
+(=)c(NULL;)-133 36281 y Fi(25)2989 b Fo(})3852 37609
+y(return)665 b(0;)2524 38937 y(})p 863 40432 V 2524 43753
+a Fg(\017)554 b Fo(int)664 b(fusd)p 9009 43753 333 45
+v 399 w(get)p 11400 43753 V 400 w(ioctl)p 15120 43753
+V 399 w(request)p Fn(\227An)267 b(ioctl')-61 b(s)265
+b(request)h(\223command)j(number\224)f(\(i.e.,)e(the)h(\002rst)d(ar)-20
+b(gument)268 b(of)d(an)3631 45081 y(ioctl\).)2524 47295
+y Fg(\017)554 b Fo(int)664 b(fusd)p 9009 47295 V 399
+w(get)p 11400 47295 V 400 w(ioctl)p 15120 47295 V 399
+w(arg)p Fn(\227The)425 b(second)g(ar)-20 b(gument)425
+b(of)f(an)g(ioctl)f(for)g(non-data-bearing)k Fo(ioctl)d
+Fn(requests)3631 48623 y(\(i.e.,)p 5972 48623 V 674 w
+Fo(IO)277 b Fn(commands\).)2524 50837 y Fg(\017)554 b
+Fo(void)664 b(*fusd)p 10337 50837 V 400 w(get)p 12729
+50837 V 399 w(ioctl)p 16448 50837 V 399 w(buffer)p Fn(\227The)438
+b(data)f(b)-22 b(uf)-28 b(fer)436 b(for)f(data-bearing)j
+Fo(ioctl)f Fn(requests)f(\()p 45349 50837 V 398 w Fo(IOR)p
+Fn(,)p 48452 50837 V 834 w Fo(IOW)p Fn(,)3631 52165 y(and)p
+5572 52165 V 676 w Fo(IORW)278 b Fn(commands\).)2524
+54379 y Fg(\017)554 b Fo(int)664 b(fusd)p 9009 54379
+V 399 w(get)p 11400 54379 V 400 w(poll)p 14456 54379
+V 399 w(diff)p 17511 54379 V 399 w(cached)p 21894 54379
+V 399 w(state)p Fn(\227See)279 b(Section)g(9.)2524 57257
+y(W)-89 b(e)411 b(got)h(a)-17 b(w)-11 b(ay)412 b(without)g(using)f
+(these)h(accessor)f(functions)h(in)f(our)g Fo(pager.c)h
+Fn(e)-17 b(xample)414 b(because)f(the)e(pager)h(doesn')-20
+b(t)863 58586 y(actually)340 b(return)e(data\227it)h(just)e(blocks)i
+(and)h(unblocks)g Fo(read)f Fn(calls.)526 b(Ho)-28 b(we)g(v)-17
+b(er)-44 b(,)356 b(the)339 b(FUSD)g(distrib)-22 b(ution)338
+b(contains)i(another)863 59914 y(e)-17 b(xample)280 b(program,)d
+Fo(logring)p Fn(,)h(that)f(demonstrates)h(their)f(use.)2524
+61907 y Fo(logring)441 b Fn(mak)-11 b(es)442 b(it)e(easy)i(to)e(access)
+i(the)f(most)g(recent)g(\(and)h(only)g(the)f(most)f(recent\))i(output)g
+(from)e(a)h(process.)835 b(It)863 63235 y(w)-11 b(orks)433
+b(just)f(lik)-11 b(e)433 b Fo(tail)665 b(-f)433 b Fn(on)h(a)f(log)g
+(\002le,)472 b(e)-17 b(xcept)435 b(that)e(the)h(storage)f(required)h
+(ne)-28 b(v)-17 b(er)435 b(gro)-28 b(ws.)812 b(This)432
+b(can)i(be)g(useful)f(in)863 64563 y(embedded)345 b(systems)c(where)i
+(there)f(isn')-20 b(t)340 b(enough)k(memory)f(or)e(disk)h(space)h(for)e
+(k)-11 b(eeping)344 b(complete)f(log)f(\002les,)357 b(b)-22
+b(ut)342 b(the)g(most)863 65892 y(recent)278 b(deb)-22
+b(ugging)280 b(messages)e(are)f(sometimes)h(needed)h(\(e.g.,)e(after)f
+(an)i(error)f(is)f(observ)-17 b(ed\).)2524 67884 y Fo(logring)257
+b Fn(uses)e(FUSD)j(to)d(implement)j(a)e(character)h(de)-28
+b(vice,)263 b Fo(/dev/logring)p Fn(,)e(that)c(acts)f(lik)-11
+b(e)256 b(a)g(named)i(pipe)f(that)f(has)863 69213 y(a)298
+b(\002nite,)304 b(circular)298 b(b)-22 b(uf)-28 b(fer)-61
+b(.)407 b(The)299 b(size)f(of)g(the)h(b)-22 b(uf)-28
+b(fer)298 b(is)f(gi)-28 b(v)-17 b(en)300 b(as)e(a)g(command-line)j(ar)
+-20 b(gument.)408 b(As)298 b(more)g(data)h(is)e(written)h(into)25405
+74071 y(29)p eop
+%%Page: 30 33
+30 32 bop 863 2974 a Fn(the)356 b(b)-22 b(uf)-28 b(fer)-44
+b(,)374 b(the)355 b(oldest)g(data)h(is)e(discarded.)579
+b(A)355 b(process)g(that)g(reads)h(from)e(the)i(logring)g(de)-28
+b(vice)357 b(will)d(\002rst)g(read)i(the)f(e)-17 b(xisting)863
+4302 y(b)-22 b(uf)-28 b(fer)-44 b(,)277 b(then)h(block)g(and)g(see)g
+(ne)-28 b(w)278 b(data)g(as)f(it')-61 b(s)275 b(written,)i(similar)e
+(to)i(monitoring)h(a)g(log)f(\002le)g(using)h Fo(tail)665
+b(-f)p Fn(.)2524 6295 y(Y)-122 b(ou)380 b(can)g(run)f(this)f(e)-17
+b(xample)381 b(program)f(by)g(typing)g Fo(logring)665
+b(<logsize>)p Fn(,)406 b(where)380 b Fo(logsize)g Fn(is)e(the)h(size)g
+(of)g(the)863 7623 y(circular)337 b(b)-22 b(uf)-28 b(fer)337
+b(in)f(bytes.)523 b(Then,)353 b(type)337 b Fo(cat)665
+b(/dev/logring)338 b Fn(in)f(a)g(shell.)521 b(The)338
+b Fo(cat)f Fn(process)g(will)f(block,)352 b(w)-11 b(aiting)338
+b(for)863 8951 y(data.)403 b(From)297 b(another)h(shell,)j(write)296
+b(to)h(the)g(logring)g(\(e.g.,)k Fo(echo)665 b(Hi)f(there)h(>)g
+(/dev/logring)p Fn(\).)403 b(The)298 b Fo(cat)f Fn(process)863
+10280 y(will)276 b(see)i(the)f(message)h(appear)-61 b(.)2524
+12272 y(\(This)343 b(e)-17 b(xample)347 b(program)f(is)d(based)j(on)f
+Fk(emlo)-11 b(g)p Fn(,)362 b(a)344 b(\(real\))g(Linux)h(k)-11
+b(ernel)346 b(module)f(with)g(identical)g(functionality)-72
+b(.)547 b(If)343 b(you)863 13601 y(\002nd)278 b(logring)g(useful,)f(b)
+-22 b(ut)277 b(w)-11 b(ant)278 b(to)f(use)g(it)f(on)i(a)f(system)g
+(that)g(does)h(not)f(ha)-22 b(v)-17 b(e)279 b(FUSD,)f(check)h(out)f
+(the)f(original)h(emlog)47280 13199 y Ff(12)48111 13601
+y Fn(.\))863 18001 y Fm(9)1594 b(Implementing)399 b Fa(select)p
+Fm(able)i(De)-24 b(vices)863 21139 y Fn(One)376 b(important)g(feature)f
+(that)g(almost)g(e)-28 b(v)-17 b(ery)377 b(de)-28 b(vice)377
+b(dri)-28 b(v)-17 b(er)376 b(in)f(a)g(system)g(should)h(ha)-22
+b(v)-17 b(e)376 b(is)e(support)i(for)e(the)i Fo(select\(2\))863
+22467 y Fn(system)423 b(call.)780 b Fo(select)424 b Fn(allo)-28
+b(ws)423 b(clients)g(to)g(assemble)g(a)g(set)g(of)f(\002le)h
+(descriptors)g(and)h(ask)f(to)g(be)h(noti\002ed)g(when)g(one)g(of)863
+23796 y(them)287 b(becomes)i(readable)f(or)e(writable.)372
+b(This)287 b(simple)f(feature)h(is)f(decepti)-28 b(v)-17
+b(ely)290 b(po)-28 b(werful\227it)287 b(allo)-28 b(ws)286
+b(clients)h(to)f(w)-11 b(ait)287 b(for)e(an)-17 b(y)863
+25124 y(number)336 b(of)e(a)h(set)f(of)g(possible)g(e)-28
+b(v)-17 b(ents)336 b(to)f(occur)-61 b(.)516 b(This)335
+b(is)e(fundamentally)k(dif)-28 b(ferent)334 b(than)i(\(for)d(e)-17
+b(xample\))336 b(a)f(blocking)h(read,)863 26452 y(which)246
+b(only)g(unblocks)h(on)f(one)g(kind)g(of)e(e)-28 b(v)-17
+b(ent.)335 b(In)245 b(this)f(section,)252 b(we')-11 b(ll)244
+b(describe)i(ho)-28 b(w)246 b(FUSD)g(can)g(be)g(used)g(to)f(create)h(a)
+f(de)-28 b(vice)863 27781 y(whose)278 b(state)f(can)h(be)g(queried)g
+(by)g(a)f(client')-61 b(s)277 b(call)g(to)g Fo(select\(2\))p
+Fn(.)2524 29773 y(This)367 b(section)g(is)g(limited)f(to)h(a)h
+(discussion)f(what)h(a)f(FUSD)h(dri)-28 b(v)-17 b(er)368
+b(writer)f(needs)h(to)f(kno)-28 b(w)369 b(to)e(implement)h(a)g
+(selectable)863 31102 y(de)-28 b(vice.)346 b(Details)276
+b(of)h(the)g(FUSD)i(implementation)g(required)f(to)f(support)g(this)g
+(feature)g(are)h(described)g(in)f(Section)i(11.1)863
+34935 y Fj(9.1)1329 b(P)-27 b(oll)333 b(state)f(and)g(the)f
+Fc(poll)p 17203 34935 399 45 v 479 w(diff)h Fj(callback)863
+37674 y Fn(FUSD')-61 b(s)329 b(implementation)i(of)d(selectable)i(de)
+-28 b(vices)330 b(depends)h(on)e(the)g(concept)i(of)e
+Fk(poll)f(state)p Fn(.)498 b(A)328 b(\002le)h(descriptor')-61
+b(s)329 b(poll)f(state)863 39003 y(is)436 b(a)i(bitmask)g(that)f
+(describes)h(its)e(current)i(properties\227readable,)480
+b(writable,)d(or)437 b(e)-17 b(xception)440 b(raised.)824
+b(These)439 b(three)f(states)863 40331 y(correspond)279
+b(to)e Fo(select\(2\))p Fn(')-61 b(s)278 b(three)f Fo(fd)p
+18072 40331 333 45 v 399 w(set)p Fn(s.)343 b(FUSD)278
+b(has)f(constants)h(used)g(to)f(describe)h(these)g(states:)2524
+43209 y Fg(\017)554 b Fo(FUSD)p 6353 43209 V 399 w(NOTIFY)p
+10736 43209 V 399 w(INPUT)p Fn(\227Input)279 b(is)d(a)-22
+b(v)-28 b(ailable;)278 b(a)g(read)f(will)g(not)g(block.)2524
+45423 y Fg(\017)554 b Fo(FUSD)p 6353 45423 V 399 w(NOTIFY)p
+10736 45423 V 399 w(OUTPUT)p Fn(\227Output)279 b(space)f(is)f(a)-22
+b(v)-28 b(ailable;)278 b(a)f(write)g(will)f(not)i(block.)2524
+47637 y Fg(\017)554 b Fo(FUSD)p 6353 47637 V 399 w(NOTIFY)p
+10736 47637 V 399 w(EXCEPT)p Fn(\227An)279 b(e)-17 b(xception)280
+b(has)d(occurred.)2524 50515 y(These)c(constants)f(can)h(be)g(combined)
+h(with)e(C')-61 b(s)271 b(bitwise-or)g(operator)-61 b(.)343
+b(F)-17 b(or)272 b(e)-17 b(xample,)275 b(a)d(descriptor)h(that)e(is)g
+(both)i(readable)863 51843 y(and)370 b(writable)e(is)g(e)-17
+b(xpressed)370 b(as)e Fo(FUSD)p 16660 51843 V 400 w(NOTIFY)p
+21044 51843 V 399 w(INPUT)665 b(|)g(FUSD)p 29413 51843
+V 399 w(NOTIFY)p 33796 51843 V 399 w(OUTPUT)p Fn(.)369
+b(0)g(means)h(a)e(\002le)h(descriptor)g(is)863 53172
+y(not)278 b(readable,)g(not)g(writable,)f(and)h(not)f(in)g(the)h(e)-17
+b(xception)280 b(set.)2524 55164 y(F)-17 b(or)263 b(a)g(FUSD)g(de)-28
+b(vice)265 b(to)e(be)g(selectable,)k(its)261 b(dri)-28
+b(v)-17 b(er)263 b(must)g(implement)h(a)e(callback)j(called)f
+Fo(poll)p 40529 55164 V 399 w(diff)p Fn(.)339 b(This)262
+b(callback)j(is)863 56493 y(v)-17 b(ery)239 b(dif)-28
+b(ferent)237 b(than)i(the)f(others;)250 b(it)237 b(is)f(not)i(a)f
+(\223direct)h(line\224)h(between)g(the)f(client)g(and)g(the)g(dri)-28
+b(v)-17 b(er)238 b(as)f(is)g(the)h(case)g(with)f(a)h(call)f(such)863
+57821 y(as)323 b Fo(ioctl)p Fn(.)480 b(A)323 b(dri)-28
+b(v)-17 b(er')-61 b(s)323 b(response)g(to)g Fo(poll)p
+19068 57821 V 399 w(diff)g Fn(is)f Fk(not)h Fn(the)g(return)g(v)-28
+b(alue)324 b(seen)f(by)h(a)e(client')-61 b(s)323 b(call)g(to)f
+Fo(select)p Fn(.)481 b(When)863 59149 y(a)388 b(client)g(tries)f(to)g
+Fo(select)i Fn(on)f(a)g(set)f(of)h(\002le)g(descriptors,)415
+b(the)388 b(k)-11 b(ernel)389 b(collects)f(the)g(responses)g(from)g
+(all)f(the)h(appropriate)863 60478 y(callbacks\227)p
+Fo(poll)364 b Fn(for)c(\002le)h(descriptors)g(managed)j(by)e(k)-11
+b(ernel)362 b(dri)-28 b(v)-17 b(ers,)382 b(and)363 b
+Fo(poll)p 35062 60478 V 399 w(diff)e Fn(callbacks)i(those)e(managed)j
+(by)863 61806 y(FUSD)278 b(dri)-28 b(v)-17 b(ers\227and)279
+b(synthesizes)f(all)f(of)f(that)i(information)f(into)h(the)f(return)g
+(v)-28 b(alue)279 b(seen)f(by)f(the)h(client.)2524 63799
+y(FUSD)360 b(k)-11 b(eeps)361 b(a)f(cache)i(of)e(the)g(poll)g(state)g
+(it)f(has)h(most)f(recently)i(recei)-28 b(v)-17 b(ed)363
+b(from)c(each)j(FUSD)f(de)-28 b(vice)362 b(dri)-28 b(v)-17
+b(er)-44 b(,)380 b(initially)863 65127 y(assumed)241
+b(to)f(be)h(0.)331 b(This)239 b(state)h(is)f(returned)i(to)f(clients)g
+(trying)g(to)g Fo(select\(\))h Fn(on)f(de)-28 b(vices)242
+b(managed)h(by)d(those)h(dri)-28 b(v)-17 b(ers.)331 b(Under)863
+66455 y(certain)322 b(conditions,)333 b(FUSD)322 b(sends)f(a)g(query)h
+(to)f(the)g(dri)-28 b(v)-17 b(er)322 b(in)f(order)g(to)g(ensure)h(that)
+f(the)g(k)-11 b(ernel')-61 b(s)321 b(poll)g(state)g(cache)i(is)d(up)h
+(to)863 67784 y(date.)451 b(This)313 b(query)h(tak)-11
+b(es)313 b(the)g(form)f(of)h(a)g Fo(poll)p 20014 67784
+V 399 w(diff)g Fn(callback)i(acti)-28 b(v)g(ation,)323
+b(which)314 b(is)e(gi)-28 b(v)-17 b(en)315 b(a)e(single)f(ar)-20
+b(gument:)417 b(the)313 b(poll)p 863 68732 20076 45 v
+1738 69472 a Fe(12)2457 69785 y Fd(http://www)-58 b(.circlemud.or)-16
+b(g/jelson/softw)-9 b(are/emlog)25405 74071 y Fn(30)p
+eop
+%%Page: 31 34
+31 33 bop 863 2974 a Fn(state)245 b(that)f(FUSD)i(currently)g(has)f
+(cached.)335 b(The)245 b(dri)-28 b(v)-17 b(er)246 b(should)g(consult)f
+(its)e(internal)j(data)f(structures)f(to)h(determine)h(the)f(actual,)
+863 4302 y(current)278 b(poll)f(state)g(\(i.e.,)e(whether)k(or)e(not)g
+(b)-22 b(uf)-28 b(fers)277 b(ha)-22 b(v)-17 b(e)279 b(readable)g
+(data\).)343 b(Then:)2524 7180 y Fg(\017)554 b Fn(If)273
+b(the)i(FUSD)h(cache)g(is)e(incorrect)h(\(that)f(is,)g(the)h(current)g
+(true)g(poll)f(state)h(is)e(dif)-28 b(ferent)275 b(than)g(FUSD')-61
+b(s)275 b(cached)i(state\),)e(the)3631 8509 y(current)i(poll)g(state)g
+(should)h(be)g(returned)g(immediately)-72 b(.)2524 10723
+y Fg(\017)554 b Fn(If)398 b(the)i(FUSD)h(cache)h(is)d(up)h(to)g(date)h
+(\(that)e(is,)429 b(it)399 b(matches)i(the)f(real)g(current)h(state\),)
+429 b(the)400 b(callback)i(should)f(sa)-22 b(v)-17 b(e)401
+b(the)3631 12051 y Fo(fusd)p 6353 12051 333 45 v 399
+w(file)p 9408 12051 V 399 w(info)248 b Fn(pointer)h(and)g(return)f
+Fo(-FUSD)p 24152 12051 V 400 w(NOREPLY)p Fn(.)g(Later)-44
+b(,)254 b(when)c(the)e(poll)g(state)g(changes,)256 b(the)248
+b(dri)-28 b(v)-17 b(er)249 b(can)3631 13379 y(call)277
+b Fo(fusd)p 8228 13379 V 399 w(return\(\))h Fn(to)f(update)i(FUSD')-61
+b(s)277 b(cache.)2524 16257 y(In)442 b(other)i(w)-11
+b(ords,)484 b(when)445 b(a)e(dri)-28 b(v)-17 b(er')-61
+b(s)443 b Fo(poll)p 20338 16257 V 400 w(diff)g Fn(callback)i(is)d(acti)
+-28 b(v)g(ated,)487 b(the)443 b(k)-11 b(ernel)444 b(is)e(ef)-28
+b(fecti)g(v)-17 b(ely)446 b(saying)e(to)f(the)863 17586
+y(dri)-28 b(v)-17 b(er)-44 b(,)304 b(\223Here)298 b(is)f(what)i(I)e
+(think)h(the)h(current)f(poll)g(state)f(of)h(this)f(\002le)h
+(descriptor)h(is;)307 b(let)297 b(me)h(kno)-28 b(w)300
+b(when)f(that)f(state)g Fk(c)-17 b(hang)-11 b(es)p Fn(.)-77
+b(\224)863 18914 y(The)373 b(dri)-28 b(v)-17 b(er)373
+b(can)g(either)f(respond)h(immediately)g(\(if)e(the)h(k)-11
+b(ernel')-61 b(s)372 b(cache)i(is)d(already)i(kno)-28
+b(wn)374 b(to)e(be)h(out)f(of)f(date\),)396 b(or)372
+b(return)863 20242 y Fo(-FUSD)p 4249 20242 V 400 w(NOREPLY)278
+b Fn(if)f(no)i(update)g(is)e(immediately)i(necessary)-72
+b(.)347 b(Later)-44 b(,)278 b(when)h(the)f(poll)g(state)g(changes)i
+(\(for)d(e)-17 b(xample,)280 b(if)d(ne)-28 b(w)863 21571
+y(data)285 b(arri)-28 b(v)-17 b(es)284 b(that)f(mak)-11
+b(es)285 b(a)f(de)-28 b(vice)285 b(readable\),)i(the)d(dri)-28
+b(v)-17 b(er)284 b(can)h(used)f(its)f(sa)-22 b(v)-17
+b(ed)285 b Fo(fusd)p 35352 21571 V 399 w(file)p 38407
+21571 V 399 w(info)f Fn(pointer)g(to)g(send)g(a)g(poll)863
+22899 y(state)277 b(update)i(to)e(the)g(k)-11 b(ernel.)2524
+24892 y(When)336 b(a)g(FUSD)h(dri)-28 b(v)-17 b(er)336
+b(sends)g(a)g(poll)f(state)h(update,)352 b(it)334 b(might)i(\(or)f
+(might)h(not\))g(ha)-22 b(v)-17 b(e)337 b(the)f(ef)-28
+b(fect)336 b(of)g(w)-11 b(aking)337 b(up)f(a)g(client)863
+26220 y(that)322 b(w)-11 b(as)323 b(block)-11 b(ed)324
+b(in)e Fo(select\(2\))p Fn(.)480 b(On)322 b(the)h(same)g(note,)334
+b(it')-61 b(s)320 b(w)-11 b(orth)323 b(reiterating)f(that)g(a)h
+Fo(-FUSD)p 40693 26220 V 399 w(NOREPLY)g Fn(response)g(to)863
+27548 y(a)282 b Fo(poll)p 4358 27548 V 399 w(diff)g Fn(callback)h
+Fk(does)f(not)g Fn(necessarily)g(block)h(the)e(client\227other)i
+(descriptors)e(in)g(the)h(client')-61 b(s)281 b Fo(select)h
+Fn(set)f(might)863 28877 y(be)d(readable,)h(for)d(e)-17
+b(xample.)863 32710 y Fj(9.2)1329 b(Recei)-13 b(ving)332
+b(a)g Fc(poll)p 13969 32710 399 45 v 478 w(diff)g Fj(r)-24
+b(equest)331 b(when)g(the)h(pr)-24 b(e)k(vious)331 b(one)h(has)f(not)h
+(been)f(r)-24 b(etur)k(ned)330 b(y)-13 b(et)863 35449
+y Fn(Calls)307 b(such)i(as)e Fo(read)h Fn(and)h Fo(write)g
+Fn(are)f(synchronous)i(from)d(the)h(standpoint)h(of)f(an)g(indi)-28
+b(vidual)309 b(client\227a)g(request)f(is)f(made,)863
+36778 y(and)343 b(the)g(requester)g(blocks)g(until)f(a)g(reply)h(is)e
+(recei)-28 b(v)-17 b(ed.)541 b(This)342 b(means)h(that)f(there)h(can')
+-20 b(t)343 b(e)-28 b(v)-17 b(er)343 b(be)g(more)g(than)g(a)f(single)h
+Fo(read)863 38106 y Fn(request)268 b(outstanding)h(for)d(a)h(single)h
+(client)f(at)g(a)g(time.)340 b(\(The)268 b(dri)-28 b(v)-17
+b(er)268 b(as)e(a)i(whole)g(may)g(be)g(k)-11 b(eeping)269
+b(track)e(of)g(man)-17 b(y)269 b(outstanding)863 39434
+y Fo(read)278 b Fn(requests)f(in)g(parallel,)g(b)-22
+b(ut)277 b(no)h(tw)-11 b(o)277 b(of)g(them)g(will)g(be)g(from)g(the)g
+(same)h(client)f(\002le)h(descriptor)-61 b(.\))2524 41427
+y(As)288 b(we)h(mentioned)i(in)e(the)g(pre)-28 b(vious)290
+b(section,)j(the)c Fo(poll)p 25515 41427 333 45 v 399
+w(diff)g Fn(callback)i(is)d(dif)-28 b(ferent)289 b(from)f(other)i
+(callbacks.)380 b(It)288 b(is)g(not)863 42755 y(part)324
+b(of)g(a)h(synchronous)h(request/reply)f(sequence)i(that)d(causes)h
+(the)g(client)f(to)h(block.)485 b(It)324 b(is)f(also)h(an)h(interf)-11
+b(ace)325 b(to)f(the)g Fk(k)-11 b(ernel)p Fn(,)863 44084
+y(not)254 b(directly)f(to)h(the)f(client.)336 b(So,)258
+b(it)252 b Fk(is)h Fn(possible)g(to)g(recei)-28 b(v)-17
+b(e)256 b(a)d Fo(poll)p 27386 44084 V 399 w(diff)h Fn(request)g(while)f
+(there)h(is)e(already)j(one)f(outstanding.)863 45412
+y(This)273 b(happens)i(if)d(the)h(k)-11 b(ernel')-61
+b(s)273 b(poll)g(state)g(cache)i(changes,)h(causing)e(it)e(to)h(notify)
+g(the)g(dri)-28 b(v)-17 b(er)274 b(that)f(it)f(has)h(a)g(ne)-28
+b(w)274 b(cached)i(v)-28 b(alue.)2524 47404 y(This)276
+b(is)h(easy)g(to)g(handle;)i(the)e(client)h(should)g(simply)2247
+50283 y(1.)554 b(Destro)-11 b(y)440 b(the)g(old)h(\(no)-28
+b(w)441 b(out-of-date\))f Fo(poll)p 22346 50283 V 399
+w(diff)h Fn(request)g(using)f(the)h Fo(fusd)p 36766 50283
+V 399 w(destroy)g Fn(function)g(we)g(sa)-17 b(w)440 b(in)3631
+51611 y(Section)278 b(8.2.3.)2247 53825 y(2.)554 b(Either)277
+b(respond)h(to)f(or)g(sa)-22 b(v)-17 b(e)278 b(the)f(ne)-28
+b(w)279 b Fo(poll)p 21381 53825 V 399 w(diff)e Fn(request,)h(e)-17
+b(xactly)279 b(as)d(described)j(in)e(the)g(pre)-28 b(vious)279
+b(section.)2524 56703 y(The)f(ne)-17 b(xt)278 b(section)g(will)e(sho)
+-28 b(w)278 b(an)f(e)-17 b(xample)280 b(of)d(this)f(technique.)863
+60536 y Fj(9.3)1329 b(Adding)331 b Fc(select)h Fj(support)f(to)i
+Fc(pager.c)863 63275 y Fn(Gi)-28 b(v)-17 b(en)223 b(the)f(e)-17
+b(xplanation)225 b(of)c Fo(poll)p 14554 63275 V 399 w(diff)h
+Fn(in)g(the)g(pre)-28 b(vious)222 b(sections,)233 b(it)221
+b(might)g(seem)h(that)g(implementing)h(a)f(selectable)h(de)-28
+b(vice)863 64604 y(is)239 b(a)g(daunting)j(task.)330
+b(It')-61 b(s)238 b(actually)j(not)f(as)f(bad)i(as)e(it)g
+(sounds\227the)i(e)-17 b(xample)242 b(code)f(may)f(well)f(be)i(shorter)
+e(than)h(its)e(e)-17 b(xplanation!)2524 66596 y(Program)359
+b(14)g(sho)-28 b(ws)359 b(the)f(implementation)j(of)d
+Fo(poll)p 24277 66596 V 399 w(diff)h Fn(in)f Fo(pager.c)p
+Fn(,)379 b(which)360 b(mak)-11 b(es)359 b(its)e(noti\002cation)j
+(interf)-11 b(ace)863 67925 y(\()p Fo(/dev/pager/notify)p
+Fn(\))320 b(selectable.)468 b(It)317 b(is)h(decomposed)j(into)d(a)h
+(\223top)g(half)61 b(\224)318 b(and)i(\223bottom)f(half)61
+b(\224)319 b(function,)329 b(e)-17 b(xactly)320 b(as)863
+69253 y(we)352 b(did)f(for)g(the)h(blocking)h Fo(read)f
+Fn(implementation)h(in)e(Program)h(12.)567 b(First,)368
+b(on)352 b(lines)f(1\22620,)371 b(we)352 b(see)f(the)h(the)f(callback)j
+(for)863 70581 y Fo(poll)p 3585 70581 V 399 w(diff)313
+b Fn(callback)h(itself.)447 b(It)311 b(is)g(virtually)h(identical)h(to)
+f(the)g Fo(read)h Fn(callback)h(in)e(Program)h(12.)449
+b(The)313 b(main)f(dif)-28 b(ference)314 b(is)25405 74071
+y(31)p eop
+%%Page: 32 35
+32 34 bop 863 4495 50191 89 v 863 5479 a Fl(Pr)-20 b(ogram)280
+b(14)e Fn(pager)-61 b(.c)278 b(\(P)-17 b(art)277 b(4\):)343
+b(Supporting)279 b Fo(select\(2\))g Fn(by)e(implementing)i(a)e
+Fo(poll)p 36925 5479 333 45 v 400 w(diff)g Fn(callback)p
+863 5985 50191 45 v 365 6940 a Fi(1)1661 b Fo(ssize_t)665
+b(pager_notify_polldiff\(struct)k(fusd_file_info)e(*file,)22449
+8268 y(unsigned)e(int)g(cached_state\))2524 9597 y({)3852
+10925 y(struct)g(pager_client)h(*c)f(=)f(\(struct)i(pager_client)g(*\))
+f(file->private_data;)365 12253 y Fi(5)3852 13582 y Fo(if)f(\(c)h(==)g
+(NULL\))5180 14910 y(return)h(-EINVAL;)3852 17567 y(/*)e(if)h(we're)g
+(already)g(holding)h(a)e(polldiff)i(request)f(that)g(we)g(haven't)-133
+18895 y Fi(10)3653 b Fo(*)664 b(replied)i(to)e(yet,)h(destroy)h(the)e
+(old)h(one)g(and)g(hold)f(onto)h(only)g(the)g(new)4516
+20223 y(*)f(one)h(*/)3852 21552 y(if)f(\(c->polldiff)j(!=)d(NULL\))h({)
+5180 22880 y(fusd_destroy\(c->polldiff\);)5180 24208
+y(c->polldiff)h(=)f(NULL;)-133 25537 y Fi(15)2989 b Fo(})3852
+28193 y(c->polldiff)666 b(=)e(file;)3852 29522 y
+(pager_notify_complete_polldiff\(c\);)3852 30850 y(return)h
+(-FUSD_NOREPLY;)-133 32178 y Fi(20)1661 b Fo(})2524 34835
+y(void)664 b(pager_notify_complete_polldiff\(struct)671
+b(pager_client)666 b(*c\))2524 36164 y({)3852 37492 y(int)f
+(curr_state,)h(cached_state;)-133 38820 y Fi(25)3852
+40149 y Fo(/*)e(if)h(there)g(is)g(no)f(outstanding)i(polldiff,)g(do)f
+(nothing)g(*/)3852 41477 y(if)f(\(c)h(==)g(NULL)f(||)h(c->polldiff)h
+(==)f(NULL\))5180 42805 y(return;)-133 45462 y Fi(30)2989
+b Fo(/*)664 b(figure)i(out)e(the)h("current")h(state:)f(i.e.)g(whether)
+g(or)g(not)g(the)f(pager)4516 46790 y(*)g(is)h(readable)h(for)e(this)h
+(client)g(based)g(on)g(the)g(last)g(page)f(it)h(saw)g(*/)3852
+48119 y(if)f(\(c->last_page_seen)k(<)c(last_page\))5180
+49447 y(curr_state)i(=)f(FUSD_NOTIFY_INPUT;)i(/*)d(readable)i(*/)3852
+50775 y(else)-133 52104 y Fi(35)4317 b Fo(curr_state)666
+b(=)f(0;)f(/*)h(not)f(readable)i(or)e(writable)i(*/)3852
+54760 y(/*)e(cached_state)j(is)d(what)h(the)g(kernel)g(*thinks*)h(the)e
+(state)h(is)g(*/)3852 56089 y(cached_state)h(=)f
+(fusd_get_poll_diff_cached_state\(c->polldiff\);)-133
+58746 y Fi(40)2989 b Fo(/*)664 b(if)h(the)g(state)g(is)f(not)h(what)g
+(the)g(kernel)g(thinks)g(it)g(is,)f(notify)i(the)5844
+60074 y(kernel)g(of)e(the)h(change)g(*/)3852 61402 y(if)f(\(curr_state)
+j(!=)d(cached_state\))j({)5180 62731 y(fusd_return\(c->polldiff,)i
+(curr_state\);)5180 64059 y(c->polldiff)d(=)f(NULL;)-133
+65387 y Fi(45)2989 b Fo(})2524 66716 y(})p 863 68210
+V 25405 74071 a Fn(32)p eop
+%%Page: 33 36
+33 35 bop 863 2974 a Fn(that)317 b(it)e(\002rst)h(checks)i(\(on)f(line)
+g(12\))f(to)h(see)g(if)e(a)i Fo(poll)p 22128 2974 333
+45 v 399 w(diff)g Fn(request)g(is)e(already)j(outstanding)h(when)f(a)e
+(ne)-28 b(w)318 b(request)f(comes)863 4302 y(in.)343
+b(If)276 b(so,)h(the)g(out-of-date)h(request)g(is)e(destro)-11
+b(yed)278 b(using)g Fo(fusd)p 26176 4302 V 399 w(destroy)p
+Fn(,)g(as)e(we)i(described)h(in)d(Section)j(9.2.)2524
+6295 y(The)322 b(bottom)g(half)g(is)e(sho)-28 b(wn)323
+b(on)f(lines)g(22-46.)478 b(First,)331 b(on)322 b(lines)f(32\22635,)335
+b(it)320 b(computes)j(the)f(current)g(poll)g(state\227if)f(a)g(page)863
+7623 y(has)349 b(arri)-28 b(v)-17 b(ed)351 b(that)e(the)h(client)f
+(hasn')-20 b(t)349 b(seen)h(yet,)367 b(the)350 b(\002le)f(is)f
+(readable;)387 b(otherwise,)367 b(it)349 b(isn')-20 b(t.)558
+b(Ne)-17 b(xt,)368 b(the)349 b(dri)-28 b(v)-17 b(er)350
+b(compares)h(the)863 8951 y(current)262 b(poll)f(state)h(with)f(the)g
+(poll)h(state)f(that)g(the)h(k)-11 b(ernel)262 b(has)g(cached.)340
+b(If)261 b(the)g(k)-11 b(ernel')-61 b(s)262 b(cache)h(is)e(out)g(of)h
+(date,)j(the)c(current)h(state)863 10280 y(is)276 b(returned)i(to)f
+(the)h(k)-11 b(ernel.)344 b(Otherwise,)277 b(it)f(does)i(nothing.)2524
+12272 y(As)242 b(with)g(the)h Fo(read)f Fn(callback)j(we)e(sa)-17
+b(w)242 b(pre)-28 b(viously)-72 b(,)251 b(notice)244
+b(that)e Fo(pager)p 31445 12272 V 399 w(notify)p 35828
+12272 V 400 w(complete)p 41540 12272 V 400 w(polldiff)h
+Fn(is)f(called)863 13601 y(in)277 b(tw)-11 b(o)277 b(dif)-28
+b(ferent)277 b(cases:)2247 16338 y(1.)554 b(It)418 b(is)h(called)i
+(immediately)g(from)e(the)i Fo(pager)p 22631 16338 V
+399 w(notify)p 27014 16338 V 400 w(polldiff)f Fn(callback)i(itself.)770
+b(This)420 b(causes)g(the)g(current)3631 17666 y(poll)309
+b(state)f(to)h(be)h(returned)g(to)f(the)g(k)-11 b(ernel)310
+b(immediately)h(when)f(the)g(request)f(arri)-28 b(v)-17
+b(es)310 b(if)e(the)h(dri)-28 b(v)-17 b(er)310 b(already)g(kno)-28
+b(ws)311 b(the)3631 18995 y(k)-11 b(ernel')-61 b(s)277
+b(state)g(needs)h(to)f(be)g(updated.)2247 21138 y(2.)554
+b(It)256 b(is)g(called)i(when)g(ne)-28 b(w)259 b(data)e(arri)-28
+b(v)-17 b(es)258 b(that)f(causes)h(the)f(poll)h(state)e(to)h(change.)
+339 b(Refer)258 b(back)g(to)f(Program)h(12)g(on)g(page)h(27;)3631
+22467 y(in)253 b(the)h(callback)h(that)f(recei)-28 b(v)-17
+b(es)255 b(ne)-28 b(w)254 b(pages,)259 b(notice)c(on)f(line)f(45)h
+(that)g(the)g Fo(poll)p 34999 22467 V 399 w(diff)g Fn(completion)h
+(function)f(is)f(called)3631 23795 y(alongside)278 b(the)g
+Fo(read)f Fn(completion)i(function.)2524 26533 y(W)-44
+b(ith)282 b(this)g Fo(poll)p 9580 26533 V 399 w(diff)h
+Fn(implementation,)k(it)282 b(is)g(possible)h(for)f(a)h(client)g(to)g
+(open)i Fo(/dev/pager/notify)p Fn(,)i(and)d(block)863
+27861 y(in)262 b(a)h Fo(select\(2\))g Fn(system)f(call.)339
+b(If)261 b(another)i(client)g(writes)e Fo(page)i Fn(to)f
+Fo(/dev/pager/input)p Fn(,)267 b(the)c(\002rst)e(client')-61
+b(s)262 b Fo(select)863 29189 y Fn(will)276 b(unblock,)j(indicating)g
+(the)e(\002le)h(has)f(become)i(readable.)2524 31182 y(F)-17
+b(or)258 b(additional)g(e)-17 b(xample)260 b(code,)j(tak)-11
+b(e)258 b(a)f(look)i(at)e(the)h Fo(logring)g Fn(e)-17
+b(xample)260 b(program)e(we)g(\002rst)e(mentioned)k(in)d(Section)i
+(8.3.)863 32510 y(It)276 b(also)h(supports)g Fo(select)h
+Fn(by)g(implementing)h(a)e(similar)f Fo(poll)p 26819
+32510 V 399 w(diff)i Fn(callback.)863 36879 y Fm(10)1594
+b(P)-32 b(erf)-40 b(ormance)398 b(of)g(User)-59 b(-Space)398
+b(De)-24 b(vices)863 40017 y Fn(This)278 b(section)h(hasn')-20
+b(t)279 b(been)h(written)e(yet.)348 b(I)277 b(ha)-22
+b(v)-17 b(e)281 b(some)d(pretty)h(graphs)g(and)h(whatnot,)f(b)-22
+b(ut)279 b(no)g(time)f(to)h(write)f(about)h(them)g(here)863
+41345 y(before)f(the)f(release.)863 45714 y Fm(11)1594
+b(FUSD)399 b(Implementation)f(Notes)863 48852 y Fn(In)349
+b(this)f(section,)367 b(we)349 b(describe)g(some)h(of)e(the)h(details)g
+(of)f(ho)-28 b(w)350 b(FUSD)g(is)e(implemented.)560 b(It')-61
+b(s)347 b(not)i(necessary)h(to)f(understand)863 50180
+y(these)253 b(details)g(in)g(order)g(to)g(use)g(FUSD.)g(Ho)-28
+b(we)g(v)-17 b(er)-44 b(,)260 b(these)253 b(notes)g(can)h(be)g(useful)f
+(for)f(people)i(who)g(are)f(trying)g(to)g(understand)i(the)863
+51509 y(FUSD)278 b(frame)-28 b(w)-11 b(ork)278 b(itself\227hack)-11
+b(ers,)277 b(deb)-22 b(uggers,)279 b(or)e(the)g(generally)i(curious.)
+863 55310 y Fj(11.1)1329 b(The)332 b(situation)g(with)g
+Fc(poll)p 18449 55310 399 45 v 479 w(diff)863 58050 y
+Fn(In-k)-11 b(ernel)368 b(de)-28 b(vice)370 b(dri)-28
+b(v)-17 b(ers)369 b(support)f(select)g(by)g(implementing)i(a)e
+(callback)i(called)e Fo(poll)p Fn(.)616 b(This)368 b(dri)-28
+b(v)-17 b(er')-61 b(s)368 b(callback)h(is)e(sup-)863
+59378 y(posed)435 b(to)f(do)h(tw)-11 b(o)434 b(things.)814
+b(First,)471 b(it)433 b(should)i(return)f(the)h(current)f(state)g(of)g
+(a)g(\002le)g(descriptor)-22 b(\227a)435 b(combination)h(of)e(being)863
+60706 y(readable,)351 b(writable,)e(or)334 b(ha)-22 b(ving)336
+b(e)-17 b(xceptions.)519 b(Second,)351 b(it)334 b(should)h(pro)-17
+b(vide)337 b(a)e(pointer)g(to)f(one)i(of)e(the)h(dri)-28
+b(v)-17 b(er')-61 b(s)335 b(internal)g(w)-11 b(ait)863
+62035 y(queues)264 b(that)e(will)g(be)g(a)-17 b(w)-11
+b(ak)g(ened)266 b(whene)-28 b(v)-17 b(er)266 b(the)c(state)g(changes.)
+341 b(The)263 b Fo(poll)f Fn(call)h(itself)d(should)k(ne)-28
+b(v)-17 b(er)264 b(block\227it)f(should)g(just)863 63363
+y(instantaneously)279 b(report)e(what)h(the)g Fk(curr)-41
+b(ent)277 b Fn(state)g(is.)2524 65356 y(FUSD')-61 b(s)368
+b(implementation)h(of)f(selectable)h(de)-28 b(vices)369
+b(is)e(dif)-28 b(ferent,)390 b(b)-22 b(ut)368 b(attempts)g(to)f
+(maintain)i(three)f(properties)g(that)g(we)863 66684
+y(thought)279 b(to)e(be)g(most)g(important)h(from)e(the)i(point)f(of)g
+(vie)-28 b(w)278 b(of)f(a)g(client)h(using)f Fo(select)p
+Fn(.)344 b(Speci\002cally:)2247 69421 y(1.)554 b(The)267
+b Fo(select\(2\))h Fn(call)e(itself)f(should)i(ne)-28
+b(v)-17 b(er)269 b(become)g(block)-11 b(ed.)341 b(F)-17
+b(or)267 b(e)-17 b(xample,)271 b(if)266 b(one)h(\002le)g(descriptor)g
+(in)f(its)f(set)h(isn')-20 b(t)3631 70750 y(readable,)278
+b(that)f(shouldn')-20 b(t)278 b(pre)-28 b(v)-17 b(ent)280
+b(it)c(from)g(reporting)i(other)g(\002le)f(descriptors)g(that)g(are.)
+25405 74071 y(33)p eop
+%%Page: 34 37
+34 36 bop 2247 2974 a Fn(2.)554 b(If)319 b Fo(select\(2\))i
+Fn(indicates)g(a)g(\002le)f(descriptor)h(is)e(readable)j(\(or)e
+(writable\),)330 b(a)321 b(read)g(\(or)e(write\))h(on)h(that)f(\002le)h
+(descriptor)3631 4302 y(shouldn')-20 b(t)277 b(block.)2247
+6516 y(3.)554 b(Clients)331 b(should)i(be)g(allo)-28
+b(wed)334 b(to)e(seamlessly)g Fo(select)h Fn(on)g(an)-17
+b(y)333 b(set)f(of)g(\002le)g(descriptors,)346 b(e)-28
+b(v)-17 b(en)335 b(if)c(that)h(set)g(contains)h(a)3631
+7844 y(mix)277 b(of)g(both)g(FUSD)i(and)f(non-FUSD)h(de)-28
+b(vices.)2524 10723 y(The)349 b(FUSD)h(k)-11 b(ernel)350
+b(module)g(k)-11 b(eeps)350 b(a)f(cache)i(of)d(the)h(dri)-28
+b(v)-17 b(er')-61 b(s)349 b(most)f(recent)i(answer)f(for)g(each)h
+(\002le)f(descriptor)-44 b(,)366 b(initially)863 12051
+y(assumed)278 b(to)f(be)h(0.)343 b(When)278 b(the)g(k)-11
+b(ernel)278 b(module')-61 b(s)278 b(internal)f Fo(poll)h
+Fn(callback)h(is)d(acti)-28 b(v)g(ated,)279 b(it:)2247
+14929 y(1.)554 b(Dispatches)330 b(a)f Fk(non-)p Fn(blocking)j
+Fo(poll)p 18591 14929 333 45 v 399 w(diff)e Fn(to)f(the)h(associated)g
+(user)-22 b(-space)330 b(dri)-28 b(v)-17 b(er)-44 b(,)343
+b(asking)330 b(for)e(a)i(cache)h(update\227if)3631 16257
+y(and)278 b(only)g(if)e(there)h(isn')-20 b(t)276 b(already)j(an)e
+(outstanding)i(poll)e(dif)-28 b(f)277 b(request)g(out)h(that)f(has)g
+(the)h(same)f(v)-28 b(alue.)2247 18471 y(2.)554 b(Immediately)278
+b(returns)f(the)g(cached)j(v)-28 b(alue)278 b(to)f(the)h(k)-11
+b(ernel)2524 21349 y(In)285 b(addition,)k(the)d(cached)i(v)-28
+b(alue')-61 b(s)286 b(readable)i(bit)d(is)g(cleared)i(on)f(e)-28
+b(v)-17 b(ery)287 b(read;)k(the)286 b(writable)f(bit)g(is)g(cleared)i
+(on)f(e)-28 b(v)-17 b(ery)288 b(write.)863 22678 y(This)248
+b(is)f(necessary)j(to)e(pre)-28 b(v)-17 b(ent)250 b(old)e(poll)g
+(state\227which)i(says)d(\223de)-28 b(vice)251 b(is)c
+(readable\224\227from)k(being)e(returned)g(out)g(of)e(the)i(cache)863
+24006 y(when)410 b(it)e(might)h(be)h(in)-44 b(v)-28 b(alid.)739
+b(FUSD)410 b(assumes)f(that)g(an)-17 b(y)410 b(read)g(to)f(a)g(de)-28
+b(vice)411 b(can)f(mak)-11 b(e)410 b(it)e(potentially)i(unreadable.)741
+b(This)863 25334 y(mechanism)279 b(is)d(what)i(causes)g(an)f(updated)j
+(poll)d(dif)-28 b(f)276 b(to)h(be)h(sent)f(to)g(a)g(client)g(before)h
+(the)g(pre)-28 b(vious)278 b(one)g(has)g(been)g(returned.)2524
+27327 y(\(this)d(section)j(isn')-20 b(t)276 b(\002nished)j(yet;)e(f)-11
+b(anc)-17 b(y)278 b(time)f(diagrams)h(coming)h(someday\))863
+31160 y Fj(11.2)1329 b(Restartable)332 b(System)f(Calls)863
+33900 y Fn(No)278 b(time)e(to)h(write)g(this)f(section)i(yet...)863
+38300 y Fm(A)1594 b(Using)399 b Fa(strace)863 41438 y
+Fn(This)277 b(section)h(hasn')-20 b(t)277 b(been)i(written)d(yet.)344
+b(Contrib)-22 b(utions)278 b(are)f(welcome.)25405 74071
+y(34)p eop
+%%Trailer
+end
+userdict /end-hook known{end-hook}if
+%%EOF

Added: trunk/fusd/doc/fusd.tex
===================================================================
--- trunk/fusd/doc/fusd.tex	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/fusd.tex	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,2013 @@
+%
+%
+% FUSD - Framework for User-Space Devices
+% Programming Manual & Tutorial
+%
+% Jeremy Elson, (c) 2001 Sensoria Corporation, 2003 UCLA
+% Released under open-source, BSD license
+% See LICENSE file for full license
+%
+% $Id: fusd.tex,v 1.63 2003/08/20 22:00:55 jelson Exp $
+
+\documentclass{article}
+\addtolength{\topmargin}{-.5in}        % repairing LaTeX's huge margins...
+\addtolength{\textheight}{1in}         % more margin hacking
+\addtolength{\textwidth}{1.5in}
+\addtolength{\oddsidemargin}{-0.75in}
+\addtolength{\evensidemargin}{-0.75in}
+
+\usepackage{graphicx,float,alltt,tabularx}
+\usepackage{wrapfig,floatflt}
+\usepackage{amsmath}
+\usepackage{latexsym}
+\usepackage{moreverb}
+\usepackage{times}
+\usepackage{html}
+%\usepackage{draftcopy}
+
+%\setcounter{bottomnumber}{3}
+%\renewcommand{\topfraction}{0}
+%\renewcommand{\bottomfraction}{0.7}
+%\renewcommand{\textfraction}{0}
+%\renewcommand{\floatpagefraction}{2.0}
+
+\renewcommand{\topfraction}{1.0}
+\renewcommand{\bottomfraction}{1.0}
+\renewcommand{\textfraction}{0.0}
+\renewcommand{\floatpagefraction}{0.9}
+
+\floatstyle{ruled}
+\newfloat{Program}{tp}{lop}
+
+
+\title{FUSD:
+A Linux {\bf F}ramework for {\bf U}ser-{\bf S}pace {\bf D}evices}
+
+\author{Jeremy Elson\\
+jelson at circlemud.org\\
+http://www.circlemud.org/\tilde{}jelson/software/fusd}
+\date{19 August 2003, Documentation for FUSD 1.10}
+
+\begin{document}
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Title Page %%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{center}
+\begin{latexonly}\vspace*{2in}\end{latexonly}
+{\Huge FUSD:} \\
+\vspace{2\baselineskip}
+{\huge A Linux {\bf F}ramework for {\bf U}ser-{\bf S}pace {\bf D}evices}
+
+\begin{latexonly}\vspace{2in}\end{latexonly}
+\vspace{\baselineskip}
+
+\vfill
+
+{\large Jeremy Elson \\
+\begin{latexonly}\vspace{.5\baselineskip}\end{latexonly}}
+\vspace{\baselineskip}
+{\tt jelson at circlemud.org\\
+http://www.circlemud.org/jelson/software/fusd}
+
+\vspace{2\baselineskip}
+19 August 2003\\
+Documentation for FUSD 1.10\\
+
+\end{center}
+\thispagestyle{empty}
+\clearpage
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\begin{latexonly}
+\pagenumbering{roman}
+
+\tableofcontents
+\bigskip
+\listof{Program}{List of Example Programs}
+\setlength{\parskip}{10pt}
+
+\clearpage
+\end{latexonly}
+
+% This resets the page counter to 1
+\pagenumbering{arabic}
+\addtolength{\parskip}{0.5\baselineskip}
+
+\section{Introduction}
+
+\subsection{What is FUSD?}
+
+FUSD (pronounced {\em fused}) is a Linux framework for proxying device
+file callbacks into user-space, allowing device files to be
+implemented by daemons instead of kernel code.  Despite being
+implemented in user-space, FUSD devices can look and act just like any
+other file under /dev which is implemented by kernel callbacks.
+
+A user-space device driver can do many of the things that kernel
+drivers can't, such as perform a long-running computation, block while
+waiting for an event, or read files from the file system.  Unlike
+kernel drivers, a user-space device driver can {\em use other device
+drivers}---that is, access the network, talk to a serial port, get
+interactive input from the user, pop up GUI windows, or read from
+disks.  User-space drivers implemented using FUSD can be much easier
+to debug; it is impossible for them to crash the machine, are easily
+traceable using tools such as {\tt gdb}, and can be killed and
+restarted without rebooting even if they become corrupted.  FUSD
+drivers don't have to be in C---Perl, Python, or any other language
+that knows how to read from and write to a file descriptor can work
+with FUSD.  User-space drivers can be swapped out, whereas kernel
+drivers lock physical memory.
+
+Of course, as with almost everything, there are trade-offs.
+User-space drivers are slower than kernel drivers because they require
+three times as many system calls, and additional memory copies (see
+section~\ref{performance}).  User-space drivers can not receive
+interrupts, and do not have the full power to modify arbitrary kernel
+data structures as kernel drivers do.  Despite these limitations, we
+have found user-space device drivers to be a powerful programming
+paradigm with a wide variety of uses (see Section~\ref{use-cases}).
+
+FUSD is free software, distributed under a GPL-compatible license (the
+``new'' BSD license, with the advertising clause removed).
+
+\subsection{How does FUSD work?}
+
+FUSD drivers are conceptually similar to kernel drivers: a set of
+callback functions called in response to system calls made on file
+descriptors by user programs.  FUSD's C library provides a device
+registration function, similar to the kernel's {\tt
+devfs\_register\_chrdev()} function, to create new devices.  {\tt
+fusd\_register()} accepts the device name and a structure full of
+pointers.  Those pointers are callback functions which are called in
+response to certain user system calls---for example, when a process
+tries to open, close, read from, or write to the device file.  The
+callback functions should conform to the standard definitions of POSIX
+system call behavior.  In many ways, the user-space FUSD callback
+functions are identical to their kernel counterparts.
+
+Perhaps the best way to show what FUSD does is by example.
+Program~\ref{helloworld.c} is a simple FUSD device driver.  When the
+program is run, a device called {\tt /dev/hello-world} appears under
+the {\tt /dev} directory.  If that device is read (e.g., using {\tt
+cat}), the read returns {\tt Hello, world!} followed by an EOF.
+Finally, when the driver is stopped (e.g., by hitting Control-C), the
+device file disappears.
+
+\begin{Program}
+\listinginput[5]{1}{helloworld.c.example}
+\caption{helloworld.c: A simple program using FUSD to
+	create {\tt /dev/hello-world}}
+\label{helloworld.c}
+\end{Program}
+
+On line 40 of the source, we use {\tt fusd\_register()} to create the
+{\tt /dev/hello-world} device, passing pointers to callbacks for the
+open(), close() and read() system calls.  (Lines 36--39 use the GNU C
+extension that allows initializer field naming; the 2.4 series of
+Linux kernels use also that extension for the same purpose.)  The
+``Hello, World'' read() callback itself is virtually identical to what
+a kernel driver for this device would look like.  It can inspect and
+modify the user's file pointer, copy data into the user-provided
+buffer, control the system call return value (either positive, EOF, or
+error), and so forth.
+
+The proxying of kernel system calls that makes this kind of program
+possible is implemented by FUSD, using a combination of a kernel
+module and cooperating user-space library.  The kernel module
+implements a character device, {\tt /dev/fusd}, which is used as a
+control channel between the two.  fusd\_register() uses this channel
+to send a message to the FUSD kernel module, telling the name of the
+device the user wants to register.  The kernel module, in turn,
+registers that device with the kernel proper using devfs.  devfs and
+the kernel don't know anything unusual is happening; it appears from
+their point of view that the registered devices are simply being
+implemented by the FUSD module.
+
+Later, when kernel makes a callback due to a system call (e.g.\ when
+the character device file is opened or read), the FUSD kernel module's
+callback blocks the calling process, marshals the arguments of the
+callback into a message and sends it to user-space.  Once there, the
+library half of FUSD unmarshals it and calls whatever user-space
+callback the FUSD driver passed to fusd\_register().  When that
+user-space callback returns a value, the process happens in reverse:
+the return value and its side-effects are marshaled by the library
+and sent to the kernel.  The FUSD kernel module unmarshals this
+message, matches it up with a corresponding outstanding request, and
+completes the system call.  The calling process is completely unaware
+of this trickery; it simply enters the kernel once, blocks, unblocks,
+and returns from the system call---just as it would for any other
+blocking call.
+
+One of the primary design goals of FUSD is {\em stability}.  It should
+not be possible for a FUSD driver to corrupt or crash the kernel,
+either due to error or malice.  Of course, a buggy driver itself may
+corrupt itself (e.g., due to a buffer overrun).  However, strict error
+checking is implemented at the user-kernel boundary which should
+prevent drivers from corrupting the kernel or any other user-space
+process---including the errant driver's own clients, and other FUSD
+drivers.
+
+
+\subsection{What FUSD {\em Isn't}}
+
+FUSD looks similar to certain other Linux facilities that are already
+available.  It also skirts near a few of the kernel's hot-button
+political issues.  So, to avoid confusion, we present a list of
+things that FUSD is {\em not}.
+
+\begin{itemize}
+
+\item {\bf A FUSD driver is not a kernel module.}  Kernel modules
+allow---well, modularity of kernel code.  They let you insert and
+remove kernel modules dynamically after the kernel boots.  However,
+once inserted, the kernel modules are actually part of the kernel
+proper.  They run in the kernel's address space, with all the same
+privileges and restrictions that native kernel code does.  A FUSD
+device driver, in contrast, is more similar to a daemon---a program
+that runs as a user-space process, with a process ID.
+
+\item {\bf FUSD is not, and doesn't replace, devfs.}  When a FUSD
+driver registers a FUSD device, it automatically creates a device file
+in {\tt /dev}.  However, FUSD is not a replacement for devfs---quite
+the contrary, FUSD creates those device files by {\em using} devfs.
+In a normal Linux system, only kernel modules proper---not user-space
+programs---can register with devfs (see above).
+
+\item {\bf FUSD is not UDI.}  UDI, the \htmladdnormallinkfoot{Uniform
+Driver Interface}{http://www.projectudi.org}, aims to create a binary
+API for drivers that is uniform across operating systems.  It's true
+that FUSD could conceivably be used for a similar purpose (inasmuch as
+it defines a system call messaging structure).  However, this was not
+the goal of FUSD as much as an accidental side effect.  We do not
+advocate publishing drivers in binary-only form, even though FUSD does
+make this possible in some cases.
+
+\item {\bf FUSD is not an attempt to turn Linux into a microkernel.}
+We aren't trying to port existing drivers into user-space for a
+variety of reasons (not the least of which is performance).  We've
+used FUSD as a tool to write new drivers that are much easier from
+user-space than they would be in the kernel; see
+Section~\ref{use-cases} for use cases.
+
+
+\end{itemize}
+
+
+\subsection{Related Work}
+
+FUSD is a new implementation, but certainly not a new idea---the
+theory of its operation is the same as any microkernel operating
+system.  A microkernel (roughly speaking) is one that implements only
+very basic resource protection and message passing in the kernel.
+Implementation of device drivers, file systems, network stacks, and so
+forth are relegated to userspace.  Patrick Bridges maintains a list of
+such \htmladdnormallinkfoot{microkernel operating systems}{http://www.cs.arizona.edu/people/bridges/os/microkernel.html}.
+
+Also related is the idea of a user-space filesystem, which has been
+implemented in a number of contexts.  Some examples include Klaus
+Schauser's \htmladdnormallinkfoot{UFO
+Project}{http://www.cs.ucsb.edu/projects/ufo/index.html} for Solaris,
+and Jeremy Fitzhardinge's (no longer maintained)
+\htmladdnormallinkfoot{UserFS}{http://www.goop.org/~jeremy/userfs/}
+for Linux 1.x.  The \htmladdnormallinkfoot{UFO
+paper}{http://www.cs.ucsb.edu/projects/ufo/97-usenix-ufo.ps} is also
+notable because it has a good survey of similar projects that
+integrate user-space code with system calls.
+
+\subsection{Limitations and Future Work}
+
+In its current form, FUSD is useful and has proven to be quite
+stable---we use it in production systems.  However, it does have some
+limitations that could benefit from the attention of developers.
+Contributions to correct any of these deficiencies are welcomed!
+(Many of these limitations will not make sense without having read the
+rest of the documentation first.)
+
+
+\begin{itemize}
+\item Currently, FUSD only supports implementation of character
+devices.  Block devices and network devices are not supported yet.
+
+\item The kernel has 15 different callbacks in its {\tt
+file\_operations} structure.  The current version of FUSD does not
+proxy some of the more obscure ones out to userspace.
+
+\item Currently, all system calls that FUSD understands are proxied
+from the FUSD kernel module to userspace.  Only the userspace library
+knows which callbacks have actually been registered by the FUSD
+driver.  For example, the kernel may proxy a write() system call to
+user-space even if the driver has not registered a write() callback
+with fusd\_register().
+
+fusd\_register() should, but currently does not, tell the kernel
+module which callbacks it wants to receive, per-device.  This will be
+more efficient because it will prevent useless system calls for
+unsupported operations.  In addition, it will lead to more logical and
+consistent behavior by allowing the kernel to use its default
+implementations of certain functions such as writev(), instead of
+being fooled into thinking the driver has an implementation of it in
+cases where it doesn't.
+
+\item It should be possible to write a FUSD library in any language
+that supports reads and writes on raw file descriptors.  In the
+future, it might be possible to write FUSD device drivers in a variety
+of languages---Perl, Python, maybe even Java.  However, the current
+implementation has only a C library.
+
+\item It's possible for drivers that use FUSD to deadlock---for
+example, if a driver tries to open itself.  In this one case, FUSD
+returns {\tt -EDEADLOCK}.  However, deadlock protection should be
+expanded to more general detection of cycles of arbitrary length.
+
+\item FUSD should provide a /proc interface that gives debugging and
+status information, and allows parameter tuning.
+
+\item FUSD was written with efficiency in mind, but a number of
+important optimizations have not yet been implemented.  Specifically,
+we'd like to try to reduce the number of memory copies by using a
+buffer shared between user and kernel space to pass messages.
+
+\item FUSD currently requires devfs, which is used to dynamically
+create device files under {\tt /dev} when a FUSD driver registers
+itself.   This is, perhaps, the most convenient and useful paradigm
+for FUSD.  However, some users have asked if it's possible to use FUSD
+without devfs.  This should be possible if FUSD drivers bind to device
+major numbers instead of device file names.
+
+\end{itemize}
+
+
+
+
+\subsection{Author Contact Information and Acknowledgments}
+
+The original version of FUSD was written by Jeremy Elson
+\htmladdnormallink{(jelson at circlemud.org)}{mailto:jelson at circlemud.org}
+and Lewis Girod at Sensoria Corporation.
+Sensoria no longer maintains public releases of FUSD, but the same
+authors have since forked the last public release and continue to
+maintain FUSD from the University of California, Los Angeles.
+
+If you have bug reports, patches, suggestions, or any other comments,
+please feel free to contact the authors.
+
+FUSD has two
+\htmladdnormallinkfoot{SourceForge}{http://www.sourceforge.net}-host
+mailing lists: a low-traffic list for announcements ({\tt fusd-announce})
+and a list for general discussion ({\tt fusd-devel}).  Subscription
+information for both lists is available at the
+\htmladdnormallink{SourceForge's FUSD mailing list
+page}{http://sourceforge.net/mail/?group_id=36326}.
+
+For the latest releases and information about FUSD, please see the
+\htmladdnormallinkfoot{official FUSD home
+page}{http://www.circlemud.org/jelson/software/fusd}.
+
+
+
+\subsection{Licensing Information}
+
+FUSD is free software, distributed under a GPL-compatible license (the
+``new'' BSD license, with the advertising clause removed).  The
+license is enumerated in its entirety below.
+
+Copyright (c) 2001, Sensoria Corporation; (c) 2003 University of
+California, Los Angeles.  All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+\begin{itemize}
+\item Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+\item Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+\item Neither the names of Sensoria Corporation or UCLA, nor the
+names of other contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+\end{itemize}
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+\section{Why use FUSD?}
+\label{use-cases}
+
+One basic question about FUSD that one might ask is: what is it good
+for?  Why use it?  In this section, we describe some of the situations
+in which FUSD has been the solution for us.
+
+\subsection{Device Driver Layering}
+
+A problem that comes up frequently in modern operating systems is
+contention for a single resource by multiple competing processes.  In
+UNIX, it's the job of a device driver to coordinate access to such
+resources.  By accepting requests from user processes and (for
+example) queuing and serializing them, it becomes safe for processes
+that know nothing about each other to make requests in parallel to the
+same resource.  Of course, kernel drivers do this job already, but
+they typically operate on top of hardware directly.  However, kernel
+drivers can't easily be layered on top of {\em other device drivers}.
+
+For example, consider a device such as a modem that is connected to a
+host via a serial port.  Let's say we want to implement a device
+driver that allows multiple users to dial the telephone (e.g., {\tt
+echo 1-310-555-1212 > /dev/phone-dialer}).  Such a driver should be
+layered {\em on top of} the serial port driver---that is, it most
+likely wants to write to {\tt /dev/ttyS0}, not directly to the UART
+hardware itself.
+
+While it is possible to write to a logical file from within a kernel
+device driver, it is both tricky and considered bad practice.  In the
+\htmladdnormallinkfoot{words of kernel hacker Dick Johnson}
+{http://www.uwsg.indiana.edu/hypermail/linux/kernel/0005.3/0061.html},
+``You should never write a [kernel] module that requires reading or
+writing to any logical device. The kernel is the thing that translates
+physical I/O to logical I/O. Attempting to perform logical I/O in the
+kernel is effectively going backwards.''
+
+With FUSD, it's possible to layer device drivers because the driver is
+a user-space process, not a kernel module.  A FUSD implementation of
+our hypothetical {\tt /dev/phone-dialer} can open {\tt /dev/ttyS0}
+just as any other process would.
+
+Typically, such layering is accomplished by system daemons.  For
+example, the {\tt lpd} daemon manages printers at a high level.  Since
+it is a user-space process, it can access the physical printer devices
+using kernel device drivers (for example, using printer or network
+drivers).  There a number of advantages to using FUSD instead:
+\begin{itemize}
+\item Using FUSD, a daemon/driver can create a standard device file
+which is accessible by any program that knows how to use the POSIX
+system call interface.  Some trickery is possible using named
+pipes and FIFOs, but quickly becomes difficult because of multiplexed
+writes from multiple processes.
+\item FUSD drivers receive the UID, GID, and process ID along with
+every file operation, allowing the same sorts of security policies to
+be implemented as would be possible with a real kernel driver.  In
+contrast, writes to a named pipe, UDP, and so forth are ``anonymous.''
+\end{itemize}
+
+\subsection{Use of User-Space Libraries}
+
+Since a FUSD driver is just a regular user-space program, it can
+naturally use any of the enormous body of existing libraries that
+exist for almost any task.  FUSD drivers can easily incorporate user
+interfaces, encryption, network protocols, threads, and almost
+anything else.  In contrast, porting arbitrary C code into the kernel
+is difficult and usually a bad idea.
+
+\subsection{Driver Memory Protection}
+
+Since FUSD drivers run in their own process space, the rest of the
+system is protected from them.  A buggy or malicious FUSD driver, at
+the very worst, can only corrupt itself.  It's not possible for it to
+corrupt the kernel, other FUSD drivers, or even the processes that are
+using its devices.  In contrast, a buggy kernel module can bring down
+any process in the system, or the entire kernel itself.
+
+\subsection{Giving libraries language independence and standard
+notification interfaces}
+
+One particularly interesting application of FUSD that we've found very
+useful is as a way to let regular user-space libraries export device
+file APIs.  For example, imagine you had a library which factored
+large composite numbers.  Typically, it might have a C
+interface---say, a function called {\tt int\ *factorize(int\ bignum)}.
+With FUSD, it's possible to create a device file interface---say, a
+device called {\tt /dev/factorize} to which clients can {\tt write(2)}
+a big number, then {\tt read(2)} back its factors.
+
+This may sound strange, but device file APIs have at least three
+advantages over a typical library API.  First, it becomes much more
+language independent---any language that can make system calls can
+access the factorization library.  Second, the factorization code is
+running in a different address space; if it crashes, it won't crash or
+corrupt the caller.  Third, and most interestingly, it is possible to
+use {\tt select(2)} to wait for the factorization to complete.  {\tt
+select(2)} would make it easy for a client to factor a large number
+while remaining responsive to {\em other} events that might happen in
+the meantime.  In other words, FUSD allows normal user-space libraries
+to integrate seamlessly with UNIX's existing, POSIX-standard event
+notification interface: {\tt select(2)}.
+
+\subsection{Development and Debugging Convenience}
+
+FUSD processes can be developed and debugged with all the normal
+user-space tools.  Buggy drivers won't crash the system, but instead
+dump cores that can be analyzed.  All of your favorite visual
+debuggers, memory bounds checkers, leak detectors, profilers, and
+other tools can be applied to FUSD drivers as they would to any other
+program.
+
+\section{Installing FUSD}
+
+This section describes the installation procedure for FUSD.  It
+assumes a good working knowledge of Linux system administration.
+
+
+\subsection{Prerequisites}
+
+Before installing FUSD, make sure you have all of the following
+packages installed and working correctly:
+
+\begin{itemize}
+\item {\bf Linux kernel 2.4.0 or later}.  FUSD was developed under
+2.4.0 and should work with any kernel in the 2.4 series.
+
+\item {\bf devfs installed and running.}  FUSD dynamically registers
+devices using devfs, the Linux device filesystem by Richard Gooch.
+For FUSD to work, devfs must be installed and running on your system.
+For more information about devfs installation, see the
+\htmladdnormallinkfoot{devfs home
+page}{http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html}.
+
+Note that some distributions make installation devfs easier.  RedHat
+7.1, for example, already has all of the necessary daemons and
+configuration changes integrated.  devfs can be installed simply by
+recompiling the kernel with devfs support enabled and reconfiguring
+LILO to pass {\tt "devfs=mount"} to the kernel.
+\end{itemize}
+
+
+\subsection{Compiling FUSD as a Kernel Module}
+
+Before compiling anything, take a look at the Makefile in FUSD's home
+directory.  Adjust any constants that are not correct.  In particular,
+make sure {\tt KERNEL\_HOME} correctly reflects the place where your
+kernel sources are installed, if they aren't in the default location
+of {\tt /usr/src/linux}.
+
+Then, type {\tt make}.  It should generate a directory whose name
+looks something like {\tt obj.i686-linux}, or some variation depending
+on your architecture.  Inside of that directory will be a number of
+files, including:
+\begin{itemize}
+\item kfusd.o -- The FUSD kernel module
+\item libfusd.a -- The C library used to talk to the kernel module
+\item Example programs -- linked against libfusd.a
+\end{itemize}
+
+Compilation of the kernel module will fail if the dependencies
+described in the previous section are not satisfied.  The module must
+be compiled again Linux kernel must be v2.4.0 or later, and the kernel
+must have devfs support enabled.
+
+
+\subsection{Testing and Troubleshooting}
+
+Once everything has been compiled, give it a try to see if it actually
+does something.  First, use {\tt insmod} to insert the FUSD kernel
+module, e.g. {\tt insmod obj.i686-linux/kfusd.o}.  A greeting message
+similar to ``{\tt fusd: starting, Revision: 1.50}'' should appear in
+the kernel log (accessed using the {\tt dmesg} command, or by typing
+{\tt cat /proc/kmsg}).  You can verify the module has been inserted by
+typing {\tt lsmod}, or alternatively {\tt cat /proc/modules}.
+
+Once the module has been inserted successfully, trying running the
+{\tt helloworld} example program.  When run, the program should print
+a greeting message similar to {\tt /dev/hello-world should now exist -
+calling fusd\_run}.  This means everything is working; the daemon is
+now blocked, waiting for requests to the new device.  From another
+shell, type {\tt cat /dev/hello-world}.  You should see {\tt Hello,
+world!} printed in response.  Try killing the test program; the
+corresponding device file should disappear.
+
+If nothing seems to be working, try looking at the kernel message log
+(type {\tt dmesg} or {\tt cat /proc/kmsg}) to see if there are any
+errors.  If nothing seems obviously wrong, try turning on FUSD kernel
+module debugging by defining {\tt CONFIG\_FUSD\_DEBUG} in kfusd.c,
+then recompiling and reinserting the module.
+
+
+\subsection{Installation}
+
+Typing {\tt make install} will copy the FUSD library, header files,
+and man pages into {\tt /usr/local}.  The FUSD kernel module is {\em
+not} installed automatically because of variations among different
+Linux distributions in how this is accomplished.  You may want to
+arrange to have the module start automatically on boot by (for
+example) copying it into {\tt /lib/modules/your-kernel-version}, and
+adding it to {\tt /etc/modules.conf}.
+
+
+\subsection{Making FUSD Part of the Kernel Proper}
+
+The earlier instructions, by default, create a FUSD kernel module.
+If desired, it's also very easy to build FUSD right into the kernel,
+instead:
+\begin{enumerate}
+\item Unpack the 2.4 kernel sources and copy all the files in the {\tt
+include} and {\tt kfusd} directories into your kernel source tree,
+under {\tt drivers/char}.  For example, if FUSD is in
+your home directory, and your kernel is in {\tt /usr/src/linux}:
+\begin{verbatim}
+        cp ~/fusd/kfusd/* ~/fusd/include/* /usr/src/linux/drivers/char
+\end{verbatim}
+
+\item Apply the patch found in FUSD's {\tt patches} directory to your
+kernel source tree.  For example:
+\begin{verbatim}
+        cd /usr/src/linux
+        patch -p0 < ~/fusd/patches/fusd-inkernel.patch
+\end{verbatim}
+The FUSD in-kernel patch doesn't actually change any kernel sources
+proper; it just adds FUSD to the kernel configuration menu and
+Makefile.
+\item Using your kernel configurator of choice (e.g. {\tt make
+menuconfig}), turn on the FUSD options.  It will be under the
+``Character devices'' menu.
+\item Build and install the kernel as usual.
+\end{enumerate}
+
+
+\section{Basic Device Creation}
+
+Enough introduction---it's time to actually create a basic device
+driver using FUSD!
+
+This following sections will illustrate various techniques using
+example programs.  To save space, interesting excerpts are shown
+instead of entire programs.  However, the {\tt examples} directory
+of the FUSD distribution contains all the examples in their
+entirety.  They can actually be compiled and run on a system with the
+FUSD kernel module installed.
+
+Where this text refers to example program line numbers, it refers to
+the line numbers printed alongside the excerpts in the manual---not
+the line numbers of the actual programs in the {\tt examples}
+directory.
+
+
+\subsection{Using {\tt fusd\_register} to create a new device}
+\label{using-fusd-register}
+
+We saw an example of a simple driver, helloworld.c, in
+Program~\ref{helloworld.c} on page~\pageref{helloworld.c}.  Let's go
+back and examine that program now in more detail.
+
+The FUSD ball starts rolling when the {\tt fusd\_register} function is
+called, as shown on line 40.  This function tells the FUSD kernel
+module:
+\begin{itemize}
+\item {\tt char *name}---The name of the device being created.  The
+prefix (such as {\tt /dev/}) must match the location where devfs has
+been mounted.  Names containing slashes (e.g., {\tt
+/dev/my-devices/dev1}) are legal; devfs creates subdirectories
+automatically.
+\item {\tt mode\_t mode}---The device's default permissions.  This is
+usually specified using an octal constant with a leading 0---{\tt 0666}
+(readable and writable by everyone) instead of the incorrect decimal
+constant {\tt 666}.
+\item {\tt void *device\_info}---Private data that should be passed to
+callback functions for this device.  The use of this field is
+described in Section~\ref{device-info}.
+\item {\tt struct fusd\_file\_operations *fops}---A structure containing
+pointers to the callback functions that should be called by FUSD
+in response to certain events.
+\end{itemize}
+
+If device registration is successful, {\tt fusd\_register} returns a
+{\em device handle}---a small integer $\ge0$.  On errors, it returns
+-1 and sets the global variable {\tt errno} appropriately.  In
+reality, the device handle you get is a plain old file descriptor,
+as we'll see in Section~\ref{selecting}.
+
+Although Program~\ref{helloworld.c} only calls {\tt fusd\_register}
+once, it can be called multiple times if the FUSD driver is handling
+more than one device as we'll see in Program~\ref{drums.c}.
+
+There is intentional similarity between {\tt fusd\_register()} and the
+kernel's device registration functions, such as {\tt
+devfs\_register()} and {\tt register\_chrdev()}.  In many ways, FUSD's
+interface is meant to mirror the kernel interface as closely as
+possible.
+
+The {\tt fusd\_file\_operations} structure, defined in {\tt fusd.h},
+contains a list of callbacks that are used in response to different
+system calls executed on a file.  It is similar to the kernel's {\tt
+file\_operations} structure, accepting callbacks for system calls such
+as {\tt open()}, {\tt close()}, {\tt read()}, {\tt write()}, and {\tt
+ioctl()}.  For the most part, the prototypes of FUSD file operation
+callbacks are the same as their kernel cousins, with one important
+exception.  The first argument of FUSD callbacks is always a pointer
+to a {\tt fusd\_file\_info} structure; it contains information that
+can be used to identify the file.  This structure is used instead of
+the kernel's {\tt file} and {\tt inode} structures, and will be
+described in more detail later.
+
+In lines 35--38 of Program~\ref{helloworld.c}, we create and
+initialize a {\tt fusd\_file\_operations} structure.  A GCC-specific C
+extension allows us to name structure fields explicitly in the
+initializer.  This style may look strange, but it guards against
+errors in the future in case the order of fields in the structure ever
+changes.  The 2.4 kernel series uses the same trick.
+
+After calling {\tt fusd\_register()} on line 40, the example program
+calls {\tt fusd\_run()} on line 44.  This function turns control over
+to the FUSD framework.  fusd\_run blocks the driver until one of the
+devices it registered needs to be serviced.  Then, it calls the
+appropriate callback and blocks again until the next event.
+
+Now, imagine that a user types {\tt cat /dev/hello-world}.  What
+happens?  Recall first what the {\tt cat} program itself does: opens a
+file, reads from it until it receives an EOF (printing whatever it
+reads to stdout), then closes it.  {\tt cat} works the same way
+regardless of what it's reading---be it a a FUSD device, a regular
+file, a serial port, or anything else.  The {\tt strace} program is a
+great way to see this in action; see Appendix~\ref{strace} for
+details.
+
+\subsection{The {\tt open} and {\tt close} callbacks}
+\label{open-close}
+
+The first two callbacks that most drivers typically implement are {\tt
+open} and {\tt close}.  Each of these two functions are passed just
+one argument---the {\tt fusd\_file\_info} structure that describes the
+instance of the file being opened or closed.  Use of the information
+in that structure will be covered in more detail in
+Section~\ref{fusd-file-info}.
+
+The semantics of an {\tt open} callback's return value are exactly the
+same as inside the kernel:
+\begin{itemize}
+\item 0 means success, and the file is opened.  If the file is allowed
+to open, the kernel returns a valid file descriptor to the client.
+Using that descriptor, other callbacks may be called for that file,
+including (at least) a {\tt close} callback.
+
+\item A negative number indicates a failure, and that the file should
+not be opened.  Such return values should {\em always} be the
+specified as a negative {\tt errno} value such as {\tt -EPERM}, {\tt
+-EBUSY}, {\tt -ENODEV}, {\tt -ENOMEM}, and so on.  For example, if the
+callback returns {\tt -EPERM}, the caller's {\tt open()} will return
+-1, with {\tt errno} set to {\tt EPERM}.  A complete list of possible
+return values can be found in the Linux kernel sources, under {\tt
+include/asm/errno.h}.
+\end{itemize}
+
+If an {\tt open} callback returns 0 (success), a driver is {\em
+guaranteed} to receive exactly one {\tt close} callback for that file
+later.  By the same token, the close callback {\em will not} be called
+if the open fails.  Therefore, {\tt open} callbacks that can return
+failure must be sure to deallocate any resources they might have
+allocated before returning a failure.
+
+Let's return to our example in Program~\ref{helloworld.c}, which
+creates the {\tt /dev/hello-world} device.  If a user types {\tt cat
+/dev/hello-world}, {\tt cat} will will use the {\tt open(2)} system
+call to open the file.  FUSD will then proxy that system call to the
+driver and activate the callback that was registered as the {\tt open}
+callback.  Recall from line 36 of Program~\ref{helloworld.c} that we
+registered {\tt do\_open\_or\_close}, which appears on line 8.
+
+In {\tt helloworld.c}, the {\tt open} callback always returns 0, or
+success.  However, in a real driver, something more interesting will
+probably happen---permissions checks, memory allocation for
+state-keeping, and so forth.  The corresponding {\em de}-allocation of
+those resources should occur in the {\tt close} callback, which is
+called when a user application calls {\tt close} on their file
+descriptor.  {\tt close} callbacks are allowed to return error values,
+but this does not prevent the file from actually closing.
+
+
+
+\subsection{The {\tt read} callback}
+\label{read-callback}
+
+Returning to our {\tt cat /dev/hello-world} example, what happens
+after the {\tt open} is successful?  Next, {\tt cat} will try to use
+{\tt read(2)}, which will get proxied by FUSD to the function {\tt
+do\_read} on line 13.  This function takes some additional arguments
+that we didn't see in the open and close callbacks:
+\begin{itemize}
+\item {\tt struct fusd\_file\_info *file}---The first argument to all
+callbacks, containing information which describes the file; see
+Section~\ref{fusd-file-info}.
+\item {\tt char *user\_buffer}---The buffer that the callback should use to
+write data that it is returning to the user.
+\item {\tt size\_t user\_length}---The maximum number of bytes
+requested by the user.  The driver is allowed to return fewer bytes,
+but should never write more then {\tt user\_length} bytes into {\tt
+user\_buffer}.
+\item {\tt loff\_t *offset}---A pointer to an integer which represents
+the caller's offset into the file (i.e., the user's file pointer).
+This value can be modified by the callback; any change will be
+propagated back to the user's file pointer inside the kernel.
+\end{itemize}
+
+The semantics of the return value are the same as if the
+callback were being written inside the kernel itself:
+\begin{itemize}
+\item Positive return values indicate success.  If the call is
+successful, and the driver has copied data into {\tt buffer}, the
+return value indicates how many bytes were copied.  This number should
+never be greater than the {\tt user\_length} argument.
+\item A 0 return value indicates EOF has been reached on the file.
+\item As in the {\tt open} and {\tt close} callbacks, negative values
+(such as -EPERM, -EPIPE, or -ENOMEM) indicate errors. Such values will
+cause the user's {\tt read()} to return -1 with errno set
+appropriately.
+\end{itemize}
+
+The first time a read is done on a device file, the user's file
+pointer ({\tt *offset}) is 0.  In the case of this first read, a
+greeting message of {\tt Hello, world!} is copied back to the user, as
+seen on line 24.  The user's file pointer is then advanced.  The next
+read therefore fails the comparison at line 20, falling straight
+through to return 0, or EOF.
+
+In this simple program, we also see an example of an error return on
+line 22: if the user tries to do a read smaller than the length of the
+greeting message, the read will fail with -EINVAL.  (In an actual
+driver, it would normally not be an error for a user to provide a
+smaller read buffer than the size of the available data.  The right
+way for drivers to handle this situation is to return partial data,
+then move {\tt *offset} forward so that the remainder is returned on
+the next {\tt read()}.  We see an example of this in
+Program~\ref{echo.c}.)
+
+\subsection{The {\tt write} callback}
+
+Program~\ref{helloworld.c} illustrated how a driver could return data
+{\em to} a client using the {\tt read} callback.  As you might expect, there
+is a corresponding {\tt write} callback that allows the driver to
+receive data {\em from} a client.  {\tt write} takes four arguments,
+similar to the {\tt read} callback:
+
+\begin{itemize}
+\item {\tt struct fusd\_file\_info *file}---The first argument to all
+callbacks, containing information which describes the file; see
+Section~\ref{fusd-file-info}.
+\item {\tt const char *user\_buffer}---Pointer to data being written
+by the client (read-only).
+\item {\tt size\_t user\_length}---The number of bytes pointed to by
+{\tt user\_buffer}.
+\item {\tt loff\_t *offset}---A pointer to an integer which represents
+the caller's offset into the file (i.e., the user's file pointer).
+This value can be modified by the callback; any change will be
+propagated back to the user's file pointer inside the kernel.
+\end{itemize}
+
+The semantics of {\tt write}'s return value are the same as in a
+kernel callback:
+\begin{itemize}
+\item Positive return values indicate success and indicate how many
+bytes of the user's buffer were successfully written (i.e.,
+successfully processed by the driver in some way).  The return value
+may be less than or equal to the {\tt user\_length} argument, but
+should never be greater.
+\item 0 should only be returned in response to a {\tt write} of length
+0.
+\item Negative values (such as -EPERM, -EPIPE, or -ENOMEM) indicate
+errors.  Such values will cause the user's {\tt write()} to return -1
+with errno set appropriately.
+\end{itemize}
+
+Program~\ref{echo.c}, echo.c, is an example implementation of a device
+({\tt /dev/echo}) that uses both {\tt read()} and {\tt write()}
+callbacks.  A client that tries to {\tt read()} from this device will
+get the contents of the most recent {\tt write()}.  For example:\\
+\begin{minipage}{\textwidth}
+\vspace{\baselineskip}
+\begin{verbatim}
+% echo Hello there > /dev/echo
+% cat /dev/echo
+Hello there
+% echo Device drivers are fun > /dev/echo
+% cat /dev/echo
+Device drivers are fun
+
+\end{verbatim}
+\end{minipage}
+
+\begin{Program}
+\listinginput[5]{1}{echo.c.example}
+\caption{echo.c: Using both {\tt read} and {\tt write} callbacks}
+\label{echo.c}
+\end{Program}
+
+The implementation of {\tt /dev/echo} keeps a global variable, {\tt
+data}, which serves as a cache for the data most recently written to
+the driver by a client program.  The driver does not assume the data
+is null-terminated, so it also keeps track of the number of bytes of
+data available.  (These two variables appear on lines 1--2.)
+
+The driver's {\tt write} callback first frees any data which might
+have been allocated by a previous call to write (lines 26--29).  Next,
+on line 33, it attempts to allocate new memory for the new data
+arriving.  If the allocation fails, {\tt -ENOMEM} is returned to the
+client.  If the allocation is successful, the driver copies the data
+into its local buffer and stores its length (lines 37--38).  Finally,
+the driver tells the user that the entire buffer was consumed by
+returning a value equal to the number of bytes the user tried to write
+({\tt user\_length}).
+
+The {\tt read} callback has some extra features that we did not see in
+Program~\ref{helloworld.c}'s {\tt read()} callback.  The most
+important is that it allows the driver to read the available data {\em
+incrementally}, instead of requiring that the first {\tt read()}
+executed by the client has enough space for all the data the driver
+has available.  In other words, a client can do two 50-byte reads,
+and expect the same effect as if it had done a single 100-byte read.
+
+This is implemented using {\tt *offset}, the user's file pointer.  If
+the user is trying to read past the amount of data we have available,
+the driver returns EOF (lines 8--9).  Normally, this happens after the
+client has finished reading data.  However, in this driver, it might
+happen on a client's first read if nothing has been written to the
+driver yet or if the most recent write's memory allocation failed.
+
+If there is data to return, the driver computes the number of bytes
+that should be copied back to the client---the minimum of the number
+of bytes the user asked for, and the number of bytes of data that this
+client hasn't seen yet (line 12).  This data is copied back to the
+user's buffer (line 15), and the user's file pointer is advanced
+accordingly (line 16).  Finally, on line 19, the client is told how
+many bytes were copied to its buffer.
+
+
+\subsection{Unregistering a device with {\tt fusd\_unregister()}}
+
+All devices registered by a driver are unregistered automatically when
+the program exits (or crashes).  However, the {\tt fusd\_unregister()}
+function can be used to unregister a device without terminating the
+entire driver.  {\tt fusd\_unregister} takes one argument: a device
+handle (i.e., the return value from {\tt fusd\_register()}).
+
+A device can be unregistered at any time.  Any client system calls
+that are pending when a device is unregistered will return immediately
+with an error.  In this case, {\tt errno} will be set to {\tt -EPIPE}.
+
+
+\section{Using Information in {\tt fusd\_file\_info}}
+
+\label{fusd-file-info}
+
+We mentioned in the previous sections that the first argument to every
+callback is a pointer to a {\tt fusd\_file\_info} structure.  This
+structure contains information that can be useful to driver
+implementers in deciding how to respond to a system call request.
+
+The fields of {\tt fusd\_file\_info} structures fall into several
+categories:
+\begin{itemize}
+\item {\em Read-only.}  The driver can inspect the value, but changing
+it will have no effect.
+\begin{itemize}
+\item {\tt pid\_t pid}: The process ID of the process making the
+request
+\item {\tt uid\_t uid}: The user ID of the owner of the process making
+the request
+\item {\tt gid\_t gid}: The group ID of the owner of the process making
+the request
+\end{itemize}
+\item {\em Read-write.}  Any changes to the value will be propagated
+back to the kernel and be written to the appropriate in-kernel
+structure.
+\begin{itemize}
+\item {\tt unsigned int flags}: A copy of the {\tt f\_flags} field in
+the kernel's {\tt file} structure.  The flags are an or'd-together set
+of the kernel's {\tt O\_} series of flags: {\tt O\_NONBLOCK}, {\tt
+O\_APPEND}, {\tt O\_SYNC}, etc.
+\item {\tt void *device\_info}: The data passed to {\tt
+fusd\_register} when the device was registered; see
+Section~\ref{device-info} for details
+\item {\tt void *private\_data}: A generic per-file-descriptor pointer
+usable by the driver for its own purposes, such as to keep state (or a
+pointer to state) that should be maintained between operations on the
+same instance of an open file.  It is guaranteed to be NULL when the
+file is first opened.  See Section~\ref{private-data} for more
+details.
+\end{itemize}
+\item {\em Hidden fields.}  The driver should not touch these fields
+(such as {\tt fd}).  They contain state used by the FUSD library to
+generate the reply sent to the kernel.
+\end{itemize}
+
+{\bf Important note:} the value of the {\tt fusd\_file\_info} pointer
+itself has {\em no meaning}.  Repeated requests on the same file
+descriptor {\em will not} generate callbacks with identical {\tt
+fusd\_file\_info} pointer values, as would be the case with an
+in-kernel driver.  In other words, if a driver needs to keep state in
+between successive system calls on a user's file descriptor, it {\em
+must} store that state using the {\tt private\_data} field.  The {\tt
+fusd\_file\_info} pointer itself is ephemeral; the data to which it
+points is persistent.
+
+Program~\ref{uid-filter.c} shows an example of how a driver might make
+use of the data in the {\tt fusd\_file\_info} structure.  Much of the
+driver is identical to helloworld.c.  However, instead of printing a
+static greeting, this new program generates a custom message each time
+the device file is read, as seen on line 25.  The message contains the
+PID of the user process that requested the read ({\tt file->pid}).
+
+\begin{Program}
+\listinginput[5]{1}{uid-filter.c.example}
+\caption{uid-filter.c: Inspecting data in {\tt fusd\_file\_info} such
+as UID and PID of the calling process}
+\label{uid-filter.c}
+\end{Program}
+
+In addition, Program~\ref{uid-filter.c}'s {\tt open} callback does not
+return 0 (success) unconditionally as it did in
+Program~\ref{helloworld.c}.  Instead, it checks (on line 7) to make
+sure the UID of the process trying to read from the device ({\tt
+file->uid}) matches the UID under which the driver itself is running
+({\tt getuid()}).  If they don't match, -EPERM is returned.  In other
+words, only the user who ran the driver is allowed to read from the
+device that it creates.  If any other user---including root!---tries
+to open it, a ``Permission denied'' error will be generated.
+
+
+\subsection{Registration of Multiple Devices, and Passing Data to Callbacks}
+
+\label{device-info}
+
+Device drivers frequently expose several different ``flavors'' of a
+device.  For example, a single magnetic tape drive will often have
+many different device files in {\tt /dev}.  Each device file
+represents a different combination of options such as
+rewind/no-rewind, or compressed/uncompressed.  However, they access
+the same physical tape drive.
+
+Traditionally, the device file's {\em minor number} was used to
+communicate the desired options with device drivers.  But, since devfs
+dynamically (and unpredictably) generates both major and minor numbers
+every time a device is registered, a different technique was
+developed.  When using devfs, drivers are allowed to associate a value
+(of type {\tt void *}) with each device they register.  This facility
+takes the place of the minor number.
+
+The devfs solution is also used by FUSD.  The mysterious third
+argument to {\tt fusd\_register} that we mentioned in
+Section~\ref{using-fusd-register} is an arbitrary piece of data that
+can be passed to FUSD when a device is registered.  Later, when a
+callback is activated, the contents of that argument are available in
+the {\tt device\_info} member of the {\tt fusd\_file\_info} structure.
+
+Program~\ref{drums.c} shows an example of this technique, inspired by 
+Alessandro Rubini's similar devfs tutorial
+\htmladdnormallinkfoot{published in Linux
+Magazine}{http://www.linux.it/kerneldocs/devfs/}.  It creates a number
+of devices in the {\tt /dev/drums} directory, each of which is useful
+for generating a different kind of ``sound''---{\tt /dev/drums/bam},
+{\tt /dev/drums/boom}, and so on.  Reading from any of these devices
+will return a string equal to the device's name.
+
+\begin{Program}
+\listinginput[5]{1}{drums.c.example}
+\caption{drums.c: Passing private data to {\tt fusd\_register} and
+retrieving it from {\tt device\_info}}
+\label{drums.c}
+\end{Program}
+
+The first thing to notice about {\tt drums.c} is that it registers
+more than one FUSD device.  In the loop starting in line 31, it calls
+{\tt fusd\_register()} once for every device named in {\tt
+drums\_strings} on line 1.  When {\tt fusd\_run()} is called, it
+automatically watches every device the driver registered, and
+activates the callbacks associated with each device as needed.
+Although {\tt drums.c} uses the same set of callbacks for every device
+it registers (as can be seen on line 33), each device could have
+different callbacks if desired.  (Not shown is the initialization of
+{\tt drums\_fops}, which assigns {\tt drums\_read} to be the {\tt
+read} callback.)
+
+If {\tt drums\_read} is called for all 6 types of drums, how does it
+know which device it's supposed to be servicing when it gets called?
+The answer is in the third argument of {\tt fusd\_register()}, which
+we were previously ignoring.  Whatever value is passed to {\tt
+fusd\_register()} will be passed back to the callback in the {\tt
+device\_info} field of the {\tt fusd\_file\_info} structure.  The name
+of the drum sound is passed to {\tt fusd\_register} on line 33, and
+later retrieved by the driver on line 12.
+
+Although this example uses a string as its {\tt device\_info}, the
+pointer can be used for anything---a mode number, a pointer to a
+configuration structure, and so on.
+
+
+\subsection{The difference between {\tt device\_info} and {\tt
+private\_data}}
+
+\label{private-data}
+
+As we mentioned in Section~\ref{fusd-file-info}, the {\tt
+fusd\_file\_info} structure has two seemingly similar fields, both of
+which can be used by drivers to store their own data: {\tt
+device\_info} and {\tt private\_data}.  However, there is an important
+difference between them:
+
+\begin{itemize}
+
+\item {\tt private\_data} is stored {\em per file descriptor}.  If 20
+processes open a FUSD device (or, one process opens a FUSD device 20
+times), each of those 20 file descriptors will have their own copy of
+{\tt private\_data} associated with them.  This field is therefore
+useful to drivers that need to differentiate multiple requests to a
+single device that might be serviced in parallel.  (Note that most
+UNIX variants, including Linux, do allow multiple processes to share a
+single file descriptor---specifically, if a process {\tt open}s a
+file, then {\tt fork}s.  In this case, processes will also share a
+single copy of {\tt private\_data}.)
+
+The first time a FUSD driver sees {\tt private\_data} (in the {\tt
+open} callback), it is guaranteed to be NULL.  Any changes to it by a
+driver callback will only affect the state associated with that single
+file descriptor.
+
+\item {\tt device\_info} is kept {\em per device}.  That is, {\em all}
+clients of a device share a {\em single} copy of {\tt device\_info}.
+Unlike {\tt private\_data}, which is always initialized to NULL, {\tt
+device\_info} is always initialized to whatever value the driver
+passed to {\tt fusd\_register} as described in the previous section.
+If a callback changes the copy of {\tt device\_info} in the {\tt
+fusd\_file\_info} structure, this has no effect; {\tt device\_info}
+can only be set at registration time, with {\tt fusd\_register}.
+
+\end{itemize}
+
+In short, {\tt device\_info} is used to differentiate {\em devices}.
+{\tt private\_data} is used to differentiate {\em users of those
+devices}.
+
+Program~\ref{drums2.c}, drums2.c, illustrates the difference between
+{\tt device\_info} and {\tt private\_data}.  Like the original
+drums.c, it creates a bunch of devices in {\tt /dev/drums/}, each of
+which ``plays'' a different sound.  However, it also does something
+new: keeps track of how many times each device has been opened.  Every
+read to any drum gives you the name of its sound as well as your
+unique ``user number''.  And, instead of returning just a single line
+(as drums.c did), it will keep generating more ``sound'' every time a
+{\tt read()} system call arrives.
+
+\begin{Program}
+\listinginput[5]{1}{drums2.c.example}
+\caption{drums2.c: Using both {\tt device\_info} and {\tt private\_data}}
+\label{drums2.c}
+\end{Program}
+
+The trick is that we want to keep users separate from each other.  For
+example, user one might type:\\
+\begin{minipage}{\textwidth}
+\vspace{\baselineskip}
+\begin{verbatim}
+% more /dev/drums/bam
+You are user 1 to hear a drum go 'bam'!
+You are user 1 to hear a drum go 'bam'!
+You are user 1 to hear a drum go 'bam'!
+...
+
+\end{verbatim}
+\end{minipage}
+
+Meanwhile, another user in a different shell might type the same
+command at the same time, and get different results:\\
+\begin{minipage}{\textwidth}
+\vspace{\baselineskip}
+\begin{verbatim}
+% more /dev/drums/bam
+You are user 2 to hear a drum go 'bam'!
+You are user 2 to hear a drum go 'bam'!
+You are user 2 to hear a drum go 'bam'!
+...
+
+\end{verbatim}
+\end{minipage}
+
+The idea is that no matter how long those two users go on reading
+their devices, the driver always generates a message that is specific
+to that user.  The two users' data are not intermingled.
+
+To implement this, Program~\ref{drums2.c} introduces a new {\tt
+drum\_info} structure (lines 1-4), which keeps track of both the
+drum's name, and the number of time each drum device has been opened.
+An instance of this structure, {\tt drums}, is initialized on lines
+4-8.  Note that the call to {\tt fusd\_register} (line 45) now passes
+a pointer to a {\tt drum\_info} structure.  (This {\tt drum\_info *}
+pointer is shared by every instance of a client that opens a
+particular type of drum.)
+
+Each time a drum device is opened, its {\tt drum\_info} structure is
+retrieved from {\tt device\_info} (line 15).  Then, on line 18, the
+{\tt num\_users} field is incremented and the new user number is
+stored in {\tt fusd\_file\_info}'s {\tt private\_data} field.  To
+reiterate our earlier point: {\em {\tt device\_info} contains
+information global to all users of a device, while {\tt private\_data}
+has information specific to a particular user of the device.}
+
+It's also worthwhile to note that when we increment {\tt num\_users}
+on line 18, a simple {\tt num\_users++} is correct.  If this was a
+driver inside the kernel, we'd have to use something like {\tt
+atomic\_inc()} because a plain {\tt i++} is not atomic.  Such a
+non-atomic statement will result in a race condition on SMP platforms,
+if an interrupt handler also touches {\tt num\_users}, or in some
+future Linux kernel that is preemptive.  Since this FUSD driver is
+just a plain, single-threaded user-space application, good old {\tt
+++} still works.
+
+
+\section{Writing {\tt ioctl} Callbacks}
+
+The POSIX API provides for a function called {\tt ioctl}, which allows
+``out-of-band'' configuration information to be passed to a device
+driver through a file descriptor.  Using FUSD, you can write a device
+driver with a callback to handle {\tt ioctl} requests from clients.
+For the most part, it's just like writing a callback for {\tt read} or
+{\tt write}, as we've seen in previous sections.  From the client's
+point of view, {\tt ioctl} traditionally takes three arguments: a file
+descriptor, a command number, and a pointer to any additional data
+that might be required for the command.
+
+\subsection{Using macros to generate {\tt ioctl} command numbers}
+
+The Linux header file {\tt /usr/include/asm/ioctl.h} defines macros
+that {\em must} be used to create the {\tt ioctl} command number.
+These macros take various combinations of three arguments:
+
+\begin{itemize}
+
+\item {\tt type}---an 8-bit integer selected to be specific to the
+device driver.  {\tt type} should be chosen so as not to conflict with
+other drivers that might be ``listening'' to the same file descriptor.
+(Inside the kernel, for example, the TCP and IP stacks use distinct
+numbers since an {\tt ioctl} sent to a socket file descriptor might be
+examined by both stacks.)
+
+\item {\tt number}---an 8-bit integer ``command number.''  Within a
+driver, distinct numbers should be chosen for each different kind of
+{\tt ioctl} command that the driver services.
+
+\item {\tt data\_type}---The name of a type used to compute how many
+bytes are exchanged between the client and the driver.  This argument
+is, for example, the name of a structure.
+
+\end{itemize}
+
+The macros used to generate command numbers are:
+
+\begin{itemize}
+
+\item {\tt \_IO(int type, int number)} -- used for a simple ioctl that
+sends nothing but the type and number, and receives back nothing but
+an (integer) retval.
+
+\item {\tt \_IOR(int type, int number, data\_type)} -- used for an
+ioctl that reads data {\em from} the device driver.  The driver will
+be allowed to return {\tt sizeof(data\_type)} bytes to the user.
+
+\item {\tt \_IOW(int type, int number, data\_type)} -- similar to
+\_IOR, but used to write data {\em to} the driver.
+
+\item {\tt \_IORW(int type, int number, data\_type)} -- a combination
+of {\tt \_IOR} and {\tt \_IOW}.  That is, data is both written to the
+driver and then read back from the driver by the client.
+\end{itemize}
+
+\begin{Program}
+\listinginput[5]{1}{ioctl.h.example}
+\caption{ioctl.h: Using the {\tt \_IO} macros to generate {\tt ioctl}
+command numbers}
+\label{ioctl.h}
+\end{Program}
+
+Program~\ref{ioctl.h} is an example header file showing the use of
+these macros.  In real programs, the client executing an ioctl and the
+driver that services it must share the same header file.
+
+\subsection{Example client calls and driver callbacks}
+
+Program~\ref{ioctl-client.c} shows a client program that executes {\tt
+ioctl}s using the ioctl command numbers defined in
+Program~\ref{ioctl.h}.  The {\tt ioctl\_data\_t} is
+application-specific; our simple test program defines it as a
+structure containing two arrays of characters.  The first {\tt ioctl}
+call (line 10) sends the command {\tt IOCTL\_TEST3}, which retrieves
+strings {\em from} the driver.  The second {\tt ioctl} uses the
+command {\tt IOCTL\_TEST4} (line 18), which sends strings {\em to} the
+driver.
+
+\begin{Program}
+\listinginput[5]{1}{ioctl-client.c.example}
+\caption{ioctl-client.c: A program that makes {\tt ioctl} requests on
+a file descriptor}
+\label{ioctl-client.c}
+\end{Program}
+
+The portion of the FUSD driver that services these calls is shown in
+Program~\ref{ioctl-server.c}.
+
+\begin{Program}
+\listinginput[5]{1}{ioctl-server.c.example}
+\caption{ioctl-server.c: A driver that handles {\tt ioctl} requests}
+\label{ioctl-server.c}
+\end{Program}
+
+The ioctl example header file and test programs shown in this document
+(Programs~\ref{ioctl.h}, \ref{ioctl-client.c}, and
+\ref{ioctl-server.c}) are actually contained in a larger, single
+example program included in the FUSD distribution called {\tt
+ioctl.c}.  That source code shows other variations on calling and
+servicing {\tt ioctl} commands.
+
+
+\section{Integrating FUSD With Your Application Using {\tt fusd\_dispatch()}}
+\label{selecting}
+
+The example applications we've seen so far have something in common:
+after initialization and device registration, they call {\tt
+fusd\_run()}.  This gives up control of the program's flow, turning it
+over to the FUSD library instead.  This worked fine for our simple
+example programs, but doesn't work in a real program that needs to
+wait for events other than FUSD callbacks.  For this reason, our
+framework provides another way to activate callbacks that does not
+require the driver to give up control of its {\tt main()}.
+
+\subsection{Using {\tt fusd\_dispatch()}}
+
+Recall from Section~\ref{using-fusd-register} that {\tt
+fusd\_register} returns a {\em file descriptor} for every device that
+is successfully registered.  This file descriptor can be used to
+activate device callbacks ``manually,'' without passing control of the
+application to {\tt fusd\_run()}.  Whenever the file descriptor
+becomes readable according to {\tt select(2)}, it should be passed to
+{\tt fusd\_dispatch()}, which in turn will activate callbacks in the
+same way that {\tt fusd\_run()} does.  In other words, an application
+can:
+\begin{enumerate}
+\item Save the file descriptors returned by {\tt fusd\_register()};
+\item Add those FUSD file descriptors to an {\tt fd\_set} that is
+passed to {\tt select}, along with any other file
+descriptors that might be interesting to the application; and
+\item Pass every FUSD file descriptor that {\tt select} indicates is
+readable to {\tt fusd\_dispatch}.
+\end{enumerate}
+
+{\tt fusd\_dispatch()} returns 0 if at least one callback was
+successfully activated.  On error, -1 is returned with {\tt errno} set
+appropriately.  {\tt fusd\_dispatch()} will never block---if no
+messages are available from the kernel, it will return -1 with {\tt
+errno} set to {\tt EAGAIN}.
+
+\subsection{Helper Functions for Constructing an {\tt fd\_set}}
+
+The FUSD library provides two (optional) utility functions that can
+make it easier to write applications that integrate FUSD into their
+own {\tt select()} loops.  Specifically:
+\begin{itemize}
+\item {\tt void fusd\_fdset\_add(fd\_set *set, int *max)}---is meant
+to help construct an {\tt fd\_set} that will be passed as the
+``readable fds'' set to select.  This function adds the file
+descriptors of all previously registered FUSD devices to the fd\_set
+{\tt set}.  It assumes that {\tt set} has already been initialized by
+the caller.  The integer {\tt max} is updated to reflect the largest
+file descriptor number in the set.  {\tt max} is not changed if the
+value passed to {\tt fusd\_fdset\_add} is already larger than the
+largest FUSD file descriptor added to the set.
+
+\item {\tt void fusd\_dispatch\_fdset(fd\_set *set)}---is meant to be
+called on the {\tt fd\_set} that is {\em returned} by select.  It
+assumes that {\tt set} contains a set file descriptors that {\tt
+select()} has indicated are readable.  {\tt fusd\_dispatch\_fdset()}
+calls {\tt fusd\_dispatch} on every descriptor in {\tt set} that is a
+valid FUSD descriptor.  Non-FUSD descriptors in {\tt set} are
+ignored.
+\end{itemize}
+
+
+\begin{Program}
+\listinginput[5]{1}{drums3.c.example}
+\caption{drums3.c: Waiting for both FUSD and non-FUSD events in a
+{\tt select} loop}
+\label{drums3.c}
+\end{Program}
+
+The excerpt of {\tt drums3.c} shown in Program~\ref{drums3.c}
+demonstrates the use of these helper functions.  This program is
+similar to the earlier drums.c example: it creates a number of musical
+instruments such as {\tt /dev/drums/bam} and {\tt /dev/drums/boom}.
+However, in addition to servicing its musical callbacks, the driver
+also prints a prompt to standard input asking how ``loud'' the drums
+should be.  Instead of turning control of {\tt main()} over to {\tt
+fusd\_run()} as in the previous examples, {\tt drums3} uses {\tt
+select()} to simultaneously watch its FUSD file descriptors and standard
+input.  It responds to input from both sources.
+
+On lines 2--5, an {\tt fd\_set} and its associated ``max'' value are
+initialized to contain stdin's file descriptor.  On line 9, we use
+{\tt fusd\_fdset\_add} to add the FUSD file descriptors for all
+registered devices.  (Not shown in this excerpt is the device
+registration, which is the same as the registration code we saw in
+{\tt drums.c}.)  On line 13 we call select, which blocks until one of
+the fd's in the set is readable.  On lines 17 and 18, we check to see
+if standard input is readable; if so, a function is called which reads
+the user's response from standard input and prints a new prompt.
+Finally, on line 21, we call {\tt fusd\_dispatch\_fdset}, which in
+turn will activate the callbacks for devices that have pending system
+calls waiting to be serviced.
+
+It's worth reiterating that drivers are not required to use the FUSD
+helper functions {\tt fusd\_fdset\_add} and {\tt
+fusd\_dispatch\_fdset}.  If it's more convenient, a driver can
+manually save all of the file descriptors returned by {\tt
+fusd\_register}, construct its own {\tt fd\_set}, and then call {\tt
+fusd\_dispatch} on each descriptor that is readable.  This method is
+sometimes required for integration with other frameworks that want to
+take over your {\tt main()}.  For example, the 
+\htmladdnormallinkfoot{GTK user interface
+framework}{http://www.gtk.org} is event-driven and requires that you
+pass control of your {\tt main} to it.  However, it does allow you to
+give it a file descriptor and a function pointer, saying ``Call this
+callback when {\tt select} indicates this file descriptor has become
+readable.''  A GTK application that implements FUSD devices can work
+by giving GTK all the FUSD file descriptors individually, and calling
+{\tt fusd\_dispatch()} when GTK calls the associated callbacks.
+
+
+
+\section{Implementing Blocking System Calls}
+
+All of the example drivers that we've seen until now have had an
+important feature missing: they never had to {\em wait} for anything.
+So far, a driver's response to a system call has always been
+immediately available---allowing the driver to response immediately.
+However, real devices are often not that lucky: they usually have to
+wait for something to happen before completing a client's system call.
+For example, a driver might be waiting for data to arrive from the
+serial port or over the network, or even waiting for a user action.
+
+In situations like this, a basic capability most device drivers must
+have is the ability to {\em block} the caller.  Blocking operations
+are important because they provide a simple interface to user programs
+that does flow control, rather than something more expensive like
+continuous polling.  For example, user programs expect to be able to
+execute a statement like {\tt read(fd, buf, sizeof(buf))}, and expect
+the read call to block (stop the flow of the calling program) until
+data is available.  This is much simpler and more efficient than
+polling repeatedly.
+
+In the following sections, we'll describe how to block and unblock
+system calls for devices that use FUSD.
+
+
+\subsection{Blocking the caller by blocking the driver}
+
+The easiest (but least useful) way to block a client's system call is
+simply to block the driver, too.  For example, consider
+Program~\ref{console-read.c}, which implements a device called {\tt
+/dev/console-read}.  Whenever a process tries to read from this
+device, the driver prints a prompt to standard input, asking for a
+reply.  (The prompt appears in the shell the driver was run in, not
+the shell that's trying to read from the device.)  When the user
+enters a line of text, the response is returned to the client that did
+the original {\tt read()}.  By blocking the driver waiting for the
+reply, the client that issued the system call is blocked as well.
+
+\begin{Program}
+\listinginput[5]{1}{console-read.c.example}
+\caption{console-read.c: A simple blocking system call}
+\label{console-read.c}
+\end{Program}
+
+Blocking the driver this way is safe---unlike programming in the
+kernel proper, where doing something like this would block the entire
+system.  It's also easy to implement, as seen from the example above.
+However, it makes the driver unresponsive to system call requests that
+might be coming from other clients.  If another process tries to do
+anything at all with a blocked driver's device---even an {\tt
+open()}---it will block until the driver wakes up again.  This
+limitation makes blocking drivers inappropriate for any device driver
+that expects to service more than one client at a time.
+
+
+\subsection{Blocking the caller using {\tt -FUSD\_NOREPLY};
+unblocking it using {\tt fusd\_return()}}
+\label{fusd-noreply}
+
+If a device driver expects more than one client at a time---as is
+often the case---a slightly different programming model is needed for
+system calls that can potentially block.  Instead of blocking, the
+driver immediately sends a message to the FUSD framework that says, in
+essence, ``Don't unblock the client that issued this system call, but
+continue sending additional system call requests that might be coming
+from other clients.''  Driver callbacks can send this message to FUSD
+by returning the special value {\tt -FUSD\_NOREPLY} instead of a
+normal system call return value.
+
+Before a callback blocks the caller by returning {\tt -FUSD\_NOREPLY},
+it must save the {\tt fusd\_file\_info} pointer that was provided to
+the callback as its first argument.  Later, when an event occurs which
+allows the client's blocked system call to complete, the driver should
+call {\tt fusd\_return()}, which will unblock the calling process and
+complete its system call.  {\tt fusd\_return()} takes two arguments:
+\begin{itemize}
+\item The {\tt fusd\_file\_info} pointer that the callback saved
+earlier; and
+\item The system call's return value (in other words, the value that
+would have been returned by the callback function had it not returned
+{\tt -FUSD\_NOREPLY}).  FUSD itself {\em does not} examine the return
+value passed as the second argument to {\tt fusd\_return}; it simply
+propagates that value back to the kernel as the return value of the
+blocked system call.
+\end{itemize}
+
+Drivers should never call {\tt fusd\_return} more than once on a
+single {\tt fusd\_file\_info} pointer.  Doing so will have undefined
+results, similar to calling {\tt free()} twice on the same pointer.
+
+It also bears repeating that a callback can call {\em either} call
+fusd\_return() explicitly {\em or} return a normal return value (i.e.,
+not {\tt -FUSD\_NOREPLY}), but not both.
+
+{\tt -FUSD\_NOREPLY} and {\tt fusd\_return()} make it easy for a
+driver to block a process, then unblock it later when data becomes
+available.  When the callback returns {\tt -FUSD\_NOREPLY}, the driver
+is freed up to wait for other events, even though the process making
+the system call is still blocked.  The driver can then wait for
+something to happen that unblocks the original caller---for example,
+another FUSD event, data from a serial port, or data from the network.
+(Recall from Section~\ref{selecting} that a FUSD driver can
+simultaneously wait for both FUSD and non-FUSD events.)
+
+FUSD includes an example program, {\tt pager.c}, which demonstrates
+these techniques.  The pager driver implements a simple notification
+interface which lets any number of ``waiters'' wait for a signal from
+a ``notifier.''  All the waiters wait by trying to read from {\tt
+/dev/pager/notify}.  Those reads will block until a notifier writes
+the string {\tt page} to {\tt /dev/pager/input}.  It's easy to try
+the application out---run the driver, and then open three other
+shells.  In two of them, type {\tt cat /dev/pager/notify}.  The reads
+will block.  Then, in the third shell, type {\tt echo page >
+/dev/pager/input}---the other two shells should become unblocked.
+
+Let's take a look at how this application is implemented, step by
+step.
+
+\subsubsection{Keeping Per-Client State}
+
+The first thing to notice about {\tt pager.c} is that it keeps {\em
+per-client state}.  That is, for every file descriptor open to the
+driver, a structure is allocated that has information relating to that
+file descriptor.  Previous driver examples were, for the most part,
+{\em reactive}---they received requests, and immediately generated
+responses.  Since there was never more than one request outstanding,
+there was no need to keep a list of them.  The pager application is
+the first one that must keep track of an arbitrary number of requests
+that might be outstanding at the same time.  The first excerpt of {\tt
+pager.c}, which appears in Program~\ref{pager-open.c}, shows the code
+which creates this per-client state.  Lines 1--6 define a structure,
+{\tt pager\_client}, which keeps all the information we need about
+each client attached to the driver.  The {\tt open} callback for {\tt
+/dev/pager/notify}, shown on lines 12--31, allocates memory for an
+instance of this structure and adds it to a linked list.  (If the
+memory allocation fails, an error is returned to the client on line
+18; this will prevent the file from opening.)  Note on line 25 that we
+use the {\tt private\_data} field to store a pointer to the client
+state; this allows the structure to be retrieved when later callbacks
+on this file descriptor arrive.  The memory is deallocated when the
+file is closed; we'll see that in a later section.
+
+\begin{Program}
+\listinginput[5]{1}{pager-open.c.example}
+\caption{pager.c (Part 1): Creating state for every client using the
+driver}
+\label{pager-open.c}
+\end{Program}
+
+Another thing to notice about the open callback is the use of the {\tt
+last\_page\_seen} variable.  The driver gives a sequence number to
+every page it receives; {\tt last\_page\_seen} stores the number of
+the most recent page seen by a client.  When a new client arrives
+(i.e., it opens {\tt /dev/pager/notify}), its {\tt last\_page\_seen}
+state is set equal to the page that has most recently arrived; this
+forces a new client to wait for the {\em next} page, rather than
+immediately being notified of a page that has arrived in the past.
+
+\subsubsection{Blocking and completing reads}
+
+The next part of {\tt pager.c} is shown in Program~\ref{pager-read.c}.
+The {\tt pager\_notify\_read} function seen on line 1 is registered as
+the {\tt read} callback for the {\tt /dev/pager/notify} device.  It
+blocks the read request using the technique we described earlier: it
+stores the {\tt fusd\_file\_info} pointer in that client's state
+structure, and returns {\tt -FUSD\_NOREPLY}.  (Note that the pointer
+to the client's state structure comes from the {\tt private\_data}
+field of {\tt fusd\_file\_info}, where the open callback stored it.)
+
+\begin{Program}
+\listinginput[5]{1}{pager-read.c.example}
+\caption{pager.c (Part 2): Block clients' {\tt read} requests, and later
+completing the blocked reads}
+\label{pager-read.c}
+\end{Program}
+
+
+{\tt pager\_notify\_complete\_read} {\em unblocks} previously blocked
+reads.  This function first checks to see that there is, in fact, a blocked
+read (line 19).  It then checks to see if a page has arrived that the
+client hasn't seen yet (line 23).  Finally, it updates the client
+state and unblocks the blocked read by calling {\tt fusd\_return}.
+Note the second argument to {\tt fusd\_return} is a 0; as we
+saw in Section~\ref{read-callback}, a 0 return value to a {\tt read}
+system call means EOF.  (The system call will be unblocked regardless
+of the return value.)
+
+{\tt pager\_notify\_complete\_read} is called every time a new page
+arrives.  New pages are processed by {\tt pager\_input\_write} (line
+34), which is the {\tt write} callback for {\tt /dev/pager/input}.
+After recording the fact that a new page has arrived, it calls {\tt
+pager\_notify\_complete\_read} for each client that has an open file
+descriptor.  This will complete the reads of any clients who have not
+yet seen this new data, and have no effect on clients that don't have
+outstanding reads.
+
+There is another interesting point to notice about {\tt
+pager\_notify\_read}.  On line 12, after it stores the blocked system
+call's pointer, but before we return {\tt -FUSD\_NOREPLY}, it calls
+the completion function.  This has the effect of returning any data
+that might already be available back to the caller immediately.  If
+that happens, we will end up calling {\tt fusd\_return} {\em before}
+we return {\tt -FUSD\_NOREPLY}.  This probably seems strange, but it's
+legal.  Recall that a callback can call fusd\_return() explicitly {\em
+or} return a normal (not {\tt -FUSD\_NOREPLY}) return value, but not
+both; the order doesn't matter.
+
+\subsubsection{Using {\tt fusd\_destroy()} to clean up client state}
+\label{fusd-destroy}
+
+Finally, let's take a look at one last aspect of the pager program:
+how it cleans up the per-client state when a client leaves.  This is
+mostly straightforward, with one exception: a client may have an
+outstanding read request out when a close request comes in.  Normally,
+a client can't make another system call request while a previous
+system call is still blocked.  However, the {\tt close} system call is
+an exception: it gets called when a client dies (for example, if it
+receives an interrupt signal).  If a {\tt close} comes in while
+another system call is still outstanding, the state associated with
+the outstanding request should be freed to avoid a memory leak.  The
+{\tt fusd\_destroy} function is used to do this, seen on linen 12-14
+of Program~\ref{pager-close.c}.
+
+\begin{Program}
+\listinginput[5]{1}{pager-close.c.example}
+\caption{pager.c (Part 3): Cleaning up when a client leaves}
+\label{pager-close.c}
+\end{Program}
+
+
+\subsection{Retrieving a blocked system call's arguments from a {\tt
+fusd\_file\_info} pointer}
+
+\label{logring}
+
+In the previous section, we showed how the {\tt fusd\_return} function
+can be used to specify the return value of a system call that was
+previously blocked.  However, many system calls have side effects in
+addition to returning a value---for example, in a {\tt read()}
+request, the data being returned has to be copied into the caller's
+buffer.  To facilitate this, FUSD provides accessor functions that let
+drivers retrieve the arguments that had been passed to its callbacks
+at the time the call was originally issued.  For example, the {\tt
+fusd\_get\_read\_buffer()} function will return a pointer to the data
+buffer that is provided with {\tt read()} callbacks.  Drivers can use
+these accessor functions to affect change to a client {\em before}
+calling {\tt fusd\_return()}.
+
+The following accessor functions are available, all of which take a
+single {\tt fusd\_file\_info *} argument:
+\begin{itemize}
+\item {\tt int char *fusd\_get\_read\_buffer}---The destination buffer
+for data that a driver is returning to a process doing a {\tt read()}.
+\item {\tt const char *fusd\_get\_write\_buffer}---The source buffer
+containing data sent to the driver by a process doing a {\tt write()}.
+\item {\tt fusd\_get\_length}---The length (in bytes) of the buffer
+for either a {\tt read()} or a {\tt write()}.
+\item {\tt loff\_t fusd\_get\_offset}---The file descriptor's byte
+offset, typically used in {\tt read()} and {\tt write()} callbacks.
+\item {\tt int fusd\_get\_ioctl\_request}---An ioctl's request
+``command number'' (i.e., the first argument of an ioctl).
+\item {\tt int fusd\_get\_ioctl\_arg}---The second argument of an
+ioctl for non-data-bearing {\tt ioctl} requests (i.e., {\tt \_IO}
+commands).
+\item {\tt void *fusd\_get\_ioctl\_buffer}---The data buffer for
+data-bearing {\tt ioctl} requests ({\tt \_IOR}, {\tt \_IOW}, and
+{\tt \_IORW} commands).
+\item {\tt int fusd\_get\_poll\_diff\_cached\_state}---See
+Section~\ref{selectable}.
+\end{itemize}
+
+We got away without using these accessor functions in our {\tt
+pager.c} example because the pager doesn't actually return data---it
+just blocks and unblocks {\tt read} calls.  However, the FUSD
+distribution contains another example program, {\tt logring}, that
+demonstrates their use.
+
+{\tt logring} makes it easy to access the most recent (and only the most
+recent) output from a process. It works just like {\tt tail -f} on a
+log file, except that the storage required never grows. This can be
+useful in embedded systems where there isn't enough memory or disk
+space for keeping complete log files, but the most recent debugging
+messages are sometimes needed (e.g., after an error is observed).
+
+{\tt logring} uses FUSD to implement a character device, {\tt
+/dev/logring}, that acts like a named pipe that has a finite, circular
+buffer.  The size of the buffer is given as a command-line argument.
+As more data is written into the buffer, the oldest data is discarded.
+A process that reads from the logring device will first read the
+existing buffer, then block and see new data as it's written, similar
+to monitoring a log file using {\tt tail -f}.
+
+You can run this example program by typing {\tt logring <logsize>},
+where {\tt logsize} is the size of the circular buffer in bytes.
+Then, type {\tt cat /dev/logring} in a shell.  The {\tt cat} process
+will block, waiting for data.  From another shell, write to the
+logring (e.g., {\tt echo Hi there > /dev/logring}).  The {\tt cat}
+process will see the message appear.
+
+(This example program is based on {\em emlog}, a (real) Linux kernel
+module with identical functionality.  If you find logring useful, but
+want to use it on a system that does not have FUSD, check out the
+original
+\htmladdnormallinkfoot{emlog}{http://www.circlemud.org/jelson/software/emlog}.)
+
+
+
+
+
+\section{Implementing {\tt select}able Devices}
+\label{selectable}
+
+One important feature that almost every device driver in a system
+should have is support for the {\tt select(2)} system call.  {\tt
+select} allows clients to assemble a set of file descriptors and ask
+to be notified when one of them becomes readable or writable.  This
+simple feature is deceptively powerful---it allows clients to wait for
+any number of a set of possible events to occur.  This is
+fundamentally different than (for example) a blocking read, which only
+unblocks on one kind of event.  In this section, we'll describe how
+FUSD can be used to create a device whose state can be queried by a
+client's call to {\tt select(2)}.
+
+This section is limited to a discussion what a FUSD driver writer
+needs to know to implement a selectable device.  Details of the FUSD
+implementation required to support this feature are described in
+Section~\ref{poll-diff-implementation}
+
+
+\subsection{Poll state and the {\tt poll\_diff} callback}
+
+FUSD's implementation of selectable devices depends on the concept of
+{\em poll state}.  A file descriptor's poll state is a bitmask that
+describes its current properties---readable, writable, or exception
+raised.  These three states correspond to {\tt select(2)}'s three
+{\tt fd\_set}s.  FUSD has constants used to describe these states:
+\begin{itemize}
+\item {\tt FUSD\_NOTIFY\_INPUT}---Input is available; a read will not
+block.
+\item {\tt FUSD\_NOTIFY\_OUTPUT}---Output space is available; a write
+will not block.
+\item {\tt FUSD\_NOTIFY\_EXCEPT}---An exception has occurred.
+\end{itemize}
+
+These constants can be combined with C's bitwise-or operator.  For
+example, a descriptor that is both readable and writable is expressed
+as {\tt FUSD\_NOTIFY\_INPUT | FUSD\_NOTIFY\_OUTPUT}.  0 means a file
+descriptor is not readable, not writable, and not in the exception
+set.
+
+For a FUSD device to be selectable, its driver must implement a
+callback called {\tt poll\_diff}.  This callback is very different
+than the others; it is not a ``direct line'' between the client and
+the driver as is the case with a call such as {\tt ioctl}.  A driver's
+response to {\tt poll\_diff} is {\em not} the return value seen by a
+client's call to {\tt select}.  When a client tries to {\tt select} on
+a set of file descriptors, the kernel collects the responses from all
+the appropriate callbacks---{\tt poll} for file descriptors managed by
+kernel drivers, and {\tt poll\_diff} callbacks those managed by FUSD
+drivers---and synthesizes all of that information into the return
+value seen by the client.
+
+FUSD keeps a cache of the poll state it has most recently received
+from each FUSD device driver, initially assumed to be 0.  This state
+is returned to clients trying to {\tt select()} on devices managed by
+those drivers.  Under certain conditions, FUSD sends a query to the
+driver in order to ensure that the kernel's poll state cache is up to
+date.  This query takes the form of a {\tt poll\_diff} callback
+activation, which is given a single argument: the poll state that FUSD
+currently has cached.  The driver should consult its internal data
+structures to determine the actual, current poll state (i.e., whether
+or not buffers have readable data).  Then:
+\begin{itemize}
+\item If the FUSD cache is incorrect (that is, the current true poll
+state is different than FUSD's cached state), the current poll state
+should be returned immediately.
+\item If the FUSD cache is up to date (that is, it matches the real
+current state), the callback should save the {\tt fusd\_file\_info}
+pointer and return {\tt -FUSD\_NOREPLY}.  Later, when the poll
+state changes, the driver can call {\tt fusd\_return()} to update
+FUSD's cache.
+\end{itemize}
+
+In other words, when a driver's {\tt poll\_diff} callback is
+activated, the kernel is effectively saying to the driver, ``Here is
+what I think the current poll state of this file descriptor is; let me
+know when that state {\em changes}.''  The driver can either respond
+immediately (if the kernel's cache is already known to be out of
+date), or return {\tt -FUSD\_NOREPLY} if no update is immediately
+necessary.  Later, when the poll state changes (for example, if new
+data arrives that makes a device readable), the driver can used its
+saved {\tt fusd\_file\_info} pointer to send a poll state update to
+the kernel.
+
+When a FUSD driver sends a poll state update, it might (or might not)
+have the effect of waking up a client that was blocked in {\tt
+select(2)}.  On the same note, it's worth reiterating that a {\tt
+-FUSD\_NOREPLY} response to a {\tt poll\_diff} callback {\em does not}
+necessarily block the client---other descriptors in the client's {\tt
+select} set might be readable, for example.
+
+\subsection{Receiving a {\tt poll\_diff} request when the previous one
+has not been returned yet}
+\label{multiple-polldiffs}
+
+Calls such as {\tt read} and {\tt write} are synchronous from the
+standpoint of an individual client---a request is made, and the
+requester blocks until a reply is received.  This means that there
+can't ever be more than a single {\tt read} request outstanding for a
+single client at a time.  (The driver as a whole may be keeping track
+of many outstanding {\tt read} requests in parallel, but no two of them will
+be from the same client file descriptor.)
+
+As we mentioned in the previous section, the {\tt poll\_diff} callback
+is different from other callbacks.  It is not part of a synchronous
+request/reply sequence that causes the client to block.  It is also an
+interface to the {\em kernel}, not directly to the client.  So, it
+{\em is} possible to receive a {\tt poll\_diff} request while there is
+already one outstanding.  This happens if the kernel's poll state
+cache changes, causing it to notify the driver that it has a new
+cached value.
+
+This is easy to handle; the client should simply
+\begin{enumerate}
+\item Destroy the old (now out-of-date) {\tt poll\_diff} request
+using the {\tt fusd\_destroy} function we saw in
+Section~\ref{fusd-destroy}.
+\item Either respond to or save the new {\tt poll\_diff} request,
+exactly as described in the previous section.
+\end{enumerate}
+
+The next section will show an example of this technique.
+
+
+\subsection{Adding {\tt select} support to {\tt pager.c}}
+
+Given the explanation of {\tt poll\_diff} in the previous sections, it
+might seem that implementing a selectable device is a daunting task.
+It's actually not as bad as it sounds---the example code may well be
+shorter than its explanation!
+
+\begin{Program}
+\listinginput[5]{1}{pager-polldiff.c.example}
+\caption{pager.c (Part 4): Supporting {\tt select(2)} by implementing a
+{\tt poll\_diff} callback}
+\label{pager-polldiff.c}
+\end{Program}
+
+Program~\ref{pager-polldiff.c} shows the implementation of {\tt
+poll\_diff} in {\tt pager.c}, which makes its notification interface
+({\tt /dev/pager/notify}) selectable.  It is decomposed into a ``top
+half'' and ``bottom half'' function, exactly as we did for the
+blocking {\tt read} implementation in Program~\ref{pager-read.c}.
+First, on lines 1--20, we see the the callback for {\tt poll\_diff}
+callback itself.  It is virtually identical to the {\tt read} callback
+in Program~\ref{pager-read.c}.  The main difference is that it first
+checks (on line 12) to see if a {\tt poll\_diff} request is already
+outstanding when a new request comes in.  If so, the out-of-date
+request is destroyed using {\tt fusd\_destroy}, as we described in
+Section~\ref{multiple-polldiffs}.
+
+The bottom half is shown on lines 22-46.  First, on lines 32--35, it
+computes the current poll state---if a page has arrived that the
+client hasn't seen yet, the file is readable; otherwise, it isn't.
+Next, the driver compares the current poll state with the poll state
+that the kernel has cached.  If the kernel's cache is out of date, the
+current state is returned to the kernel.  Otherwise, it does nothing.
+
+As with the {\tt read} callback we saw previously, notice that {\tt
+pager\_notify\_complete\_polldiff} is called in two different cases:
+\begin{enumerate}
+\item It is called immediately from the {\tt pager\_notify\_polldiff}
+callback itself.  This causes the current poll state to be returned to
+the kernel immediately when the request arrives if the driver already
+knows the kernel's state needs to be updated.
+\item It is called when new data arrives that causes the poll state to
+change.  Refer back to Program~\ref{pager-read.c} on
+page~\pageref{pager-read.c}; in the callback that receives new pages,
+notice on line 45 that the {\tt poll\_diff} completion function is called
+alongside the {\tt read} completion function.
+\end{enumerate}
+
+With this {\tt poll\_diff} implementation, it is possible for a client
+to open {\tt /dev/pager/notify}, and block in a {\tt select(2)} system
+call.  If another client writes {\tt page} to {\tt /dev/pager/input},
+the first client's {\tt select} will unblock, indicating the file has
+become readable.
+
+For additional example code, take a look at the {\tt logring} example
+program we first mentioned in Section~\ref{logring}.  It also supports
+{\tt select} by implementing a similar {\tt poll\_diff} callback.
+
+\section{Performance of User-Space Devices}
+\label{performance}
+
+This section hasn't been written yet.  I have some pretty graphs and
+whatnot, but no time to write about them here before the release.
+
+\section{FUSD Implementation Notes}
+
+In this section, we describe some of the details of how FUSD is
+implemented.  It's not necessary to understand these details in order
+to use FUSD.  However, these notes can be useful for people who are
+trying to understand the FUSD framework itself---hackers, debuggers,
+or the generally curious.
+
+\subsection{The situation with {\tt poll\_diff}}
+\label{poll-diff-implementation}
+
+
+In-kernel device drivers support select by implementing a callback
+called {\tt poll}.  This driver's callback is supposed to do two
+things.  First, it should return the current state of a file
+descriptor---a combination of being readable, writable, or having
+exceptions.  Second, it should provide a pointer to one of the
+driver's internal wait queues that will be awakened whenever the state
+changes.  The {\tt poll} call itself should never block---it should
+just instantaneously report what the {\em current} state is.
+
+FUSD's implementation of selectable devices is different, but attempts
+to maintain three properties that we thought to be most important from
+the point of view of a client using {\tt select}.  Specifically:
+\begin{enumerate}
+\item The {\tt select(2)} call itself should never become blocked.
+For example, if one file descriptor in its set isn't readable, that
+shouldn't prevent it from reporting other file descriptors that are.
+\item If {\tt select(2)} indicates a file descriptor is readable (or
+writable), a read (or write) on that file descriptor shouldn't block.
+\item Clients should be allowed to seamlessly {\tt select} on any set
+of file descriptors, even if that set contains a mix of both FUSD and
+non-FUSD devices.
+\end{enumerate}
+
+
+The FUSD kernel module keeps a cache of the driver's most recent
+answer for each file descriptor, initially assumed to be 0.  When the
+kernel module's internal {\tt poll} callback is activated, it:
+\begin{enumerate}
+\item Dispatches a {\em non-}blocking {\tt poll\_diff} to the
+associated user-space driver, asking for a cache update---if and only
+if there isn't already an outstanding poll diff request out that has
+the same value.
+\item Immediately returns the cached value to the kernel
+\end{enumerate}
+
+In addition, the cached value's readable bit is cleared on every read;
+the writable bit is cleared on every write.  This is necessary to
+prevent old poll state---which says ``device is readable''---from
+being returned out of the cache when it might be invalid.  FUSD
+assumes that any read to a device can make it potentially unreadable.
+This mechanism is what causes an updated poll diff to be sent to a
+client before the previous one has been returned.
+
+(this section isn't finished yet; fancy time diagrams coming someday)
+
+\subsection{Restartable System Calls}
+
+No time to write this section yet...
+
+
+\appendix
+
+\section{Using {\tt strace}}
+\label{strace}
+
+This section hasn't been written yet.  Contributions are welcome.
+
+\end{document}
+

Added: trunk/fusd/doc/html.sty
===================================================================
--- trunk/fusd/doc/html.sty	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/html.sty	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,1158 @@
+%
+% $Id: html.sty,v 1.1 2001/05/12 00:38:48 cvs Exp $
+% LaTeX2HTML Version 99.2 : html.sty
+% 
+% This file contains definitions of LaTeX commands which are
+% processed in a special way by the translator. 
+% For example, there are commands for embedding external hypertext links,
+% for cross-references between documents or for including raw HTML.
+% This file includes the comments.sty file v2.0 by Victor Eijkhout
+% In most cases these commands do nothing when processed by LaTeX.
+%
+% Place this file in a directory accessible to LaTeX (i.e., somewhere
+% in the TEXINPUTS path.)
+%
+% NOTE: This file works with LaTeX 2.09 or (the newer) LaTeX2e.
+%       If you only have LaTeX 2.09, some complex LaTeX2HTML features
+%       like support for segmented documents are not available.
+
+% Changes:
+% See the change log at end of file.
+
+
+% Exit if the style file is already loaded
+% (suggested by Lee Shombert <las at potomac.wash.inmet.com>
+\ifx \htmlstyloaded\relax \endinput\else\let\htmlstyloaded\relax\fi
+\makeatletter
+
+% allow for the hyperref package to be cleanly loaded
+% either before or after this package,
+% and ensure it is already loaded, when using pdf-TeX
+
+\ifx\undefined\hyperref
+ \ifx\pdfoutput\undefined \let\pdfunknown\relax
+  \let\html at new=\newcommand
+ \else
+  \ifx\pdfoutput\relax \let\pdfunknown\relax
+   \RequirePackage{hyperref}\let\html at new=\renewcommand
+  \else
+   \RequirePackage{hyperref}\let\html at new=\newcommand
+  \fi
+ \fi
+\else
+ \let\html at new=\renewcommand
+\fi
+
+\providecommand{\latextohtml}{\LaTeX2\texttt{HTML}}
+
+%%% LINKS TO EXTERNAL DOCUMENTS
+%
+% This can be used to provide links to arbitrary documents.
+% The first argumment should be the text that is going to be
+% highlighted and the second argument a URL.
+% The hyperlink will appear as a hyperlink in the HTML 
+% document and as a footnote in the dvi or ps files.
+%
+\ifx\pdfunknown\relax
+ \html at new{\htmladdnormallinkfoot}[2]{#1\footnote{#2}} 
+\else
+ \def\htmladdnormallinkfoot#1#2{\footnote{\href{#2}{#1}}}
+\fi
+
+% This is an alternative definition of the command above which
+% will ignore the URL in the dvi or ps files.
+\ifx\pdfunknown\relax
+ \html at new{\htmladdnormallink}[2]{#1}
+\else
+ \def\htmladdnormallink#1#2{\href{#2}{#1}}
+\fi
+
+% This command takes as argument a URL pointing to an image.
+% The image will be embedded in the HTML document but will
+% be ignored in the dvi and ps files.
+%
+\ifx\pdfunknown\relax
+ \html at new{\htmladdimg}[1]{}
+\else
+ \def\htmladdimg#1{\hyperimage{#1}}
+\fi
+
+
+%%% CROSS-REFERENCES BETWEEN (LOCAL OR REMOTE) DOCUMENTS
+%
+% This can be used to refer to symbolic labels in other Latex 
+% documents that have already been processed by the translator.
+% The arguments should be:
+% #1 : the URL to the directory containing the external document
+% #2 : the path to the labels.pl file of the external document.
+% If the external document lives on a remote machine then labels.pl 
+% must be copied on the local machine.
+%
+%e.g. \externallabels{http://cbl.leeds.ac.uk/nikos/WWW/doc/tex2html/latex2html}
+%                    {/usr/cblelca/nikos/tmp/labels.pl}
+% The arguments are ignored in the dvi and ps files.
+%
+\newcommand{\externallabels}[2]{}
+
+
+% This complements the \externallabels command above. The argument
+% should be a label defined in another latex document and will be
+% ignored in the dvi and ps files.
+%
+\newcommand{\externalref}[1]{}
+
+
+% Suggested by  Uffe Engberg (http://www.brics.dk/~engberg/)
+% This allows the same effect for citations in external bibliographies.
+% An  \externallabels  command must be given, locating a labels.pl file
+% which defines the location and keys used in the external .html file.
+%  
+\newcommand{\externalcite}{\nocite}
+
+% This allows a section-heading in the TOC or mini-TOC to be just
+% a hyperlink to an external document.
+%
+%   \htmladdTOClink[<path_to_labels>]{<section-level>}{<title>}{<URL>}
+% where <section-level> is  'chapter' , 'section' , 'subsection' etc.
+% and <path_to_labels> is the path to find a  labels.pl  file,
+% so that external cross-referencing may work, as with \externallabels
+%
+%\ifx\pdfunknown\relax
+ \newcommand{\htmladdTOClink}[4][]{}
+%
+% can do something here, using the \pdfoutline primitive
+%\else
+% \def\htmladdTOClink#1#2#3#4{\pdfoutline user {/S /URI /URI #4}
+%   name{#2} count{#1}{#3}}
+%\fi
+
+
+%%% HTMLRULE
+% This command adds a horizontal rule and is valid even within
+% a figure caption.
+% Here we introduce a stub for compatibility.
+\newcommand{\htmlrule}{\protect\HTMLrule}
+\newcommand{\HTMLrule}{\@ifstar\htmlrulestar\htmlrulestar}
+\newcommand{\htmlrulestar}[1]{}
+
+%%% HTMLCLEAR
+% This command puts in a <BR> tag, with CLEAR="ALL"
+\newcommand{\htmlclear}{}
+
+% This command adds information within the <BODY> ... </BODY> tag
+%
+\newcommand{\bodytext}[1]{}
+\newcommand{\htmlbody}{}
+
+
+%%% HYPERREF 
+% Suggested by Eric M. Carol <eric at ca.utoronto.utcc.enfm>
+% Similar to \ref but accepts conditional text. 
+% The first argument is HTML text which will become ``hyperized''
+% (underlined).
+% The second and third arguments are text which will appear only in the paper
+% version (DVI file), enclosing the fourth argument which is a reference to a label.
+%
+%e.g. \hyperref{using the tracer}{using the tracer (see Section}{)}{trace}
+% where there is a corresponding \label{trace}
+%
+% avoid possible confict with  hyperref  package
+\ifx\undefined\hyperref
+ \newcommand{\hyperrefhyper}[4]{#4}%
+ \def\next{\newcommand}%
+\else
+ \let\hyperrefhyper\hyperref
+ \def\next{\renewcommand}%
+\fi
+\next{\hyperref}{\hyperrefi[]}\let\next=\relax
+
+\def\hyperrefi[#1]{{\def\next{#1}\def\tmp{}%
+ \ifx\next\tmp\aftergroup\hyperrefdef
+ \else\def\tmp{ref}\ifx\next\tmp\aftergroup\hyperrefref
+ \else\def\tmp{pageref}\ifx\next\tmp\aftergroup\hyperrefpageref
+ \else\def\tmp{page}\ifx\next\tmp\aftergroup\hyperrefpage
+ \else\def\tmp{noref}\ifx\next\tmp\aftergroup\hyperrefnoref
+ \else\def\tmp{no}\ifx\next\tmp\aftergroup\hyperrefno
+ \else\def\tmp{hyper}\ifx\next\tmp\aftergroup\hyperrefhyper
+ \else\def\tmp{html}\ifx\next\tmp\aftergroup\hyperrefhtml
+ \else\typeout{*** unknown option \next\space to  hyperref ***}%
+ \fi\fi\fi\fi\fi\fi\fi\fi}}
+\newcommand{\hyperrefdef}[4]{#2\ref{#4}#3}
+\newcommand{\hyperrefpageref}[4]{#2\pageref{#4}#3}
+\newcommand{\hyperrefnoref}[3]{#2}
+\let\hyperrefref=\hyperrefdef
+\let\hyperrefpage=\hyperrefpageref
+\let\hyperrefno=\hyperrefnoref
+\ifx\undefined\hyperrefhyper\newcommand{\hyperrefhyper}[4]{#4}\fi
+\let\hyperrefhtml=\hyperrefdef
+
+%%% HYPERCITE --- added by RRM
+% Suggested by Stephen Simpson <simpson at math.psu.edu>
+% effects the same ideas as in  \hyperref, but for citations.
+% It does not allow an optional argument to the \cite, in LaTeX.
+%
+%   \hypercite{<html-text>}{<LaTeX-text>}{<opt-text>}{<key>}
+%
+% uses the pre/post-texts in LaTeX, with a  \cite{<key>}
+%
+%   \hypercite[ext]{<html-text>}{<LaTeX-text>}{<key>}
+%   \hypercite[ext]{<html-text>}{<LaTeX-text>}[<prefix>]{<key>}
+%
+% uses the pre/post-texts in LaTeX, with a  \nocite{<key>}
+% the actual reference comes from an \externallabels  file.
+%
+\newcommand{\hypercite}{\hypercitei[]}
+\def\hypercitei[#1]{{\def\next{#1}\def\tmp{}%
+ \ifx\next\tmp\aftergroup\hypercitedef
+ \else\def\tmp{int}\ifx\next\tmp\aftergroup\hyperciteint
+ \else\def\tmp{cite}\ifx\next\tmp\aftergroup\hypercitecite
+ \else\def\tmp{ext}\ifx\next\tmp\aftergroup\hyperciteext
+ \else\def\tmp{nocite}\ifx\next\tmp\aftergroup\hypercitenocite
+ \else\def\tmp{no}\ifx\next\tmp\aftergroup\hyperciteno
+ \else\typeout{*** unknown option \next\space to  hypercite ***}%
+ \fi\fi\fi\fi\fi\fi}}
+\newcommand{\hypercitedef}[4]{#2{\def\tmp{#3}\def\emptyopt{}%
+ \ifx\tmp\emptyopt\cite{#4}\else\cite[#3]{#4}\fi}}
+\newcommand{\hypercitenocite}[2]{#2\hypercitenocitex[]}
+\def\hypercitenocitex[#1]#2{\nocite{#2}}
+\let\hypercitecite=\hypercitedef
+\let\hyperciteint=\hypercitedef
+\let\hyperciteext=\hypercitenocite
+\let\hyperciteno=\hypercitenocite
+
+%%% HTMLREF
+% Reference in HTML version only.
+% Mix between \htmladdnormallink and \hyperref.
+% First arg is text for in both versions, second is label for use in HTML
+% version.
+\ifx\pdfunknown\relax
+ \html at new{\htmlref}[2]{#1}
+\else
+ \def\htmlref#1#2{\hyperefhyper[#2]{#1}}
+\fi
+
+%%% HTMLCITE
+% Reference in HTML version only.
+% Mix between \htmladdnormallink and \hypercite.
+% First arg is text for both versions, second is citation for use in HTML
+% version.
+\newcommand{\htmlcite}[2]{#1}
+
+
+%%% HTMLIMAGE
+% This command can be used inside any environment that is converted
+% into an inlined image (eg a "figure" environment) in order to change
+% the way the image will be translated. The argument of \htmlimage
+% is really a string of options separated by commas ie 
+% [scale=<scale factor>],[external],[thumbnail=<reduction factor>
+% The scale option allows control over the size of the final image.
+% The ``external'' option will cause the image not to be inlined 
+% (images are inlined by default). External images will be accessible
+% via a hypertext link. 
+% The ``thumbnail'' option will cause a small inlined image to be 
+% placed in the caption. The size of the thumbnail depends on the
+% reduction factor. The use of the ``thumbnail'' option implies
+% the ``external'' option.
+%
+% Example:
+% \htmlimage{scale=1.5,external,thumbnail=0.2}
+% will cause a small thumbnail image 1/5th of the original size to be
+% placed in the final document, pointing to an external image 1.5
+% times bigger than the original.
+% 
+\newcommand{\htmlimage}[1]{}
+
+
+% \htmlborder causes a border to be placed around an image or table
+% when the image is placed within a <TABLE> cell.
+\newcommand{\htmlborder}[1]{}
+
+% Put \begin{makeimage}, \end{makeimage} around LaTeX to ensure its
+% translation into an image.
+% This shields sensitive text from being translated.
+\newenvironment{makeimage}{}{}
+
+
+% A dummy environment that can be useful to alter the order
+% in which commands are processed, in LaTeX2HTML
+\newenvironment{tex2html_deferred}{}{}
+
+
+%%% HTMLADDTONAVIGATION
+% This command appends its argument to the buttons in the navigation
+% panel. It is ignored by LaTeX.
+%
+% Example:
+% \htmladdtonavigation{\htmladdnormallink
+%              {\htmladdimg{http://server/path/to/gif}}
+%              {http://server/path}}
+\newcommand{\htmladdtonavigation}[1]{}
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% based upon Eijkhout's  comment.sty v2.0
+% with modifications to avoid conflicts with later versions
+% of this package, should a user be requiring it.
+%	Ross Moore,  10 March 1999
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Comment.sty   version 2.0, 19 June 1992
+% selectively in/exclude pieces of text: the user can define new
+% comment versions, and each is controlled separately.
+% This style can be used with plain TeX or LaTeX, and probably
+% most other packages too.
+%
+% Examples of use in LaTeX and TeX follow \endinput
+%
+% Author
+%    Victor Eijkhout
+%    Department of Computer Science
+%    University Tennessee at Knoxville
+%    104 Ayres Hall
+%    Knoxville, TN 37996
+%    USA
+%
+%    eijkhout at cs.utk.edu
+%
+% Usage: all text included in between
+%    \comment ... \endcomment
+% or \begin{comment} ... \end{comment}
+% is discarded. The closing command should appear on a line
+% of its own. No starting spaces, nothing after it.
+% This environment should work with arbitrary amounts
+% of comment.
+%
+% Other 'comment' environments are defined by
+% and are selected/deselected with
+% \includecomment{versiona}
+% \excludecoment{versionb}
+%
+% These environments are used as
+% \versiona ... \endversiona
+% or \begin{versiona} ... \end{versiona}
+% with the closing command again on a line of its own.
+%
+% Basic approach:
+% to comment something out, scoop up  every line in verbatim mode
+% as macro argument, then throw it away.
+% For inclusions, both the opening and closing comands
+% are defined as noop
+%
+% Changed \next to \html at next to prevent clashes with other sty files
+% (mike at emn.fr)
+% Changed \html at next to \htmlnext so the \makeatletter and
+% \makeatother commands could be removed (they were causing other
+% style files - changebar.sty - to crash) (nikos at cbl.leeds.ac.uk)
+% Changed \htmlnext back to \html at next...
+
+\def\makeinnocent#1{\catcode`#1=12 }
+\def\csarg#1#2{\expandafter#1\csname#2\endcsname}
+
+\def\ThrowAwayComment#1{\begingroup
+    \def\CurrentComment{#1}%
+    \let\do\makeinnocent \dospecials
+    \makeinnocent\^^L% and whatever other special cases
+%%RRM
+%%  use \xhtmlComment for \xComment
+%%  use \html at next    for \next
+    \endlinechar`\^^M \catcode`\^^M=12 \xhtmlComment}
+{\catcode`\^^M=12 \endlinechar=-1 %
+ \gdef\xhtmlComment#1^^M{\def\test{#1}\edef\test{\meaning\test}
+      \csarg\ifx{PlainEnd\CurrentComment Test}\test
+          \let\html at next\endgroup
+      \else \csarg\ifx{LaLaEnd\CurrentComment Test}\test
+            \edef\html at next{\endgroup\noexpand\end{\CurrentComment}}
+      \else \csarg\ifx{LaInnEnd\CurrentComment Test}\test
+            \edef\html at next{\endgroup\noexpand\end{\CurrentComment}}
+      \else \let\html at next\xhtmlComment
+      \fi \fi \fi \html at next}
+}
+
+%%\def\includecomment	%%RRM
+\def\htmlincludecomment
+ #1{\expandafter\def\csname#1\endcsname{}%
+    \expandafter\def\csname end#1\endcsname{}}
+%%\def\excludecomment	%%RRM
+\def\htmlexcludecomment
+ #1{\expandafter\def\csname#1\endcsname{\ThrowAwayComment{#1}}%
+    {\escapechar=-1\relax
+     \edef\tmp{\string\\end#1}%
+      \csarg\xdef{PlainEnd#1Test}{\meaning\tmp}%
+     \edef\tmp{\string\\end\string\{#1\string\}}%
+      \csarg\xdef{LaLaEnd#1Test}{\meaning\tmp}%
+     \edef\tmp{\string\\end \string\{#1\string\}}%
+      \csarg\xdef{LaInnEnd#1Test}{\meaning\tmp}%
+    }}
+
+%%\excludecomment{comment}	%%RRM
+\htmlexcludecomment{comment}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% end Comment.sty
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\let\includecomment=\htmlincludecomment
+\let\excludecomment=\htmlexcludecomment
+
+%
+% Alternative code by Robin Fairbairns, 22 September 1997
+% revised to cope with % and unnested { }, by Ross Moore, 4 July 1998
+% further revised to cope with & and # in tables, 10 March 1999
+%
+\def\raw at catcodes{\catcode`\%=12 \catcode`\{=12 \catcode`\}=12
+ \catcode`\&=12 \catcode`\#=12 }
+\newcommand\@gobbleenv{\bgroup\raw at catcodes
+ \let\reserved at a\@currenvir\@gobble at nv}
+\bgroup
+ \def\expansionhead{\gdef\@gobble at nv@i##1}
+ \def\expansiontail{{\def\reserved at b{##1}\@gobble at nv@ii}}
+ \def\expansionheadii{\long\gdef\@gobble at nv##1\end}
+ \def\expansiontailii{{\@gobble at nv@i}}
+ \def\expansionmidii{##2}
+ \raw at catcodes\relax
+ \expandafter\expansionhead\expandafter}\expansiontail
+\egroup
+\long\gdef\@gobble at nv#1\end#2{\@gobble at nv@i}
+%\long\def\@gobble at nv#1\end#2{\def\reserved at b{#2}%
+\def\@gobble at nv@ii{%
+ \ifx\reserved at a\reserved at b
+  \edef\reserved at a{\egroup\noexpand\end{\reserved at a}}%
+  \expandafter\reserved at a
+ \else
+  \expandafter\@gobble at nv
+ \fi}
+
+\renewcommand{\htmlexcludecomment}[1]{%
+    \csname newenvironment\endcsname{#1}{\@gobbleenv}{}}
+\newcommand{\htmlreexcludecomment}[1]{%
+    \csname renewenvironment\endcsname{#1}{\@gobbleenv}{}}
+
+%%% RAW HTML 
+% 
+% Enclose raw HTML between a \begin{rawhtml} and \end{rawhtml}.
+% The html environment ignores its body
+%
+\htmlexcludecomment{rawhtml}
+
+
+%%% HTML ONLY
+%
+% Enclose LaTeX constructs which will only appear in the 
+% HTML output and will be ignored by LaTeX with 
+% \begin{htmlonly} and \end{htmlonly}
+%
+\htmlexcludecomment{htmlonly}
+% Shorter version
+\newcommand{\html}[1]{}
+
+% for images.tex only
+\htmlexcludecomment{imagesonly}
+
+%%% LaTeX ONLY
+% Enclose LaTeX constructs which will only appear in the 
+% DVI output and will be ignored by latex2html with 
+%\begin{latexonly} and \end{latexonly}
+%
+\newenvironment{latexonly}{}{}
+% Shorter version
+\newcommand{\latex}[1]{#1}
+
+
+%%% LaTeX or HTML
+% Combination of \latex and \html.
+% Say \latexhtml{this should be latex text}{this html text}
+%
+%\newcommand{\latexhtml}[2]{#1}
+\long\def\latexhtml#1#2{#1}
+
+
+%%% tracing the HTML conversions
+% This alters the tracing-level within the processing
+% performed by  latex2html  by adjusting  $VERBOSITY
+% (see  latex2html.config  for the appropriate values)
+%
+\newcommand{\htmltracing}[1]{}
+\newcommand{\htmltracenv}[1]{}
+
+
+%%%  \strikeout for HTML only
+% uses <STRIKE>...</STRIKE> tags on the argument
+% LaTeX just gobbles it up.
+\newcommand{\strikeout}[1]{}
+
+%%%  \htmlurl  and  \url
+%  implement \url as the simplest thing, if not already defined
+%  let \htmlurl#1  be equivalent to it 
+%
+\def\htmlurlx#1{\begin{small}\texttt{#1}\end{small}}%
+\expandafter\ifx\csname url\endcsname\relax
+ \let\htmlurl=\htmlurlx \else \let\htmlurl=\url \fi
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% JCL - stop input here if LaTeX2e is not present
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\ifx\if at compatibility\undefined
+  %LaTeX209
+  \makeatother\relax\expandafter\endinput
+\fi
+\if at compatibility
+  %LaTeX2e in LaTeX209 compatibility mode
+  \makeatother\relax\expandafter\endinput
+\fi
+
+%\let\real at TeXlogo = \TeX
+%\DeclareRobustCommand{\TeX}{\relax\real at TeXlogo}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Start providing LaTeX2e extension:
+% This is currently:
+%  - additional optional argument for \htmladdimg
+%  - support for segmented documents
+%
+
+\ProvidesPackage{html}
+          [1999/07/19 v1.38 hypertext commands for latex2html (nd, hws, rrm)]
+
+%
+% Ensure that \includecomment and \excludecomment are bound
+% to the version defined here.
+%
+\AtBeginDocument{%
+ \let\includecomment=\htmlincludecomment
+ \let\excludecomment=\htmlexcludecomment
+ \htmlreexcludecomment{comment}}
+
+%%%  bind \htmlurl to \url if that is later loaded
+%
+\expandafter\ifx\csname url\endcsname\relax
+ \AtBeginDocument{\@ifundefined{url}{}{\let\htmlurl=\url}}\fi
+
+%%%%MG
+
+% This command takes as argument a URL pointing to an image.
+% The image will be embedded in the HTML document but will
+% be ignored in the dvi and ps files.  The optional argument
+% denotes additional HTML tags.
+%
+% Example:  \htmladdimg[ALT="portrait" ALIGN=CENTER]{portrait.gif}
+%
+\ifx\pdfunknown\relax
+ \renewcommand{\htmladdimg}[2][]{}
+\else
+ \renewcommand{\htmladdimg}[2][]{\hyperimage{#2}}
+\fi
+
+%%% HTMLRULE for LaTeX2e
+% This command adds a horizontal rule and is valid even within
+% a figure caption.
+%
+% This command is best used with LaTeX2e and HTML 3.2 support.
+% It is like \hrule, but allows for options via key--value pairs
+% as follows:  \htmlrule[key1=value1, key2=value2, ...] .
+% Use \htmlrule* to suppress the <BR> tag.
+% Eg. \htmlrule[left, 15, 5pt, "none", NOSHADE] produces
+% <BR CLEAR="left"><HR NOSHADE SIZE="15">.
+% Renew the necessary part.
+\renewcommand{\htmlrulestar}[1][all]{}
+
+%%% HTMLCLEAR for LaTeX2e
+% This command puts in a <BR> tag, with optional CLEAR="<attrib>"
+%
+\renewcommand{\htmlclear}[1][all]{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  renew some definitions to allow optional arguments
+%
+% The description of the options is missing, as yet.
+%
+\renewcommand{\latextohtml}{\textup{\LaTeX2\texttt{HTML}}}
+\ifx\pdfunknown\relax
+ \renewcommand{\htmladdnormallinkfoot}[3][]{#2\footnote{#3}} 
+ \renewcommand{\htmladdnormallink}[3][]{#2}
+\else
+ \renewcommand{\htmladdnormallinkfoot}[1][]{\def\next{#1}%
+   \ifx\next\@empty\def\next{\htmladdnonamedlinkfoot}%
+   \else\def\next{\htmladdnamedlinkfoot{#1}}\fi \next}
+ \newcommand{\htmladdnonamedlinkfoot}[2]{%
+   #1\footnote{\href{#2}{#2}}}
+ \newcommand{\htmladdnamedlinkfoot}[3]{%
+   \hypertarget{#1}{#2}\footnote{\href{#3}{#3}}}
+ \renewcommand{\htmladdnormallink}[1][]{\def\next{#1}%
+  \ifx\next\@empty\def\next{\htmladdnonamedlink}%
+  \else\def\next{\htmladdnamedlink{#1}}\fi \next}
+ \newcommand{\htmladdnonamedlink}[2]{\href{#2}{#1}}
+ \newcommand{\htmladdnamedlink}[3]{%
+   \hypertarget{#1}{\hskip2bp}\href{#3}{#2}}
+\fi
+
+\renewcommand{\htmlbody}[1][]{}
+\renewcommand{\htmlborder}[2][]{}
+\renewcommand{\externallabels}[3][]{}
+\renewcommand{\externalref}[2][]{}
+\renewcommand{\externalcite}[1][]{\nocite}
+\renewcommand{\hyperref}[1][]{\hyperrefi[#1]}
+\renewcommand{\hypercite}[1][]{\hypercitei[#1]}
+\renewcommand{\hypercitenocite}[2]{#2\hypercitenocitex}
+\renewcommand{\hypercitenocitex}[2][]{\nocite{#2}}
+\let\hyperciteno=\hypercitenocite
+\let\hyperciteext=\hypercitenocite
+
+\ifx\pdfunknown\relax
+ \renewcommand{\htmlimage}[2][]{}
+ \renewcommand{\htmlref}[2][]{#2{\def\tmp{#1}\ifx\tmp\@empty
+  \aftergroup\htmlrefdef\else\aftergroup\htmlrefext\fi}}
+ \newcommand{\htmlrefdef}[1]{}
+ \newcommand{\htmlrefext}[2][]{}
+ \renewcommand{\htmlcite}[2][]{#2{\def\tmp{#1}\ifx\tmp\@empty
+  \aftergroup\htmlcitedef\else\aftergroup\htmlciteext\fi}}
+ \newcommand{\htmlciteext}[2][]{}
+\else
+ \renewcommand{\htmlimage}[2][]{\hyperimage{#2}}
+ \renewcommand{\htmlref}[1][]{\def\htmp@{#1}\ifx\htmp@\@empty
+  \def\htmp@{\htmlrefdef}\else\def\htmp@{\htmlrefext{#1}}\fi\htmp@}
+ \newcommand{\htmlrefdef}[2]{\hyperref[hyper][#2]{#1}}
+ \newcommand{\htmlrefext}[3]{%
+  \hypertarget{#1}{\hskip2bp}\hyperref[hyper][#3]{#2}}
+ \renewcommand{\htmlcite}[2][]{#2{\def\htmp@{#1}\ifx\htmp@\@empty
+  \aftergroup\htmlcitedef\else\aftergroup\htmlciteext\fi}}
+ \newcommand{\htmlciteext}[1][]{\cite}
+\fi
+\newcommand{\htmlcitedef}[1]{ \nocite{#1}}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  HTML  HTMLset  HTMLsetenv
+%
+%  These commands do nothing in LaTeX, but can be used to place
+%  HTML tags or set Perl variables during the LaTeX2HTML processing;
+%  They are intended for expert use only.
+
+\newcommand{\HTMLcode}[2][]{}
+\ifx\undefined\HTML\newcommand{\HTML}[2][]{}\else
+\typeout{*** Warning: \string\HTML\space had an incompatible definition ***}%
+\typeout{*** instead use \string\HTMLcode\space for raw HTML code ***}%
+\fi 
+\newcommand{\HTMLset}[3][]{}
+\newcommand{\HTMLsetenv}[3][]{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The following commands pertain to document segmentation, and
+% were added by Herbert Swan <dprhws at edp.Arco.com> (with help from
+% Michel Goossens <goossens at cern.ch>):
+%
+%
+% This command inputs internal latex2html tables so that large
+% documents can to partitioned into smaller (more manageable)
+% segments.
+%
+\newcommand{\internal}[2][internals]{}
+
+%
+%  Define a dummy stub \htmlhead{}.  This command causes latex2html
+%  to define the title of the start of a new segment.  It is not
+%  normally placed in the user's document.  Rather, it is passed to
+%  latex2html via a .ptr file written by \segment.
+%
+\newcommand{\htmlhead}[3][]{}
+
+%  In the LaTeX2HTML version this will eliminate the title line
+%  generated by a \segment command, but retains the title string
+%  for use in other places.
+%
+\newcommand{\htmlnohead}{}
+
+
+%  In the LaTeX2HTML version this put a URL into a <BASE> tag
+%  within the <HEAD>...</HEAD> portion of a document.
+%
+\ifx\pdfunknown\relax
+ \newcommand{\htmlbase}[1]{}
+\else
+ \let\htmlbase=\hyperbaseurl
+\fi
+
+
+%  Include style information into the stylesheet; e.g. CSS
+%
+\newcommand{\htmlsetstyle}[3][]{}
+\newcommand{\htmladdtostyle}[3][]{}
+
+%  Define a style-class for information in a particular language
+%
+\newcommand{\htmllanguagestyle}[2][]{}
+
+
+%
+%  The dummy command \endpreamble is needed by latex2html to
+%  mark the end of the preamble in document segments that do
+%  not contain a \begin{document}
+%
+\newcommand{\startdocument}{}
+
+
+% \tableofchildlinks, \htmlinfo
+%     by Ross Moore  ---  extensions dated 27 September 1997
+%
+%  These do nothing in LaTeX but for LaTeX2HTML they mark 
+%  where the table of child-links and info-page should be placed,
+%  when the user wants other than the default.
+%	\tableofchildlinks	 % put mini-TOC at this location
+%	\tableofchildlinks[off]	 % not on current page
+%	\tableofchildlinks[none] % not on current and subsequent pages
+%	\tableofchildlinks[on]   % selectively on current page
+%	\tableofchildlinks[all]  % on current and all subsequent pages
+%	\htmlinfo	 	 % put info-page at this location
+%	\htmlinfo[off]		 % no info-page in current document
+%	\htmlinfo[none]		 % no info-page in current document
+%  *-versions omit the preceding <BR> tag.
+%
+\newcommand{\tableofchildlinks}{%
+  \@ifstar\tableofchildlinksstar\tableofchildlinksstar}
+\newcommand{\tableofchildlinksstar}[1][]{}
+
+\newcommand{\htmlinfo}{\@ifstar\htmlinfostar\htmlinfostar}
+\newcommand{\htmlinfostar}[1][]{}
+
+
+%  This redefines  \begin  to allow for an optional argument
+%  which is used by LaTeX2HTML to specify `style-sheet' information
+
+\let\realLaTeX at begin=\begin
+\renewcommand{\begin}[1][]{\realLaTeX at begin}
+
+
+%
+%  Allocate a new set of section counters, which will get incremented
+%  for "*" forms of sectioning commands, and for a few miscellaneous
+%  commands.
+%
+
+\@ifundefined{c at part}{\newcounter{part}}{}%
+\newcounter{lpart}
+\newcounter{lchapter}[part]
+\@ifundefined{c at chapter}%
+ {\let\Hchapter\relax \newcounter{chapter}\let\thechapter\relax
+  \newcounter{lsection}[part]}%
+ {\let\Hchapter=\chapter \newcounter{lsection}[chapter]}
+\newcounter{lsubsection}[section]
+\newcounter{lsubsubsection}[subsection]
+\newcounter{lparagraph}[subsubsection]
+\newcounter{lsubparagraph}[paragraph]
+%\newcounter{lequation}
+
+%
+%  Redefine "*" forms of sectioning commands to increment their
+%  respective counters.
+%
+\let\Hpart=\part
+%\let\Hchapter=\chapter
+\let\Hsection=\section
+\let\Hsubsection=\subsection
+\let\Hsubsubsection=\subsubsection
+\let\Hparagraph=\paragraph
+\let\Hsubparagraph=\subparagraph
+\let\Hsubsubparagraph=\subsubparagraph
+
+\ifx\c at subparagraph\undefined
+ \newcounter{lsubsubparagraph}[lsubparagraph]
+\else
+ \newcounter{lsubsubparagraph}[subparagraph]
+\fi
+
+%
+%  The following definitions are specific to LaTeX2e:
+%  (They must be commented out for LaTeX 2.09)
+%
+\expandafter\ifx\csname part\endcsname\relax\else
+\renewcommand{\part}{\@ifstar{\stepcounter{lpart}%
+  \bgroup\def\tmp{*}\H at part}{\bgroup\def\tmp{}\H at part}}\fi
+\newcommand{\H at part}[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hpart\tmp}
+
+\ifx\Hchapter\relax\else
+ \def\chapter{\resetsections \@ifstar{\stepcounter{lchapter}%
+   \bgroup\def\tmp{*}\H at chapter}{\bgroup\def\tmp{}\H at chapter}}\fi
+\newcommand{\H at chapter}[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hchapter\tmp}
+
+\renewcommand{\section}{\resetsubsections
+ \@ifstar{\stepcounter{lsection}\bgroup\def\tmp{*}%
+   \H at section}{\bgroup\def\tmp{}\H at section}}
+\newcommand{\H at section}[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hsection\tmp}
+
+\renewcommand{\subsection}{\resetsubsubsections
+ \@ifstar{\stepcounter{lsubsection}\bgroup\def\tmp{*}%
+   \H at subsection}{\bgroup\def\tmp{}\H at subsection}}
+\newcommand{\H at subsection}[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hsubsection\tmp}
+
+\renewcommand{\subsubsection}{\resetparagraphs
+ \@ifstar{\stepcounter{lsubsubsection}\bgroup\def\tmp{*}%
+   \H at subsubsection}{\bgroup\def\tmp{}\H at subsubsection}}
+\newcommand{\H at subsubsection}[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hsubsubsection\tmp}
+
+\renewcommand{\paragraph}{\resetsubparagraphs
+ \@ifstar{\stepcounter{lparagraph}\bgroup\def\tmp{*}%
+   \H at paragraph}{\bgroup\def\tmp{}\H at paragraph}}
+\newcommand\H at paragraph[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hparagraph\tmp}
+
+\ifx\Hsubparagraph\relax\else\@ifundefined{subparagraph}{}{%
+\renewcommand{\subparagraph}{\resetsubsubparagraphs
+ \@ifstar{\stepcounter{lsubparagraph}\bgroup\def\tmp{*}%
+   \H at subparagraph}{\bgroup\def\tmp{}\H at subparagraph}}}\fi
+\newcommand\H at subparagraph[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hsubparagraph\tmp}
+
+\ifx\Hsubsubparagraph\relax\else\@ifundefined{subsubparagraph}{}{%
+\def\subsubparagraph{%
+ \@ifstar{\stepcounter{lsubsubparagraph}\bgroup\def\tmp{*}%
+   \H at subsubparagraph}{\bgroup\def\tmp{}\H at subsubparagraph}}}\fi
+\newcommand\H at subsubparagraph[1][]{\def\tmp at a{#1}\check at align
+ \expandafter\egroup\expandafter\Hsubsubparagraph\tmp}
+
+\def\check at align{\def\empty{}\ifx\tmp at a\empty
+ \else\def\tmp at b{center}\ifx\tmp at a\tmp at b\let\tmp at a\empty
+ \else\def\tmp at b{left}\ifx\tmp at a\tmp at b\let\tmp at a\empty
+ \else\def\tmp at b{right}\ifx\tmp at a\tmp at b\let\tmp at a\empty
+ \else\expandafter\def\expandafter\tmp at a\expandafter{\expandafter[\tmp at a]}%
+ \fi\fi\fi \def\empty{}\ifx\tmp\empty\let\tmp=\tmp at a \else 
+  \expandafter\def\expandafter\tmp\expandafter{\expandafter*\tmp at a}%
+ \fi\fi}
+%
+\def\resetsections{\setcounter{section}{0}\setcounter{lsection}{0}%
+ \reset at dependents{section}\resetsubsections }
+\def\resetsubsections{\setcounter{subsection}{0}\setcounter{lsubsection}{0}%
+ \reset at dependents{subsection}\resetsubsubsections }
+\def\resetsubsubsections{\setcounter{subsubsection}{0}\setcounter{lsubsubsection}{0}%
+ \reset at dependents{subsubsection}\resetparagraphs }
+%
+\def\resetparagraphs{\setcounter{lparagraph}{0}\setcounter{lparagraph}{0}%
+ \reset at dependents{paragraph}\resetsubparagraphs }
+\def\resetsubparagraphs{\ifx\c at subparagraph\undefined\else
+  \setcounter{subparagraph}{0}\fi \setcounter{lsubparagraph}{0}%
+ \reset at dependents{subparagraph}\resetsubsubparagraphs }
+\def\resetsubsubparagraphs{\ifx\c at subsubparagraph\undefined\else
+  \setcounter{subsubparagraph}{0}\fi \setcounter{lsubsubparagraph}{0}}
+%
+\def\reset at dependents#1{\begingroup\let \@elt \@stpelt
+ \csname cl@#1\endcsname\endgroup}
+%
+%
+%  Define a helper macro to dump a single \secounter command to a file.
+%
+\newcommand{\DumpPtr}[2]{%
+\count255=\csname c@#1\endcsname\relax\def\dummy{dummy}\def\tmp{#2}%
+\ifx\tmp\dummy\def\ctr{#1}\else
+ \def\ctr{#2}\advance\count255 by \csname c@#2\endcsname\relax\fi
+\immediate\write\ptrfile{%
+\noexpand\setcounter{\ctr}{\number\count255}}}
+%\expandafter\noexpand\expandafter\setcounter\expandafter{\ctr}{\number\count255}}}
+
+%
+%  Define a helper macro to dump all counters to the file.
+%  The value for each counter will be the sum of the l-counter
+%      actual LaTeX section counter.
+%  Also dump an \htmlhead{section-command}{section title} command
+%      to the file.
+%
+\newwrite\ptrfile
+\def\DumpCounters#1#2#3#4{%
+\begingroup\let\protect=\noexpand
+\immediate\openout\ptrfile = #1.ptr
+\DumpPtr{part}{lpart}%
+\ifx\Hchapter\relax\else\DumpPtr{chapter}{lchapter}\fi
+\DumpPtr{section}{lsection}%
+\DumpPtr{subsection}{lsubsection}%
+\DumpPtr{subsubsection}{lsubsubsection}%
+\DumpPtr{paragraph}{lparagraph}%
+\DumpPtr{subparagraph}{lsubparagraph}%
+\DumpPtr{equation}{dummy}%
+\DumpPtr{footnote}{dummy}%
+\def\tmp{#4}\ifx\tmp\@empty
+\immediate\write\ptrfile{\noexpand\htmlhead{#2}{#3}}\else
+\immediate\write\ptrfile{\noexpand\htmlhead[#4]{#2}{#3}}\fi
+\dumpcitestatus \dumpcurrentcolor
+\immediate\closeout\ptrfile
+\endgroup }
+
+
+%% interface to natbib.sty
+
+\def\dumpcitestatus{}
+\def\loadcitestatus{\def\dumpcitestatus{%
+  \ifciteindex\immediate\write\ptrfile{\noexpand\citeindextrue}%
+  \else\immediate\write\ptrfile{\noexpand\citeindexfalse}\fi }%
+}
+\@ifpackageloaded{natbib}{\loadcitestatus}{%
+ \AtBeginDocument{\@ifpackageloaded{natbib}{\loadcitestatus}{}}}
+
+
+%% interface to color.sty
+
+\def\dumpcurrentcolor{}
+\def\loadsegmentcolors{%
+ \let\real at pagecolor=\pagecolor
+ \let\pagecolor\segmentpagecolor
+ \let\segmentcolor\color
+ \ifx\current at page@color\undefined \def\current at page@color{{}}\fi
+ \def\dumpcurrentcolor{\bgroup\def\@empty@{{}}%
+   \expandafter\def\expandafter\tmp\space####1@{\def\thiscol{####1}}%
+  \ifx\current at color\@empty@\def\thiscol{}\else
+   \expandafter\tmp\current at color @\fi
+  \immediate\write\ptrfile{\noexpand\segmentcolor{\thiscol}}%
+  \ifx\current at page@color\@empty@\def\thiscol{}\else
+   \expandafter\tmp\current at page@color @\fi
+  \immediate\write\ptrfile{\noexpand\segmentpagecolor{\thiscol}}%
+ \egroup}%
+ \global\let\loadsegmentcolors=\relax
+}
+
+% These macros are needed within  images.tex  since this inputs
+% the <segment>.ptr files for a segment, so that counters are
+% colors are synchronised.
+%
+\newcommand{\segmentpagecolor}[1][]{%
+ \@ifpackageloaded{color}{\loadsegmentcolors\bgroup
+  \def\tmp{#1}\ifx\@empty\tmp\def\next{[]}\else\def\next{[#1]}\fi
+  \expandafter\segmentpagecolor@\next}%
+ {\@gobble}}
+\def\segmentpagecolor@[#1]#2{\def\tmp{#1}\def\tmpB{#2}%
+ \ifx\tmpB\@empty\let\next=\egroup
+ \else
+  \let\realendgroup=\endgroup
+  \def\endgroup{\edef\next{\noexpand\realendgroup
+   \def\noexpand\current at page@color{\current at color}}\next}%
+  \ifx\tmp\@empty\real at pagecolor{#2}\def\model{}%
+  \else\real at pagecolor[#1]{#2}\def\model{[#1]}%
+  \fi
+  \edef\next{\egroup\def\noexpand\current at page@color{\current at page@color}%
+  \noexpand\real at pagecolor\model{#2}}%
+ \fi\next}
+%
+\newcommand{\segmentcolor}[2][named]{\@ifpackageloaded{color}%
+ {\loadsegmentcolors\segmentcolor[#1]{#2}}{}}
+
+\@ifpackageloaded{color}{\loadsegmentcolors}{\let\real at pagecolor=\@gobble
+ \AtBeginDocument{\@ifpackageloaded{color}{\loadsegmentcolors}{}}}
+
+
+%  Define the \segment[align]{file}{section-command}{section-title} command,
+%  and its helper macros.  This command does four things:
+%       1)  Begins a new LaTeX section;
+%       2)  Writes a list of section counters to file.ptr, each
+%           of which represents the sum of the LaTeX section
+%           counters, and the l-counters, defined above;
+%       3)  Write an \htmlhead{section-title} command to file.ptr;
+%       4)  Inputs file.tex.
+
+\newcommand{\segment}{\@ifstar{\@@htmls}{\@@html}}
+%\tracingall
+\newcommand{\@endsegment}[1][]{}
+\let\endsegment\@endsegment
+\newcommand{\@@htmls}[1][]{\@@htmlsx{#1}}
+\newcommand{\@@html}[1][]{\@@htmlx{#1}}
+\def\@@htmlsx#1#2#3#4{\csname #3\endcsname* {#4}%
+                   \DumpCounters{#2}{#3*}{#4}{#1}\input{#2}}
+\def\@@htmlx#1#2#3#4{\csname #3\endcsname {#4}%
+                   \DumpCounters{#2}{#3}{#4}{#1}\input{#2}}
+
+\makeatother
+\endinput
+
+
+% Modifications:
+%
+% (The listing of Initiales see Changes)
+
+% $Log: html.sty,v $
+% Revision 1.1  2001/05/12 00:38:48  cvs
+% *** empty log message ***
+%
+% Revision 1.1  2000/03/22 05:04:32  jelson
+% added parapin documentation
+%
+% Revision 1.38  1999/07/19 13:23:20  RRM
+%  --  compatibility with pdflatex and hyperref.sty
+%  	citations are not complete yet, I think
+%  --  ensure that \thechapter remains undefined; some packages use it
+%  	as a test for the type of documentclass being used.
+%
+% Revision 1.37  1999/03/12 07:02:38  RRM
+%  --  change macro name from \addTOCsection to \htmladdTOClink
+%  --  it has 3 + 1 optional argument, to allow a local path to a labels.pl
+%  	file for the external document, for cross-references
+%
+% Revision 1.36  1999/03/10 05:46:00  RRM
+%  --  extended the code for compatibilty with comment.sty
+%  --  allow excluded environments to work within tables,
+%  	with the excluded material spanning headers and several cells
+%  	thanks Avinash Chopde for recognising the need for this.
+%  --  added LaTeX support (ignores it) for  \htmladdTOCsection
+%  	thanks to Steffen Klupsch and Uli Wortmann for this idea.
+%
+% Revision 1.35  1999/03/08 11:16:16  RRM
+% 	html.sty  for LaTeX2HTML V99.1
+%
+%  --  ensure that html.sty can be loaded *after* hyperref.sty
+%  --  support new command  \htmlclear for <BR> in HTML, ignored by LaTeX
+%  --  ensure {part} and {chapter} counters are defined, even if not used
+%
+% Revision 1.34  1998/09/19 10:37:29  RRM
+%  --  fixed typo with \next{\hyperref}{....}
+%
+% Revision 1.33  1998/09/08 12:47:51  RRM
+%  --  changed macro-names for the \hyperref and \hypercite options
+% 	allows easier compatibility with other packages
+%
+% Revision 1.32  1998/08/24 12:15:14  RRM
+%  --  new command  \htmllanguagestyle  to associate a style class
+%  	with text declared as a particular language
+%
+% Revision 1.31  1998/07/07 14:15:41  RRM
+%  --  new commands  \htmlsetstyle  and  \htmladdtostyle
+%
+% Revision 1.30  1998/07/04 02:42:22  RRM
+%  --  cope with catcodes of % { } in rawhtml/comment/htmlonly environments
+%
+% Revision 1.29  1998/06/23 13:33:23  RRM
+%  --  use \begin{small} with the default for URLs
+%
+% Revision 1.28  1998/06/21 09:38:39  RRM
+%  --  implement \htmlurl  to agree with \url if already defined
+%     or loaded subsequently (LaTeX-2e only)
+%  --  get LaTeX to print the revision number when loading
+%
+% Revision 1.27  1998/06/20 15:13:10  RRM
+%  --  \TeX is already protected in recent versions of LaTeX
+% 	so \DeclareRobust doesn't work --- causes looping
+%  --  \part and \subparagraph need not be defined in some styles
+%
+% Revision 1.26  1998/06/01 08:36:49  latex2html
+%  --  implement optional argument for \endsegment
+%  --  made the counter value output from \DumpPtr more robust
+%
+% Revision 1.25  1998/05/09 05:43:35  latex2html
+%  --   conditionals for avoiding undefined counters
+%
+% Revision 1.23  1998/02/26 10:32:24  latex2html
+%  --  use \providecommand for  \latextohtml
+%  --  implemented \HTMLcode to do what \HTML did previously
+% 	\HTML still works, unless already defined by another package
+%  --  fixed problems remaining with undefined \chapter
+%  --  defined \endsegment
+%
+% Revision 1.22  1997/12/05 11:38:18  RRM
+%  --  implemented an optional argument to \begin for style-sheet info.
+%  --  modified use of an optional argument with sectioning-commands
+%
+% Revision 1.21  1997/11/05 10:28:56  RRM
+%  --  replaced redefinition of \@htmlrule with \htmlrulestar
+%
+% Revision 1.20  1997/10/28 02:15:58  RRM
+%  --  altered the way some special html-macros are defined, so that
+% 	star-variants are explicitly defined for LaTeX
+% 	 -- it is possible for these to occur within  images.tex
+% 	e.g. \htmlinfostar \htmlrulestar \tableofchildlinksstar
+%
+% Revision 1.19  1997/10/11 05:47:48  RRM
+%  --  allow the dummy {tex2html_nowrap} environment in LaTeX
+% 	use it to make its contents be evaluated in environment order
+%
+% Revision 1.18  1997/10/04 06:56:50  RRM
+%  --  uses Robin Fairbairns' code for ignored environments,
+%      replacing the previous  comment.sty  stuff.
+%  --  extensions to the \tableofchildlinks command
+%  --  extensions to the \htmlinfo command
+%
+% Revision 1.17  1997/07/08 11:23:39  RRM
+%     include value of footnote counter in .ptr files for segments
+%
+% Revision 1.16  1997/07/03 08:56:34  RRM
+%     use \textup  within the \latextohtml macro
+%
+% Revision 1.15  1997/06/15 10:24:58  RRM
+%      new command  \htmltracenv  as environment-ordered \htmltracing
+%
+% Revision 1.14  1997/06/06 10:30:37  RRM
+%  -   new command:  \htmlborder  puts environment into a <TABLE> cell
+%      with a border of specified width, + other attributes.
+%  -   new commands: \HTML  for setting arbitrary HTML tags, with attributes
+%                    \HTMLset  for setting Perl variables, while processing
+%                    \HTMLsetenv  same as \HTMLset , but it gets processed
+%                                 as if it were an environment.
+%  -   new command:  \latextohtml  --- to set the LaTeX2HTML name/logo
+%  -   fixed some remaining problems with \segmentcolor & \segmentpagecolor
+%
+% Revision 1.13  1997/05/19 13:55:46  RRM
+%      alterations and extra options to  \hypercite
+%
+% Revision 1.12  1997/05/09 12:28:39  RRM
+%  -  Added the optional argument to \htmlhead, also in \DumpCounters
+%  -  Implemented \HTMLset as a no-op in LaTeX.
+%  -  Fixed a bug in accessing the page at color settings.
+%
+% Revision 1.11  1997/03/26 09:32:40  RRM
+%  -  Implements LaTeX versions of  \externalcite  and  \hypercite  commands.
+%     Thanks to  Uffe Engberg  and  Stephen Simpson  for the suggestions.
+%
+% Revision 1.10  1997/03/06 07:37:58  RRM
+% Added the  \htmltracing  command, for altering  $VERBOSITY .
+%
+% Revision 1.9  1997/02/17 02:26:26  RRM
+% - changes to counter handling (RRM)
+% - shuffled around some definitions
+% - changed \htmlrule of 209 mode
+%
+% Revision 1.8  1997/01/26 09:04:12  RRM
+% RRM: added optional argument to sectioning commands
+%      \htmlbase  sets the <BASE HREF=...> tag
+%      \htmlinfo  and  \htmlinfo* allow the document info to be positioned
+%
+% Revision 1.7  1997/01/03 12:15:44  L2HADMIN
+% % - fixes to the  color  and  natbib  interfaces
+% % - extended usage of  \hyperref, via an optional argument.
+% % - extended use comment environments to allow shifting expansions
+% %     e.g. within \multicolumn  (`bug' reported by Luc De Coninck).
+% % - allow optional argument to: \htmlimage, \htmlhead,
+% %     \htmladdimg, \htmladdnormallink, \htmladdnormallinkfoot
+% % - added new commands: \htmlbody, \htmlnohead
+% % - added new command: \tableofchildlinks
+%
+% Revision 1.6  1996/12/25 03:04:54  JCL
+% added patches to segment feature from Martin Wilck
+%
+% Revision 1.5  1996/12/23 01:48:06  JCL
+%  o introduced the environment makeimage, which may be used to force
+%    LaTeX2HTML to generate an image from the contents.
+%    There's no magic, all what we have now is a defined empty environment
+%    which LaTeX2HTML will not recognize and thus pass it to images.tex.
+%  o provided \protect to the \htmlrule commands to allow for usage
+%    within captions.
+%
+% Revision 1.4  1996/12/21 19:59:22  JCL
+% - shuffled some entries
+% - added \latexhtml command
+%
+% Revision 1.3  1996/12/21 12:22:59  JCL
+% removed duplicate \htmlrule, changed \htmlrule back not to create a \hrule
+% to allow occurrence in caption
+%
+% Revision 1.2  1996/12/20 04:03:41  JCL
+% changed occurrence of \makeatletter, \makeatother
+% added new \htmlrule command both for the LaTeX2.09 and LaTeX2e
+% sections
+%
+%
+% jcl 30-SEP-96
+%  - Stuck the commands commonly used by both LaTeX versions to the top,
+%    added a check which stops input or reads further if the document
+%    makes use of LaTeX2e.
+%  - Introduced rrm's \dumpcurrentcolor and \bodytext
+% hws 31-JAN-96 - Added support for document segmentation
+% hws 10-OCT-95 - Added \htmlrule command
+% jz 22-APR-94 - Added support for htmlref
+% nd  - Created

Added: trunk/fusd/doc/make-examples.pl
===================================================================
--- trunk/fusd/doc/make-examples.pl	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/doc/make-examples.pl	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,50 @@
+#!/usr/bin/perl -w
+
+
+foreach $path (@ARGV) {
+    $writing = 0;
+
+    if (!open(IN, $path)) {
+	print "trying to open $path: $!\n";
+	next;
+    }
+
+    while ($line = <IN>) {
+	if ($line =~ /EXAMPLE (\w*) ([\w\-\.]*)/) {
+	    $command = $1;
+	    $filename = $2 . ".example";
+
+	    if ($command eq 'START') {
+		if ($writing == 0) {
+		    if (!open(OUT, ">>$filename")) {
+			print "trying to write to $filename: $!\n";
+		    } else {
+			print "$path: writing to $filename\n";
+			$writing = 1;
+		    }
+		} else {
+		    print "$path: got $line while already writing!\n";
+		}
+	    }
+
+	    if ($command eq 'STOP') {
+		if ($writing == 1) {
+		    close(OUT);
+		    $writing = 0;
+		} else {
+		    chomp($line);
+		    die "$path line $.: got $line when not writing!\n";
+		}
+	    }
+	} else {
+	    if ($writing && $line !~ /SKIPLINE/) {
+		print OUT $line;
+	    }
+	}
+    }
+    if ($writing) {
+	close(OUT);
+    }
+    close(IN);
+}
+


Property changes on: trunk/fusd/doc/make-examples.pl
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/examples/console-read.c
===================================================================
--- trunk/fusd/examples/console-read.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/console-read.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * console-read: This example demonstrates the easiest possible way to
+ * block a client system call: by blocking the driver itself.  Not
+ * recommended for anything but the most trivial drivers -- if you
+ * need a template from which to start on a real driver, use pager.c
+ * instead.
+ *
+ * $Id: console-read.c,v 1.4 2003/07/11 22:29:38 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "fusd.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+int do_open_or_close(struct fusd_file_info *file)
+{
+  return 0; /* attempts to open and close this file always succeed */
+}
+
+/* EXAMPLE START console-read.c */
+int do_read(struct fusd_file_info *file, char *user_buffer, 
+	    size_t user_length, loff_t *offset)
+{
+  char buf[128];
+  
+  if (*offset > 0)
+    return 0;
+
+  /* print a prompt */
+  printf("Got a read from pid %d.  What do we say?\n> ", file->pid);
+  fflush(stdout);
+
+  /* get a response from the console */
+  if (fgets(buf, sizeof(buf) - 1, stdin) == NULL)
+    return 0;
+
+  /* compute length of the response, and return */
+  user_length = MIN(user_length, strlen(buf));
+  memcpy(user_buffer, buf, user_length);
+  *offset += user_length;
+  return user_length;
+}
+/* EXAMPLE STOP */
+
+int main(int argc, char *argv[])
+{
+  struct fusd_file_operations fops = {
+    open: do_open_or_close,
+    read: do_read,
+    close: do_open_or_close };
+  
+  if (fusd_register("/dev/console-read", "misc", "console-read", 0666, NULL, &fops) < 0)
+    perror("Unable to register device");
+  else {
+    printf("/dev/console-read should now exist - calling fusd_run...\n");
+    fusd_run();
+  }
+  return 0;
+}
+

Added: trunk/fusd/examples/drums.c
===================================================================
--- trunk/fusd/examples/drums.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/drums.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,117 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * drums.c: Example of how to pass data to a callback, inspired by
+ * Alessandro Rubini's similar example in his article for Linux
+ * Magazine (http://www.linux.it/kerneldocs/devfs/)
+ *
+ * This example creates a bunch of devices in the /dev/drums
+ * directory: /dev/drums/bam, /dev/drums/bum, etc.  If you cat one of
+ * these devices, it returns a string that's the same as its name.
+ *
+ * $Id: drums.c,v 1.4 2003/07/11 22:29:38 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "fusd.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+
+/* EXAMPLE START drums.c */
+static char *drums_strings[] = {"bam", "bum", "beat", "boom",
+				"bang", "crash", NULL};
+
+int drums_read(struct fusd_file_info *file, char *user_buffer,
+	       size_t user_length, loff_t *offset)
+{
+  int len;
+  char sound[128];
+
+  /* file->device_info is what we passed to fusd_register when we
+   * registered the device */
+  strcpy(sound, (char *) file->device_info);
+  strcat(sound, "\n");
+
+  /* 1st read returns the sound; 2nd returns EOF */
+  if (*offset != 0)
+    return 0;
+
+  /* NEVER return more data than the user asked for */
+  len = MIN(user_length, strlen(sound));
+  memcpy(user_buffer, sound, len);
+  *offset += len;
+  return len;
+}
+
+/* EXAMPLE STOP drums.c */
+
+
+int do_open_or_close(struct fusd_file_info *file)
+{
+  return 0; /* opens and closes always succeed */
+}
+
+
+struct fusd_file_operations drums_fops = {
+  open: do_open_or_close,
+  read: drums_read,
+  close: do_open_or_close
+};
+
+/* EXAMPLE START drums.c */
+int main(int argc, char *argv[])
+{
+  int i;
+  char buf[128];
+  char devname[128];
+
+  for (i = 0; drums_strings[i] != NULL; i++) {
+    sprintf(buf, "/dev/drums/%s", drums_strings[i]);
+    sprintf(devname, "drum%s", drums_strings[i]);
+    if (fusd_register(buf, "drums", devname, 0666, drums_strings[i], &drums_fops) < 0)
+      fprintf(stderr, "%s register failed: %m\n", drums_strings[i]);
+  }
+
+  fprintf(stderr, "calling fusd_run...\n");
+  fusd_run();
+  return 0;
+}
+/* EXAMPLE STOP drums.c */

Added: trunk/fusd/examples/drums2.c
===================================================================
--- trunk/fusd/examples/drums2.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/drums2.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * drums2.c: Another example of how to pass data to a callback,
+ * inspired by Alessandro Rubini's similar example in his article for
+ * Linux Magazine (http://www.linux.it/kerneldocs/devfs/)
+ *
+ * Like the original drums.c, this example creates a bunch of devices
+ * in the /dev/drums directory: /dev/drums/bam, /dev/drums/bum, etc.
+ * However, it also uses the private_data structure to keep per-file
+ * state, and return a string unique to each user of the device.
+ *
+ * Note, unlike the original drums.c, this driver does not use *offset
+ * to remember if this user has read before; cat /dev/drums/X will
+ * read infinitely
+ *
+ * $Id: drums2.c,v 1.6 2003/07/11 22:29:38 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "fusd.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+
+/* EXAMPLE START drums2.c */
+struct drum_info {
+  char *name;
+  int num_users;
+} drums[] = {
+  { "bam", 0 },
+  { "bum", 0 },
+  /* ... */
+/* EXAMPLE STOP */
+  { "beat", 0 },
+  { "boom", 0 },
+  { "bang", 0 },
+  { "crash", 0 },
+/* EXAMPLE START drums2.c */
+  { NULL, 0 }
+};
+
+int drums_open(struct fusd_file_info *file)
+{
+  /* file->device_info is what we passed to fusd_register when we
+   * registered the device.  It's a pointer into the "drums" struct. */
+  struct drum_info *d = (struct drum_info *) file->device_info;
+
+  /* Store this user's unique user number in their private_data */
+  file->private_data = (void *) ++(d->num_users);
+
+  return 0; /* return success */
+}
+
+int drums_read(struct fusd_file_info *file, char *user_buffer,
+	       size_t user_length, loff_t *offset)
+{
+  struct drum_info *d = (struct drum_info *) file->device_info;
+  int len;
+  char sound[128];
+
+  sprintf(sound, "You are user %d to hear a drum go '%s'!\n",
+	  (int) file->private_data, d->name);
+
+  len = MIN(user_length, strlen(sound));
+  memcpy(user_buffer, sound, len);
+  return len;
+}
+/* EXAMPLE STOP */
+
+
+int drums_close(struct fusd_file_info *file)
+{
+  return 0; /* closes always succeed */
+}
+
+
+struct fusd_file_operations drums_fops = {
+  open: drums_open,
+  read: drums_read,
+  close: drums_close
+};
+
+/* EXAMPLE START drums2.c */
+
+int main(int argc, char *argv[])
+{
+  struct drum_info *d;
+  char buf[128];
+  char devname[128];
+
+  for (d = drums; d->name != NULL; d++) {
+    sprintf(buf, "/dev/drums/%s", d->name);
+    sprintf(devname, "drum%s", d->name);
+    if (fusd_register(buf, "drums", devname, 0666, d, &drums_fops) < 0)
+      fprintf(stderr, "%s register failed: %m\n", d->name);
+  }
+  /* ... */
+/* EXAMPLE STOP */
+
+  fprintf(stderr, "calling fusd_run...\n");
+  fusd_run();
+  return 0;
+}
+
+
+

Added: trunk/fusd/examples/drums3.c
===================================================================
--- trunk/fusd/examples/drums3.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/drums3.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * drums3.c: This example shows how to wait for both FUSD and non-FUSD
+ * events at the same time.  Instead of using fusd_run, we keep
+ * control of main() by using our own select loop.
+ *
+ * Like the original drums.c, this example creates a bunch of devices
+ * in the /dev/drums directory: /dev/drums/bam, /dev/drums/bum, etc.
+ * However, it also prints a prompt to the console, asking the user if
+ * how loud the drums should be.
+ *
+ * $Id: drums3.c,v 1.3 2003/07/11 22:29:38 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "fusd.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+
+static char *drums_strings[] = {"bam", "bum", "beat", "boom",
+				"bang", "crash", NULL};
+
+int volume = 2; /* default volume is 2 */
+
+int drums_read(struct fusd_file_info *file, char *user_buffer,
+	       size_t user_length, loff_t *offset)
+{
+  int len;
+  char sound[128], *c;
+
+  /* 1st read returns the sound; 2nd returns EOF */
+  if (*offset != 0)
+    return 0;
+
+  if (volume == 1)
+    strcpy(sound, "(you hear nothing)");
+  else {
+    strcpy(sound, (char *) file->device_info);
+
+    if (volume >= 3)
+      for (c = sound; *c; c++)
+	*c = toupper(*c);
+
+    if (volume >= 4)
+      strcat(sound, "!!!!!");
+  }
+
+  strcat(sound, "\n");
+
+  /* NEVER return more data than the user asked for */
+  len = MIN(user_length, strlen(sound));
+  memcpy(user_buffer, sound, len);
+  *offset += len;
+  return len;
+}
+
+
+int do_open_or_close(struct fusd_file_info *file)
+{
+  return 0; /* opens and closes always succeed */
+}
+
+
+struct fusd_file_operations drums_fops = {
+  open: do_open_or_close,
+  read: drums_read,
+  close: do_open_or_close
+};
+
+
+void read_volume(int fd)
+{
+  char buf[100];
+  int new_vol = 0, retval;
+
+  if (fd < 0)
+    goto prompt;
+
+  retval = read(fd, buf, sizeof(buf)-1);
+
+  if (retval >= 0) {
+    buf[retval] = '\0';
+
+    if (*buf == 'q') {
+      printf("Goodbye...\n");
+      exit(0);
+    }
+
+    new_vol = atoi(buf);
+  }
+
+  if (new_vol >= 1 && new_vol <= 4) {
+    volume = new_vol;
+    printf("Volume changed to %d\n", volume);
+  } else {
+    printf("Invalid volume!\n");
+  }
+
+ prompt:
+  printf("\nHow loud would you like the /dev/drums?\n");
+  printf("   1 - Inaudible\n");
+  printf("   2 - Quiet\n");
+  printf("   3 - Loud\n");
+  printf("   4 - Permanent ear damage!\n");
+  printf("   q - Exit program\n");
+  printf("Your choice? ");
+  fflush(stdout);
+}
+
+
+  
+
+int main(int argc, char *argv[])
+{
+  int i;
+  char buf[128];
+  char devname[128];
+  fd_set fds, tmp;
+  int max;
+
+  for (i = 0; drums_strings[i] != NULL; i++) {
+    sprintf(buf, "/dev/drums/%s", drums_strings[i]);
+    sprintf(devname, "drum%s", drums_strings[i]);
+    if (fusd_register(buf, "drums", devname, 0666, drums_strings[i], &drums_fops) < 0)
+      fprintf(stderr, "%s register failed: %m\n", drums_strings[i]);
+  }
+
+  /* print the initial prompt to the user */
+  read_volume(-1);
+
+/* EXAMPLE START drums3.c */
+  /* initialize the set */
+  FD_ZERO(&fds);
+
+  /* add stdin to the set */
+  FD_SET(STDIN_FILENO, &fds);
+  max = STDIN_FILENO;
+
+  /* add all FUSD fds to the set */
+  fusd_fdset_add(&fds, &max);
+
+  while (1) {
+    tmp = fds;
+    if (select(max+1, &tmp, NULL, NULL, NULL) < 0)
+      perror("selecting");
+    else {
+      /* if stdin is readable, read the user's response */
+      if (FD_ISSET(STDIN_FILENO, &tmp))
+	read_volume(STDIN_FILENO);
+
+      /* call any FUSD callbacks that have messages waiting */
+      fusd_dispatch_fdset(&tmp);
+    }
+  }
+/* EXAMPLE STOP drums3.c */
+}
+
+
+

Added: trunk/fusd/examples/echo.c
===================================================================
--- trunk/fusd/examples/echo.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/echo.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * echo.c: Example of how to use both the 'read' and 'write' callbacks.
+ *
+ * This example creates a single device, /dev/echo.  If you write
+ * something to /dev/echo (e.g., "echo HI THERE > /dev/echo"), it gets
+ * stored.  Then, when you read (e.g. "cat /dev/echo"), you get back
+ * whatever you wrote most recently.
+ *
+ * $Id: echo.c,v 1.6 2003/07/11 22:29:38 cerpa Exp $ 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "fusd.h"
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/* EXAMPLE START echo.c */
+char *data = NULL;
+int data_length = 0;
+
+int echo_read(struct fusd_file_info *file, char *user_buffer,
+	      size_t user_length, loff_t *offset)
+{
+  /* if the user has read past the end of the data, return EOF */
+  if (*offset >= data_length)
+    return 0;
+
+  /* only return as much data as we have */
+  user_length = MIN(user_length, data_length - *offset);
+
+  /* copy data to user starting from the first byte they haven't seen */
+  memcpy(user_buffer, data + *offset, user_length);
+  *offset += user_length;
+
+  /* tell them how much data they got */
+  return user_length;
+}
+
+ssize_t echo_write(struct fusd_file_info *file, const char *user_buffer,
+		   size_t user_length, loff_t *offset)
+{
+  /* free the old data, if any */
+  if (data != NULL) {
+    free(data);
+    data = NULL;
+    data_length = 0;
+  }
+
+  /* allocate space for new data; return error if that fails */
+  if ((data = malloc(user_length)) == NULL)
+    return -ENOMEM;
+
+  /* make a copy of user's data; tell the user we copied everything */
+  memcpy(data, user_buffer, user_length);
+  data_length = user_length;
+  return user_length;
+}
+/* EXAMPLE STOP */
+
+int do_open_or_close(struct fusd_file_info *file)
+{
+  return 0; /* opens and closes always succeed */
+}
+
+
+struct fusd_file_operations echo_fops = {
+  open: do_open_or_close,
+  read: echo_read,
+  write: echo_write,
+  close: do_open_or_close
+};
+
+
+int main(int argc, char *argv[])
+{
+  if (fusd_register("/dev/echo", "misc", "echo", 0666, NULL, &echo_fops) < 0) {
+    perror("register of /dev/echo failed");
+    exit(1);
+  }
+
+  fprintf(stderr, "calling fusd_run...\n");
+  fusd_run();
+  return 0;
+}

Added: trunk/fusd/examples/helloworld
===================================================================
(Binary files differ)


Property changes on: trunk/fusd/examples/helloworld
___________________________________________________________________
Name: svn:executable
   + 
Name: svn:mime-type
   + application/octet-stream

Added: trunk/fusd/examples/helloworld.c
===================================================================
--- trunk/fusd/examples/helloworld.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/helloworld.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,91 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * hello-world: Simply creates a device called /dev/hello-world, which
+ * greets you when you try to get it.
+ *
+ * $Id: helloworld.c,v 1.11 2003/07/11 22:29:38 cerpa Exp $
+ */
+
+/* EXAMPLE START helloworld.c */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "fusd.h"
+
+#define GREETING "Hello, world!\n"
+
+int do_open_or_close(struct fusd_file_info *file)
+{
+  return 0; /* attempts to open and close this file always succeed */
+}
+
+ssize_t do_read(struct fusd_file_info *file, char *user_buffer, 
+	    size_t user_length, loff_t *offset)
+{
+  int retval = 0;
+  
+  /* The first read to the device returns a greeting.  The second read
+   * returns EOF. */
+  if (*offset == 0) {
+    if (user_length < strlen(GREETING))
+      retval = -EINVAL;  /* the user must supply a big enough buffer! */
+    else {
+      memcpy(user_buffer, GREETING, strlen(GREETING)); /* greet user */
+      retval = strlen(GREETING); /* retval = number of bytes returned */
+      *offset += retval; /* advance user's file pointer */
+    }
+  }
+  
+  return retval;
+}
+
+int main(int argc, char *argv[])
+{
+  struct fusd_file_operations fops = {
+    open: do_open_or_close,
+    read: do_read,
+    close: do_open_or_close };
+  
+  if (fusd_register("/dev/hello-world", "test", "hello-world", 0666, NULL, &fops) < 0)
+    perror("Unable to register device");
+  else {
+    printf("/dev/hello-world should now exist - calling fusd_run...\n");
+    fusd_run();
+  }
+  return 0;
+}
+/* EXAMPLE STOP helloworld.c */

Added: trunk/fusd/examples/ioctl.c
===================================================================
--- trunk/fusd/examples/ioctl.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/ioctl.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,292 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * ioctl.c: Shows both the client side and server side of FUSD ioctl
+ * servicing.
+ *
+ * There's a lot of extra cruft in this example program (compared to
+ * the other examples, anyway), because this program is both an
+ * example and part of the regression test suite.
+ *
+ * $Id: ioctl.c,v 1.4 2003/07/11 22:29:39 cerpa Exp $ 
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include "fusd.h"
+
+/* EXAMPLE START ioctl.h */
+/* definition of the structure exchanged between client and server */
+struct ioctl_data_t {
+  char string1[60];
+  char string2[60];
+};
+
+#define IOCTL_APP_TYPE 71 /* arbitrary number unique to this app */
+
+#define IOCTL_TEST0 _IO(IOCTL_APP_TYPE, 0) /* no argument */  /* SKIPLINE */
+#define IOCTL_TEST1 _IO(IOCTL_APP_TYPE, 1) /* int argument */ /* SKIPLINE */
+#define IOCTL_TEST2 _IO(IOCTL_APP_TYPE, 2) /* int argument */
+#define IOCTL_TEST3 _IOR(IOCTL_APP_TYPE, 3, struct ioctl_data_t)
+#define IOCTL_TEST4 _IOW(IOCTL_APP_TYPE, 4, struct ioctl_data_t)
+#define IOCTL_TEST5 _IOWR(IOCTL_APP_TYPE, 5, struct ioctl_data_t)
+/* EXAMPLE STOP ioctl.h */
+#define IOCTL_TEST_TERMINATE _IO(IOCTL_APP_TYPE, 6)
+
+#define TEST1_NUM 12345
+#define TEST3_STRING1 "This is test3 - string1"
+#define TEST3_STRING2 "This is test3 - string2"
+#define TEST4_STRING1 "This is test 4's string1"
+#define TEST4_STRING2 "This is test 4's string2"
+#define TEST5_STRING1_IN  "If you're happy and you know it"
+#define TEST5_STRING2_IN  "clap your hands!"
+#define TEST5_STRING1_OUT "IF YOU'RE HAPPY AND YOU KNOW IT"
+#define TEST5_STRING2_OUT "CLAP YOUR HANDS!"
+
+
+#define CHECK(condition) do { \
+  if (!(condition)) { \
+      printf("%s: TEST FAILED\n", __STRING(condition)); \
+      errors++; \
+  } \
+} while(0)
+
+
+int zeroreturn(struct fusd_file_info *file) { return 0; }
+
+/* EXAMPLE START ioctl-server.c */
+/* This function is run by the driver */
+int do_ioctl(struct fusd_file_info *file, int cmd, void *arg)
+{
+  static int errors = 0;	/* SKIPLINE */
+  char *c;			/* SKIPLINE */
+  struct ioctl_data_t *d;
+
+  if (_IOC_TYPE(cmd) != IOCTL_APP_TYPE)
+    return 0;
+
+  switch (cmd) {
+/* EXAMPLE STOP ioctl-server.c */
+  case IOCTL_TEST0:
+    printf("ioctl server: got test0, returning 0\n");
+    return 0;
+    break;
+
+  case IOCTL_TEST1:
+  case IOCTL_TEST2:
+    printf("ioctl server: got test1/2, arg=%d, returning it\n", (int) arg);
+    return (int) arg;
+    break;
+
+/* EXAMPLE START ioctl-server.c */
+  case IOCTL_TEST3: /* returns data to the client */
+    d = arg;
+    printf("ioctl server: got test3 request (read-only)\n");/* SKIPLINE */
+    printf("ioctl server: ...returning test strings for client to read\n"); /* SKIPLINE */
+    strcpy(d->string1, TEST3_STRING1);
+    strcpy(d->string2, TEST3_STRING2);
+    return 0;
+    break;
+
+  case IOCTL_TEST4: /* gets data from the client */
+    d = arg;
+    printf("ioctl server: got test4 request (write-only)\n"); /* SKIPLINE */
+    printf("ioctl server: ...got the following strings written by client:\n"); /* SKIPLINE */
+    printf("ioctl server: test4, string1: got '%s'\n", d->string1);
+    printf("ioctl server: test4, string2: got '%s'\n", d->string2);
+    CHECK(!strcmp(d->string1, TEST4_STRING1));/* SKIPLINE */
+    CHECK(!strcmp(d->string2, TEST4_STRING2)); /* SKIPLINE */
+    return 0;
+    break;
+/* EXAMPLE STOP ioctl-server.c */
+
+  case IOCTL_TEST5:
+    d = arg;
+    printf("ioctl server: got test5 request (read+write)\n");
+    printf("ioctl server: test5, string1: got '%s'\n", d->string1);
+    printf("ioctl server: test5, string2: got '%s'\n", d->string2);
+    printf("ioctl server: capitalizing the strings and returning them\n");
+    for (c = d->string1; *c; c++)
+      *c = toupper(*c);
+    for (c = d->string2; *c; c++)
+      *c = toupper(*c);
+    return 0;
+    break;
+
+  case IOCTL_TEST_TERMINATE:
+    printf("ioctl server: got request to terminate, calling exit(%d)\n",
+	   errors);
+    printf("ioctl server: note: client should see -EPIPE\n");
+    exit(errors);
+    break;
+
+/* EXAMPLE START ioctl-server.c */
+  default:
+    printf("ioctl server: got unknown cmd, sigh, this is broken\n");
+    return -EINVAL;
+    break;
+  }
+
+  return 0;
+}
+/* EXAMPLE STOP ioctl-server.c */
+
+int main(int argc, char *argv[])
+{
+  pid_t server_pid, retpid;
+
+  if ((server_pid = fork()) < 0) {
+    perror("error creating server");
+    exit(1);
+  }
+
+  if (server_pid == 0) {
+    /* ioctl server */
+    struct fusd_file_operations f = { open: zeroreturn, close: zeroreturn,
+				      ioctl: do_ioctl};
+    if (fusd_register("ioctltest", 0666, NULL, &f) < 0)
+      perror("registering ioctltest");
+    printf("server starting\n");
+    fusd_run();
+  } else {
+    /* ioctl client */
+/* EXAMPLE START ioctl-client.c */
+    int fd, ret;
+    struct ioctl_data_t d;
+/* EXAMPLE STOP ioctl-client.c */
+    int errors, status;
+
+    errors = 0;
+
+    sleep(1);
+/* EXAMPLE START ioctl-client.c */
+
+    if ((fd = open("/dev/ioctltest", O_RDWR)) < 0) {
+      perror("client: can't open ioctltest");
+      exit(1);
+    }
+
+/* EXAMPLE STOP ioctl-client.c */
+    errors = 0;
+
+    /* test0: simply issue a command and get a retval */
+    ret = ioctl(fd, IOCTL_TEST0);
+    printf("ioctl test0: got %d (expecting 0)\n\n", ret);
+    CHECK(ret == 0);
+
+    /* test1: issue a command with a simple (integer) argument */
+    ret = ioctl(fd, IOCTL_TEST1, TEST1_NUM);
+    CHECK(ret == TEST1_NUM);
+    CHECK(errno == 0);
+    printf("ioctl test1: got %d, errno=%d (expecting %d, errno=0)\n\n",
+	   ret, errno, TEST1_NUM);
+
+    /* test2 again: make sure errno is set properly */
+    ret = ioctl(fd, IOCTL_TEST2, -ELIBBAD);
+    CHECK(errno == ELIBBAD);
+    CHECK(ret == -1);
+    printf("ioctl test2: got %d, errno=%d (expecting -1, errno=%d)\n\n",
+	   ret, errno, ELIBBAD);
+
+    printf("ioctl test3: expecting retval 0, string This Is Test3\n");
+/* EXAMPLE START ioctl-client.c */
+    /* test3: make sure we can get data FROM a driver using ioctl */
+    ret = ioctl(fd, IOCTL_TEST3, &d);
+    CHECK(ret == 0);		/* SKIPLINE */
+    CHECK(!strcmp(d.string1, TEST3_STRING1)); /* SKIPLINE */
+    CHECK(!strcmp(d.string2, TEST3_STRING2)); /* SKIPLINE */
+    printf("ioctl test3: got retval=%d\n", ret);
+    printf("ioctl test3: got string1='%s'\n", d.string1);
+    printf("ioctl test3: got string2='%s'\n", d.string2);
+    printf("\n");		/* SKIPLINE */
+
+    /* test4: make sure we can send data TO a driver using an ioctl */
+    printf("ioctl test4: server should see string 'This Is Test4'\n");/* SKIPLINE */
+    sprintf(d.string1, TEST4_STRING1);
+    sprintf(d.string2, TEST4_STRING2);
+    ret = ioctl(fd, IOCTL_TEST4, &d);
+/* EXAMPLE STOP ioctl-client.c */
+    CHECK(ret == 0);
+    printf("\n");
+
+    /* test5: we send 2 strings to the ioctl server, they should come
+     * back in all caps */
+    printf("ioctl test5: we send strings that should come back capitalized\n");
+    sprintf(d.string1, TEST5_STRING1_IN);
+    sprintf(d.string2, TEST5_STRING2_IN);
+    printf("ioctl test5: sending  string1='%s'\n", d.string1);
+    printf("ioctl test5: sending  string2='%s'\n", d.string2);
+    ret = ioctl(fd, IOCTL_TEST5, &d);
+    CHECK(ret == 0);
+    CHECK(!strcmp(d.string1, TEST5_STRING1_OUT));
+    CHECK(!strcmp(d.string2, TEST5_STRING2_OUT));
+    printf("ioctl test5: got retval=%d\n", ret);
+    printf("ioctl test5: got back string1='%s'\n", d.string1);
+    printf("ioctl test5: got back string2='%s'\n", d.string2);
+    printf("\n");
+
+    /* now tell the server to terminate, we should get EPIPE */
+    ret = ioctl(fd, IOCTL_TEST_TERMINATE);
+    CHECK(errno == EPIPE);
+    CHECK(ret == -1);
+    printf("ioctl termination test: got %d (errno=%d)\n", ret, errno);
+    printf("ioctl termination tets: expecting ret=-1, errno=%d\n\n", EPIPE);
+
+    printf("ioctl client: waiting for server to terminate...\n");
+    retpid = wait(&status);
+    CHECK(retpid == server_pid);
+    CHECK(WEXITSTATUS(status) == 0);
+      
+    printf("ioctl test done - %d errors\n", errors);
+    if (errors) {
+      printf("IOCTL REGRESSION TEST FAILED\n");
+      exit(1);
+    } else {
+      printf("all tests passed\n");
+      exit(0);
+    }
+  }
+
+  return 0;
+}

Added: trunk/fusd/examples/logring.c
===================================================================
--- trunk/fusd/examples/logring.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/logring.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,455 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * logring.c: Implementation of a circular buffer log device
+ *
+ * logring makes it easy to access the most recent (and only the most
+ * recent) output from a process. It works just like "tail -f" on a
+ * log file, except that the storage required never grows. This can be
+ * useful in embedded systems where there isn't enough memory or disk
+ * space for keeping complete log files, but the most recent debugging
+ * messages are sometimes needed (e.g., after an error is observed).
+ *
+ * Logring uses FUSD to implement a character device, /dev/logring,
+ * that acts like a named pipe that has a finite, circular buffer.
+ * The size of the buffer is given as a command-line argument.  As
+ * more data is written into the buffer, the oldest data is discarded.
+ * A process that reads from the logring device will first read the
+ * existing buffer, then block and see new data as it's written,
+ * similar to monitoring a log file using "tail -f".
+ *
+ * Non-blocking reads are supported; if a process needs to get the
+ * current contents of the log without blocking to wait for new data,
+ * it can set the O_NONBLOCK flag when it does the open(), or set it
+ * later using ioctl().
+ *
+ * The select() interface is also supported; programs can select on
+ * /dev/logring to be notified when new data is available.
+ *
+ * Run this example program by typing "logring X", where X is the size
+ * of the circular buffer in bytes.  Then, type "cat /dev/logring" in
+ * one shell.  The cat process will block, waiting for data, similar
+ * to "tail -f".  From another shell, write to the logring (e.g.,
+ * "echo Hi there > /dev/logring".)  The 'cat' process will see the
+ * message appear.
+ *
+ * Note: this example program is based on "emlog", a true Linux kernel
+ * module with identical functionality.  If you find logring useful,
+ * but want to use it on a system that does not have FUSD, check out
+ * emlog at http://www.circlemud.org/~jelson/software/emlog.
+ *
+ * $Id: logring.c,v 1.8 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <fusd.h>
+
+
+/* per-client structure to keep track of who has an open FD to us */
+struct logring_client {
+
+  /* used to store outstanding read and polldiff requests */
+  struct fusd_file_info *read;
+  struct fusd_file_info *polldiff;
+
+  /* to construct the linked list */
+  struct logring_client *next;
+};
+
+/* list of currently open file descriptors */
+struct logring_client *client_list = NULL;
+
+char *logring_data = NULL;	/* the data buffer used for the logring */
+int logring_size = 0;		/* buffer space in the logring */
+int logring_writeindex = 0;	/* write point in the logring array */
+int logring_readindex = 0;	/* read point in the logring array */
+int logring_offset = 0;		/* how far into the total stream is
+				 * logring_read pointing? */
+
+/* amount of data in the queue */
+#define LOGRING_QLEN  (logring_writeindex >= logring_readindex ? \
+         logring_writeindex - logring_readindex : \
+         logring_size - logring_readindex + logring_writeindex)
+
+/* stream byte number of the last byte in the queue */
+#define LOGRING_FIRST_EMPTY_BYTE (logring_offset + LOGRING_QLEN)
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
+/************************************************************************/
+
+/* 
+ * this function removes an element from a linked list.  the
+ * pointer-manipulation insanity below is a trick that prevents the
+ * "element to be removed is the head of the list" from being a
+ * special case.
+ */
+void client_list_remove(struct logring_client *c)
+{
+  struct logring_client **ptr;
+
+  if (c == NULL || client_list == NULL)
+    return;
+
+  for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
+    if (!*ptr) {
+      fprintf(stderr, "trying to remove a client that isn't in the list\n");
+      return;
+    }
+  }
+  *ptr = c->next;
+}
+
+
+/* open on /dev/logring: create state for this client */
+static int logring_open(struct fusd_file_info *file)
+{
+  /* create state for this client */
+  struct logring_client *c = malloc(sizeof(struct logring_client));
+
+  if (c == NULL)
+    return -ENOBUFS;
+
+  /* initialize fields of this client state */
+  memset(c, 0, sizeof(struct logring_client));
+
+  /* save the pointer to this state so it gets returned to us later */
+  file->private_data = c;
+
+  /* add this client to the client list */
+  c->next = client_list;
+  client_list = c;
+  
+  return 0;
+}
+
+
+/* close on /dev/logring: destroy state for this client */
+static int logring_close(struct fusd_file_info *file)
+{
+  struct logring_client *c;
+
+  if ((c = (struct logring_client *) file->private_data) != NULL) {
+
+    /* take this client off our client list */
+    client_list_remove(c);
+
+    /* if there is a read outstanding, free the state */
+    if (c->read != NULL) {
+      fusd_destroy(c->read);
+      c->read = NULL;
+    }
+    /* destroy any outstanding polldiffs */
+    if (c->polldiff != NULL) {
+      fusd_destroy(c->polldiff);
+      c->polldiff = NULL;
+    }
+
+    /* get rid of the struct */
+    free(c);
+    file->private_data = NULL;
+  }
+  return 0;
+}
+
+
+
+/*
+ * This function "completes" a read: that is, matches up a client who
+ * is requesting data with data that's waiting to be served.
+ *
+ * This function is called in two cases:
+ *
+ *   1- When a new read request comes in (it might be able to complete
+ *   immediately, if there's data waiting that the client hasn't seen
+ *   yet)
+ *
+ *   2- When new data comes in (the new data might be able to complete
+ *   a read that had been previously blocked)
+ */
+void logring_complete_read(struct logring_client *c)
+{
+  loff_t *user_offset;
+  char *user_buffer;
+  size_t user_length;
+  int bytes_copied = 0, n, start_point, retval;
+
+
+  /* if there is no outstanding read, do nothing */
+  if (c == NULL || c->read == NULL)
+    return;
+
+  /* retrieve the read callback's arguments */
+  user_offset = fusd_get_offset(c->read);
+  user_buffer = fusd_get_read_buffer(c->read);
+  user_length = fusd_get_length(c->read);
+
+  /* is the client trying to read data that has scrolled off? */
+  if (*user_offset < logring_offset)
+    *user_offset = logring_offset;
+
+  /* is there new data this user hasn't seen yet, or are we at EOF? */
+  /* If we have reached EOF:
+   *     If this is a nonblocking read, return EAGAIN.  
+   *     else return without doing anything; keep the read blocked.
+   */
+  if (*user_offset >= LOGRING_FIRST_EMPTY_BYTE) {
+    if (c->read->flags & O_NONBLOCK) {
+      retval = -EAGAIN;
+      goto done;
+    } else {
+      return;
+    }
+  }
+
+  /* find the smaller of the total bytes we have available and what
+   * the user is asking for */
+  user_length = MIN(user_length, LOGRING_FIRST_EMPTY_BYTE - *user_offset);
+  retval = user_length;
+
+   /* figure out where to start copying data from, based on user's offset */
+  start_point =
+    (logring_readindex + (*user_offset-logring_offset)) % logring_size;
+
+  /* copy the (possibly noncontiguous) data into user's buffer) */
+  while (user_length) {
+    n = MIN(user_length, logring_size - start_point);
+    memcpy(user_buffer + bytes_copied, logring_data + start_point, n);
+    bytes_copied += n;
+    user_length -= n;
+    start_point = (start_point + n) % logring_size;
+  }
+
+  /* advance the user's file pointer */
+  *user_offset += retval;
+
+ done:
+  /* and complete the read system call */
+  fusd_return(c->read, retval);
+  c->read = NULL;
+}
+
+
+
+/*
+ * read on /dev/logring: store the fusd_file_info pointer.  then call
+ * complete_read, which will immediately call fusd_return, if there is
+ * data already waiting.
+ *
+ * Note that this shows a trick we use commonly in FUSD drivers: you
+ * are allowed to call fusd_return() from within a callback as long as
+ * you return -FUSD_NOREPLY.  In other words, a driver can EITHER
+ * return a real return value from its callback, OR call fusd_return
+ * explicitly, but not both.
+ */
+static ssize_t logring_read(struct fusd_file_info *file, char *buffer,
+			    size_t len, loff_t *offset)
+{
+  struct logring_client *c = (struct logring_client *) file->private_data;
+
+  if (c == NULL || c->read != NULL) {
+    fprintf(stderr, "logring_read's arguments are confusd, alas");
+    return -EINVAL;
+  }
+
+  c->read = file;
+  logring_complete_read(c);
+  return -FUSD_NOREPLY;
+}
+
+
+/*
+ * complete_polldiff: if a client has an outstanding 'polldiff'
+ * request, possibly return updated poll-state information to the
+ * kernel, if indeed the state has changed.
+ */
+void logring_complete_polldiff(struct logring_client *c)
+
+{
+  int curr_state, cached_state;
+
+  /* if there is no outstanding polldiff, do nothing */
+  if (c == NULL || c->polldiff == NULL)
+    return;
+
+  /* figure out the "current" state: i.e. whether or not the logring
+   * is readable for this client based on its current position in the
+   * stream.  The logring is *always* writable. */
+  if (*(fusd_get_offset(c->polldiff)) < LOGRING_FIRST_EMPTY_BYTE)
+    curr_state = FUSD_NOTIFY_INPUT | FUSD_NOTIFY_OUTPUT; /* read and write */
+  else
+    curr_state = FUSD_NOTIFY_OUTPUT; /* writable only */
+
+  /* cached_state is what the kernel *thinks* the state is */
+  cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
+
+  /* if the state is not what the kernel thinks it is, notify the
+     kernel of the change */
+  if (curr_state != cached_state) {
+    fusd_return(c->polldiff, curr_state);
+    c->polldiff = NULL;
+  }
+}
+
+
+/* This function is only called on behalf of clients who are trying to
+ * use select().  The kernel keeps us up to date on what it thinks the
+ * current "poll state" is, i.e. readable and/or writable.  The kernel
+ * calls this function every time its assumption about the current
+ * poll state changes.  Every time the driver's notion of the state
+ * differs from what the kernel thinks it is, it should return the
+ * poll_diff request with the updated state.  Note that a 2nd request
+ * may come from the kernel before the driver has returned the first
+ * one; if this happens, use fusd_destroy() to get rid of the older one.
+ */
+ssize_t logring_polldiff(struct fusd_file_info *file, unsigned int flags)
+{
+  struct logring_client *c = (struct logring_client *) file->private_data;
+
+  if (c == NULL)
+    return -EIO;
+
+  /* if we're already holding a polldiff request that we haven't
+   * replied to yet, destroy the old one and hold onto only the new
+   * one */
+  if (c->polldiff != NULL) {
+    fusd_destroy(c->polldiff);
+    c->polldiff = NULL;
+  }
+
+  c->polldiff = file;
+  logring_complete_polldiff(c);
+  return -FUSD_NOREPLY;
+}
+
+
+/*
+ * a write on /dev/logring: first, copy the data from the user into our
+ * data queue.  Then, complete any reads and polldiffs that might be
+ * outstanding.
+ */
+ssize_t logring_write(struct fusd_file_info *file, const char *buffer,
+		      size_t len, loff_t *offset)
+{
+  struct logring_client *c;
+  int overflow = 0, bytes_copied = 0, n, retval;
+
+  /* if the message is longer than the buffer, just take the beginning
+   * of it, in hopes that the reader (if any) will have time to read
+   * before we wrap around and obliterate it */
+  len = MIN(len, logring_size - 1);
+  retval = len;
+
+  if (len + LOGRING_QLEN >= (logring_size-1)) {
+    overflow = 1;
+
+    /* in case of overflow, figure out where the new buffer will
+     * begin.  we start by figuring out where the current buffer ENDS:
+     * logring_offset + LOGRING_QLEN.  we then advance the end-offset
+     * by the length of the current write, and work backwards to
+     * figure out what the oldest unoverwritten data will be (i.e.,
+     * size of the buffer).  was that all quite clear? :-) */
+    logring_offset = logring_offset + LOGRING_QLEN + len - logring_size + 1;
+  }
+    
+  while (len) {
+    /* how many contiguous bytes are available from the write point to
+     * the end of the circular buffer? */
+    n = MIN(len, logring_size - logring_writeindex);
+    memcpy(logring_data + logring_writeindex, buffer + bytes_copied, n);
+    bytes_copied += n;
+    len -= n;
+    logring_writeindex = (logring_writeindex + n) % logring_size;
+  }
+
+  /* if there was an overflow (i.e., new data wrapped around and
+   * overwrote old data that had not yet been read), then, reset the
+   * read point to be whatever the oldest data is that we have. */
+  if (overflow)
+    logring_readindex = (logring_writeindex + 1) % logring_size;
+
+  /* now, complete any blocked reads and/or polldiffs */
+  for (c = client_list; c != NULL; c = c->next) {
+    logring_complete_read(c);
+    logring_complete_polldiff(c);
+  }
+
+  /* now tell the client how many bytes we acutally wrote */
+  return retval;
+}
+
+
+int main(int argc, char *argv[])
+{
+  char *name;
+
+  /* size must be provided, and an optional logring name */
+  if (argc != 2 && argc != 3) {
+    fprintf(stderr, "usage: %s <logring-size> [logring-name]\n", argv[0]);
+    exit(1);
+  }
+
+  name = (argc == 3 ? argv[2] : "/dev/logring");
+
+  /* convert the arg to an int and alloc memory for the logring */
+  if ((logring_size = atoi(argv[1])) <= 0) {
+    fprintf(stderr, "invalid logring size; it must be >0\n");
+    exit(1);
+  }
+
+  if ((logring_data = (char *) malloc(sizeof(char) * logring_size)) == NULL) {
+    fprintf(stderr, "couldn't allocate %d bytes!\n", logring_size);
+    exit(1);
+  }
+
+  /* register the fusd device */
+  fusd_simple_register(name, "misc", "logring", 0666, NULL,
+                       open: logring_open, close: logring_close,
+                       read: logring_read, write: logring_write,
+		       poll_diff: logring_polldiff);
+
+  printf("calling fusd_run; reads from /dev/logring will now block\n"
+	 "until someone writes to /dev/logring...\n");
+  fusd_run();
+
+  return 0;
+}
+

Added: trunk/fusd/examples/pager.c
===================================================================
--- trunk/fusd/examples/pager.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/pager.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,386 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * pagerd: simple daemon to accept page signals from underlying page
+ * devices, and redistribute those pages to applications.
+ *
+ * The application itself is not especially useful, but this example
+ * program has proved very valuable as a generic template for FUSD
+ * drivers that service multiple clients, and implement both blocking
+ * and selectable devices.  This file is a good place to start for
+ * writing drivers.  See logring.c for a more complex real-world
+ * application based on this template.
+ *
+ * How to use the pager:
+ *
+ * Interface for devices that generate pages: write "page" to
+ * /dev/pager/input
+ *
+ * Interface for programs waiting for pages: read from (or, select on
+ * and then read from) /dev/pager/notify.  reads will unblock when a
+ * page arrives.  Note that if more than one page arrives before you
+ * read, you'll only get the most recent one.  In other words, you are
+ * guaranteed to get at least one page. 
+ *
+ * Important: in order to guarantee that you do not miss any pages,
+ * you MUST NOT close the file descriptor in between reads/selects.
+ * If you close the FD and then reopen it, there will be a race (pages
+ * that arrive between the close and open will not be delivered).
+ *
+ * $Id: pager.c,v 1.9 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <fusd.h>
+
+
+/* EXAMPLE START pager-open.c */
+/* per-client structure to keep track of who has an open FD to us */
+struct pager_client {
+  int last_page_seen;   /* seq. no. of last page this client has seen */
+  struct fusd_file_info *read; /* outstanding read request, if any */
+  struct fusd_file_info *polldiff; /* outstanding polldiff request */ 
+  struct pager_client *next;   /* to construct the linked list */
+};
+
+struct pager_client *client_list = NULL; /* list of clients (open FDs) */
+int last_page = 0; /* seq. no. of the most recent page to arrive */
+
+/* EXAMPLE STOP pager-open.c */
+
+void pager_notify_complete_read(struct pager_client *c);
+void pager_notify_complete_polldiff(struct pager_client *c);
+
+
+/************************************************************************/
+
+/* 
+ * this function removes an element from a linked list.  the
+ * pointer-manipulation insanity below is a trick that prevents the
+ * "element to be removed is the head of the list" from being a
+ * special case.
+ */
+void client_list_remove(struct pager_client *c)
+{
+  struct pager_client **ptr;
+
+  if (c == NULL || client_list == NULL)
+    return;
+
+  for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
+    if (!*ptr) {
+      fprintf(stderr, "trying to remove a client that isn't in the list\n");
+      return;
+    }
+  }
+  *ptr = c->next;
+}
+
+
+/* EXAMPLE START pager-open.c */
+/* open on /dev/pager/notify: create state for this client */
+static int pager_notify_open(struct fusd_file_info *file)
+{
+  /* create state for this client */
+  struct pager_client *c = malloc(sizeof(struct pager_client));
+
+  if (c == NULL)
+    return -ENOBUFS;
+
+  /* initialize fields of this client state */
+  memset(c, 0, sizeof(struct pager_client));
+  c->last_page_seen = last_page;
+
+  /* save the pointer to this state so it gets returned to us later */
+  file->private_data = c;
+
+  /* add this client to the client list */
+  c->next = client_list;
+  client_list = c;
+  
+  return 0;
+}
+/* EXAMPLE STOP pager-open.c */
+
+
+/* EXAMPLE START pager-close.c */
+/* close on /dev/pager/notify: destroy state for this client */
+static int pager_notify_close(struct fusd_file_info *file)
+{
+  struct pager_client *c;
+
+  if ((c = (struct pager_client *) file->private_data) != NULL) {
+
+    /* take this client off our client list */
+    client_list_remove(c);
+
+    /* if there is a read outstanding, free the state */
+    if (c->read != NULL) {
+      fusd_destroy(c->read);
+      c->read = NULL;
+    }
+    /* destroy any outstanding polldiffs */
+    if (c->polldiff != NULL) {
+      fusd_destroy(c->polldiff);
+      c->polldiff = NULL;
+    }
+
+    /* get rid of the struct */
+    free(c);
+    file->private_data = NULL;
+  }
+  return 0;
+}
+/* EXAMPLE STOP pager-close.c */
+
+
+/*
+ * read on /dev/pager/notify: store the fusd_file_info pointer.  then call
+ * complete_read, which will immediately call fusd_return, if there is
+ * a page already waiting.
+ *
+ * Note that this shows a trick we use commonly in FUSD drivers: you
+ * are allowed to call fusd_return() from within a callback as long as
+ * you return -FUSD_NOREPLY.  In other words, a driver can EITHER
+ * return a real return value from its callback, OR call fusd_return
+ * explicitly, but not both.
+ */
+/* EXAMPLE START pager-read.c */
+ssize_t pager_notify_read(struct fusd_file_info *file, char *buffer,
+			  size_t len, loff_t *offset)
+{
+  struct pager_client *c = (struct pager_client *) file->private_data;
+
+  if (c == NULL || c->read != NULL) {
+    fprintf(stderr, "pager_read's arguments are confusd, alas");
+    return -EINVAL;
+  }
+
+  c->read = file;
+  pager_notify_complete_read(c);
+  return -FUSD_NOREPLY;
+}
+
+/* EXAMPLE STOP pager-read.c */
+
+/*
+ * This function "completes" a read: that is, matches up a client who
+ * is requesting data with data that's waiting to be served.
+ *
+ * This function is called in two cases:
+ *
+ *   1- When a new read request comes in.  The driver might be able to
+ *   complete immediately, if a page arrived between the time the
+ *   process opened the device and performed the read.  This is the
+ *   common case for clients that use select.  hasn't seen yet - this
+ *   is normal if )
+ *
+ *   2- When a new page arrives, all readers are unblocked
+ */
+/* EXAMPLE START pager-read.c */
+void pager_notify_complete_read(struct pager_client *c)
+{
+  /* if there is no outstanding read, do nothing */
+  if (c == NULL || c->read == NULL)
+    return;
+
+  /* if there are no outstanding pages, do nothing */
+  if (c->last_page_seen >= last_page)
+    return;
+
+  /* bring this client up to date with the most recent page */
+  c->last_page_seen = last_page;
+
+  /* and notify the client by unblocking the read (read returns 0) */
+  fusd_return(c->read, 0);
+  c->read = NULL;
+}
+/* EXAMPLE STOP pager-read.c */
+
+
+/* This function is only called on behalf of clients who are trying to
+ * use select().  The kernel keeps us up to date on what it thinks the
+ * current "poll state" is, i.e. readable and/or writable.  The kernel
+ * calls this function every time its assumption about the current
+ * poll state changes.  Every time the driver's notion of the state
+ * differs from what the kernel's cached value, it should return the
+ * poll_diff request with the updated state.  Note that a 2nd request
+ * may come from the kernel before the driver has returned the first
+ * one; if this happens, use fusd_destroy() to get rid of the older one.
+ */
+/* EXAMPLE START pager-polldiff.c */
+ssize_t pager_notify_polldiff(struct fusd_file_info *file,
+			      unsigned int cached_state)
+{
+  struct pager_client *c = (struct pager_client *) file->private_data;
+
+  if (c == NULL)
+    return -EINVAL;
+
+  /* if we're already holding a polldiff request that we haven't
+   * replied to yet, destroy the old one and hold onto only the new
+   * one */
+  if (c->polldiff != NULL) {
+    fusd_destroy(c->polldiff);
+    c->polldiff = NULL;
+  }
+
+  c->polldiff = file;
+  pager_notify_complete_polldiff(c);
+  return -FUSD_NOREPLY;
+}
+
+/* EXAMPLE STOP pager-polldiff.c */
+
+
+/*
+ * complete_polldiff: if a client has an outstanding 'polldiff'
+ * request, possibly return updated poll-state information to the
+ * kernel, if indeed the state has changed.
+ */
+/* EXAMPLE START pager-polldiff.c */
+void pager_notify_complete_polldiff(struct pager_client *c)
+{
+  int curr_state, cached_state;
+
+  /* if there is no outstanding polldiff, do nothing */
+  if (c == NULL || c->polldiff == NULL)
+    return;
+
+  /* figure out the "current" state: i.e. whether or not the pager
+   * is readable for this client based on the last page it saw */
+  if (c->last_page_seen < last_page)
+    curr_state = FUSD_NOTIFY_INPUT; /* readable */
+  else
+    curr_state = 0; /* not readable or writable */
+
+  /* cached_state is what the kernel *thinks* the state is */
+  cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
+
+  /* if the state is not what the kernel thinks it is, notify the
+     kernel of the change */
+  if (curr_state != cached_state) {
+    fusd_return(c->polldiff, curr_state);
+    c->polldiff = NULL;
+  }
+}
+/* EXAMPLE STOP pager-polldiff.c */
+
+
+
+/*
+ * this handles a write on /dev/pager/input.  this is called by one of
+ * the underlying page devices when a page arrives.  if a device
+ * writes "page" to this interface, a page is queued for everyone
+ * using the notify interface.
+ */
+#define CASE(x) if ((found == 0) && !strcmp(tmp, x) && (found = 1))
+/* EXAMPLE START pager-read.c */
+
+ssize_t pager_input_write(struct fusd_file_info *file,
+			  const char *buffer, size_t len, loff_t *offset)
+{
+  struct pager_client *c;
+
+  /* ... */
+  /* EXAMPLE STOP pager-read.c */
+  char tmp[1024];
+  int found = 0;
+
+  if (len > sizeof(tmp) - 1)
+    len = sizeof(tmp) - 1;
+  
+  strncpy(tmp, buffer, len);
+  tmp[len] = '\0';
+
+  /* strip trailing \n's */
+  while (tmp[len-1] == '\n')
+    tmp[--len] = '\0';
+
+  /* EXAMPLE START pager-read.c */
+
+  CASE("page") {
+    last_page++;
+
+    for (c = client_list; c != NULL; c = c->next) {
+      pager_notify_complete_polldiff(c);
+      pager_notify_complete_read(c);
+    }
+  }
+  /* EXAMPLE STOP pager-read.c */
+
+  /* other commands (if there ever are any) can go here */
+
+  if (!found)
+    return -EINVAL;
+  else
+    return len;
+}
+#undef CASE
+
+
+static int fusd_success(struct fusd_file_info *file)
+{
+  return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+  /* register the input device */
+  fusd_simple_register("/dev/pager/input", "pager", "input", 0666, NULL,
+                       open: fusd_success, close: fusd_success,
+                       write: pager_input_write);
+
+  /* register the notification device */
+  fusd_simple_register("/dev/pager/notify", "pager", "notify", 0666, NULL,
+                       open: pager_notify_open,
+		       close: pager_notify_close,
+                       read: pager_notify_read,
+  		       poll_diff: pager_notify_polldiff);
+
+  printf("calling fusd_run; reads from /dev/pager/notify will now block\n"
+	 "until someone writes 'page' to /dev/pager/input...\n");
+  fusd_run();
+
+  return 0;
+}
+

Added: trunk/fusd/examples/uid-filter.c
===================================================================
--- trunk/fusd/examples/uid-filter.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/examples/uid-filter.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,113 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD - The Framework for UserSpace Devices - Example program
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ *
+ * uid-filter.  This program shows how you can use some of the
+ * meta-data provided in the fusd_file_info structure to affect your
+ * driver's behavior.
+ *
+ * In particular, this driver creates a device, /dev/my-pid, that can
+ * not be read by anyone other than the driver owner (not even root!).
+ * When you read from the device, it returns your PID to you.
+ *
+ * $Id: uid-filter.c,v 1.4 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "fusd.h"
+
+
+int do_close(struct fusd_file_info *file)
+{
+  return 0; /* attempts to close the file always succeed */
+}
+
+/* EXAMPLE START uid-filter.c */
+int do_open(struct fusd_file_info *file)
+{
+  /* If the UID of the process trying to do the read doesn't match the
+   * UID of the owner of the driver, return -EPERM.  If you run this
+   * driver as a normal user, even root won't be able to read from the
+   * device file created! */
+  if (file->uid != getuid())
+    return -EPERM;
+
+  return 0;
+}
+
+int do_read(struct fusd_file_info *file, char *user_buffer,
+	    size_t user_length, loff_t *offset)
+{
+  char buf[128];
+  int len;
+
+  /* The first read to the device returns a greeting.  The second read
+   * returns EOF. */
+  if (*offset != 0)
+    return 0;
+
+  /* len gets set to the number of characters written to buf */
+  len = sprintf(buf, "Your PID is %d.  Have a nice day.\n", file->pid);
+
+  /* NEVER return more data than the user asked for */
+  if (user_length < len)
+    len = user_length;
+
+  memcpy(user_buffer, buf, len);
+  *offset += len;
+  return len;
+}
+/* EXAMPLE STOP uid-filter.c */
+
+
+int main(int argc, char *argv[])
+{
+  struct fusd_file_operations fops = {
+    open: do_open,
+    read: do_read,
+    close: do_close };
+  
+  if (fusd_register("/dev/my-pid", "misc", "my-pid", 0666, NULL, &fops) < 0)
+    perror("Unable to register device");
+  else {
+    printf("/dev/my-pid should now exist - calling fusd_run...\n");
+    fusd_run();
+  }
+  return 0;
+}

Added: trunk/fusd/include/fusd.h
===================================================================
--- trunk/fusd/include/fusd.h	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/include/fusd.h	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,285 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD: the Framework for User-Space Devices
+ *
+ * Public header function for user-space library.  This is the API
+ * that user-space device drivers should write to.
+ */
+
+#ifndef __FUSD_H__
+#define __FUSD_H__
+
+#ifndef __KERNEL__
+#include <sys/types.h>
+
+__BEGIN_DECLS
+#endif
+
+
+#include "fusd_msg.h"
+
+/* FUSD_NOREPLY is a special error code.  If a user-space driver
+ * implementing a system call returns -FUSD_NOREPLY (note it's
+ * negative!), the calling application will be blocked.  When
+ * conditions enable a response to the system call (e.g. the read or
+ * write has completed), the user-space driver must call the
+ * fusd_return() function.  */
+#define FUSD_NOREPLY  0x1000
+
+/* FUSD defines several bitmasks for describing which channels of  
+ * notification are being requested or signaled.  These flags are
+ * used in the arguments and return value of the notify() callback. */
+#define FUSD_NOTIFY_INPUT   0x1
+#define FUSD_NOTIFY_OUTPUT  0x2
+#define FUSD_NOTIFY_EXCEPT  0x4
+
+
+#define FUSD_KOR_HACKED_VERSION
+
+struct fusd_file_info; /* forward decl */
+
+typedef
+struct fusd_file_operations {
+  int (*open) (struct fusd_file_info *file);
+  int (*close) (struct fusd_file_info *file);
+  ssize_t (*read) (struct fusd_file_info *file, char *buffer, size_t length,
+		   loff_t *offset);
+  ssize_t (*write) (struct fusd_file_info *file, const char *buffer,
+		    size_t length, loff_t *offset);
+  int (*ioctl) (struct fusd_file_info *file, int request, void *data);
+  int (*poll_diff) (struct fusd_file_info *file, unsigned int cached_state);
+  int (*unblock) (struct fusd_file_info *file);
+  int (*mmap) (struct fusd_file_info *file, int offset, size_t length, int flags, void** addr, size_t* out_length);
+} fusd_file_operations_t;
+
+
+/* state-keeping structure passed to device driver callbacks */
+typedef
+struct fusd_file_info {
+  void *device_info;		/* This is set by the library to
+				 * whatever you passed to
+				 * fusd_register.  Changing this in a
+				 * file_operations callback has no
+				 * effect. */
+
+  void *private_data;		/* File-specific data you can change
+				 * in a file_operations callback.
+				 * e.g., you can set this in an open()
+				 * callback, then get it in a
+				 * corresponding read() callback. */
+
+  unsigned int flags;		/* Kept synced with file->f_flags */
+  pid_t pid;			/* PID of process making the request */
+  uid_t uid;			/* UID of process making the request */
+  gid_t gid;			/* GID of process making the request */
+
+  /* other info might be added later, e.g. state needed to complete
+     operations... */
+
+  /* request message associated with this call */
+  int fd;
+  fusd_msg_t *fusd_msg;
+
+} fusd_file_info_t;
+
+
+
+
+/*************************** Library Functions ****************************/
+
+/* fusd_register: create a device file and register callbacks for it
+ *
+ * Arguments:
+ *
+ *    name - the name of the device file, to be created wherever devfs
+ *    is mounted (usually dev).  example: pass "mydevice" will create
+ *    /dev/mydevice.
+ *
+ *    As a convenience, passing a string that starts with "/dev/" will
+ *    automatically skip over that portion of the name.
+ *
+ *    mode - the file protections to be given to the device
+ *
+ *    device_info - you can provide arbitrary data that will later be
+ *    passed back to your driver's callbacks in file->device_info.
+ *    value has no effect on FUSD itself.
+ *
+ *    fops - a table of callbacks to be called for this device; see
+ *    structure above.
+ *
+ * Return value:
+ *    On failure, -1 is returned and errno is set to indicate the error.
+ *
+ *    On success, a valid file descriptor is returned which represents
+ *    the control channel to your new device.  You should never read
+ *    from or write to that control channel directcly, but you can
+ *    select on it to see when it needs attention (see fusd_run and
+ *    fusd_dispatch).
+ */
+
+int fusd_register(const char *name, const char* clazz, const char* devname, mode_t mode, void *device_info,
+		  struct fusd_file_operations *fops);
+
+
+
+/* "simple" interface to fusd_register. */
+#define fusd_simple_register(name, clazz, devname, perms, arg, ops...) do { \
+   struct fusd_file_operations f = { ops } ; \
+   if (fusd_register(name, clazz, devname, perms, arg, &f) < 0) \
+      perror("warning: fusd unavailable"); \
+} while(0)
+
+/* fusd_unregister: unregister a previously registered device
+ *
+ * Arguments:
+ *    fd - the file descriptor previously returned to you by fusd_register.
+ *
+ * Return value:
+ *    0 on success.
+ *   -1 on failure with errno set to indicate the failure.
+ */
+int fusd_unregister(int fd);
+
+
+/* fusd_return: unblock a previously blocked system call
+ * 
+ * Arguments:
+ *   file - the file info struct that was previously blocked
+ *   retval - the return value that would have been returned by the
+ *            returning system call
+ *    
+ * Return value:
+ *   0 on success.
+ *  -1 on failure with errno set to indicate the failure
+ */
+int fusd_return(struct fusd_file_info *file, ssize_t retval);
+
+
+/*
+ * fusd_destroy destroys all state associated with a fusd_file_info
+ * pointer.  (It is implicitly called by fusd_return.)  If a driver
+ * saves a fusd_file_info pointer by calling -FUSD_NOREPLY in order to
+ * block a read, but gets a "close" request on the file before the
+ * pointer is returned with fusd_return, it should be thrown away
+ * using fusd_destroy.  
+ */
+void fusd_destroy(struct fusd_file_info *file);
+
+
+/* fusd_dispatch: handles an event on a fusd file descriptor
+ * 
+ * Arguments:
+ *   fd - the file descriptor of the device that received an event
+ *    
+ * Return value:
+ *    None.
+ *
+ * Side effects:
+ *    May (but may not) call a callback function originally passed to
+ *    fusd_register.
+ *
+ *    Prints an error to stderr in case of a dispatching error.
+ */
+void fusd_dispatch(int fd);
+
+
+/* 
+ * fusd_run: convenience function that handles dispatch for all
+ *           fusd devices
+ *
+ * No return value; runs forever.
+ */
+void fusd_run(void);
+
+
+/*
+ * fusd_fdset_add: given an FDSET and "max", add the currently valid
+ * FUSD fds to the set and update max accordingly.
+ */
+void fusd_fdset_add(fd_set *set, int *max);
+
+
+/*
+ * fusd_dispatch_fdset: given an fd_set full of descriptors, call
+ * fusd_dispatch on every descriptor in the set which is a valid FUSD
+ * fd.
+ */
+void fusd_dispatch_fdset(fd_set *set);
+
+
+/********************************************************************
+ *
+ *  Direct access API
+ *
+ *  This API enables a driver implementation to store state about a 
+ *  blocked call more easily, extracting the call arguments directly
+ *  with no need to store them separately.
+ *
+ ********************************************************************/
+
+/* accessors */
+static inline int fusd_get_call_type(struct fusd_file_info *file)
+{ return file->fusd_msg->subcmd; }
+
+static inline char * fusd_get_read_buffer(struct fusd_file_info *file)
+{ return file->fusd_msg->data; }
+
+static inline const char * fusd_get_write_buffer(struct fusd_file_info *file)
+{ return (const char *)file->fusd_msg->data; }
+
+static inline size_t fusd_get_length(struct fusd_file_info *file)
+{ return (size_t)file->fusd_msg->datalen; }
+
+static inline loff_t *fusd_get_offset(struct fusd_file_info *file)
+{ return &(file->fusd_msg->parm.fops_msg.offset); }
+
+static inline int fusd_get_ioctl_request(struct fusd_file_info *file)
+{ return file->fusd_msg->parm.fops_msg.cmd; }
+
+static inline unsigned long fusd_get_ioctl_arg(struct fusd_file_info *file)
+{ return file->fusd_msg->parm.fops_msg.arg.arg; }
+
+static inline void * fusd_get_ioctl_buffer(struct fusd_file_info *file)
+{ return (void *)file->fusd_msg->data; }
+
+static inline int fusd_get_poll_diff_cached_state(struct fusd_file_info *file)
+{ return file->fusd_msg->parm.fops_msg.cmd; }
+
+/* returns static string representing the flagset (e.g. RWE) */
+char *fusd_unparse_flags(int flags);
+
+#ifndef __KERNEL__
+__END_DECLS
+#endif
+
+#endif /* __FUSD_H__ */


Property changes on: trunk/fusd/include/fusd.h
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/include/fusd_msg.h
===================================================================
--- trunk/fusd/include/fusd_msg.h	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/include/fusd_msg.h	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,151 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD: the Framework for User-Space Devices
+ *
+ * Defines the interface between the kernel module and userspace library.
+ *
+ */
+
+#ifndef __FUSD_MSG_H__
+#define __FUSD_MSG_H__
+
+/* filenames */
+#define DEFAULT_DEV_ROOT           "/dev/"
+#define FUSD_CONTROL_FILENAME      "fusd/control"
+#define FUSD_STATUS_FILENAME       "fusd/status"
+
+#define FUSD_CONTROL_DEVNAME       DEFAULT_DEV_ROOT FUSD_CONTROL_FILENAME
+#define FUSD_STATUS_DEVNAME        DEFAULT_DEV_ROOT FUSD_STATUS_FILENAME
+
+/* ioctl number to tell FUSD status device to return binary info */
+#define FUSD_STATUS_USE_BINARY     _IO('F', 100)
+
+/* constants */
+#define FUSD_MAX_NAME_LENGTH       47 /* 47, to avoid expanding union size */
+
+
+/* commands */
+#define FUSD_REGISTER_DEVICE       0 /* device registration */
+#define FUSD_UNREGISTER_DEVICE     1 /* device unregistration */
+
+/* these two must have successive numbers */
+#define FUSD_FOPS_CALL             2 /* synchronous round-trip call: request */
+#define FUSD_FOPS_REPLY            (FUSD_FOPS_CALL + 1)
+
+/* these two must have successive numbers */
+#define FUSD_FOPS_NONBLOCK         4 /* call that does not block for a reply */
+#define FUSD_FOPS_NONBLOCK_REPLY   (FUSD_FOPS_NONBLOCK + 1)
+
+#define FUSD_FOPS_CALL_DROPREPLY   6 /* call that doesn't want a reply */
+
+/* subcommands */
+#define FUSD_OPEN                  100
+#define FUSD_CLOSE                 101
+#define FUSD_READ                  102
+#define FUSD_WRITE                 103
+#define FUSD_IOCTL                 104
+#define FUSD_POLL_DIFF             105
+#define FUSD_UNBLOCK               106
+#define FUSD_MMAP                  107
+
+/* other constants */
+#define FUSD_MSG_MAGIC      0x7a6b93cd
+
+/* user->kernel: register a device */
+typedef struct {
+  char name[FUSD_MAX_NAME_LENGTH+1];
+	char clazz[FUSD_MAX_NAME_LENGTH+1];
+	char devname[FUSD_MAX_NAME_LENGTH+1];
+  mode_t mode;
+  void *device_info;
+} register_msg_t;
+
+
+/* kernel->user: fops request message (common data) */
+typedef struct {
+  pid_t pid;
+  uid_t uid;
+  gid_t gid;
+  unsigned int flags;		/* flags from file struct */
+  void *device_info;		/* device info */
+  void *private_info;		/* file info */
+
+  /* parameters and return values for various calls.  should be a
+   * union but it just makes things too complex and doesn't save all
+   * that much memory anyway */
+  ssize_t retval;
+  size_t length;
+  loff_t offset;
+  unsigned int cmd;  /* ioctl cmd, poll_diff cached_state */
+
+  union {
+    unsigned long arg; /* ioctl */
+    void *ptr_arg;
+  } arg;
+
+  /* the following are cookies that have meaning internal to the kernel
+   * but must be returned, untouched, by userspace */
+  void *fusd_file;
+  long transid;
+  int hint;
+} fops_msg_t;
+
+
+/* the message struct written to FUSD control channel */
+typedef struct {
+  int magic;
+  short int cmd;
+  short int subcmd;
+
+  char *data;  /* yes, it's slightly inefficient to push this useless
+		* pointer between user and kernel space, but it makes
+		* it much easier to have a pointer available in this
+		* structure that both the kernel and userlib can make
+		* their own use of. */
+  int datalen;
+  union {
+    register_msg_t register_msg; /* device registration (U->K) */
+    fops_msg_t fops_msg;	/* U->K and K->U fops messages */
+  } parm;
+} fusd_msg_t;
+
+
+/* structure read from FUSD binary status device */
+typedef struct {
+  char name[FUSD_MAX_NAME_LENGTH+1];
+  int zombie;
+  pid_t pid;
+  int num_open;
+} fusd_status_t;
+
+#endif /* __FUSD_MSG_H__ */


Property changes on: trunk/fusd/include/fusd_msg.h
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/include/kfusd.h
===================================================================
--- trunk/fusd/include/kfusd.h	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/include/kfusd.h	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,288 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD: the Framework for User-Space Devices
+ *
+ * Jeremy Elson <jelson at circlemud.org>
+ * Copyright (c) Sensoria Corporation 2001
+ *
+ * Private header file used by the Linux Kernel Module
+ *
+ * $Id: kfusd.h,v 1.41 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+#ifndef __KFUSD_H__
+#define __KFUSD_H__
+
+#include "fusd_msg.h"
+
+/* magic numbers for structure checking; unique w.r.t
+ * /usr/src/linux/Documentation/magic-number.txt */
+#define FUSD_DEV_MAGIC      0x8b43a123
+#define FUSD_FILE_MAGIC     0x613aa8fe
+
+/* number of devices that can be created with fusd */
+#define MAX_FUSD_DEVICES    128
+
+/* number of times each device can be opened simultaneously */
+#define MIN_FILEARRAY_SIZE  8  /* initialize allocation */
+#define MAX_FILEARRAY_SIZE  1024 /* maximum it can grow to */
+
+/* maximum read/write size we're willing to service */
+#define MAX_RW_SIZE         (1024*128)
+
+
+/********************** Structure Definitions *******************************/
+
+/* Container for a fusd msg */
+typedef struct fusd_msgC_s_t fusd_msgC_t;
+
+struct fusd_msgC_s_t {
+  fusd_msg_t fusd_msg;		/* the message itself */
+  fusd_msgC_t *next;		/* pointer to next one in the list */
+
+  /* 1-bit flags */
+  unsigned int peeked:1;	/* has the first half of this been read? */
+};
+
+struct fusd_transaction
+{
+	struct list_head list;
+	long transid;
+	int subcmd;
+	int pid;
+	int size;
+	fusd_msg_t* msg_in;
+};
+
+/* magical forward declarations to break the circular dependency */
+struct fusd_dev_t_s;
+typedef struct fusd_dev_t_s fusd_dev_t;
+struct CLASS;
+struct class_device;
+
+/* state kept per opened file (i.e., an instance of a device) */
+typedef struct {
+  /* general state management */
+  int magic;			/* magic number for sanity checking */
+  fusd_dev_t *fusd_dev;		/* fusd device associated with this file */
+  long fusd_dev_version;	/* version number of fusd device */
+  void *private_data;		/* the user's private data (we ignore it) */
+  struct file *file;		/* kernel's file pointer for this file */
+  int index;			/* our index in our device's file array */
+  struct semaphore file_sem;	/* Semaphore for file structure */
+  int cached_poll_state;	/* Latest result from a poll diff req */
+  int last_poll_sent;		/* Last polldiff request we sent */
+
+  /* structures used for messaging */
+  wait_queue_head_t file_wait;	/* Wait on this for a user->kernel msg */
+  wait_queue_head_t poll_wait;  /* Given to kernel for poll() queue */
+	struct list_head transactions;
+	struct semaphore transactions_sem;
+	
+} fusd_file_t;
+
+
+/* state kept per device registered under fusd */
+struct fusd_dev_t_s {
+  int magic;			/* Magic number for sanity checking */
+  long version;			/* Instance number of this device */
+  int zombie;			/* Is the device dead? */
+  pid_t pid;			/* PID of device driver */
+  struct task_struct* task;
+  
+  char *name;			/* Name of the device under devfs (/dev) */
+	char *class_name;
+	char *dev_name;
+	struct CLASS *clazz;
+	int owns_class;
+	struct class_device *class_device;
+	
+  void *private_data;		/* User's private data */
+	struct cdev* handle;
+	dev_t dev_id;
+//  devfs_handle_t handle;	/* The devfs-provided handle */
+
+  fusd_file_t **files;		/* Array of this device's open files */
+  int array_size;		/* Size of the array pointed to by 'files' */
+  int num_files;		/* Number of array entries that are valid */
+  int open_in_progress;		/* File is referencing this struct,
+                                   but not yet part of the file array */
+  /* messaging */
+  fusd_msgC_t *msg_head;	/* linked list head for message queue */
+  fusd_msgC_t *msg_tail;	/* linked list tail for message queue */
+
+  /* synchronization */
+  wait_queue_head_t dev_wait;	/* Wait queue for kernel->user msgs */
+  struct semaphore dev_sem;	/* Sempahore for device structure */
+
+  /* pointer to allow a dev to be placed on a dev_list */
+  struct list_head devlist;
+};
+  
+
+/**** Function Prototypes ****/
+
+STATIC int maybe_free_fusd_dev(fusd_dev_t *fusd_dev);
+
+STATIC int find_fusd_file(fusd_dev_t *fusd_dev, fusd_file_t *fusd_file);
+STATIC int free_fusd_file(fusd_dev_t *fusd_dev, fusd_file_t *fusd_file);
+
+STATIC int fusd_fops_call_send(fusd_file_t *fusd_file_arg,
+			       fusd_msg_t *fusd_msg, struct fusd_transaction** transaction);
+STATIC int fusd_fops_call_wait(fusd_file_t *fusd_file_arg,
+			       fusd_msg_t **fusd_msg_reply, struct fusd_transaction* transaction);
+STATIC void fusd_fops_call_done(fusd_file_t *fusd_file);
+
+STATIC void fusd_forge_close(fusd_msg_t *msg, fusd_dev_t *fusd_dev);
+
+STATIC int fusd_add_transaction(fusd_file_t *fusd_file, int transid, int subcmd, int size, struct fusd_transaction** out_transaction);
+STATIC void fusd_cleanup_transaction(fusd_file_t *fusd_file, struct fusd_transaction* transaction);
+STATIC void fusd_remove_transaction(fusd_file_t *fusd_file, struct fusd_transaction* transaction);
+STATIC struct fusd_transaction* fusd_find_transaction(fusd_file_t *fusd_file, int transid);
+STATIC struct fusd_transaction* fusd_find_transaction_by_pid(fusd_file_t *fusd_file, int pid);
+
+
+
+/**** Utility functions & macros ****/
+
+#ifdef CONFIG_FUSD_USE_WAKEUPSYNC
+#define WAKE_UP_INTERRUPTIBLE_SYNC(x) wake_up_interruptible_sync(x)
+#else
+#define WAKE_UP_INTERRUPTIBLE_SYNC(x) wake_up_interruptible(x)
+#endif /* CONFIG_FUSD_USE_WAKEUPSYNC */
+
+#ifdef CONFIG_FUSD_DEBUG
+static void rdebug_real(char *fmt, ...)
+  __attribute__ ((format (printf, 1, 2)));
+
+#define RDEBUG(message_level, args...) do { \
+   if (fusd_debug_level >= message_level) rdebug_real(args); \
+} while(0)
+#else
+#define RDEBUG(message_level, args...)
+#endif /* CONFIG_FUSD_DEBUG */
+
+
+#define ZOMBIE(fusd_dev)  ((fusd_dev)->zombie)
+
+
+#define GET_FUSD_DEV(candidate, fusd_dev) do { \
+  fusd_dev = candidate; \
+  if (fusd_dev == NULL || fusd_dev->magic != FUSD_DEV_MAGIC) \
+        goto invalid_dev; \
+} while (0)
+
+#define GET_FUSD_FILE_AND_DEV(candidate, fusd_file, fusd_dev) do { \
+  fusd_file = candidate; \
+  if (fusd_file == NULL || fusd_file->magic != FUSD_FILE_MAGIC) \
+     goto invalid_file; \
+  GET_FUSD_DEV(fusd_file->fusd_dev, fusd_dev); \
+  if (fusd_dev->version != fusd_file->fusd_dev_version) \
+    goto invalid_file; \
+} while (0)
+
+
+#define LOCK_FUSD_DEV(fusd_dev) \
+  do { down(&fusd_dev->dev_sem); \
+  if (ZOMBIE(fusd_dev)) { up(&fusd_dev->dev_sem); goto zombie_dev; } \
+ } while (0)
+
+/* rawlock does not do a zombie check */
+#define RAWLOCK_FUSD_DEV(fusd_dev) \
+  do { down(&fusd_dev->dev_sem); } while (0)
+
+#define UNLOCK_FUSD_DEV(fusd_dev) \
+  do { up(&fusd_dev->dev_sem); } while (0)
+
+
+#define LOCK_FUSD_FILE(fusd_file) \
+  do { down(&fusd_file->file_sem); \
+ } while (0)
+
+#define UNLOCK_FUSD_FILE(fusd_file) \
+  do { up(&fusd_file->file_sem); } while (0)
+
+#define FREE_FUSD_MSGC(fusd_msgc) do { \
+   if ((fusd_msgc)->fusd_msg.data != NULL) VFREE(fusd_msgc->fusd_msg.data); \
+   KFREE(fusd_msgc); \
+} while (0)
+
+#define NAME(fusd_dev) ((fusd_dev)->name == NULL ? \
+			"<noname>" : (fusd_dev)->name)
+
+#ifdef CONFIG_FUSD_MEMDEBUG
+static int  fusd_mem_init(void);
+static void fusd_mem_cleanup(void);
+static void fusd_mem_add(void *ptr, int line, int size);
+static void fusd_mem_del(void *ptr);
+static void *fusd_kmalloc(size_t size, int type, int line);
+static void fusd_kfree(void *ptr);
+static void *fusd_vmalloc(size_t size, int line);
+static void fusd_vfree(void *ptr);
+# define KMALLOC(size, type) fusd_kmalloc(size, type, __LINE__)
+# define KFREE(ptr) fusd_kfree(ptr)
+# define VMALLOC(size) fusd_vmalloc(size, __LINE__)
+# define VFREE(ptr) fusd_vfree(ptr)
+#else /* no memory debugging */
+# define KMALLOC(size, type) kmalloc(size, type)
+# define KFREE(ptr) kfree(ptr)
+/*# define VMALLOC(size) vmalloc(size)*/
+# define VMALLOC(size) kmalloc(size, GFP_KERNEL)
+# define VFREE(ptr) kfree(ptr)
+#endif /* CONFIG_FUSD_MEMDEBUG */
+
+
+
+/* Functions like this should be in the kernel, but they are not.  Sigh. */
+#ifdef CONFIG_SMP
+
+DECLARE_MUTEX(atomic_ops);
+
+static __inline__ int atomic_inc_and_ret(int *i)
+{
+  int val;
+
+  down(&atomic_ops);
+  val = (++(*i));
+  up(&atomic_ops);
+  return val;
+}
+#else
+static __inline__ int atomic_inc_and_ret(int *i)
+{
+  return (++(*i));
+}
+#endif
+
+
+#endif /* __KFUSD_H__ */


Property changes on: trunk/fusd/include/kfusd.h
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/kfusd/Makefile
===================================================================
--- trunk/fusd/kfusd/Makefile	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/kfusd/Makefile	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,19 @@
+ifneq ($(KERNELRELEASE),)
+obj-m := kfusd.o
+else
+KDIR	?= /lib/modules/$(shell uname -r)/build
+PWD	:= $(shell pwd)
+
+default:
+	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) EXTRA_CFLAGS=-I$(PWD)/../include modules
+
+install: 
+	$(INSTALL) -d -m 0755 /lib/modules/$(shell uname -r)/kernel/drivers/misc
+	$(INSTALL) -m 0755 kfusd.ko /lib/modules/$(shell uname -r)/kernel/drivers/misc
+	/sbin/depmod -a
+
+clean:
+	rm -f .kfusd* Modules.symvers \
+		kfusd.ko kfusd.o kfusd.mod.o kfusd.mod.c built-in.o *~
+	rm -rf .tmp_versions
+endif


Property changes on: trunk/fusd/kfusd/Makefile
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/kfusd/Module.symvers
===================================================================

Added: trunk/fusd/kfusd/kfusd.c
===================================================================
--- trunk/fusd/kfusd/kfusd.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/kfusd/kfusd.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,2943 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * FUSD: the Framework for User-Space Devices
+ *
+ * Linux Kernel Module
+ *
+ * Jeremy Elson  <jelson at circlemud.org>
+ * Copyright (c) 2001, Sensoria Corporation
+ * Copyright (c) 2002-2003, Regents of the University of California
+ *
+ * $Id: kfusd.c,v 1.97 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+/*
+ * Note on debugging messages: Unexpected errors (i.e., indicators of
+ * bugs in this kernel module) should always contain '!'.  Expected
+ * conditions, even if exceptional (e.g., the device-driver-provider
+ * disappears while a file is waiting for a return from a system call)
+ * must NOT contain '!'.
+ */
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
+//#include <linux/config.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+//#include <linux/devfs_fs_kernel.h>
+#include <linux/poll.h>
+#include <linux/version.h>
+#include <linux/major.h>
+#include <linux/uio.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/highmem.h>
+
+#include <asm/atomic.h>
+#include <asm/uaccess.h>
+#include <asm/ioctl.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+#define STATIC
+
+/* Define this if you want to emit debug messages (adds ~8K) */
+#define CONFIG_FUSD_DEBUG
+
+/* Default debug level for FUSD messages.  Has no effect unless
+ * CONFIG_FUSD_DEBUG is defined. */
+#ifndef CONFIG_FUSD_DEBUGLEVEL
+#define CONFIG_FUSD_DEBUGLEVEL 2
+#endif
+
+/* Define this to check for memory leaks */
+/*#define CONFIG_FUSD_MEMDEBUG*/
+
+/* Define this to use the faster wake_up_interruptible_sync instead of
+ * the normal wake_up_interruptible.  Note: you can't do this unless
+ * you're bulding fusd as part of the kernel (not a module); or you've
+ * patched kernel/ksyms.s to add __wake_up_sync in addition to
+ * __wake_up. */
+/* #define CONFIG_FUSD_USE_WAKEUPSYNC */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,9)
+# define vsnprintf(str, size, format, ap) vsprintf(str, format, ap)
+# define  snprintf(str, len, args...)      sprintf(str, args)
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
+
+#define CLASS class_simple
+#define class_create class_simple_create
+#define class_destroy class_simple_destroy
+#define CLASS_DEVICE_CREATE(a, b, c, d, e) class_simple_device_add(a, c, d, e)
+#define class_device_destroy(a, b) class_simple_device_remove(b)
+
+#else
+
+#define CLASS class
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
+
+#define CLASS_DEVICE_CREATE(a, b, c, d, e) class_device_create(a, c, d, e)
+
+#else
+
+#define CLASS_DEVICE_CREATE(a, b, c, d, e) class_device_create(a, b, c, d, e)
+
+#endif
+
+#endif
+
+/**************************************************************************/
+
+#include "fusd.h"
+#include "fusd_msg.h"
+#include "kfusd.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+# error "***FUSD doesn't work before Linux Kernel v2.4.0"
+#endif
+
+STATIC struct cdev* fusd_control_device;
+STATIC struct cdev* fusd_status_device;
+
+STATIC dev_t control_id;
+STATIC dev_t status_id;
+
+static struct CLASS *fusd_class;
+
+static struct class_device *fusd_control_class_device;
+static struct class_device *fusd_status_class_device;
+
+extern struct CLASS *sound_class;
+
+/* version number incremented for each registered device */
+STATIC int last_version = 1;
+
+/* version number incremented for each transaction to userspace */
+STATIC int last_transid = 1;
+
+/* wait queue that is awakened when new devices are registered */
+STATIC DECLARE_WAIT_QUEUE_HEAD(new_device_wait);
+
+/* the list of valid devices, and sem to protect it */
+LIST_HEAD(fusd_devlist_head);
+DECLARE_MUTEX(fusd_devlist_sem);
+
+//#ifdef MODULE_LICENSE
+MODULE_AUTHOR("Jeremy Elson <jelson at acm.org> (c)2001");
+MODULE_LICENSE("GPL");
+//#endif
+
+/***************************Debugging Support*****************************/
+
+#ifdef CONFIG_FUSD_DEBUG
+
+STATIC int fusd_debug_level = CONFIG_FUSD_DEBUGLEVEL;
+module_param(fusd_debug_level, int, S_IRUGO);
+
+#define BUFSIZE 1000 /* kernel's kmalloc pool has a 1012-sized bucket */
+
+STATIC void rdebug_real(char *fmt, ...)
+{
+  va_list ap;
+  int len;
+  char *message;
+
+  /* I'm kmallocing since you don't really want 1k on the stack. I've
+   * had stack overflow problems before; the kernel stack is quite
+   * small... */
+  if ((message = KMALLOC(BUFSIZE, GFP_KERNEL)) == NULL)
+    return;
+
+  va_start(ap, fmt);
+  len = vsnprintf(message, BUFSIZE-1, fmt, ap);
+  va_end(ap);
+
+  if (len >= BUFSIZE) {
+    printk("WARNING: POSSIBLE KERNEL CORRUPTION; MESSAGE TOO LONG\n");
+  } else {
+    printk("fusd: %.975s\n", message); /* note msgs are truncated at
+                      * ~1000 chars to fit inside the 1024 printk
+                      * limit imposed by the kernel */
+  }
+
+  KFREE(message);
+}
+
+#endif /* CONFIG_FUSD_DEBUG */
+
+/******************** Memory Debugging ************************************/
+
+#ifdef CONFIG_FUSD_MEMDEBUG
+
+#define MAX_MEM_DEBUG 10000
+
+DECLARE_MUTEX(fusd_memdebug_sem);
+
+typedef struct {
+  void *ptr;
+  int line;
+  int size;
+} mem_debug_t;
+
+mem_debug_t *mem_debug;
+
+STATIC int fusd_mem_init(void)
+{
+  int i;
+
+  mem_debug = kmalloc(sizeof(mem_debug_t) * MAX_MEM_DEBUG, GFP_KERNEL);
+
+  if (mem_debug == NULL) {
+    RDEBUG(2, "argh - memdebug malloc failed!");
+    return -ENOMEM;
+  }
+
+  /* initialize */
+  for (i = 0; i < MAX_MEM_DEBUG; i++)
+    mem_debug[i].ptr = NULL;
+
+  RDEBUG(2, "FUSD memory debugger activated");
+  return 0;
+}
+
+STATIC void fusd_mem_cleanup(void)
+{
+  int i;
+  int count=0;
+  for (i = 0; i < MAX_MEM_DEBUG; i++)
+    if (mem_debug[i].ptr != NULL) {
+      RDEBUG(0, "memdebug: failed to free memory allocated at line %d (%d b)",
+	     mem_debug[i].line, mem_debug[i].size);
+      count++;
+    }
+  if (!count)
+    RDEBUG(2, "congratulations - memory debugger is happy!");
+  kfree(mem_debug);
+}
+
+STATIC void fusd_mem_add(void *ptr, int line, int size)
+{
+  int i;
+
+  if (ptr==NULL)
+    return;
+
+  for (i = 0; i < MAX_MEM_DEBUG; i++) {
+    if (mem_debug[i].ptr == NULL) {
+      mem_debug[i].ptr = ptr;
+      mem_debug[i].line = line;
+      mem_debug[i].size = size;
+      return;
+    }
+  }
+  RDEBUG(1, "WARNING - memdebug out of space!!!!");
+}
+
+STATIC void fusd_mem_del(void *ptr)
+{
+  int i;
+  for (i = 0; i < MAX_MEM_DEBUG; i++) {
+    if (mem_debug[i].ptr == ptr) {
+      mem_debug[i].ptr = NULL;
+      return;
+    }
+  }
+  RDEBUG(2, "WARNING - memdebug is confused!!!!");
+}
+
+
+STATIC void *fusd_kmalloc(size_t size, int type, int line)
+{
+  void *ptr = kmalloc(size, type);
+  down(&fusd_memdebug_sem);
+  fusd_mem_add(ptr, line, size);
+  up(&fusd_memdebug_sem);
+  return ptr;
+}
+
+STATIC void fusd_kfree(void *ptr)
+{
+  down(&fusd_memdebug_sem);
+  fusd_mem_del(ptr);
+  kfree(ptr);
+  up(&fusd_memdebug_sem);
+}
+
+STATIC void *fusd_vmalloc(size_t size, int line)
+{
+  void *ptr = vmalloc(size);
+  down(&fusd_memdebug_sem);
+  fusd_mem_add(ptr, line, size);
+  up(&fusd_memdebug_sem);
+  return ptr;
+}
+
+STATIC void fusd_vfree(void *ptr)
+{
+  down(&fusd_memdebug_sem);
+  fusd_mem_del(ptr);
+  vfree(ptr);
+  up(&fusd_memdebug_sem);
+}
+
+#endif /* CONFIG_FUSD_MEMDEBUG */
+
+
+/********************* FUSD Device List ***************************/
+
+
+
+/*************************************************************************/
+/************** STATE MANAGEMENT AND BOOKKEEPING UTILITIES ***************/
+/*************************************************************************/
+
+STATIC inline void init_fusd_msg(fusd_msg_t *fusd_msg)
+{
+  if (fusd_msg == NULL)
+    return;
+
+  memset(fusd_msg, 0, sizeof(fusd_msg_t));
+  fusd_msg->magic = FUSD_MSG_MAGIC;
+  fusd_msg->cmd = FUSD_FOPS_CALL; /* typical, but can be overwritten */
+}
+
+/*
+ * free a fusd_msg, and NULL out the pointer that points to that fusd_msg.
+ */
+STATIC inline void free_fusd_msg(fusd_msg_t **fusd_msg)
+{
+  if (fusd_msg == NULL || *fusd_msg == NULL)
+    return;
+
+  if ((*fusd_msg)->data != NULL) {
+      VFREE((*fusd_msg)->data);
+      (*fusd_msg)->data = NULL;
+    }
+  KFREE(*fusd_msg);
+  *fusd_msg = NULL;
+}
+
+
+/* adjust the size of the 'files' array attached to the device to
+ * better match the number of files.  In all cases, size must be at
+ * least MIN_ARRAY_SIZE.  Subject to that constraint: if
+ * num_files==array_size, the size is doubled; if
+ * num_files<array_size/4, the size is halved.  Array is kept as is if
+ * the malloc fails.  Returns a pointer to the new file struct or NULL
+ * if there isn't one. */
+STATIC fusd_file_t **fusd_dev_adjsize(fusd_dev_t *fusd_dev)
+{
+  fusd_file_t **old_array;
+  int old_size;
+
+  old_array = fusd_dev->files;
+  old_size = fusd_dev->array_size;
+
+  /* compute the new size of the array */
+  if (fusd_dev->array_size > 4*fusd_dev->num_files)
+    fusd_dev->array_size /= 2;
+  else if (fusd_dev->array_size == fusd_dev->num_files)
+    fusd_dev->array_size *= 2;
+
+  /* respect the minimums and maximums (policy) */
+  if (fusd_dev->array_size < MIN_FILEARRAY_SIZE)
+    fusd_dev->array_size = MIN_FILEARRAY_SIZE;
+  if (fusd_dev->array_size > MAX_FILEARRAY_SIZE)
+    fusd_dev->array_size = MAX_FILEARRAY_SIZE;
+
+  /* make sure it's sane */
+  if (fusd_dev->array_size < fusd_dev->num_files) {
+    RDEBUG(0, "fusd_dev_adjsize is royally screwed up!!!!!");
+    return fusd_dev->files;
+  }
+
+  /* create a new array.  if successful, copy the contents of the old
+   * one.  if not, revert back to the old. */
+  fusd_dev->files = KMALLOC(fusd_dev->array_size * sizeof(fusd_file_t *),
+			    GFP_KERNEL);
+  if (fusd_dev->files == NULL) {
+    RDEBUG(1, "malloc failed in fusd_dev_adjsize!");
+    fusd_dev->files = old_array;
+    fusd_dev->array_size = old_size;
+  } else {
+    RDEBUG(10, "/dev/%s now has space for %d files (had %d)", NAME(fusd_dev),
+	   fusd_dev->array_size, old_size);
+    memset(fusd_dev->files, 0, fusd_dev->array_size * sizeof(fusd_file_t *));
+    memcpy(fusd_dev->files, old_array,
+	   fusd_dev->num_files * sizeof(fusd_file_t *));
+    KFREE(old_array);
+  }
+
+  return fusd_dev->files;
+}
+
+
+/*
+ * DEVICE LOCK MUST BE HELD TO CALL THIS FUNCTION
+ * 
+ * This function frees a device IF there is nothing left that is
+ * referencing it.
+ *
+ * Specifically, we do not free the device if:
+ *   - The driver is still active (i.e. device is not a zombie)
+ *   - There are still files with the device open
+ *   - There is an open in progress, i.e. a client has verified that
+ *   this is a valid device and is getting ready to add itself as an
+ *   open file.
+ *
+ * If the device is safe to free, it is removed from the valid list
+ * (in verysafe mode only) and freed.
+ *
+ * Returns:  1 if the device was freed
+ *           0 if the device still exists (and can be unlocked) */
+STATIC int maybe_free_fusd_dev(fusd_dev_t *fusd_dev)
+{
+  fusd_msgC_t *ptr, *next;
+
+  down(&fusd_devlist_sem);
+
+  /* DON'T free the device under conditions listed above */
+  if (!fusd_dev->zombie || fusd_dev->num_files || fusd_dev->open_in_progress) {
+    up(&fusd_devlist_sem);
+    return 0;
+  }
+
+  /* OK - bombs away!  This fusd_dev_t is on its way out the door! */
+
+  RDEBUG(8, "freeing state associated with /dev/%s", NAME(fusd_dev));
+
+  /* delete it off the list of valid devices, and unlock */
+  list_del(&fusd_dev->devlist);
+  up(&fusd_devlist_sem);
+
+  /* free any outgoing messages that the device might have waiting */
+  for (ptr = fusd_dev->msg_head; ptr != NULL; ptr = next) {
+    next = ptr->next;
+    FREE_FUSD_MSGC(ptr);
+  }
+  
+     /* free the device's dev name */
+  if (fusd_dev->dev_name != NULL) {
+    KFREE(fusd_dev->dev_name);
+    fusd_dev->dev_name = NULL;
+  }
+  
+    /* free the device's class name */
+  if (fusd_dev->class_name != NULL) {
+    KFREE(fusd_dev->class_name);
+    fusd_dev->class_name = NULL;
+  }
+
+  /* free the device's name */
+  if (fusd_dev->name != NULL) {
+    KFREE(fusd_dev->name);
+    fusd_dev->name = NULL;
+  }
+  
+
+  /* free the array used to store pointers to fusd_file_t's */
+  if (fusd_dev->files != NULL) {
+    KFREE(fusd_dev->files);
+    fusd_dev->files = NULL;
+  }
+
+  /* clear the structure and free it! */
+  memset(fusd_dev, 0, sizeof(fusd_dev_t));
+  KFREE(fusd_dev);
+
+  /* notify fusd_status readers that there has been a change in the
+   * list of registered devices */
+  atomic_inc_and_ret(&last_version);
+  wake_up_interruptible(&new_device_wait);
+
+  //MOD_DEC_USE_COUNT;
+  return 1;
+}
+
+
+/*
+ *
+ * DO NOT CALL THIS FUNCTION UNLESS THE DEVICE IS ALREADY LOCKED
+ *
+ * zombify_device: called when the driver disappears.  Indicates that
+ * the driver is no longer available to service requests.  If there
+ * are no outstanding system calls waiting for the fusd_dev state, the
+ * device state itself is freed.
+ *
+ */
+STATIC void zombify_dev(fusd_dev_t *fusd_dev)
+{
+  int i;
+
+  if (fusd_dev->zombie) {
+    RDEBUG(1, "zombify_device called on a zombie!!");
+    return;
+  }
+
+  fusd_dev->zombie = 1;
+
+  RDEBUG(3, "/dev/%s turning into a zombie (%d open files)", NAME(fusd_dev),
+	 fusd_dev->num_files);
+
+  /* If there are files holding this device open, wake them up. */
+  for (i = 0; i < fusd_dev->num_files; i++) {
+    wake_up_interruptible(&fusd_dev->files[i]->file_wait);
+    wake_up_interruptible(&fusd_dev->files[i]->poll_wait);
+  }
+}
+
+
+
+/* utility function to find the index of a fusd_file in a fusd_dev.
+ * returns index if found, -1 if not found.  ASSUMES WE HAVE A VALID
+ * fusd_dev.  fusd_file may be NULL if we are searching for an empty
+ * slot. */
+STATIC int find_fusd_file(fusd_dev_t *fusd_dev, fusd_file_t *fusd_file)
+{
+  int i, num_files = fusd_dev->num_files;
+  fusd_file_t **files = fusd_dev->files;
+
+  for (i = 0; i < num_files; i++)
+    if (files[i] == fusd_file)
+      return i;
+
+  return -1;
+}
+
+
+/*
+ * DEVICE LOCK MUST BE HELD BEFORE THIS IS CALLED
+ *
+ * Returns 1 if the device was also freed.  0 if only the file was
+ * freed.  If the device is freed, then do not try to unlock it!
+ * (Callers: Check the return value before unlocking!)
+ */
+STATIC int free_fusd_file(fusd_dev_t *fusd_dev, fusd_file_t *fusd_file)
+{
+  int i;
+  struct list_head *tmp, *it;
+   
+  /* find the index of the file in the device's file-list... */
+  if ((i = find_fusd_file(fusd_dev, fusd_file)) < 0)
+    panic("corrupted fusd_dev: releasing a file that we think is closed");
+    
+  /* ...and remove it (by putting the last entry into its place) */
+  fusd_dev->files[i] = fusd_dev->files[--(fusd_dev->num_files)];
+
+  /* there might be an incoming message waiting for a restarted system
+   * call.  free it -- after possibly forging a close (see
+   * fusd_forge_close). */
+   
+   
+	list_for_each_safe(it, tmp, &fusd_file->transactions)
+	{
+		struct fusd_transaction* transaction = list_entry(it, struct fusd_transaction, list);
+		if(transaction->msg_in)
+		{
+      if (transaction->msg_in->subcmd == FUSD_OPEN && transaction->msg_in->parm.fops_msg.retval == 0)
+        fusd_forge_close(transaction->msg_in, fusd_dev);
+      free_fusd_msg(&transaction->msg_in);
+		} 
+		KFREE(transaction);
+	}
+  
+  /* free state associated with this file */
+  memset(fusd_file, 0, sizeof(fusd_file_t));
+  KFREE(fusd_file);
+
+  /* reduce the size of the file array if necessary */
+  if (fusd_dev->array_size > MIN_FILEARRAY_SIZE &&
+      fusd_dev->array_size > 4*fusd_dev->num_files)
+    fusd_dev_adjsize(fusd_dev);
+
+  /* renumber the array */
+  for (i = 0; i < fusd_dev->num_files; i++)
+    fusd_dev->files[i]->index = i;
+
+  /* try to free the device -- this may have been its last file */
+  return maybe_free_fusd_dev(fusd_dev);
+}
+
+
+/****************************************************************************/
+/********************** CLIENT CALLBACK FUNCTIONS ***************************/
+/****************************************************************************/
+
+
+/* todo
+ * fusd_restart_check: Called from the beginning of most system calls
+ * to see if we are restarting a system call.
+ *
+ * In the common case -- that this is NOT a restarted syscall -- we
+ * return 0.
+ *
+ * In the much less common case, we return ERESTARTSYS, and expect the
+ * caller to jump right to its fusd_fops_call() call.
+ *
+ * In the even LESS (hopefully very rare) case when one PID had an
+ * interrupted syscall, but a different PID is the next to do a system
+ * call on that file descriptor -- well, we lose.  Clear state of that
+ * old syscall out and continue as usual.
+ */
+STATIC struct fusd_transaction* fusd_find_incomplete_transaction(fusd_file_t *fusd_file, int subcmd)
+{
+  struct fusd_transaction* transaction = fusd_find_transaction_by_pid(fusd_file, current->pid);
+  if(transaction == NULL)
+    return NULL;
+
+
+  if (transaction->subcmd != subcmd)
+  {
+    RDEBUG(2, "Incomplete transaction %ld thrown out, was expecting subcmd %d but received %d", 
+           transaction->transid, transaction->subcmd, subcmd);
+    fusd_cleanup_transaction(fusd_file, transaction);
+    return NULL;
+  }
+  
+  RDEBUG(4, "pid %d restarting system call with transid %ld", current->pid,
+         transaction->transid);
+  return transaction;
+}
+
+
+STATIC int send_to_dev(fusd_dev_t *fusd_dev, fusd_msg_t *fusd_msg, int locked)
+{
+  fusd_msgC_t *fusd_msgC;
+
+  /* allocate a container for the message */
+  if ((fusd_msgC = KMALLOC(sizeof(fusd_msgC_t), GFP_KERNEL)) == NULL)
+    return -ENOMEM;
+
+  memset(fusd_msgC, 0, sizeof(fusd_msgC_t));
+  memcpy(&fusd_msgC->fusd_msg, fusd_msg, sizeof(fusd_msg_t));
+
+  if (!locked)
+    LOCK_FUSD_DEV(fusd_dev);
+
+  /* put the message in the device's outgoing queue.  */
+  if (fusd_dev->msg_head == NULL) {
+    fusd_dev->msg_head = fusd_dev->msg_tail = fusd_msgC;
+  } else {
+    fusd_dev->msg_tail->next = fusd_msgC;
+    fusd_dev->msg_tail = fusd_msgC;
+  }
+
+  if (!locked)
+    UNLOCK_FUSD_DEV(fusd_dev);
+
+  /* wake up the driver, which now has a message waiting in its queue */
+  WAKE_UP_INTERRUPTIBLE_SYNC(&fusd_dev->dev_wait);
+
+  return 0;
+
+ zombie_dev:
+  KFREE(fusd_msgC);
+  return -EPIPE;
+}
+
+
+/* 
+ * special case: if the driver sent back a successful "open", but
+ * there is no file that is actually open, we forge a "close" so that
+ * the driver can maintain balanced open/close pairs.  We put calls to
+ * this in fusd_fops_reply, when the reply first comes in; and,
+ * free_fusd_file, when we throw away a reply that had been
+ * pending for a restart.
+ */
+STATIC void fusd_forge_close(fusd_msg_t *msg, fusd_dev_t *fusd_dev)
+{
+  RDEBUG(2, "/dev/%s tried to complete an open for transid %ld, "
+	 "forging a close", NAME(fusd_dev), msg->parm.fops_msg.transid);
+  msg->cmd = FUSD_FOPS_CALL_DROPREPLY;
+  msg->subcmd = FUSD_CLOSE;
+  msg->parm.fops_msg.transid = atomic_inc_and_ret(&last_transid);
+  send_to_dev(fusd_dev, msg, 1);
+}
+
+
+
+/*
+ * fusd_fops_call_send: send a fusd_msg into userspace.
+ *
+ * NOTE - we are already holding the lock on fusd_file_arg when this
+ * function is called, but NOT the lock on the fusd_dev
+ */
+STATIC int fusd_fops_call_send(fusd_file_t *fusd_file_arg,
+			       fusd_msg_t *fusd_msg, struct fusd_transaction** transaction)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+
+  /* I check this just in case, shouldn't be necessary. */
+  GET_FUSD_FILE_AND_DEV(fusd_file_arg, fusd_file, fusd_dev);
+
+  /* make sure message is sane */
+  if ((fusd_msg->data == NULL) != (fusd_msg->datalen == 0)) {
+    RDEBUG(2, "fusd_fops_call: data pointer and datalen mismatch");
+    return -EINVAL;
+  }
+
+  /* fill the rest of the structure */
+  fusd_msg->parm.fops_msg.pid = current->pid;
+  fusd_msg->parm.fops_msg.uid = current->uid;
+  fusd_msg->parm.fops_msg.gid = current->gid;
+  fusd_msg->parm.fops_msg.flags = fusd_file->file->f_flags;
+  fusd_msg->parm.fops_msg.offset = fusd_file->file->f_pos;
+  fusd_msg->parm.fops_msg.device_info = fusd_dev->private_data;
+  fusd_msg->parm.fops_msg.private_info = fusd_file->private_data;
+  fusd_msg->parm.fops_msg.fusd_file = fusd_file;
+  fusd_msg->parm.fops_msg.transid = atomic_inc_and_ret(&last_transid);
+
+  /* set up certain state depending on if we expect a reply */
+  switch (fusd_msg->cmd) {
+
+  case FUSD_FOPS_CALL: /* common case */
+    fusd_msg->parm.fops_msg.hint = fusd_file->index;
+     
+    break;
+
+  case FUSD_FOPS_CALL_DROPREPLY:
+    /* nothing needed */
+    break;
+
+  case FUSD_FOPS_NONBLOCK:
+    fusd_msg->parm.fops_msg.hint = fusd_file->index;
+    break;
+
+  default:
+    RDEBUG(0, "whoa - fusd_fops_call_send got msg with unknown cmd!");
+    break;
+  }
+  
+  if(transaction != NULL)
+  {
+    int retval;
+    retval = fusd_add_transaction(fusd_file, fusd_msg->parm.fops_msg.transid, fusd_msg->subcmd,
+                    fusd_msg->parm.fops_msg.length, transaction);
+    if(retval < 0)
+      return retval;
+  }
+  
+  /* now add the message to the device's outgoing queue! */
+  return send_to_dev(fusd_dev, fusd_msg, 0);
+
+
+  /* bizarre errors go straight here */
+ invalid_dev:
+ invalid_file:
+  RDEBUG(0, "fusd_fops_call: got invalid device or file!!!!");
+  return -EPIPE;
+}
+
+
+/*
+ * fusd_fops_call_wait: wait for a driver to reply to a message
+ *
+ * NOTE - we are already holding the lock on fusd_file_arg when this
+ * function is called, but NOT the lock on the fusd_dev
+ */
+STATIC int fusd_fops_call_wait(fusd_file_t *fusd_file_arg,
+			       fusd_msg_t **fusd_msg_reply, struct fusd_transaction* transaction)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  int retval;
+
+  /* I check this just in case, shouldn't be necessary. */
+  GET_FUSD_FILE_AND_DEV(fusd_file_arg, fusd_file, fusd_dev);
+
+  /* initialize first to tell callers there is no reply (yet) */
+  if (fusd_msg_reply != NULL)
+    *fusd_msg_reply = NULL;
+
+  /*
+   * Now, lock the device, check for an incoming message, and sleep if
+   * there is not a message already waiting for us.  Note that we are
+   * unrolling the interruptible_sleep_on, as in the kernel's
+   * fs/pipe.c, to avoid race conditions between checking for the
+   * sleep condition and sleeping.
+   */
+  LOCK_FUSD_DEV(fusd_dev);
+  while (transaction->msg_in == NULL) {
+    DECLARE_WAITQUEUE(wait, current);
+
+    RDEBUG(10, "pid %d blocking on transid %ld", current->pid, transaction->transid);
+    current->state = TASK_INTERRUPTIBLE;
+    add_wait_queue(&fusd_file->file_wait, &wait);
+    UNLOCK_FUSD_DEV(fusd_dev);
+    UNLOCK_FUSD_FILE(fusd_file);
+
+    schedule();
+    remove_wait_queue(&fusd_file->file_wait, &wait);
+    current->state = TASK_RUNNING;
+
+    /*
+     * If we woke up due to a signal -- and not due to a reply message
+     * coming in -- then we are in some trouble.  The driver is already
+     * processing the request and might have changed some state that is
+     * hard to roll back.  So, we'll tell the process to restart the
+     * system call, and come back to this point when the system call is
+     * restarted.  We need to remember the PID to avoid confusion in
+     * case there is another process holding this file descriptor that
+     * is also trying to make a call.
+     */
+    if (signal_pending(current)) {
+      RDEBUG(5, "blocked pid %d got a signal; sending -ERESTARTSYS",
+	     current->pid);
+			LOCK_FUSD_FILE(fusd_file);
+      return -ERESTARTSYS;
+    }
+
+    LOCK_FUSD_FILE(fusd_file);
+    /* re-lock the device, so we can do our msg_in check again */
+    LOCK_FUSD_DEV(fusd_dev);
+  }
+  UNLOCK_FUSD_DEV(fusd_dev);
+
+  /* ok - at this point we are awake due to a message received. */
+
+  if (transaction->msg_in->cmd != FUSD_FOPS_REPLY ||
+      transaction->msg_in->subcmd != transaction->subcmd ||
+      transaction->msg_in->parm.fops_msg.transid != transaction->transid ||
+      transaction->msg_in->parm.fops_msg.fusd_file != fusd_file) {
+    RDEBUG(2, "fusd_fops_call: invalid reply!");
+    goto invalid_reply;
+  }
+
+  /* copy metadata back from userspace */
+  fusd_file->file->f_flags = transaction->msg_in->parm.fops_msg.flags;
+  fusd_file->private_data  = transaction->msg_in->parm.fops_msg.private_info;
+  /* note, changes to device_info are NO LONGER honored here */
+
+  /* if everything's okay, return the return value.  if caller is
+   * willing to take responsibility for freeing the message itself, we
+   * return the message too. */
+  retval = transaction->msg_in->parm.fops_msg.retval;
+  if (fusd_msg_reply != NULL) {
+    /* NOW TRANSFERRING RESPONSIBILITY FOR FREEING THIS DATA TO THE CALLER */
+    *fusd_msg_reply = transaction->msg_in;
+    transaction->msg_in = NULL;
+  } else {
+    /* free the message ourselves */
+    free_fusd_msg(&transaction->msg_in);
+  }
+  
+  /* success */
+  fusd_cleanup_transaction(fusd_file, transaction);
+  return retval;
+
+ invalid_reply:
+  fusd_cleanup_transaction(fusd_file, transaction);
+  return -EPIPE;
+
+  /* bizarre errors go straight here */
+ invalid_dev:
+ invalid_file:
+  RDEBUG(0, "fusd_fops_call: got invalid device or file!!!!");
+  return -EPIPE;
+
+ zombie_dev:
+  RDEBUG(2, "fusd_fops_call: %s zombified while waiting for reply",
+	 NAME(fusd_dev));
+  return -EPIPE;
+}
+
+
+/* fusd client system call handlers should call this after they call
+ * fops_call, to destroy the message that was returned to them. */
+STATIC void fusd_transaction_done(struct fusd_transaction *transaction)
+{
+	transaction->transid = -1;
+	transaction->pid = 0;
+}
+
+
+
+/********* Functions for opening a FUSD device *******************/
+
+
+/*
+ * The process of having a client open a FUSD device is surprisingly
+ * tricky -- perhaps the most complex piece of FUSD (or, a close
+ * second to poll_diffs).  Race conditions are rampant here.
+ *
+ * The main problem is that there is a race between clients trying to
+ * open the FUSD device, and providers unregistering it (e.g., the
+ * driver dying).  If the device-unregister callback starts, and is
+ * scheduled out after it locks the fusd device but before it
+ * unregisters the device with devfs, the open callback might be
+ * invoked in this interval.  This means the client will down() on a
+ * semaphore that is about to be freed when the device is destroyed.
+ *
+ * The only way to fix this, as far as I can tell, is for device
+ * registration and unregistration to both share a global lock; the
+ * client checks its 'private_data' pointer to make sure it's on the
+ * list of valid devices.  If so, it sets a flag (open_in_progress)
+ * which means "Don't free this device yet!".  Then, it releases the
+ * global lock, grabs the device lock, and tries to add itself as a
+ * "file" to the device array.  It is then safe to decrement
+ * open_in_progress, because being a member of the file array will
+ * guarantee that the device will zombify instead of being freed.
+ *
+ * Another gotcha: To avoid infinitely dining with philosophers, the
+ * global lock (fusd_devlist_sem) should always be acquired AFTER a
+ * fusd device is locked.  The code path that frees devices acquires
+ * the device lock FIRST, so the code here must do the same.
+ *
+ * Because of the complexity of opening a file, I've broken it up into
+ * multiple sub-functions.
+ */
+
+
+/*
+ * fusd_dev_is_valid: If a fusd device is valid, returns 1, and will have
+ * set the "open_in_progress" flag on the device.
+ */
+int fusd_dev_is_valid(fusd_dev_t *fusd_dev)
+{
+  struct list_head *tmp;
+  int dev_found = 0;
+
+  /* The first thing we must do is acquire the global lock on the
+   * device list, and make sure this device is valid; if so, mark it
+   * as being "in use".  If we don't do this, there's a race: after we
+   * enter this function, the device may be unregistered. */
+  down(&fusd_devlist_sem);
+  list_for_each(tmp, &fusd_devlist_head) {
+    fusd_dev_t *d = list_entry(tmp, fusd_dev_t, devlist);
+
+    if (d == fusd_dev && d->magic == FUSD_DEV_MAGIC && !ZOMBIE(d)) {
+      dev_found = 1;
+      break;
+    }
+  }
+
+  /* A device will not be deallocated when this counter is >0 */
+  if (dev_found)
+    fusd_dev->open_in_progress++;
+
+  up(&fusd_devlist_sem);
+
+  return dev_found;
+}
+
+
+int fusd_dev_add_file(struct file *file, fusd_dev_t *fusd_dev, fusd_file_t **fusd_file_ret)
+{
+  fusd_file_t *fusd_file;
+  int i;
+
+  /* Make sure the device didn't become a zombie while we were waiting
+   * for the device lock */
+  if (ZOMBIE(fusd_dev))
+    return -ENOENT;
+
+  /* this shouldn't happen.  maybe i'm insane, but i check anyway. */
+  for (i = 0; i < fusd_dev->num_files; i++)
+    if (fusd_dev->files[i]->file == file) {
+      RDEBUG(1, "warning: fusd_client_open got open for already-open file!?");
+      return -EIO;
+    }
+
+  /* You can't open your own file!  Return -EDEADLOCK if someone tries to.
+   *
+   * XXX - TODO - FIXME - This should eventually be more general
+   * deadlock detection of arbitrary length cycles */
+  if (current->pid == fusd_dev->pid) {
+    RDEBUG(3, "pid %d tried to open its own device (/dev/%s)",
+	   fusd_dev->pid, NAME(fusd_dev));
+    return -EDEADLOCK;
+  }
+
+  /* make more space in the file array if we need it */
+  if (fusd_dev->num_files == fusd_dev->array_size &&
+      fusd_dev->array_size < MAX_FILEARRAY_SIZE)
+    fusd_dev_adjsize(fusd_dev);
+
+  /* make sure we have room... adjsize may have failed */
+  if (fusd_dev->num_files >= fusd_dev->array_size) {
+    RDEBUG(1, "/dev/%s out of state space for open files!", NAME(fusd_dev));
+    return -ENOMEM;
+  }
+
+  /* create state for this file */
+  if ((fusd_file = KMALLOC(sizeof(fusd_file_t), GFP_KERNEL)) == NULL) {
+    RDEBUG(1, "yikes!  kernel can't allocate memory");
+    return -ENOMEM;
+  }
+  memset(fusd_file, 0, sizeof(fusd_file_t));
+  init_waitqueue_head(&fusd_file->file_wait);
+  init_waitqueue_head(&fusd_file->poll_wait);
+	INIT_LIST_HEAD(&fusd_file->transactions);
+  init_MUTEX(&fusd_file->file_sem);
+  init_MUTEX(&fusd_file->transactions_sem);
+  fusd_file->last_poll_sent = -1;
+  fusd_file->magic = FUSD_FILE_MAGIC;
+  fusd_file->fusd_dev = fusd_dev;
+  fusd_file->fusd_dev_version = fusd_dev->version;
+  fusd_file->file = file;
+
+  /* add this file to the list of files managed by the device */
+  fusd_file->index = fusd_dev->num_files++;
+  fusd_dev->files[fusd_file->index] = fusd_file;
+
+  /* store the pointer to this file with the kernel */
+  file->private_data = fusd_file;
+  *fusd_file_ret = fusd_file;
+
+  /* success! */
+  return 0;
+}
+
+STATIC struct fusd_dev_t_s* find_user_device(int dev_id)
+{
+	struct list_head* entry;
+	down(&fusd_devlist_sem);
+	list_for_each(entry, &fusd_devlist_head)
+	{
+    fusd_dev_t *d = list_entry(entry, fusd_dev_t, devlist);
+		if(d->dev_id == dev_id)
+		{
+			up(&fusd_devlist_sem);
+			return d;
+		}
+	}
+	up(&fusd_devlist_sem);
+ return NULL;
+}
+
+/*
+ * A client has called open() has been called on a registered device.
+ * See comment higher up for detailed notes on this function.
+ */
+STATIC int fusd_client_open(struct inode *inode, struct file *file)
+{
+  int retval;
+  int device_freed = 0;
+  fusd_dev_t *fusd_dev = find_user_device(inode->i_rdev);
+  fusd_file_t *fusd_file;
+  fusd_msg_t fusd_msg;
+  struct fusd_transaction* transaction;
+  
+  /* If the device wasn't on our valid list, stop here. */
+  if (!fusd_dev_is_valid(fusd_dev))
+    return -ENOENT;
+
+  /* fusd_dev->open_in_progress now set */
+
+  /* Lock the fusd device.  Note, when we finally do acquire the lock,
+   * the device might be a zombie (driver disappeared). */
+  RAWLOCK_FUSD_DEV(fusd_dev);
+
+  RDEBUG(3, "got an open for /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  /* Try to add ourselves to the device's file list.  If retval==0, we
+     are now part of the file array.  */
+  retval = fusd_dev_add_file(file, fusd_dev, &fusd_file);
+
+  /*
+   * It is now safe to unset the open_in_progress flag.  Either:
+   *   1) We are part of the file array, so dev won't be freed, or;
+   *   2) Something failed, so we are returning a failure now and no
+   *   longer need the device.
+   * Note, open_in_progress must be protected by the global sem, not
+   * the device lock, due to the access of it in fusd_dev_is_valid().
+   */
+  down(&fusd_devlist_sem);
+  fusd_dev->open_in_progress--;
+  up(&fusd_devlist_sem);
+
+  /* If adding ourselves to the device list failed, give up.  Possibly
+   * free the device if it was a zombie and waiting for us to complete
+   * our open. */
+  if (retval < 0) {
+    if (!maybe_free_fusd_dev(fusd_dev))
+      UNLOCK_FUSD_DEV(fusd_dev);
+    return retval;
+  }
+
+  /* send message to userspace and get retval */
+  init_fusd_msg(&fusd_msg);
+  fusd_msg.subcmd = FUSD_OPEN;
+
+  /* send message to userspace and get the reply.  Device can't be
+   * locked during that operation. */
+
+  UNLOCK_FUSD_DEV(fusd_dev);
+  retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction);
+  
+  if (retval >= 0)
+    retval = fusd_fops_call_wait(fusd_file, NULL, transaction);
+  RAWLOCK_FUSD_DEV(fusd_dev);
+
+  /* If the device zombified (while we were waiting to reacquire the
+   * lock)... consider that a failure */
+  if (ZOMBIE(fusd_dev))
+    retval = -ENOENT;
+
+  /* if retval is negative, throw away state... the file open failed */
+  if (retval < 0) {
+    RDEBUG(3, "...open failed for /dev/%s (owned by pid %d) from pid %d",
+	   NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+    device_freed = free_fusd_file(fusd_dev, fusd_file);
+  }
+
+  /* Now unlock the device, if it still exists.  (It may have been
+   * freed if the open failed, and we were the last outstanding
+   * request for it.) */
+  if (!device_freed)
+    UNLOCK_FUSD_DEV(fusd_dev);
+
+  return retval;
+}
+
+
+/* close() has been called on a registered device.  like
+ * fusd_client_open, we must lock the entire device. */
+STATIC int fusd_client_release(struct inode *inode, struct file *file)
+{
+  int retval;
+  fusd_file_t *fusd_file;
+  fusd_dev_t *fusd_dev;
+  fusd_msg_t fusd_msg;
+  struct fusd_transaction* transaction;
+  
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+
+  RDEBUG(3, "got a close on /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  /* Tell the driver that the file closed, if it still exists. */
+  init_fusd_msg(&fusd_msg);
+  fusd_msg.subcmd = FUSD_CLOSE;
+  retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction);
+	RDEBUG(5, "fusd_client_release: send returned %d", retval);
+  if (retval >= 0)
+    retval = fusd_fops_call_wait(fusd_file, NULL, transaction);
+	
+	RDEBUG(5, "fusd_client_release: call_wait %d", retval);
+  /* delete the file off the device's file-list, and free it.  note
+   * that device may be a zombie right now and may be freed when we
+   * come back from free_fusd_file.  we only release the lock if the
+   * device still exists. */
+  RAWLOCK_FUSD_DEV(fusd_dev);
+  if (!free_fusd_file(fusd_dev, fusd_file)) {
+    UNLOCK_FUSD_DEV(fusd_dev);
+  }
+
+  return retval;
+
+ invalid_dev:
+ invalid_file:
+  RDEBUG(1, "got a close on client file from pid %d, INVALID DEVICE!",
+	 current->pid);
+  return -EPIPE;
+}
+
+
+
+STATIC ssize_t fusd_client_read(struct file *file , char *buf,
+			 size_t count, loff_t *offset)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  struct fusd_transaction* transaction;
+  fusd_msg_t fusd_msg, *reply = NULL;
+  int retval = -EPIPE;
+
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+
+  RDEBUG(3, "got a read on /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  transaction = fusd_find_incomplete_transaction(fusd_file, FUSD_READ);
+  if (transaction && transaction->size > count)
+  {
+    RDEBUG(3, "Incomplete I/O transaction %ld thrown out, as the transaction's size of %d bytes was greater than "
+              "the retry's size of %d bytes", transaction->transid, transaction->size, (int)count);
+
+    fusd_cleanup_transaction(fusd_file, transaction);
+    transaction = NULL;
+  }
+
+  if(transaction == NULL)
+  {
+    /* make sure we aren't trying to read too big of a buffer */
+    if (count > MAX_RW_SIZE)
+      count = MAX_RW_SIZE;
+  
+    /* send the message */
+    init_fusd_msg(&fusd_msg);
+    fusd_msg.subcmd = FUSD_READ;
+    fusd_msg.parm.fops_msg.length = count;
+  
+    /* send message to userspace */
+    if ((retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction)) < 0)
+      goto done;
+  }
+  
+  /* and wait for the reply */
+  /* todo: store and retrieve the transid from the interrupted messsage */
+  retval = fusd_fops_call_wait(fusd_file, &reply, transaction);
+
+  /* return immediately in case of error */
+  if (retval < 0 || reply == NULL)
+    goto done;
+
+  /* adjust the reval if the retval indicates a larger read than the
+   * data that was actually provided */
+  if (reply->datalen != retval) {
+    RDEBUG(1, "warning: /dev/%s driver (pid %d) claimed it returned %d bytes "
+	   "on read but actually returned %d", 
+	   NAME(fusd_dev), fusd_dev->pid, retval, reply->datalen);
+    retval = reply->datalen;
+  }
+
+  /* adjust if the device driver gave us more data than the user asked for
+   *     (bad!  bad!  why is the driver broken???) */
+  if (retval > count) {
+    RDEBUG(1, "warning: /dev/%s driver (pid %d) returned %d bytes on read but "
+	   "the user only asked for %d", 
+	   NAME(fusd_dev), fusd_dev->pid, retval, (int) count);
+    retval = count;
+  }
+
+  /* copy the offset back from the message */
+  *offset = reply->parm.fops_msg.offset;
+
+  /* IFF return value indicates data present, copy it back */
+  if (retval > 0) {
+    if (copy_to_user(buf, reply->data, retval)) {
+      retval = -EFAULT;
+      goto done;
+    }
+  }
+
+ done:
+  /* clear the readable bit of our cached poll state */
+  fusd_file->cached_poll_state &= ~(FUSD_NOTIFY_INPUT);
+
+  free_fusd_msg(&reply);
+  UNLOCK_FUSD_FILE(fusd_file);
+  return retval;
+
+ invalid_file:
+ invalid_dev:
+  RDEBUG(3, "got a read on client file from pid %d, driver has disappeared",
+	 current->pid);
+  return -EPIPE;
+}
+
+STATIC int fusd_add_transaction(fusd_file_t *fusd_file, int transid, int subcmd, int size, struct fusd_transaction** out_transaction)
+{
+	struct fusd_transaction* transaction = (struct fusd_transaction*) KMALLOC(sizeof(struct fusd_transaction), GFP_KERNEL);
+	if(transaction == NULL)
+		return -ENOMEM;
+	
+	transaction->msg_in = NULL;
+	transaction->transid = transid;
+	transaction->subcmd = subcmd;
+	transaction->pid = current->pid;
+	transaction->size = size;
+	
+	down(&fusd_file->transactions_sem);
+	list_add_tail(&transaction->list, &fusd_file->transactions);
+	up(&fusd_file->transactions_sem);
+	
+	if(out_transaction != NULL)
+		*out_transaction = transaction;
+	
+	return 0;
+}
+
+STATIC void fusd_cleanup_transaction(fusd_file_t *fusd_file, struct fusd_transaction* transaction)
+{
+  free_fusd_msg(&transaction->msg_in);
+  fusd_remove_transaction(fusd_file, transaction);
+}
+
+STATIC void fusd_remove_transaction(fusd_file_t *fusd_file, struct fusd_transaction* transaction)
+{
+	down(&fusd_file->transactions_sem);
+	list_del(&transaction->list);
+	up(&fusd_file->transactions_sem);
+	
+	KFREE(transaction);
+}
+
+STATIC struct fusd_transaction* fusd_find_transaction(fusd_file_t *fusd_file, int transid)
+{
+	struct list_head* i;
+	down(&fusd_file->transactions_sem);
+	list_for_each(i, &fusd_file->transactions)
+	{
+		struct fusd_transaction* transaction = list_entry(i, struct fusd_transaction, list);
+		if(transaction->transid == transid)
+		{
+			up(&fusd_file->transactions_sem);
+			return transaction;
+		}
+	}
+	up(&fusd_file->transactions_sem);
+	return NULL;
+}
+
+STATIC struct fusd_transaction* fusd_find_transaction_by_pid(fusd_file_t *fusd_file, int pid)
+{
+	struct list_head* i;
+	down(&fusd_file->transactions_sem);
+	list_for_each(i, &fusd_file->transactions)
+	{
+		struct fusd_transaction* transaction = list_entry(i, struct fusd_transaction, list);
+		if(transaction->pid == pid)
+		{
+			up(&fusd_file->transactions_sem);
+			return transaction;
+		}
+	}
+	up(&fusd_file->transactions_sem);
+	return NULL;
+}
+
+STATIC ssize_t fusd_client_write(struct file *file,
+    const char *buffer,
+    size_t length,
+    loff_t *offset)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  fusd_msg_t fusd_msg;
+  fusd_msg_t *reply = NULL;
+  int retval = -EPIPE;
+  struct fusd_transaction* transaction;
+  
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+
+  RDEBUG(3, "got a write on /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  transaction = fusd_find_incomplete_transaction(fusd_file, FUSD_WRITE);
+  if (transaction && transaction->size == length)
+  {
+    RDEBUG(2, "Incomplete I/O transaction %ld thrown out, as the transaction's size of %d bytes was not equal to "
+              "the retry's size of %d bytes", transaction->transid, transaction->size, (int) length);
+
+    fusd_cleanup_transaction(fusd_file, transaction);
+    transaction = NULL;
+  }
+  if(transaction == NULL)
+  {
+    if (length < 0) {
+      RDEBUG(2, "fusd_client_write: got invalid length %d", (int) length);
+      retval = -EINVAL;
+      goto done;
+    }
+  
+    if (length > MAX_RW_SIZE)
+      length = MAX_RW_SIZE;
+  
+    init_fusd_msg(&fusd_msg);
+  
+    /* sigh.. i guess zero length writes should be legal */
+    if (length > 0) {
+      if ((fusd_msg.data = VMALLOC(length)) == NULL) {
+        retval = -ENOMEM;
+        goto done;
+      }
+  
+      if (copy_from_user(fusd_msg.data, buffer, length)) {
+        retval = -EFAULT;
+        goto done;
+      }
+      fusd_msg.datalen = length;
+    }
+    
+    fusd_msg.subcmd = FUSD_WRITE;
+    fusd_msg.parm.fops_msg.length = length;
+    
+    if ((retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction)) < 0)
+      goto done;
+  }
+	/* todo: fix transid on restart */
+  retval = fusd_fops_call_wait(fusd_file, &reply, transaction);
+
+  if (retval < 0 || reply == NULL)
+    goto done;
+
+  /* drivers should not write more bytes than they were asked to! */
+  if (retval > length) {
+    RDEBUG(1, "warning: /dev/%s driver (pid %d) returned %d bytes on write; "
+	   "the user only wanted %d", 
+	   NAME(fusd_dev), fusd_dev->pid, retval, (int) length);
+    retval = length;
+  }
+
+  *offset = reply->parm.fops_msg.offset;
+
+  /* all done! */
+
+ done:
+  /* clear the writable bit of our cached poll state */
+  fusd_file->cached_poll_state &= ~(FUSD_NOTIFY_OUTPUT);
+
+  free_fusd_msg(&reply);
+  UNLOCK_FUSD_FILE(fusd_file);
+  return retval;
+
+ invalid_file:
+ invalid_dev:
+  RDEBUG(3, "got a read on client file from pid %d, driver has disappeared",
+	 current->pid);
+  return -EPIPE;
+}
+
+
+STATIC int fusd_client_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  fusd_msg_t fusd_msg, *reply = NULL;
+  int retval = -EPIPE, dir, length;
+  struct fusd_transaction* transaction;
+  
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+
+  RDEBUG(3, "got an ioctl on /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  dir = _IOC_DIR(cmd);
+  length = _IOC_SIZE(cmd);
+
+  transaction = fusd_find_incomplete_transaction(fusd_file, FUSD_IOCTL);
+  // todo: Check to make sure the transaction is for the same IOCTL
+
+  if(transaction == NULL)
+  {
+    /* if we're trying to read or write, make sure length is sane */
+    if ((dir & (_IOC_WRITE | _IOC_READ)) &&
+        (length <= 0 || length > MAX_RW_SIZE))
+      {
+        RDEBUG(2, "client ioctl got crazy IOC_SIZE of %d", length);
+        retval = -EINVAL;
+        goto done;
+      }
+  
+    /* fill the struct */
+    init_fusd_msg(&fusd_msg);
+    fusd_msg.subcmd = FUSD_IOCTL;
+    fusd_msg.parm.fops_msg.cmd = cmd;
+    fusd_msg.parm.fops_msg.arg.arg = arg;
+  
+    /* get the data if user is trying to write to the driver */
+    if (dir & _IOC_WRITE) {
+      if ((fusd_msg.data = VMALLOC(length)) == NULL) {
+        RDEBUG(2, "can't vmalloc for client ioctl!");
+        retval = -ENOMEM;
+        goto done;
+      }
+  
+      if (copy_from_user(fusd_msg.data, (void *) arg, length)) {
+        retval = -EFAULT;
+        goto done;
+      }
+      fusd_msg.datalen = length;
+    }
+  
+    /* send request to the driver */
+    if ((retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction)) < 0)
+      goto done;
+  }
+  /* get the response */
+	/* todo: fix transid on restart */
+  if ((retval = fusd_fops_call_wait(fusd_file, &reply, transaction)) < 0 || reply == NULL)
+    goto done;
+
+  /* if user is trying to read from the driver, copy data back */
+  if (dir & _IOC_READ) {
+    if (reply->data == NULL || reply->datalen != length) {
+      RDEBUG(2, "client_ioctl read reply with screwy data (%d, %d)",
+	     reply->datalen, length);
+      retval = -EIO;
+      goto done;
+    }
+    if (copy_to_user((void *)arg, reply->data, length)) {
+      retval = -EFAULT;
+      goto done;
+    }
+  }
+
+  /* all done! */
+ done:
+  free_fusd_msg(&reply);
+  UNLOCK_FUSD_FILE(fusd_file);
+  return retval;
+
+ invalid_file:
+ invalid_dev:
+  RDEBUG(3, "got a read on client file from pid %d, driver has disappeared",
+	 current->pid);
+  return -EPIPE;
+}
+static void fusd_client_mm_open(struct vm_area_struct * vma);
+static void fusd_client_mm_close(struct vm_area_struct * vma);
+static struct page* fusd_client_nopage(struct vm_area_struct* vma, unsigned long address, int* type);
+static struct vm_operations_struct fusd_remap_vm_ops =
+{
+  open: fusd_client_mm_open,
+  close: fusd_client_mm_close,
+  nopage: fusd_client_nopage,
+};
+
+struct fusd_mmap_instance
+{
+  fusd_dev_t* fusd_dev;
+  fusd_file_t* fusd_file;
+  unsigned long addr;
+  int size;
+  atomic_t refcount;
+};
+
+static void fusd_client_mm_open(struct vm_area_struct * vma)
+{
+  struct fusd_mmap_instance* mmap_instance = (struct fusd_mmap_instance*) vma->vm_private_data;
+  atomic_inc(&mmap_instance->refcount);
+  
+}
+
+static void fusd_client_mm_close(struct vm_area_struct * vma)
+{
+  struct fusd_mmap_instance* mmap_instance = (struct fusd_mmap_instance*) vma->vm_private_data;
+  if(atomic_dec_and_test(&mmap_instance->refcount))
+  {
+    KFREE(mmap_instance);
+  }
+}
+
+static int fusd_client_mmap(struct file *file, struct vm_area_struct * vma)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  struct fusd_transaction* transaction;
+  fusd_msg_t fusd_msg, *reply = NULL;
+  int retval = -EPIPE;
+  struct fusd_mmap_instance* mmap_instance;
+
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+
+  RDEBUG(3, "got a mmap on /dev/%s (owned by pid %d) from pid %d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid);
+
+  transaction = fusd_find_incomplete_transaction(fusd_file, FUSD_MMAP);
+
+  if(transaction == NULL)
+  {
+    /* send the message */
+    init_fusd_msg(&fusd_msg);
+    fusd_msg.subcmd = FUSD_MMAP;
+    fusd_msg.parm.fops_msg.offset = vma->vm_pgoff << PAGE_SHIFT;
+    fusd_msg.parm.fops_msg.flags = vma->vm_flags;
+    fusd_msg.parm.fops_msg.length = vma->vm_end - vma->vm_start;
+    
+    /* send message to userspace */
+    if ((retval = fusd_fops_call_send(fusd_file, &fusd_msg, &transaction)) < 0)
+      goto done;
+  }
+  
+  /* and wait for the reply */
+  /* todo: store and retrieve the transid from the interrupted messsage */
+  retval = fusd_fops_call_wait(fusd_file, &reply, transaction);
+  
+  mmap_instance = 
+    (struct fusd_mmap_instance*) KMALLOC(sizeof(struct fusd_mmap_instance), GFP_KERNEL);
+  // todo: free this thing at some point
+  
+  mmap_instance->fusd_dev = fusd_dev;
+  mmap_instance->fusd_file = fusd_file;
+  mmap_instance->addr = reply->parm.fops_msg.arg.arg;
+  mmap_instance->size = reply->parm.fops_msg.length;
+  atomic_set(&mmap_instance->refcount, 0);
+  
+  retval = reply->parm.fops_msg.retval;
+  
+  vma->vm_private_data = mmap_instance;
+  vma->vm_ops = &fusd_remap_vm_ops;
+  vma->vm_flags |= VM_RESERVED;
+  
+  fusd_client_mm_open(vma);
+  
+ done:
+  free_fusd_msg(&reply);
+  UNLOCK_FUSD_FILE(fusd_file);
+  return retval;
+
+ invalid_file:
+ invalid_dev:
+  RDEBUG(3, "got a mmap on client file from pid %d, driver has disappeared",
+	 current->pid);
+  return -EPIPE;
+}
+
+static struct page* fusd_client_nopage(struct vm_area_struct* vma, unsigned long address,
+                                int* type)
+{
+  struct fusd_mmap_instance* mmap_instance = (struct fusd_mmap_instance*) vma->vm_private_data;
+  unsigned long offset;
+  struct page *page = NOPAGE_SIGBUS;
+  int result;
+  offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+  // todo: worry about size
+  if(offset > mmap_instance->size)
+    goto out;
+  
+  down_read(&mmap_instance->fusd_dev->task->mm->mmap_sem);
+  result = get_user_pages(mmap_instance->fusd_dev->task, mmap_instance->fusd_dev->task->mm, mmap_instance->addr + offset, 1, 1, 0, &page, NULL);
+  up_read(&mmap_instance->fusd_dev->task->mm->mmap_sem);
+  
+  
+  if(PageAnon(page))
+  {
+    RDEBUG(2, "Cannot mmap anonymous pages. Be sure to allocate your shared buffer with MAP_SHARED | MAP_ANONYMOUS");
+    return NOPAGE_SIGBUS;
+  }
+  
+  if(result > 0)
+  {
+    get_page(page);
+    if (type)
+      *type = VM_FAULT_MINOR;
+  }
+out:
+  return page;
+
+
+}
+
+
+/*
+ * The design of poll for clients is a bit subtle.
+ *
+ * We don't want the select() call itself to block, so we keep a cache
+ * of the most recently known state supplied by the driver.  The cache
+ * is initialized to 0 (meaning: nothing readable/writable).
+ *
+ * When a poll comes in, we do a non-blocking (!) dispatch of a
+ * command telling the driver "This is the state we have cached, reply
+ * to this call when the state changes.", and then immediately return
+ * the cached state.  We tell the kernel's select to sleep on our
+ * poll_wait wait queue.
+ *
+ * When the driver replies, we update our cached info and wake up the
+ * wait queue.  Waking up the wait queue will most likely immediately
+ * effect a poll again, in which case we will reply whatever we just
+ * cached from the driver.
+ * 
+ */
+STATIC unsigned int fusd_client_poll(struct file *file, poll_table *wait)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_file_t *fusd_file;
+  int kernel_bits = 0;
+  int send_poll = 0;
+
+  GET_FUSD_FILE_AND_DEV(file->private_data, fusd_file, fusd_dev);
+  LOCK_FUSD_FILE(fusd_file);
+  LOCK_FUSD_DEV(fusd_dev);
+
+  RDEBUG(3, "got a select on /dev/%s (owned by pid %d) from pid %d, cps=%d",
+	 NAME(fusd_dev), fusd_dev->pid, current->pid,
+	 fusd_file->cached_poll_state);
+
+  poll_wait(file, &fusd_file->poll_wait, wait);
+
+  /*
+   * If our currently cached poll state is not the same as the
+   * most-recently-sent polldiff request, then, dispatch a new
+   * request.  (We DO NOT wait for a reply, but just dispatch the
+   * request).
+   *
+   * Also, don't send a new polldiff if the most recent one resulted
+   * in an error.
+   */
+  if (fusd_file->last_poll_sent != fusd_file->cached_poll_state &&
+      fusd_file->cached_poll_state >= 0) {
+    RDEBUG(3, "sending polldiff request because lps=%d, cps=%d",
+	   fusd_file->last_poll_sent, fusd_file->cached_poll_state);
+    send_poll = 1;
+    fusd_file->last_poll_sent = fusd_file->cached_poll_state;
+  }
+
+  /* compute what to return for the state we had cached, converting to
+   * bits that have meaning to the kernel */
+  if (fusd_file->cached_poll_state > 0) {
+    if (fusd_file->cached_poll_state & FUSD_NOTIFY_INPUT)
+      kernel_bits |= POLLIN;
+    if (fusd_file->cached_poll_state & FUSD_NOTIFY_OUTPUT)
+      kernel_bits |= POLLOUT;
+    if (fusd_file->cached_poll_state & FUSD_NOTIFY_EXCEPT)
+      kernel_bits |= POLLPRI;
+  }
+
+  /* Now that we've committed to sending the poll, etc., it should be
+   * safe to unlock the device */
+  UNLOCK_FUSD_DEV(fusd_dev);
+  UNLOCK_FUSD_FILE(fusd_file);
+
+  if (send_poll) {
+    fusd_msg_t fusd_msg;
+
+    init_fusd_msg(&fusd_msg);
+    fusd_msg.cmd = FUSD_FOPS_NONBLOCK;
+    fusd_msg.subcmd = FUSD_POLL_DIFF;
+    fusd_msg.parm.fops_msg.cmd = fusd_file->cached_poll_state;
+    if (fusd_fops_call_send(fusd_file, &fusd_msg, NULL) < 0) {
+      /* If poll dispatched failed, set back to -1 so we try again.
+       * Not a race (I think), since sending an *extra* polldiff never
+       * hurts anything. */
+      fusd_file->last_poll_sent = -1;
+    }
+  }
+  return kernel_bits;
+
+ zombie_dev:
+  /* might jump here from LOCK_FUSD_DEV */
+  UNLOCK_FUSD_FILE(fusd_file);
+ invalid_dev:
+ invalid_file:
+  RDEBUG(3, "got a select on client file from pid %d, driver has disappeared",
+	 current->pid);
+  return POLLPRI;
+}
+
+
+
+STATIC struct file_operations fusd_client_fops = {
+  owner:    THIS_MODULE,
+  open:     fusd_client_open,
+  release:  fusd_client_release,
+  read:     fusd_client_read,
+  write:    fusd_client_write,
+  ioctl:    fusd_client_ioctl,
+  poll:     fusd_client_poll,
+  mmap:     fusd_client_mmap
+};
+
+
+/*************************************************************************/
+/*************************************************************************/
+/*************************************************************************/
+
+
+STATIC fusd_file_t *find_fusd_reply_file(fusd_dev_t *fusd_dev, fusd_msg_t *msg)
+{
+  /* first, try the hint */
+  int i = msg->parm.fops_msg.hint;
+  if (i >= 0 &&
+      i < fusd_dev->num_files &&
+      fusd_dev->files[i] == msg->parm.fops_msg.fusd_file)
+    {
+      RDEBUG(15, "find_fusd_reply_file: hint worked");
+    } else {
+      /* hint didn't work, fall back to a search of the whole array */
+      i = find_fusd_file(fusd_dev, msg->parm.fops_msg.fusd_file);
+      RDEBUG(15, "find_fusd_reply_file: hint failed");
+    }
+
+  /* we couldn't find anyone waiting for this message! */
+  if (i < 0) {
+    return NULL;
+  } else {
+    return fusd_dev->files[i];
+  }
+}
+
+
+/* Process an incoming reply to a message dispatched by
+ * fusd_fops_call.  Called by fusd_write when a driver writes to
+ * /dev/fusd. */
+STATIC int fusd_fops_reply(fusd_dev_t *fusd_dev, fusd_msg_t *msg)
+{
+  fusd_file_t *fusd_file;
+  struct fusd_transaction *transaction;
+
+  /* figure out the index of the file we are replying to.  usually
+   * very fast (uses a hint) */
+  if ((fusd_file = find_fusd_reply_file(fusd_dev, msg)) == NULL) {
+    RDEBUG(2, "fusd_fops_reply: got a reply on /dev/%s with no connection",
+	   NAME(fusd_dev));
+    goto discard;
+  }
+
+  /* make sure this is not an old reply going to an old instance that's gone */
+	/* todo: kor fix this */
+/*
+  if (fusd_file->fusd_dev_version != fusd_dev->version ||
+      msg->parm.fops_msg.transid != fusd_file->transid_outstanding) {
+    RDEBUG(2, "fusd_fops_reply: got an old message, discarding");
+    goto discard;
+  }*/
+  
+  transaction = fusd_find_transaction(fusd_file, msg->parm.fops_msg.transid);
+	if(transaction == NULL)
+	{
+		RDEBUG(2, "fusd_fops_reply: No transaction found with transid %ld", msg->parm.fops_msg.transid);
+		goto discard;
+	}
+	
+  RDEBUG(10, "fusd_fops_reply: /dev/%s completed transid %ld (retval %d)",
+	 NAME(fusd_dev), msg->parm.fops_msg.transid,
+	 (int) msg->parm.fops_msg.retval);
+
+  transaction->msg_in = msg;
+	mb();
+
+  WAKE_UP_INTERRUPTIBLE_SYNC(&fusd_file->file_wait);
+
+  return 0;
+
+ discard:
+  if (msg->subcmd == FUSD_OPEN && msg->parm.fops_msg.retval == 0) {
+    fusd_forge_close(msg, fusd_dev);
+    return 0;
+  } else {
+    return -EPIPE;
+  }
+}
+
+
+/* special function to process responses to POLL_DIFF */
+STATIC int fusd_polldiff_reply(fusd_dev_t *fusd_dev, fusd_msg_t *msg)
+{
+  fusd_file_t *fusd_file;
+
+  /* figure out the index of the file we are replying to.  usually
+   * very fast (uses a hint) */
+  if ((fusd_file = find_fusd_reply_file(fusd_dev, msg)) == NULL)
+    return -EPIPE;
+
+  /* record the poll state returned.  convert all negative retvals to -1. */
+  if ((fusd_file->cached_poll_state = msg->parm.fops_msg.retval) < 0)
+    fusd_file->cached_poll_state = -1;
+
+  RDEBUG(3, "got updated poll state from /dev/%s driver: %d", NAME(fusd_dev),
+	 fusd_file->cached_poll_state);
+
+  /* since the client has returned the polldiff we sent, set
+   * last_poll_sent to -1, so that we'll send a polldiff request on
+   * the next select. */
+  fusd_file->last_poll_sent = -1;
+
+  /* wake up select's queue so that a new polldiff is generated */
+  wake_up_interruptible(&fusd_file->poll_wait);
+
+  return 0;
+}
+
+STATIC int fusd_register_device(fusd_dev_t *fusd_dev,
+				register_msg_t register_msg)
+{
+  int error = 0;
+  struct list_head *tmp;
+  int dev_id;
+
+  /* make sure args are valid */
+  if (fusd_dev == NULL) {
+    RDEBUG(0, "fusd_register_device: bug in arguments!");
+    return -EINVAL;
+  }
+
+  /* user can only register one device per instance */
+//  if (fusd_dev->handle != 0)
+//    return -EBUSY;
+
+  register_msg.name[FUSD_MAX_NAME_LENGTH] = '\0';
+
+  /* make sure that there isn't already a device by this name */
+  down(&fusd_devlist_sem);
+  list_for_each(tmp, &fusd_devlist_head) {
+    fusd_dev_t *d = list_entry(tmp, fusd_dev_t, devlist);
+    
+
+    if (d && d->name && !d->zombie && !strcmp(d->name, register_msg.name)) {
+      error = -EEXIST;
+      break;
+    }
+  }
+  up(&fusd_devlist_sem);
+
+  if (error)
+    return error;
+
+
+  /* allocate memory for the name, and copy */
+  if ((fusd_dev->name = KMALLOC(strlen(register_msg.name)+1, GFP_KERNEL)) == NULL) {
+     RDEBUG(1, "yikes!  kernel can't allocate memory");
+     return -ENOMEM;
+  }
+	
+  strcpy(fusd_dev->name, register_msg.name);
+
+  /* allocate memory for the class name, and copy */
+  if ((fusd_dev->class_name = KMALLOC(strlen(register_msg.clazz)+1, GFP_KERNEL)) == NULL) {
+     RDEBUG(1, "yikes!  kernel can't allocate memory");
+     return -ENOMEM;
+  }
+	
+	strcpy(fusd_dev->class_name, register_msg.clazz);
+	
+  /* allocate memory for the class name, and copy */
+  if ((fusd_dev->dev_name = KMALLOC(strlen(register_msg.devname)+1, GFP_KERNEL)) == NULL) {
+     RDEBUG(1, "yikes!  kernel can't allocate memory");
+     return -ENOMEM;
+  }
+	
+	strcpy(fusd_dev->dev_name, register_msg.devname);
+
+	dev_id = 0;
+
+	if((error = alloc_chrdev_region(&dev_id, 0, 1, fusd_dev->name)) < 0)
+	{
+		printk(KERN_ERR "alloc_chrdev_region failed status: %d\n", error);
+		goto register_failed;
+	}
+
+	fusd_dev->dev_id = dev_id;
+
+	fusd_dev->handle = cdev_alloc();
+	if(fusd_dev->handle == NULL)
+	{
+		printk(KERN_ERR "cdev_alloc() failed\n");
+		error = -ENOMEM;
+		goto register_failed3;
+	}
+
+	fusd_dev->handle->owner = THIS_MODULE;
+	fusd_dev->handle->ops = &fusd_client_fops;
+
+	kobject_set_name(&fusd_dev->handle->kobj, fusd_dev->name);
+
+	if((error = cdev_add(fusd_dev->handle, dev_id, 1)) < 0)
+	{
+		printk(KERN_ERR "cdev_add failed status: %d\n", error);
+		kobject_put(&fusd_dev->handle->kobj);
+		goto register_failed3;
+	}
+
+	// Hack to add my class to the sound class
+	if(strcmp("sound", register_msg.clazz) == 0)
+	{
+		fusd_dev->clazz = sound_class;
+		fusd_dev->owns_class = 0;
+	}
+	else
+	{
+		fusd_dev->clazz = class_create(THIS_MODULE, fusd_dev->class_name);
+		if(IS_ERR(fusd_dev->clazz))
+		{
+			error = PTR_ERR(fusd_dev->clazz);
+			printk(KERN_ERR "class_create failed status: %d\n", error);
+			goto register_failed4;
+		}
+		fusd_dev->owns_class = 1;
+	}
+	
+	fusd_dev->class_device = CLASS_DEVICE_CREATE(fusd_dev->clazz, NULL, fusd_dev->dev_id, NULL, fusd_dev->dev_name);
+	if(fusd_dev->class_device == NULL)
+	{
+		error = PTR_ERR(fusd_dev->class_device);
+		printk(KERN_ERR "class_device_create failed status: %d\n", error);
+		goto register_failed5;
+	}
+	
+	/* make sure the registration was successful */
+  /*
+  if (fusd_dev->handle == 0) {
+    error = -EIO;
+    goto register_failed;
+  }
+  */
+
+  /* remember the user's private data so we can pass it back later */
+  fusd_dev->private_data = register_msg.device_info;
+
+  /* everything ok */
+  fusd_dev->version = atomic_inc_and_ret(&last_version);
+  RDEBUG(3, "pid %d registered /dev/%s v%ld", fusd_dev->pid, NAME(fusd_dev),
+	 fusd_dev->version);
+  wake_up_interruptible(&new_device_wait);
+  return 0;
+
+register_failed5:
+	class_destroy(fusd_dev->clazz);
+	fusd_dev->clazz = NULL;
+register_failed4:
+	cdev_del(fusd_dev->handle);
+	fusd_dev->handle = NULL;
+register_failed3:
+
+	//register_failed2:
+	unregister_chrdev_region(dev_id, 1);
+register_failed:
+  KFREE(fusd_dev->name);
+  fusd_dev->name = NULL;
+  return error;
+}
+
+
+/****************************************************************************/
+/******************** CONTROL CHANNEL CALLBACK FUNCTIONS ********************/
+/****************************************************************************/
+
+
+/* open() called on /dev/fusd itself */
+STATIC int fusd_open(struct inode *inode, struct file *file)
+{
+  fusd_dev_t *fusd_dev = NULL;
+  fusd_file_t **file_array = NULL;
+
+  /* keep the module from being unloaded during initialization! */
+  //MOD_INC_USE_COUNT;
+
+  /* allocate memory for the device state */
+  if ((fusd_dev = KMALLOC(sizeof(fusd_dev_t), GFP_KERNEL)) == NULL)
+    goto dev_malloc_failed;
+  memset(fusd_dev, 0, sizeof(fusd_dev_t));
+
+  if ((file_array = fusd_dev_adjsize(fusd_dev)) == NULL)
+    goto file_malloc_failed;
+
+  init_waitqueue_head(&fusd_dev->dev_wait);
+  init_MUTEX(&fusd_dev->dev_sem);
+  fusd_dev->magic = FUSD_DEV_MAGIC;
+  fusd_dev->pid = current->pid;
+  fusd_dev->task = current;
+  file->private_data = fusd_dev;
+
+  /* add to the list of valid devices */
+  down(&fusd_devlist_sem);
+  list_add(&fusd_dev->devlist, &fusd_devlist_head);
+  up(&fusd_devlist_sem);
+
+  RDEBUG(3, "pid %d opened /dev/fusd", fusd_dev->pid);
+  return 0;
+
+ file_malloc_failed:
+  KFREE(fusd_dev);
+ dev_malloc_failed:
+  RDEBUG(1, "out of memory in fusd_open!");
+  //MOD_DEC_USE_COUNT;
+  return -ENOMEM;
+}
+
+
+/* close() called on /dev/fusd itself.  destroy the device that
+ * was registered by it, if any. */
+STATIC int fusd_release(struct inode *inode, struct file *file)
+{
+  fusd_dev_t *fusd_dev;
+
+  GET_FUSD_DEV(file->private_data, fusd_dev);
+  LOCK_FUSD_DEV(fusd_dev);
+
+  if (fusd_dev->pid != current->pid) {
+    RDEBUG(2, "yikes!: when releasing device, pid mismatch");
+  }
+
+  RDEBUG(3, "pid %d closing /dev/fusd", current->pid);
+
+#if 0
+  /* This delay is needed to exercise the openrace.c race condition,
+   * i.e. testing to make sure that our open_in_progress stuff works */
+  {
+    int target = jiffies + 10*HZ;
+
+    RDEBUG(1, "starting to wait");
+    while (jiffies < target)
+      schedule();
+    RDEBUG(1, "stopping wait");
+  }
+#endif
+
+  if(fusd_dev->handle)
+	{
+		class_device_destroy(fusd_dev->clazz, fusd_dev->dev_id);
+		if(fusd_dev->owns_class)
+		{
+			class_destroy(fusd_dev->clazz);
+		}
+		cdev_del(fusd_dev->handle);
+		unregister_chrdev_region(fusd_dev->dev_id, 1);
+	}
+
+  /* mark the driver as being gone */
+  zombify_dev(fusd_dev);
+
+  /* ...and possibly free it.  (Release lock if it hasn't been freed) */
+  if (!maybe_free_fusd_dev(fusd_dev))
+    UNLOCK_FUSD_DEV(fusd_dev);
+
+  /* notify fusd_status readers that there has been a change in the
+   * list of registered devices */
+  atomic_inc_and_ret(&last_version);
+  wake_up_interruptible(&new_device_wait);
+
+  return 0;
+
+ zombie_dev:
+ invalid_dev:
+  RDEBUG(1, "invalid device found in fusd_release!!");
+  return -ENODEV;
+}
+
+
+/*
+ * This function processes messages coming from userspace device drivers
+ * (i.e., writes to the /dev/fusd control channel.)
+ */
+STATIC ssize_t fusd_process_write(struct file *file,
+   const char *user_msg_buffer, size_t user_msg_len,
+   const char *user_data_buffer, size_t user_data_len)
+{
+  fusd_dev_t *fusd_dev;
+  fusd_msg_t *msg = NULL;
+  int retval = 0;
+  int yield = 0;
+
+  GET_FUSD_DEV(file->private_data, fusd_dev);
+  LOCK_FUSD_DEV(fusd_dev);
+
+  /* get the header from userspace (first make sure there's enough data) */
+  if (user_msg_len != sizeof(fusd_msg_t)) {
+    RDEBUG(6, "control channel got bad write of %d bytes (wanted %d)",
+	   (int) user_msg_len, (int) sizeof(fusd_msg_t));
+    retval = -EINVAL;
+    goto out_no_free;
+  }
+  if ((msg = KMALLOC(sizeof(fusd_msg_t), GFP_KERNEL)) == NULL) {
+    retval = -ENOMEM;
+    RDEBUG(1, "yikes!  kernel can't allocate memory");
+    goto out;
+  }
+  memset(msg, 0, sizeof(fusd_msg_t));
+
+  if (copy_from_user(msg, user_msg_buffer, sizeof(fusd_msg_t))) {
+    retval = -EFAULT;
+    goto out;
+  }
+  msg->data = NULL; /* pointers from userspace have no meaning */
+
+  /* check the magic number before acting on the message at all */
+  if (msg->magic != FUSD_MSG_MAGIC) {
+    RDEBUG(2, "got invalid magic number on /dev/fusd write from pid %d",
+	   current->pid);
+    retval = -EIO;
+    goto out;
+  }
+
+  /* now get data portion of the message */
+  if (user_data_len < 0 || user_data_len > MAX_RW_SIZE) {
+    RDEBUG(2, "fusd_process_write: got invalid length %d", (int) user_data_len);
+    retval = -EINVAL;
+    goto out;
+  }
+  if (msg->datalen != user_data_len) {
+    RDEBUG(2, "msg->datalen(%d) != user_data_len(%d), sigh!",
+	   msg->datalen, (int) user_data_len);
+    retval = -EINVAL;
+    goto out;
+  }
+  if (user_data_len > 0) {
+    if (user_data_buffer == NULL) {
+      RDEBUG(2, "msg->datalen and no data buffer, sigh!");
+      retval = -EINVAL;
+      goto out;
+    }
+    if ((msg->data = VMALLOC(user_data_len)) == NULL) {
+      retval = -ENOMEM;
+      RDEBUG(1, "yikes!  kernel can't allocate memory");
+      goto out;
+    }
+    if (copy_from_user(msg->data, user_data_buffer, user_data_len)) {
+      retval = -EFAULT;
+      goto out;
+    }
+  }
+
+  /* before device registration, the only command allowed is 'register'. */
+  /*
+  if (!fusd_dev->handle && msg->cmd != FUSD_REGISTER_DEVICE) {
+    RDEBUG(2, "got a message other than 'register' on a new device!");
+    retval = -EINVAL;
+    goto out;
+  }
+  */
+
+  /* now dispatch the command to the appropriate handler */
+  switch (msg->cmd) {
+  case FUSD_REGISTER_DEVICE:
+    retval = fusd_register_device(fusd_dev, msg->parm.register_msg);
+    goto out;
+    break;
+  case FUSD_FOPS_REPLY:
+    /* if reply is successful, DO NOT free the message */
+    if ((retval = fusd_fops_reply(fusd_dev, msg)) == 0) {
+      yield = 1;
+      goto out_no_free;
+    }
+    break;
+  case FUSD_FOPS_NONBLOCK_REPLY:
+    switch (msg->subcmd) {
+    case FUSD_POLL_DIFF:
+      retval = fusd_polldiff_reply(fusd_dev, msg);
+      break;
+    default:
+      RDEBUG(2, "fusd_fops_nonblock got unknown subcmd %d", msg->subcmd);
+      retval = -EINVAL;
+    }
+    break;
+  default:
+    RDEBUG(2, "warning: unknown message type of %d received!", msg->cmd);
+    retval = -EINVAL;
+    goto out;
+    break;
+  }
+
+
+ out:
+  if (msg && msg->data) {
+    VFREE(msg->data);
+    msg->data = NULL;
+  }
+  if (msg != NULL) {
+    KFREE(msg);
+    msg = NULL;
+  }
+
+ out_no_free:
+
+  /* the functions we call indicate success by returning 0.  we
+   * convert that into a success indication by changing the retval to
+   * the length of the write. */
+  if (retval == 0)
+    retval = user_data_len + user_msg_len;
+
+  UNLOCK_FUSD_DEV(fusd_dev);
+
+  /* if we successfully completed someone's syscall, yield the
+   * processor to them immediately as a throughput optimization.  we
+   * also hope that in the case of bulk data transfer, their next
+   * syscall will come in before we are scheduled again. */
+  if (yield) {
+#ifdef SCHED_YIELD
+    current->policy |= SCHED_YIELD;
+#endif
+    schedule();
+  }
+
+  return retval;
+
+ zombie_dev:
+ invalid_dev:
+  RDEBUG(1, "fusd_process_write: got invalid device!");
+  return -EPIPE;
+}
+
+
+STATIC ssize_t fusd_write(struct file *file,
+    const char *buffer,
+    size_t length,
+    loff_t *offset)
+{
+  return fusd_process_write(file, buffer, length, NULL, 0);
+}
+
+
+STATIC ssize_t fusd_writev(struct file *file,
+			   const struct iovec *iov,
+			   unsigned long count,
+			   loff_t *offset)
+{
+  if (count != 2) {
+    RDEBUG(2, "fusd_writev: got illegal iov count of %ld", count);
+    return -EINVAL;
+  }
+
+  return fusd_process_write(file,
+			    iov[0].iov_base, iov[0].iov_len,
+			    iov[1].iov_base, iov[1].iov_len);
+}
+
+
+/* fusd_read: a process is reading on /dev/fusd. return any messages
+ * waiting to go from kernel to userspace.
+ *
+ * Important note: there are 2 possible read modes;
+ *   1) header-read mode; just the fusd_msg structure is returned.
+ *
+ *   2) data-read mode; the data portion of a call (NOT including the
+ *   fusd_msg structure) is returned.
+ *
+ * The protocol this function expects the user-space library to follow
+ * is:
+ *   1) Userspace library reads header.
+ *   2) If fusd_msg->datalen == 0, goto step 4.
+ *   3) Userspace library reads data.
+ *   4) Message gets dequeued by the kernel.
+ *
+ * In other words, userspace first reads the header.  Then, if and
+ * only if the header you read indicates that data follows, userspace
+ * follows with a read for that data.
+ *
+ * For the header read, the length requested MUST be the exact length
+ * sizeof(fusd_msg_t).  The corresponding data read must request
+ * exactly the number of bytes in the data portion of the message.  NO
+ * OTHER READ LENGTHS ARE ALLOWED - ALL OTHER READ LENGTHS WILL GET AN
+ * -EINVAL.  This is done as a basic safety measure to make sure we're
+ * talking to a userspace library that understands our protocol, and
+ * to detect framing errors.
+ *
+ * (note: normally you'd have to worry about reentrancy in a function
+ * like this because the process can block on the userspace access and
+ * another might try to read.  usually we would copy the message into
+ * a temp location to make sure two processes don't get the same
+ * message.  however in this very specialized case, we're okay,
+ * because each instance of /dev/fusd has a completely independent
+ * message queue.)  */
+
+
+/* do a "header" read: used by fusd_read */
+STATIC int fusd_read_header(char *user_buffer, size_t user_length, fusd_msg_t *msg)
+{
+  int len = sizeof(fusd_msg_t);
+
+  if (user_length != len) {
+    RDEBUG(4, "bad length of %d sent to /dev/fusd for peek", (int) user_length);
+    return -EINVAL;
+  }
+
+  if (copy_to_user(user_buffer, msg, len))
+    return -EFAULT;
+
+  return sizeof(fusd_msg_t);
+}
+
+
+/* do a "data" read: used by fusd_read */
+STATIC int fusd_read_data(char *user_buffer, size_t user_length, fusd_msg_t *msg)
+{
+  int len = msg->datalen;
+
+  if (len == 0 || msg->data == NULL) {
+    RDEBUG(1, "fusd_read_data: no data to send!");
+    return -EIO;
+  }
+
+  /* make sure the user is requesting exactly the right amount (as a
+     sanity check) */
+  if (user_length != len) {
+    RDEBUG(4, "bad read for %d bytes on /dev/fusd (need %d)", (int) user_length,len);
+    return -EINVAL;
+  }
+
+  /* now copy to userspace */
+  if (copy_to_user(user_buffer, msg->data, len))
+    return -EFAULT;
+
+  /* done! */
+  return len;
+}
+
+
+STATIC ssize_t fusd_read(struct file *file,
+    char *user_buffer,    /* The buffer to fill with data */
+    size_t user_length,   /* The length of the buffer */
+    loff_t *offset)  /* Our offset in the file */
+{
+  fusd_dev_t *fusd_dev;
+  fusd_msgC_t *msg_out;
+  int retval, dequeue = 0;
+
+  GET_FUSD_DEV(file->private_data, fusd_dev);
+  LOCK_FUSD_DEV(fusd_dev);
+
+  RDEBUG(15, "driver pid %d (/dev/%s) entering fusd_read", current->pid,
+	 NAME(fusd_dev));
+
+  /* if no messages are waiting, either block or return EAGAIN */
+  while ((msg_out = fusd_dev->msg_head) == NULL) {
+    DECLARE_WAITQUEUE(wait, current);
+
+    if (file->f_flags & O_NONBLOCK) {
+      retval = -EAGAIN;
+      goto out;
+    }
+
+    /*
+     * sleep, waiting for a message to arrive.  we are unrolling
+     * interruptible_sleep_on to avoid a race between unlocking the
+     * device and sleeping (what if a message arrives in that
+     * interval?)
+     */
+    current->state = TASK_INTERRUPTIBLE;
+    add_wait_queue(&fusd_dev->dev_wait, &wait);
+    UNLOCK_FUSD_DEV(fusd_dev);
+    schedule();
+    remove_wait_queue(&fusd_dev->dev_wait, &wait);
+    LOCK_FUSD_DEV(fusd_dev);
+
+    /* we're back awake!  --see if a signal woke us up */
+    if (signal_pending(current)) {
+      retval = -ERESTARTSYS;
+      goto out;
+    }
+  }
+
+  /* is this a header read or data read? */
+  if (!msg_out->peeked) {
+    /* this is a header read (first read) */
+    retval = fusd_read_header(user_buffer, user_length, &msg_out->fusd_msg);
+
+    /* is there data?  if so, make sure next read gets data.  if not,
+     * make sure message is dequeued now.*/
+    if (msg_out->fusd_msg.datalen) {
+      msg_out->peeked = 1;
+      dequeue = 0;
+    } else {
+      dequeue = 1;
+    }
+  } else {
+    /* this is a data read (second read) */
+    retval = fusd_read_data(user_buffer, user_length, &msg_out->fusd_msg);
+    dequeue = 1; /* message should be dequeued */
+  }
+
+  /* if this message is done, take it out of the outgoing queue */
+  if (dequeue) {
+    if (fusd_dev->msg_tail == fusd_dev->msg_head)
+      fusd_dev->msg_tail = fusd_dev->msg_head = NULL;
+    else
+      fusd_dev->msg_head = msg_out->next;
+    FREE_FUSD_MSGC(msg_out);
+  }
+
+ out:
+  UNLOCK_FUSD_DEV(fusd_dev);
+  return retval;
+
+ zombie_dev:
+ invalid_dev:
+  RDEBUG(2, "got read on /dev/fusd for unknown device!");
+  return -EPIPE;
+}
+
+
+/* a poll on /dev/fusd itself (the control channel) */
+STATIC unsigned int fusd_poll(struct file *file, poll_table *wait)
+{
+  fusd_dev_t *fusd_dev;
+  GET_FUSD_DEV(file->private_data, fusd_dev);
+
+  poll_wait(file, &fusd_dev->dev_wait, wait);
+
+  if (fusd_dev->msg_head != NULL) {
+    return POLLIN | POLLRDNORM;
+  }
+
+ invalid_dev:
+  return 0;
+}
+
+
+STATIC struct file_operations fusd_fops = {
+  owner:    THIS_MODULE,
+  open:     fusd_open,
+  read:     fusd_read,
+  write:    fusd_write,
+  writev:   fusd_writev,
+  release:  fusd_release,
+  poll:     fusd_poll,
+};
+  
+
+
+/*************************************************************************/
+
+typedef struct fusd_status_state {
+  int binary_status;
+  int need_new_status;
+  char *curr_status;
+  int curr_status_len;
+  int last_version_seen;
+} fusd_statcontext_t;
+
+/* open() called on /dev/fusd/status */
+STATIC int fusd_status_open(struct inode *inode, struct file *file)
+{
+  int error = 0;
+  fusd_statcontext_t *fs;
+
+  //MOD_INC_USE_COUNT;
+
+  if ((fs = KMALLOC(sizeof(fusd_statcontext_t), GFP_KERNEL)) == NULL) {
+    RDEBUG(1, "yikes!  kernel can't allocate memory");
+    error = -ENOMEM;
+    goto out;
+  }
+
+  memset(fs, 0, sizeof(fusd_statcontext_t));
+  fs->need_new_status = 1;
+  file->private_data = (void *) fs;
+
+ out:
+  //if (error)
+  //  MOD_DEC_USE_COUNT;
+  return error;
+}
+
+/* close on /dev/fusd_status */
+STATIC int fusd_status_release(struct inode *inode, struct file *file)
+{
+  fusd_statcontext_t *fs = (fusd_statcontext_t *) file->private_data;
+
+  if (fs) {
+    if (fs->curr_status)
+      KFREE(fs->curr_status);
+    KFREE(fs);
+  }
+
+  //MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+
+/* ioctl() on /dev/fusd/status */
+STATIC int fusd_status_ioctl(struct inode *inode, struct file *file,
+				 unsigned int cmd, unsigned long arg)
+{
+  fusd_statcontext_t *fs = (fusd_statcontext_t *) file->private_data;
+
+  if (!fs)
+    return -EIO;
+
+  switch (cmd) {
+  case FUSD_STATUS_USE_BINARY:
+    fs->binary_status = 1;
+    return 0;
+  default:
+    return -EINVAL;
+    break;
+  }
+}
+
+
+/*
+ * maybe_expand_buffer: expand a buffer exponentially as it fills.  We
+ * are given:
+ *
+ * - A reference to a pointer to a buffer (buf)
+ * - A reference to the buffer's current capacity (buf_size)
+ * - The current amount of buffer space used (len)
+ * - The amount of space we want to ensure is free in the buffer (space_needed)
+ *
+ * If there isn't at least space_needed difference between buf_size
+ * and len, the existing contents are moved into a larger buffer. 
+ */
+STATIC int maybe_expand_buffer(char **buf, int *buf_size, int len,
+			       int space_needed)
+{
+  if (*buf_size - len < space_needed) {
+    char *old_buf = *buf;
+
+    *buf_size *= 2;
+    *buf = KMALLOC(*buf_size, GFP_KERNEL);
+
+    if (*buf != NULL)
+      memmove(*buf, old_buf, len);
+    KFREE(old_buf);
+    if (*buf == NULL) {
+      RDEBUG(1, "out of memory!");
+      return -1;
+    }
+  }
+  return 0;
+}
+
+
+
+/* Build a text buffer containing current fusd status. */
+STATIC void fusd_status_build_text(fusd_statcontext_t *fs)
+{
+  int buf_size = 512;
+  char *buf = KMALLOC(buf_size, GFP_KERNEL);
+  int len = 0, total_clients = 0, total_files = 0;
+  struct list_head *tmp;
+
+  if (buf == NULL) {
+    RDEBUG(1, "fusd_status_build: out of memory!");
+    return;
+  }
+
+  len += snprintf(buf + len, buf_size - len,
+		  "  PID  Open Name\n"
+		  "------ ---- -----------------\n");
+
+  down(&fusd_devlist_sem);
+  list_for_each(tmp, &fusd_devlist_head) {
+    fusd_dev_t *d = list_entry(tmp, fusd_dev_t, devlist);
+
+    if (!d)
+      continue;
+
+    /* Possibly expand the buffer if we need more space */
+    if (maybe_expand_buffer(&buf, &buf_size, len, FUSD_MAX_NAME_LENGTH+120) < 0)
+      goto out;
+
+    len += snprintf(buf + len, buf_size - len,
+		    "%6d %4d %s%s\n", d->pid, d->num_files,
+		    d->zombie ? "<zombie>" : "", NAME(d));
+
+    total_files++;
+    total_clients += d->num_files;
+  }
+
+  len += snprintf(buf + len, buf_size - len,
+		  "\nFUSD $Revision: 1.97-kor-hacked-11 $ - %d devices used by %d clients\n",
+		  total_files, total_clients);
+
+ out:
+  fs->last_version_seen = last_version;
+  up(&fusd_devlist_sem);
+
+  if (fs->curr_status)
+    KFREE(fs->curr_status);
+
+  fs->curr_status = buf;
+  fs->curr_status_len = len;
+  fs->need_new_status = 0;
+}
+
+
+/* Build the binary version of status */
+STATIC void fusd_status_build_binary(fusd_statcontext_t *fs)
+{
+  int buf_size = 512;
+  char *buf = KMALLOC(buf_size, GFP_KERNEL);
+  int len = 0, i = 0;
+  struct list_head *tmp;
+  fusd_status_t *s;
+
+  if (buf == NULL) {
+    RDEBUG(1, "out of memory!");
+    return;
+  }
+
+  down(&fusd_devlist_sem);
+  list_for_each(tmp, &fusd_devlist_head) {
+    fusd_dev_t *d = list_entry(tmp, fusd_dev_t, devlist);
+
+    if (!d)
+      continue;
+
+    /* Possibly expand the buffer if we need more space */
+    if (maybe_expand_buffer(&buf, &buf_size, len, sizeof(fusd_status_t)) < 0)
+      goto out;
+
+    s = &((fusd_status_t *) buf)[i];
+
+    /* construct this status entry */
+    memset(s, 0, sizeof(fusd_status_t));
+    strncpy(s->name, NAME(d), FUSD_MAX_NAME_LENGTH);
+    s->zombie   = d->zombie;
+    s->pid      = d->pid;
+    s->num_open = d->num_files;
+
+    i++;
+    len += sizeof(fusd_status_t);
+  }
+  
+ out:
+  fs->last_version_seen = last_version;
+  up(&fusd_devlist_sem);
+
+  if (fs->curr_status)
+    KFREE(fs->curr_status);
+
+  fs->curr_status = buf;
+  fs->curr_status_len = len;
+  fs->need_new_status = 0;
+}
+
+
+
+STATIC ssize_t fusd_status_read(struct file *file,
+    char *user_buffer,    /* The buffer to fill with data */
+    size_t user_length,   /* The length of the buffer */
+    loff_t *offset)  /* Our offset in the file */
+{
+  fusd_statcontext_t *fs = (fusd_statcontext_t *) file->private_data;
+
+  if (!fs)
+    return -EIO;
+
+  /* create a new status page, if we aren't in the middle of one */
+  if (fs->need_new_status) {
+    if (fs->binary_status)
+      fusd_status_build_binary(fs);
+    else
+      fusd_status_build_text(fs);
+  }
+
+  /* return EOF if we're at the end */
+  if (fs->curr_status == NULL || fs->curr_status_len == 0) {
+    fs->need_new_status = 1;
+    return 0;
+  }
+
+  /* return only as much data as we have */
+  if (fs->curr_status_len < user_length)
+    user_length = fs->curr_status_len;
+  if (copy_to_user(user_buffer, fs->curr_status, user_length))
+    return -EFAULT;
+
+  /* update fs, so we don't return the same data next time */
+  fs->curr_status_len -= user_length;
+  if (fs->curr_status_len)
+    memmove(fs->curr_status, fs->curr_status + user_length, fs->curr_status_len);
+  else {
+    KFREE(fs->curr_status);
+    fs->curr_status = NULL;
+  }
+
+  return user_length;
+}
+
+
+/* a poll on /dev/fusd itself (the control channel) */
+STATIC unsigned int fusd_status_poll(struct file *file, poll_table *wait)
+{
+  fusd_statcontext_t *fs = (fusd_statcontext_t *) file->private_data;
+
+  poll_wait(file, &new_device_wait, wait);
+
+  if (fs->last_version_seen < last_version)
+    return POLLIN | POLLRDNORM;
+  else
+    return 0;
+}
+
+
+STATIC struct file_operations fusd_status_fops = {
+  owner:    THIS_MODULE,
+  open:     fusd_status_open,
+  ioctl:    fusd_status_ioctl,
+  read:     fusd_status_read,
+  release:  fusd_status_release,
+  poll:     fusd_status_poll,
+};
+  
+
+/*************************************************************************/
+
+
+STATIC int init_fusd(void)
+{
+	int retval;
+
+#ifdef CONFIG_FUSD_MEMDEBUG
+  if ((retval = fusd_mem_init()) < 0)
+    return retval;
+#endif
+
+
+  printk(KERN_INFO
+	 "fusd: starting, $Revision: 1.97-kor-hacked-11 $, $Date: 2003/07/11 22:29:39 $");
+#ifdef CVSTAG
+  printk(", release %s", CVSTAG);
+#endif
+#ifdef CONFIG_FUSD_DEBUG
+  printk(", debuglevel=%d\n", fusd_debug_level);
+#else
+  printk(", debugging messages disabled\n");
+#endif
+
+	fusd_control_device = NULL;
+	fusd_status_device = NULL;
+	
+	fusd_class = class_create(THIS_MODULE, "fusd");
+	if(IS_ERR(fusd_class))
+	{
+		retval = PTR_ERR(fusd_class);
+		printk(KERN_ERR "class_create failed status: %d\n", retval);
+		goto fail0;
+	}
+	
+	control_id = 0;
+
+	if((retval = alloc_chrdev_region(&control_id, 0, 1, FUSD_CONTROL_FILENAME)) < 0)
+	{
+		printk(KERN_ERR "alloc_chrdev_region failed status: %d\n", retval);
+		goto fail1;
+	}
+
+	fusd_control_device = cdev_alloc();
+	if(fusd_control_device == NULL)
+	{
+		printk(KERN_ERR "cdev-alloc failed\n");
+		retval = -ENOMEM;
+		goto fail3;
+	}
+
+	fusd_control_device->owner = THIS_MODULE;
+	fusd_control_device->ops = &fusd_fops;
+	kobject_set_name(&fusd_control_device->kobj, FUSD_CONTROL_FILENAME);
+
+	printk(KERN_ERR "cdev control id: %d\n", control_id);
+	if((retval = cdev_add(fusd_control_device, control_id, 1)) < 0)
+	{
+		printk(KERN_ERR "cdev_add failed status: %d\n", retval);
+		kobject_put(&fusd_control_device->kobj);
+		goto fail4;
+	}
+	
+	fusd_control_class_device = CLASS_DEVICE_CREATE(fusd_class, NULL, control_id, NULL, "control");
+	if(fusd_control_class_device == NULL)
+	{
+		retval = PTR_ERR(fusd_control_class_device);
+		printk("class_device_create failed status: %d\n", retval);
+		goto fail5;
+	}
+
+	status_id = 0;
+
+	if((retval = alloc_chrdev_region(&status_id, 0, 1, FUSD_STATUS_FILENAME)) < 0)
+	{
+		printk(KERN_ERR "alloc_chrdev_region failed status: %d\n", retval);
+		goto fail6;
+	}
+
+	fusd_status_device = cdev_alloc();
+	if(fusd_status_device == NULL)
+	{
+		retval = -ENOMEM;
+		goto fail8;
+	}
+
+	fusd_status_device->owner = THIS_MODULE;
+	fusd_status_device->ops = &fusd_status_fops;
+	kobject_set_name(&fusd_status_device->kobj, FUSD_STATUS_FILENAME);
+
+	if((retval = cdev_add(fusd_status_device, status_id, 1)) < 0)
+	{
+		printk(KERN_ERR "cdev_add failed status: %d\n", retval);
+		kobject_put(&fusd_status_device->kobj);
+		goto fail9;
+	}
+	
+	fusd_status_class_device = CLASS_DEVICE_CREATE(fusd_class, NULL, status_id, NULL, "status");
+	if(fusd_status_class_device == NULL)
+	{
+		printk(KERN_ERR "class_device_create failed status: %d\n", retval);
+		retval = PTR_ERR(fusd_status_class_device);
+		goto fail10;
+	}
+	
+  RDEBUG(1, "registration successful");
+  return 0;
+
+fail10:
+	cdev_del(fusd_status_device);
+fail9:
+	kfree(fusd_status_device);
+fail8:
+
+	//fail7:
+	unregister_chrdev_region(status_id, 1);
+fail6:
+	class_device_destroy(fusd_class, control_id);
+fail5:
+	cdev_del(fusd_control_device);
+fail4:
+	kfree(fusd_control_device);
+fail3:
+
+	//fail2:
+	unregister_chrdev_region(control_id, 1);
+
+fail1:
+	class_destroy(fusd_class);
+fail0:
+	return retval;
+}
+
+STATIC void cleanup_fusd(void)
+{
+  RDEBUG(1, "cleaning up");
+
+	class_device_destroy(fusd_class, status_id);
+	class_device_destroy(fusd_class, control_id);
+	
+	cdev_del(fusd_control_device);
+	cdev_del(fusd_status_device);
+
+	class_destroy(fusd_class);
+	
+#ifdef CONFIG_FUSD_MEMDEBUG
+  fusd_mem_cleanup();
+#endif
+}
+
+module_init(init_fusd);
+module_exit(cleanup_fusd);


Property changes on: trunk/fusd/kfusd/kfusd.c
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/libfusd/Makefile
===================================================================
--- trunk/fusd/libfusd/Makefile	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/libfusd/Makefile	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,30 @@
+SRC  = libfusd.c
+OBJ  = libfusd.o
+TARGETS = libfusd.a libfusd.so.0.0
+
+default: $(TARGETS)
+
+install: $(TARGETS)
+	$(INSTALL) -d -m 0755 $(LIBDIR)
+	$(INSTALL) -m 0755 $(TARGETS) $(LIBDIR)
+	/sbin/ldconfig
+	$(INSTALL) -d -m 0755 $(INCDIR)
+	$(INSTALL) -m 0755 ../include/*.h $(INCDIR)
+
+clean:
+	rm -f *.o *.so *.so.* *.a *.d *.d.* gmon.out *~ 
+
+$(TARGETS): 
+	$(MAKE) target CFLAGS='-g -O2 $(SCF) $(GCF)'
+
+target: $(OBJ) 
+	$(LD) $(OBJ) $(SOLDFLAGS) -o libfusd.so.0.0 $(SLF)
+	$(AR) -cr libfusd.a $(OBJ)
+
+%.d: %.c
+	$(CC) -M $(CFLAGS) $< > $@.$$$$; sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$
+
+ifeq ($(MAKECMDGOALS),target)
+include $(SRC:.c=.d)
+endif
+


Property changes on: trunk/fusd/libfusd/Makefile
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/libfusd/libfusd.c
===================================================================
--- trunk/fusd/libfusd/libfusd.c	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/libfusd/libfusd.c	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,687 @@
+/*
+ *
+ * Copyright (c) 2003 The Regents of the University of California.  All 
+ * rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * - Neither the name of the University nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+ 
+
+/*
+ * fusd userspace library: functions that know how to properly talk
+ * to the fusd kernel module
+ *
+ * authors: jelson and girod
+ *
+ * $Id: libfusd.c,v 1.61 2003/07/11 22:29:39 cerpa Exp $
+ */
+
+char libfusd_c_id[] = "$Id: libfusd.c,v 1.61 2003/07/11 22:29:39 cerpa Exp $";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+
+#include "fusd.h"
+#include "fusd_msg.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+/* maximum number of messages processed by a single call to fusd_dispatch */
+#define MAX_MESSAGES_PER_DISPATCH 40
+
+/* used for fusd_run */
+static fd_set fusd_fds;
+
+/* default prefix of devices (often "/dev/") */
+char *dev_root = NULL;
+
+/* 
+ * fusd_fops_set is an array that keeps track of the file operations
+ * struct for each fusd fd.
+ */
+static fusd_file_operations_t fusd_fops_set[FD_SETSIZE];
+fusd_file_operations_t null_fops = { NULL };
+
+/*
+ * accessor macros
+ */
+#define FUSD_GET_FOPS(fd) \
+  (fusd_fops_set + (fd))
+
+#define FUSD_SET_FOPS(fd,fi) \
+  (fusd_fops_set[(fd)]=(*fi))
+
+#define FUSD_FD_VALID(fd) \
+  (((fd)>=0) && \
+   ((fd)<FD_SETSIZE) && \
+   (memcmp(FUSD_GET_FOPS(fd), &null_fops, sizeof(fusd_file_operations_t))))
+
+
+/*
+ * fusd_init
+ * 
+ * this is called automatically before the first
+ * register call 
+ */
+void fusd_init()
+{
+  static int fusd_init_needed = 1;
+
+  if (fusd_init_needed) {
+    int i;
+
+    fusd_init_needed = 0;
+
+    for (i = 0; i < FD_SETSIZE; i++)
+      FUSD_SET_FOPS(i, &null_fops);
+    FD_ZERO(&fusd_fds);
+
+    dev_root = DEFAULT_DEV_ROOT;
+  }
+}
+
+
+int fusd_register(const char *name, const char* clazz, const char* devname, mode_t mode, void *device_info,
+		  struct fusd_file_operations *fops)
+{
+  int fd = -1, retval = 0;
+  fusd_msg_t message;
+
+  /* need initialization? */
+  fusd_init();
+
+  /* make sure the name is valid and we have a valid set of fops... */
+  if (name == NULL || fops == NULL) {
+    fprintf(stderr, "fusd_register: invalid name or fops argument\n");
+    retval = -EINVAL;
+    goto done;
+  }
+
+  /*
+   * convenience: if the first characters of the name you're trying
+   * to register are SKIP_PREFIX (usually "/dev/"), skip over them.
+   */
+  if (dev_root != NULL && strlen(name) > strlen(dev_root) &&
+      !strncmp(name, dev_root, strlen(dev_root))) {
+    name += strlen(dev_root);
+  }
+
+  if (strlen(name) > FUSD_MAX_NAME_LENGTH) {
+    fprintf(stderr, "name '%s' too long, sorry :(", name);
+    retval = -EINVAL;
+    goto done;
+  }
+
+  /* open the fusd control channel */
+  if ((fd = open(FUSD_CONTROL_DEVNAME, O_RDWR | O_NONBLOCK)) < 0) {
+
+    /* if the problem is that /dev/fusd does not exist, return the
+     * message "Package not installed", which is hopefully more
+     * illuminating than "no such file or directory" */
+    if (errno == ENOENT) {
+      fprintf(stderr, "libfusd: %s does not exist; ensure FUSD's kernel module is installed\n",
+	      FUSD_CONTROL_DEVNAME);
+      retval = -ENOPKG;
+    } else {
+      perror("libfusd: trying to open FUSD control channel");
+      retval = -errno;
+    }
+    goto done;
+  }
+
+  /* fd in use? */
+  if (FUSD_FD_VALID(fd)) {
+    retval = -EBADF;
+    goto done;
+  }
+
+  /* set up the message */
+  memset(&message, 0, sizeof(message));
+  message.magic = FUSD_MSG_MAGIC;
+  message.cmd = FUSD_REGISTER_DEVICE;
+  message.datalen = 0;
+  strcpy(message.parm.register_msg.name, name);
+  strcpy(message.parm.register_msg.clazz, clazz);
+  strcpy(message.parm.register_msg.devname, devname);
+  message.parm.register_msg.mode = mode;
+  message.parm.register_msg.device_info = device_info;
+
+  /* make the request */
+  if (write(fd, &message, sizeof(fusd_msg_t)) < 0) {
+    retval = -errno;
+    goto done;
+  }
+
+  /* OK, store the new file state */
+  FUSD_SET_FOPS(fd, fops);
+  FD_SET(fd, &fusd_fds);
+
+  /* success! */
+ done:
+  if (retval < 0) {
+    if (fd >= 0)
+      close(fd);
+    errno = -retval;
+    retval = -1;
+  } else {
+    errno = 0;
+    retval = fd;
+  }
+
+  return retval;
+}
+
+
+int fusd_unregister(int fd)
+{
+  if (FUSD_FD_VALID(fd)) {
+    /* clear fd location */
+    FUSD_SET_FOPS(fd, &null_fops);
+    FD_CLR(fd, &fusd_fds);
+    /* close */
+    return close(fd);
+  }
+  
+  else {
+    errno = EBADF;
+    return -1;
+  }
+}
+
+
+/* 
+ * fusd_run: a convenience function for automatically running a FUSD
+ * driver, for drivers that don't want to manually select on file
+ * descriptors and call fusd_dispatch.  This function will
+ * automatically select on all devices the user has registered and
+ * call fusd_dispatch on any one that becomes readable.
+ */
+void fusd_run(void)
+{
+  fd_set tfds;
+  int status;
+  int maxfd;
+  int i;
+
+  /* locate maxmimum fd in use */
+  for (maxfd=0, i=0; i < FD_SETSIZE; i++) {
+    if (FD_ISSET(i, &fusd_fds)) {
+      maxfd = i;
+    }
+  }
+  maxfd++;
+
+
+  while (1) {
+    /* select */
+    memmove(&tfds, &fusd_fds, sizeof(fd_set));
+    status = select(maxfd, &tfds, NULL, NULL, NULL);
+
+    /* error? */
+    if (status < 0) {
+      perror("libfusd: fusd_run: error on select");
+      continue;
+    }
+
+    /* readable? */
+    for (i = 0; i < maxfd; i++)
+      if (FD_ISSET(i, &tfds))
+	fusd_dispatch(i);
+  }
+}
+
+
+/************************************************************************/
+
+
+/* reads a fusd kernel-to-userspace message from fd, and puts a
+ * fusd_msg into the memory pointed to by msg (we assume we are passed
+ * a buffer managed by the caller).  if there is a data portion to the
+ * message (msg->datalen > 0), we allocate memory for it, set data to
+ * point to that memory.  the returned data pointer must also be
+ * managed by the caller. */
+static int fusd_get_message(int fd, fusd_msg_t *msg)
+{
+  /* read the header part into the kernel */
+  if (read(fd, msg, sizeof(fusd_msg_t)) < 0) {
+    if (errno != EAGAIN)
+      perror("error talking to FUSD control channel on header read");
+    return -errno;
+  }
+  msg->data = NULL; /* pointers in kernelspace have no meaning */
+
+  if (msg->magic != FUSD_MSG_MAGIC) {
+    fprintf(stderr, "libfusd magic number failure\n");
+    return -EINVAL;
+  }
+
+  /* if there's a data part to the message, read it from the kernel. */
+  if (msg->datalen) {
+    if ((msg->data = malloc(msg->datalen + 1)) == NULL) {
+      fprintf(stderr, "libfusd: can't allocate memory\n");
+      return -ENOMEM;  /* this is bad, we are now unsynced */
+    }
+
+    if (read(fd, msg->data, msg->datalen) < 0) {
+      perror("error talking to FUSD control channel on data read");
+      free(msg->data);
+      msg->data = NULL;
+      return -EIO;
+    }
+
+    /* For convenience, we now ensure that the byte *after* the buffer
+     * is set to 0.  (Note we malloc'd one extra byte above.) */
+    msg->data[msg->datalen] = '\0';
+  }
+
+  return 0;
+}
+
+
+/*
+ * fusd_fdset_add: given an FDSET and "max", add the currently valid
+ * FUSD fds to the set and update max accordingly.
+ */
+void fusd_fdset_add(fd_set *set, int *max)
+{
+  int i;
+
+  for (i = 0; i < FD_SETSIZE; i++) {
+    if (FD_ISSET(i, &fusd_fds)) {
+      FD_SET(i, set);
+      if (i > *max) {
+	*max = i;
+      }
+    }
+  }
+}
+
+
+
+/*
+ * fusd_dispatch_fdset: given an fd_set full of descriptors, call
+ * fusd_dispatch on every descriptor in the set which is a valid FUSD
+ * fd.
+ */
+void fusd_dispatch_fdset(fd_set *set)
+{
+  int i;
+
+  for (i = 0; i < FD_SETSIZE; i++)
+    if (FD_ISSET(i, set) && FD_ISSET(i, &fusd_fds))
+      fusd_dispatch(i);
+}
+
+
+/* 
+ * fusd_dispatch_one() -- read a single kernel-to-userspace message
+ * from fd, then call the appropriate userspace callback function,
+ * based on the message that was read.  finally, return the result
+ * back to the kernel, IF the return value from the callback is not
+ * FUSD_NOREPLY.
+ *
+ * On success, returns 0.
+ * On failure, returns a negative number indicating the errno.
+ */
+static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
+{
+  fusd_file_info_t *file = NULL;
+  fusd_msg_t *msg = NULL;
+  int driver_retval = 0; /* returned to the FUSD driver */
+  int user_retval = 0;    /* returned to the user who made the syscall */
+
+  /* check for valid, look up ops */
+  if (fops == NULL) {
+    fprintf(stderr, "fusd_dispatch: no fops provided!\n");
+    driver_retval = -EBADF;
+    goto out_noreply;
+  }
+
+  /* allocate memory for fusd_msg_t */
+  if ((msg = malloc(sizeof(fusd_msg_t))) == NULL) {
+    driver_retval = -ENOMEM;
+    fprintf(stderr, "libfusd: can't allocate memory\n");
+    goto out_noreply;
+  }
+  memset(msg, '\0', sizeof(fusd_msg_t));
+
+  /* read header and data, if it's there */
+  if ((driver_retval = fusd_get_message(fd, msg)) < 0)
+    goto out_noreply;
+
+  /* allocate file info struct */
+  file = malloc(sizeof(fusd_file_info_t));
+  if (NULL == file) {
+    fprintf(stderr, "libfusd: can't allocate memory\n");
+    driver_retval = -ENOMEM;
+    goto out_noreply;
+  }
+
+  /* fill the file info struct */
+  memset(file, '\0', sizeof(fusd_file_info_t));
+  file->fd = fd;
+  file->device_info = msg->parm.fops_msg.device_info;
+  file->private_data = msg->parm.fops_msg.private_info;
+  file->flags = msg->parm.fops_msg.flags;
+  file->pid = msg->parm.fops_msg.pid;
+  file->uid = msg->parm.fops_msg.uid;
+  file->gid = msg->parm.fops_msg.gid;
+  file->fusd_msg = msg;  
+
+  /* right now we only handle fops requests */
+  if (msg->cmd != FUSD_FOPS_CALL && msg->cmd != FUSD_FOPS_NONBLOCK &&
+      msg->cmd != FUSD_FOPS_CALL_DROPREPLY) {
+    fprintf(stderr, "libfusd: got unknown msg->cmd from kernel\n");
+    user_retval = -EINVAL;
+    goto send_reply;
+  }
+  
+  /* dispatch on operation type */
+  user_retval = -ENOSYS;
+  switch (msg->subcmd) {
+  case FUSD_OPEN:
+    if (fops && fops->open)
+      user_retval = fops->open(file);
+    break;
+  case FUSD_CLOSE:
+    if (fops && fops->close)
+      user_retval = fops->close(file);
+    break;
+  case FUSD_READ:
+    /* allocate a buffer and make the call */
+    if (fops && fops->read) {
+      if ((msg->data = malloc(msg->parm.fops_msg.length)) == NULL) {
+	user_retval = -ENOMEM;
+	fprintf(stderr, "libfusd: can't allocate memory\n");
+      } else {
+	msg->datalen = msg->parm.fops_msg.length;
+	user_retval = fops->read(file, msg->data, msg->datalen,
+				 &msg->parm.fops_msg.offset);
+      }
+    }
+    break;
+  case FUSD_WRITE:
+    if (fops && fops->write)
+      user_retval = fops->write(file, msg->data, msg->datalen,
+				&msg->parm.fops_msg.offset);
+    break;
+  case FUSD_MMAP:
+    if (fops && fops->mmap)
+    {
+      user_retval = fops->mmap(file, msg->parm.fops_msg.offset, msg->parm.fops_msg.length, msg->parm.fops_msg.flags,
+                               &msg->parm.fops_msg.arg.ptr_arg, &msg->parm.fops_msg.length);
+    }
+    break;
+  case FUSD_IOCTL:
+    if (fops && fops->ioctl) {
+      /* in the case of an ioctl read, allocate a buffer for the
+       * driver to write to, IF there isn't already a buffer.  (there
+       * might already be a buffer if this is a read+write) */
+      if ((_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ) &&
+	  msg->data == NULL) {
+	msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
+	if ((msg->data = malloc(msg->datalen)) == NULL) {
+	  user_retval = -ENOMEM;
+	  break;
+	}
+      }
+      if (msg->data != NULL)
+	user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd, msg->data);
+      else
+	user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd,
+				  (void *) msg->parm.fops_msg.arg.ptr_arg);
+    }
+    break;
+    
+  case FUSD_POLL_DIFF:
+    /* This callback requests notification when an event occurs on a file,
+     * e.g. becoming readable or writable */
+    if (fops && fops->poll_diff)
+      user_retval = fops->poll_diff(file, msg->parm.fops_msg.cmd);
+    break;    
+  
+  case FUSD_UNBLOCK:
+    /* This callback is called when a system call is interrupted */
+    if (fops && fops->unblock)
+      user_retval = fops->unblock(file);    
+    break;
+    
+  default:
+    fprintf(stderr, "libfusd: Got unsupported operation\n");
+    user_retval = -ENOSYS;
+    break;
+  }
+  
+  goto send_reply;
+
+
+  /* out_noreply is only used for handling errors */
+ out_noreply:
+  if (msg->data != NULL)
+    free(msg->data);
+  if (msg != NULL)
+    free(msg);
+  goto done;
+
+  /* send_reply is only used for success */
+ send_reply:
+  if (-user_retval <= 0xff) {
+    /* 0xff is the maximum legal return value (?) - return val to user */
+    driver_retval = fusd_return(file, user_retval);
+  } else {
+    /* if we got a FUSD_NOREPLY, don't free the msg structure */
+    driver_retval = 0;
+  }
+
+  /* this is common to both errors and success */
+ done:
+  if (driver_retval < 0) {
+    errno = -driver_retval;
+    driver_retval = -1;
+  }
+  return driver_retval;
+}
+
+
+/* fusd_dispatch is now a wrapper around fusd_dispatch_one that calls
+ * it repeatedly, until it fails.  this helps a lot with bulk data
+ * transfer since there is no intermediate select in between the
+ * reads.  (the kernel module helps by running the user process in
+ * between).
+ *
+ * This function now prints an error to stderr in case of error,
+ * instead of returning a -1.
+ */
+void fusd_dispatch(int fd)
+{
+  int retval, num_dispatches = 0;
+  fusd_file_operations_t *fops = NULL;
+
+  /* make sure we have a valid FD, and get its fops structure */
+  if (!FUSD_FD_VALID(fd)) {
+    errno = EBADF;
+    retval = -1;
+    goto out;
+  }
+  fops = FUSD_GET_FOPS(fd);
+
+  /* now keep dispatching until a dispatch returns an error */
+  do {
+    retval = fusd_dispatch_one(fd, fops);
+
+    if (retval >= 0)
+      num_dispatches++;
+  } while (retval >= 0 && num_dispatches <= MAX_MESSAGES_PER_DISPATCH);
+
+  /* if we've dispatched at least one message successfully, and then
+   * stopped because of EAGAIN - do not report an error.  this is the
+   * common case. */
+  if (num_dispatches > 0 && errno == EAGAIN) {
+    retval = 0;
+    errno = 0;
+  }
+
+ out:
+  if (retval < 0 && errno != EPIPE)
+    fprintf(stderr, "libfusd: fusd_dispatch error on fd %d: %m\n", fd);
+}
+
+
+/*
+ * fusd_destroy destroys all state associated with a fusd_file_info
+ * pointer.  (It is implicitly called by fusd_return.)  If a driver
+ * saves a fusd_file_info pointer by calling -FUSD_NOREPLY in order to
+ * block a read, but gets a "close" request on the file before the
+ * pointer is returned with fusd_return, it should be thrown away
+ * using fusd_destroy.  
+ */
+void fusd_destroy(struct fusd_file_info *file)
+{
+  if (file == NULL)
+    return;
+
+  if (file->fusd_msg->data != NULL)
+    free(file->fusd_msg->data);
+  free(file->fusd_msg);
+  free(file);
+}
+
+
+/*
+ * construct a user-to-kernel message in reply to a file function
+ * call. 
+ *
+ * On success, returns 0.
+ * On failure, returns a negative number indicating the errno.
+ */
+int fusd_return(fusd_file_info_t *file, ssize_t retval)
+{
+  fusd_msg_t *msg = NULL;
+  int fd;
+  int driver_retval = 0;
+  struct iovec iov[2];
+
+  if (file == NULL) {
+    fprintf(stderr, "fusd_return: NULL file\n");
+    return -EINVAL;
+  }
+
+  fd = file->fd;
+  if (!FUSD_FD_VALID(fd)) {
+    fprintf(stderr, "fusd_return: badfd (fd %d)\n", fd);
+    return -EBADF;
+  }
+
+  if ((msg = file->fusd_msg) == NULL) {
+    fprintf(stderr, "fusd_return: fusd_msg is gone\n");
+    return -EINVAL;
+  }
+
+  /* if this was a "DONTREPLY" message, just free the struct */
+  if (msg->cmd == FUSD_FOPS_CALL_DROPREPLY)
+    goto free_memory;
+
+  /* do we copy data back to kernel?  how much? */
+  switch(msg->subcmd) {
+  case FUSD_READ:
+    /* these operations can return data to userspace */
+    if (retval > 0) {
+      msg->datalen = MIN(retval, msg->parm.fops_msg.length);
+      retval = msg->datalen;
+    } else {
+      msg->datalen = 0;
+    }
+    break;
+  case FUSD_IOCTL:
+    /* ioctl CAN (in read mode) return data to userspace */
+    if ((retval == 0) && 
+	(_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ))
+      msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
+    else
+      msg->datalen = 0;
+    break;
+  default:
+    /* open, close, write, etc. do not return data */
+    msg->datalen = 0;
+    break;
+  }
+
+  /* fill the file info struct */
+  msg->cmd++; /* change FOPS_CALL to FOPS_REPLY; NONBLOCK to NONBLOCK_REPLY */
+  msg->parm.fops_msg.retval = retval;
+  msg->parm.fops_msg.device_info = file->device_info;
+  msg->parm.fops_msg.private_info = file->private_data;
+  msg->parm.fops_msg.flags = file->flags;
+  /* pid is NOT copied back. */
+
+  /* send message to kernel */
+  if (msg->datalen && msg->data != NULL) {
+    iov[0].iov_base = msg;
+    iov[0].iov_len = sizeof(fusd_msg_t);
+    iov[1].iov_base = msg->data;
+    iov[1].iov_len = msg->datalen;
+    driver_retval = writev(fd, iov, 2);
+  }
+  else {
+    driver_retval = write(fd, msg, sizeof(fusd_msg_t));
+  }
+
+ free_memory:
+  fusd_destroy(file);
+
+  if (driver_retval < 0)
+    return -errno;
+  else
+    return 0;
+}
+
+
+/* returns static string representing the flagset (e.g. RWE) */
+#define RING 5
+char *fusd_unparse_flags(int flags)
+{
+  static int i = 0;
+  static char ringbuf[RING][5];
+  char *s = ringbuf[i];
+  i = (i + 1) % RING;
+  
+  sprintf(s, "%c%c%c", 
+	  (flags & FUSD_NOTIFY_INPUT)?'R':'-',
+	  (flags & FUSD_NOTIFY_OUTPUT)?'W':'-',
+	  (flags & FUSD_NOTIFY_EXCEPT)?'E':'-');
+  
+  return s;
+}
+#undef RING


Property changes on: trunk/fusd/libfusd/libfusd.c
___________________________________________________________________
Name: svn:executable
   + 

Added: trunk/fusd/make.include
===================================================================
--- trunk/fusd/make.include	2007-01-10 06:59:01 UTC (rev 12311)
+++ trunk/fusd/make.include	2007-01-13 08:39:56 UTC (rev 12312)
@@ -0,0 +1,149 @@
+
+# auto-dependency generation makefile
+
+
+#### Default values
+
+SRCEXTENSIONS := c C cpp
+CC := gcc
+CPP := g++
+LD := ld
+AR := ar
+
+#### build object directory token 
+
+CPU := $(shell uname -m)
+OS := $(shell uname -s | tr '[A-Z]' '[a-z]')
+
+DEFAULT_ARCH := $(CPU)-$(OS)
+
+ifeq ($(strip $(ARCH)),)
+	ARCH := $(DEFAULT_ARCH)
+endif
+
+OBJTOKEN := obj.$(ARCH)
+
+#
+#  Under most circumstances, paths are simple
+#
+
+ifeq ($(POSTROOT),..)
+	MODPATH := .
+	OBJDIR := $(OBJTOKEN)
+else
+	MODPATH := $(POSTROOT)/$(MODULENAME)
+	OBJDIR := $(MODPATH)/$(OBJTOKEN)
+endif
+
+
+#
+# Directories
+#
+
+MODLIBS := \
+	-L$(OBJDIR) \
+	$(foreach dir, $(MODULES), -L$(POSTROOT)/$(dir)/$(OBJTOKEN))
+MODINCLUDES := \
+	-I$(MODPATH)/include \
+	$(foreach dir, $(MODULES), -I$(POSTROOT)/$(dir)/include)
+ALLTARGETS := \
+	$(foreach targ, $(TARGETS), $(OBJDIR)/$(targ))
+VPATH := \
+	$(MODPATH)/include \
+	$(foreach dir, $(SRCDIRS), $(MODPATH)/$(dir)) \
+	$(foreach dir, $(MODULES), $(POSTROOT)/$(dir)/include)
+
+
+#### include paths
+
+LIBPATH := $(MODLIBS)
+INCLUDEPATH += -I. -Iinclude $(MODINCLUDES)
+KCFLAGS = -O2 \
+ -Wall -Werror -Wstrict-prototypes \
+ -fno-strict-aliasing -fomit-frame-pointer \
+ -DMODULE -D__KERNEL__ 
+
+CFLAGS :=  -fPIC -Wall -O2 -g
+CCFLAGS := -Werror
+CPPFLAGS := -ftemplate-depth-30
+
+#### Architecture deps
+
+KERNEL_INCLUDE := $(KERNEL_HOME)/include
+BINSTRIP := strip
+
+KCFLAGS += $(INCLUDEPATH)
+CFLAGS += $(INCLUDEPATH) $(LIBPATH)
+
+CCFLAGS += $(CFLAGS)
+CPPFLAGS += $(CFLAGS)
+
+#
+#  targets
+# 
+
+default:	$(ALLTARGETS)
+
+####################################################
+
+
+#
+#  Dependency generation
+#
+
+
+# Get list of all source files
+SOURCES := \
+	$(notdir $(wildcard \
+		$(foreach dir, $(SRCDIRS), \
+			$(foreach ext, $(SRCEXTENSIONS), $(dir)/*.$(ext)))))
+
+# Convert all .c, .cpp, .C to .d
+SRC_AND_DEPENDS := $(foreach ext, $(SRCEXTENSIONS),\
+	$(patsubst %.$(ext),%.d,$(SOURCES)))
+
+DEPENDS := $(foreach file, $(filter %.d,$(SRC_AND_DEPENDS)), $(OBJDIR)/$(file))
+
+
+
+BASE = $(subst /,\/,$*)
+ODIR = $(subst /,\/,$(OBJDIR))
+
+# This magic is from the 'make' manual (with mods by jelson)
+$(OBJDIR)/%.d: %.c 
+	@mkdir -p $(OBJDIR)
+	set -e; $(CC) -MM -I$(KERNEL_INCLUDE) $(CFLAGS) $< \
+	| sed 's/\($(BASE)\)\.o[ :]*/$(ODIR)\/$(BASE).o $(ODIR)\/$(BASE).d : /g' > $@; \
+	[ -s $@ ] || rm -f $@
+
+$(OBJDIR)/%.d: %.C 
+	@mkdir -p $(OBJDIR)
+	set -e; $(CC) -MM $(CPPFLAGS) $< \
+	| sed 's/\($(BASE)\)\.o[ :]*/$(ODIR)\/$(BASE).o $(ODIR)\/$(BASE).d : /g' > $@; \
+	[ -s $@ ] || rm -f $@
+
+$(OBJDIR)/%.d: %.cpp 
+	@mkdir -p $(OBJDIR)
+	set -e; $(CC) -MM $(CPPFLAGS) $< \
+	| sed 's/\($(BASE)\)\.o[ :]*/$(ODIR)\/$(BASE).o $(ODIR)\/$(BASE).d : /g' > $@; \
+	[ -s $@ ] || rm -f $@
+
+
+#
+# Rules
+#
+
+$(OBJDIR)/%.o: %.cpp
+	$(CPP) $(CPPFLAGS) $< -c -o $@
+
+$(OBJDIR)/%.o: %.C
+	$(CPP) $(CPPFLAGS) $< -c -o $@
+
+$(OBJDIR)/%.o: %.c
+	$(CC) $(CCFLAGS) $< -c -o $@
+
+clean:
+	rm -f $(ALLTARGETS) $(OBJDIR)/*.[oa] $(OBJDIR)/*.so.* $(DEPENDS)
+
+
+include $(DEPENDS)



More information about the commits mailing list