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:
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;
|
||||
}
|
||||
Reference in New Issue
Block a user