Place personal working copy of fusd into revision control

git-svn-id: http://svn.xiph.org/trunk/fusd@12312 0101bb08-14d6-0310-b084-bc0e0c8e3800
This commit is contained in:
xiphmont 2007-01-13 08:39:56 +00:00
commit 0f5535a756
32 changed files with 14086 additions and 0 deletions

98
ChangeLog Normal file
View File

@ -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.

35
LICENSE Normal file
View File

@ -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.

38
Makefile Normal file
View File

@ -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

139
README Normal file
View File

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

7
doc/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
*.out
*.log
*.aux
*.dvi
*.toc
*.lop
*.[ch].example

24
doc/Makefile Normal file
View File

@ -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

BIN
doc/fusd-html-docs.tar.gz Normal file

Binary file not shown.

BIN
doc/fusd.pdf Normal file

Binary file not shown.

3951
doc/fusd.ps Normal file

File diff suppressed because it is too large Load Diff

2013
doc/fusd.tex Normal file

File diff suppressed because it is too large Load Diff

1158
doc/html.sty Normal file

File diff suppressed because it is too large Load Diff

50
doc/make-examples.pl Executable file
View File

@ -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);
}

99
examples/console-read.c Normal file
View File

@ -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;
}

117
examples/drums.c Normal file
View File

@ -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 */

144
examples/drums2.c Normal file
View File

@ -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;
}

200
examples/drums3.c Normal file
View File

@ -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 */
}

124
examples/echo.c Normal file
View File

@ -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;
}

BIN
examples/helloworld Executable file

Binary file not shown.

91
examples/helloworld.c Normal file
View File

@ -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 */

292
examples/ioctl.c Normal file
View File

@ -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;
}

455
examples/logring.c Normal file
View File

@ -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;
}

386
examples/pager.c Normal file
View File

@ -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;
}

113
examples/uid-filter.c Normal file
View File

@ -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;
}

285
include/fusd.h Executable file
View File

@ -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__ */

151
include/fusd_msg.h Executable file
View File

@ -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__ */

288
include/kfusd.h Executable file
View File

@ -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__ */

19
kfusd/Makefile Executable file
View File

@ -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

0
kfusd/Module.symvers Normal file
View File

2943
kfusd/kfusd.c Executable file

File diff suppressed because it is too large Load Diff

30
libfusd/Makefile Executable file
View File

@ -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

687
libfusd/libfusd.c Executable file
View File

@ -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

149
make.include Normal file
View File

@ -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)