commit 0f5535a756241dd697c135d13ae6b25e2fe519e7 Author: xiphmont Date: Sat Jan 13 08:39:56 2007 +0000 Place personal working copy of fusd into revision control git-svn-id: http://svn.xiph.org/trunk/fusd@12312 0101bb08-14d6-0310-b084-bc0e0c8e3800 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b5e7a63 --- /dev/null +++ b/ChangeLog @@ -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. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6affe25 --- /dev/null +++ b/LICENSE @@ -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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..671483a --- /dev/null +++ b/Makefile @@ -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 diff --git a/README b/README new file mode 100644 index 0000000..b4edb5c --- /dev/null +++ b/README @@ -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 + August 19, 2003 + + updated, + Monty + January 11, 2007 diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..a91ce05 --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,7 @@ +*.out +*.log +*.aux +*.dvi +*.toc +*.lop +*.[ch].example diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..be12586 --- /dev/null +++ b/doc/Makefile @@ -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 + diff --git a/doc/fusd-html-docs.tar.gz b/doc/fusd-html-docs.tar.gz new file mode 100644 index 0000000..65a4d50 Binary files /dev/null and b/doc/fusd-html-docs.tar.gz differ diff --git a/doc/fusd.pdf b/doc/fusd.pdf new file mode 100644 index 0000000..32b21bf Binary files /dev/null and b/doc/fusd.pdf differ diff --git a/doc/fusd.ps b/doc/fusd.ps new file mode 100644 index 0000000..8812382 --- /dev/null +++ b/doc/fusd.ps @@ -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) +@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@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()2524 7604 y(#include)g()2524 8932 +y(#include)g()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@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()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 diff --git a/doc/fusd.tex b/doc/fusd.tex new file mode 100644 index 0000000..63e5b7e --- /dev/null +++ b/doc/fusd.tex @@ -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@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@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@circlemud.org)}{mailto:jelson@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 }, +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} + diff --git a/doc/html.sty b/doc/html.sty new file mode 100644 index 0000000..65bd03e --- /dev/null +++ b/doc/html.sty @@ -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 +\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@new=\newcommand + \else + \ifx\pdfoutput\relax \let\pdfunknown\relax + \RequirePackage{hyperref}\let\html@new=\renewcommand + \else + \RequirePackage{hyperref}\let\html@new=\newcommand + \fi + \fi +\else + \let\html@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@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@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@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[]{}{}{<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@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@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@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@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@next to prevent clashes with other sty files +% (mike@emn.fr) +% Changed \html@next to \htmlnext so the \makeatletter and +% \makeatother commands could be removed (they were causing other +% style files - changebar.sty - to crash) (nikos@cbl.leeds.ac.uk) +% Changed \htmlnext back to \html@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@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@next\endgroup + \else \csarg\ifx{LaLaEnd\CurrentComment Test}\test + \edef\html@next{\endgroup\noexpand\end{\CurrentComment}} + \else \csarg\ifx{LaInnEnd\CurrentComment Test}\test + \edef\html@next{\endgroup\noexpand\end{\CurrentComment}} + \else \let\html@next\xhtmlComment + \fi \fi \fi \html@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@catcodes{\catcode`\%=12 \catcode`\{=12 \catcode`\}=12 + \catcode`\&=12 \catcode`\#=12 } +\newcommand\@gobbleenv{\bgroup\raw@catcodes + \let\reserved@a\@currenvir\@gobble@nv} +\bgroup + \def\expansionhead{\gdef\@gobble@nv@i##1} + \def\expansiontail{{\def\reserved@b{##1}\@gobble@nv@ii}} + \def\expansionheadii{\long\gdef\@gobble@nv##1\end} + \def\expansiontailii{{\@gobble@nv@i}} + \def\expansionmidii{##2} + \raw@catcodes\relax + \expandafter\expansionhead\expandafter}\expansiontail +\egroup +\long\gdef\@gobble@nv#1\end#2{\@gobble@nv@i} +%\long\def\@gobble@nv#1\end#2{\def\reserved@b{#2}% +\def\@gobble@nv@ii{% + \ifx\reserved@a\reserved@b + \edef\reserved@a{\egroup\noexpand\end{\reserved@a}}% + \expandafter\reserved@a + \else + \expandafter\@gobble@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@compatibility\undefined + %LaTeX209 + \makeatother\relax\expandafter\endinput +\fi +\if@compatibility + %LaTeX2e in LaTeX209 compatibility mode + \makeatother\relax\expandafter\endinput +\fi + +%\let\real@TeXlogo = \TeX +%\DeclareRobustCommand{\TeX}{\relax\real@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@edp.Arco.com> (with help from +% Michel Goossens <goossens@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@begin=\begin +\renewcommand{\begin}[1][]{\realLaTeX@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@part}{\newcounter{part}}{}% +\newcounter{lpart} +\newcounter{lchapter}[part] +\@ifundefined{c@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@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@part}{\bgroup\def\tmp{}\H@part}}\fi +\newcommand{\H@part}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hpart\tmp} + +\ifx\Hchapter\relax\else + \def\chapter{\resetsections \@ifstar{\stepcounter{lchapter}% + \bgroup\def\tmp{*}\H@chapter}{\bgroup\def\tmp{}\H@chapter}}\fi +\newcommand{\H@chapter}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hchapter\tmp} + +\renewcommand{\section}{\resetsubsections + \@ifstar{\stepcounter{lsection}\bgroup\def\tmp{*}% + \H@section}{\bgroup\def\tmp{}\H@section}} +\newcommand{\H@section}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsection\tmp} + +\renewcommand{\subsection}{\resetsubsubsections + \@ifstar{\stepcounter{lsubsection}\bgroup\def\tmp{*}% + \H@subsection}{\bgroup\def\tmp{}\H@subsection}} +\newcommand{\H@subsection}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsection\tmp} + +\renewcommand{\subsubsection}{\resetparagraphs + \@ifstar{\stepcounter{lsubsubsection}\bgroup\def\tmp{*}% + \H@subsubsection}{\bgroup\def\tmp{}\H@subsubsection}} +\newcommand{\H@subsubsection}[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsubsection\tmp} + +\renewcommand{\paragraph}{\resetsubparagraphs + \@ifstar{\stepcounter{lparagraph}\bgroup\def\tmp{*}% + \H@paragraph}{\bgroup\def\tmp{}\H@paragraph}} +\newcommand\H@paragraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hparagraph\tmp} + +\ifx\Hsubparagraph\relax\else\@ifundefined{subparagraph}{}{% +\renewcommand{\subparagraph}{\resetsubsubparagraphs + \@ifstar{\stepcounter{lsubparagraph}\bgroup\def\tmp{*}% + \H@subparagraph}{\bgroup\def\tmp{}\H@subparagraph}}}\fi +\newcommand\H@subparagraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubparagraph\tmp} + +\ifx\Hsubsubparagraph\relax\else\@ifundefined{subsubparagraph}{}{% +\def\subsubparagraph{% + \@ifstar{\stepcounter{lsubsubparagraph}\bgroup\def\tmp{*}% + \H@subsubparagraph}{\bgroup\def\tmp{}\H@subsubparagraph}}}\fi +\newcommand\H@subsubparagraph[1][]{\def\tmp@a{#1}\check@align + \expandafter\egroup\expandafter\Hsubsubparagraph\tmp} + +\def\check@align{\def\empty{}\ifx\tmp@a\empty + \else\def\tmp@b{center}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\def\tmp@b{left}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\def\tmp@b{right}\ifx\tmp@a\tmp@b\let\tmp@a\empty + \else\expandafter\def\expandafter\tmp@a\expandafter{\expandafter[\tmp@a]}% + \fi\fi\fi \def\empty{}\ifx\tmp\empty\let\tmp=\tmp@a \else + \expandafter\def\expandafter\tmp\expandafter{\expandafter*\tmp@a}% + \fi\fi} +% +\def\resetsections{\setcounter{section}{0}\setcounter{lsection}{0}% + \reset@dependents{section}\resetsubsections } +\def\resetsubsections{\setcounter{subsection}{0}\setcounter{lsubsection}{0}% + \reset@dependents{subsection}\resetsubsubsections } +\def\resetsubsubsections{\setcounter{subsubsection}{0}\setcounter{lsubsubsection}{0}% + \reset@dependents{subsubsection}\resetparagraphs } +% +\def\resetparagraphs{\setcounter{lparagraph}{0}\setcounter{lparagraph}{0}% + \reset@dependents{paragraph}\resetsubparagraphs } +\def\resetsubparagraphs{\ifx\c@subparagraph\undefined\else + \setcounter{subparagraph}{0}\fi \setcounter{lsubparagraph}{0}% + \reset@dependents{subparagraph}\resetsubsubparagraphs } +\def\resetsubsubparagraphs{\ifx\c@subsubparagraph\undefined\else + \setcounter{subsubparagraph}{0}\fi \setcounter{lsubsubparagraph}{0}} +% +\def\reset@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@pagecolor=\pagecolor + \let\pagecolor\segmentpagecolor + \let\segmentcolor\color + \ifx\current@page@color\undefined \def\current@page@color{{}}\fi + \def\dumpcurrentcolor{\bgroup\def\@empty@{{}}% + \expandafter\def\expandafter\tmp\space####1@{\def\thiscol{####1}}% + \ifx\current@color\@empty@\def\thiscol{}\else + \expandafter\tmp\current@color @\fi + \immediate\write\ptrfile{\noexpand\segmentcolor{\thiscol}}% + \ifx\current@page@color\@empty@\def\thiscol{}\else + \expandafter\tmp\current@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@page@color{\current@color}}\next}% + \ifx\tmp\@empty\real@pagecolor{#2}\def\model{}% + \else\real@pagecolor[#1]{#2}\def\model{[#1]}% + \fi + \edef\next{\egroup\def\noexpand\current@page@color{\current@page@color}% + \noexpand\real@pagecolor\model{#2}}% + \fi\next} +% +\newcommand{\segmentcolor}[2][named]{\@ifpackageloaded{color}% + {\loadsegmentcolors\segmentcolor[#1]{#2}}{}} + +\@ifpackageloaded{color}{\loadsegmentcolors}{\let\real@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@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 diff --git a/doc/make-examples.pl b/doc/make-examples.pl new file mode 100755 index 0000000..ea3d239 --- /dev/null +++ b/doc/make-examples.pl @@ -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); +} + diff --git a/examples/console-read.c b/examples/console-read.c new file mode 100644 index 0000000..3d595e9 --- /dev/null +++ b/examples/console-read.c @@ -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@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; +} + diff --git a/examples/drums.c b/examples/drums.c new file mode 100644 index 0000000..cdf3139 --- /dev/null +++ b/examples/drums.c @@ -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@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 */ diff --git a/examples/drums2.c b/examples/drums2.c new file mode 100644 index 0000000..62ccd46 --- /dev/null +++ b/examples/drums2.c @@ -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@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; +} + + + diff --git a/examples/drums3.c b/examples/drums3.c new file mode 100644 index 0000000..3361d69 --- /dev/null +++ b/examples/drums3.c @@ -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@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 */ +} + + + diff --git a/examples/echo.c b/examples/echo.c new file mode 100644 index 0000000..b367b7c --- /dev/null +++ b/examples/echo.c @@ -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@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; +} diff --git a/examples/helloworld b/examples/helloworld new file mode 100755 index 0000000..9a29ada Binary files /dev/null and b/examples/helloworld differ diff --git a/examples/helloworld.c b/examples/helloworld.c new file mode 100644 index 0000000..9fcb07b --- /dev/null +++ b/examples/helloworld.c @@ -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@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 */ diff --git a/examples/ioctl.c b/examples/ioctl.c new file mode 100644 index 0000000..fdadaf1 --- /dev/null +++ b/examples/ioctl.c @@ -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@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; +} diff --git a/examples/logring.c b/examples/logring.c new file mode 100644 index 0000000..31cafe3 --- /dev/null +++ b/examples/logring.c @@ -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@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; +} + diff --git a/examples/pager.c b/examples/pager.c new file mode 100644 index 0000000..6288e5a --- /dev/null +++ b/examples/pager.c @@ -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@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; +} + diff --git a/examples/uid-filter.c b/examples/uid-filter.c new file mode 100644 index 0000000..628688b --- /dev/null +++ b/examples/uid-filter.c @@ -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@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; +} diff --git a/include/fusd.h b/include/fusd.h new file mode 100755 index 0000000..a6f1ca0 --- /dev/null +++ b/include/fusd.h @@ -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__ */ diff --git a/include/fusd_msg.h b/include/fusd_msg.h new file mode 100755 index 0000000..9a7ebd7 --- /dev/null +++ b/include/fusd_msg.h @@ -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__ */ diff --git a/include/kfusd.h b/include/kfusd.h new file mode 100755 index 0000000..a3e8f05 --- /dev/null +++ b/include/kfusd.h @@ -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@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__ */ diff --git a/kfusd/Makefile b/kfusd/Makefile new file mode 100755 index 0000000..cf764b0 --- /dev/null +++ b/kfusd/Makefile @@ -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 diff --git a/kfusd/Module.symvers b/kfusd/Module.symvers new file mode 100644 index 0000000..e69de29 diff --git a/kfusd/kfusd.c b/kfusd/kfusd.c new file mode 100755 index 0000000..d736334 --- /dev/null +++ b/kfusd/kfusd.c @@ -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@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@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); diff --git a/libfusd/Makefile b/libfusd/Makefile new file mode 100755 index 0000000..c53d8b9 --- /dev/null +++ b/libfusd/Makefile @@ -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 + diff --git a/libfusd/libfusd.c b/libfusd/libfusd.c new file mode 100755 index 0000000..fc6da7b --- /dev/null +++ b/libfusd/libfusd.c @@ -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 diff --git a/make.include b/make.include new file mode 100644 index 0000000..3e59554 --- /dev/null +++ b/make.include @@ -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)