[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