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:
commit
0f5535a756
98
ChangeLog
Normal file
98
ChangeLog
Normal 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
35
LICENSE
Normal 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
38
Makefile
Normal 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
139
README
Normal 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
7
doc/.cvsignore
Normal file
@ -0,0 +1,7 @@
|
||||
*.out
|
||||
*.log
|
||||
*.aux
|
||||
*.dvi
|
||||
*.toc
|
||||
*.lop
|
||||
*.[ch].example
|
||||
24
doc/Makefile
Normal file
24
doc/Makefile
Normal 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
BIN
doc/fusd-html-docs.tar.gz
Normal file
Binary file not shown.
BIN
doc/fusd.pdf
Normal file
BIN
doc/fusd.pdf
Normal file
Binary file not shown.
3951
doc/fusd.ps
Normal file
3951
doc/fusd.ps
Normal file
File diff suppressed because it is too large
Load Diff
2013
doc/fusd.tex
Normal file
2013
doc/fusd.tex
Normal file
File diff suppressed because it is too large
Load Diff
1158
doc/html.sty
Normal file
1158
doc/html.sty
Normal file
File diff suppressed because it is too large
Load Diff
50
doc/make-examples.pl
Executable file
50
doc/make-examples.pl
Executable 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
99
examples/console-read.c
Normal 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
117
examples/drums.c
Normal 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
144
examples/drums2.c
Normal 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
200
examples/drums3.c
Normal 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
124
examples/echo.c
Normal 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
BIN
examples/helloworld
Executable file
Binary file not shown.
91
examples/helloworld.c
Normal file
91
examples/helloworld.c
Normal 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
292
examples/ioctl.c
Normal 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
455
examples/logring.c
Normal 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
386
examples/pager.c
Normal 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
113
examples/uid-filter.c
Normal 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
285
include/fusd.h
Executable 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
151
include/fusd_msg.h
Executable 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
288
include/kfusd.h
Executable 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
19
kfusd/Makefile
Executable 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
0
kfusd/Module.symvers
Normal file
2943
kfusd/kfusd.c
Executable file
2943
kfusd/kfusd.c
Executable file
File diff suppressed because it is too large
Load Diff
30
libfusd/Makefile
Executable file
30
libfusd/Makefile
Executable 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
687
libfusd/libfusd.c
Executable 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
149
make.include
Normal 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)
|
||||
Loading…
x
Reference in New Issue
Block a user