summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiles Bader <miles@gnu.org>1995-04-05 00:54:58 +0000
committerMiles Bader <miles@gnu.org>1995-04-05 00:54:58 +0000
commit28630f6da0ab09f1a87852b7221f7b99bc2ef21c (patch)
treebbec9dc8f3c59b5dba5f7f9ef48a524d916923b7
parent5360e84d14494a6bc105edca255dcd3afe327848 (diff)
Initial revision
-rw-r--r--devio/dev.c454
-rw-r--r--devio/dev.h138
-rw-r--r--devio/devio.c451
-rw-r--r--devio/devpager.c138
-rw-r--r--devio/io.c358
-rw-r--r--devio/iostate.c76
-rw-r--r--devio/iostate.h67
-rw-r--r--devio/mem.c57
-rw-r--r--devio/mem.h33
-rw-r--r--devio/open.c90
-rw-r--r--devio/open.h67
-rw-r--r--devio/ptypes.h23
-rw-r--r--devio/rdwr.c473
-rw-r--r--devio/window.c200
-rw-r--r--devio/window.h82
15 files changed, 2707 insertions, 0 deletions
diff --git a/devio/dev.c b/devio/dev.c
new file mode 100644
index 00000000..a2eed5c0
--- /dev/null
+++ b/devio/dev.c
@@ -0,0 +1,454 @@
+/* Mach devices.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <device/device.h>
+#include <assert.h>
+#include <hurd/pager.h>
+
+#include "dev.h"
+#include "iostate.h"
+
+#ifdef MSG
+#include <ctype.h>
+#endif
+#ifdef FAKE
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#endif
+
+/* ---------------------------------------------------------------- */
+
+/* Returns a pointer to a new device structure in DEV for the kernel device
+ NAME, with the given FLAGS. If BLOCK_SIZE is non-zero, it should be the
+ desired block size, and must be a multiple of the device block size.
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t
+dev_open(char *name, int flags, int block_size, struct dev **dev)
+{
+ error_t err = 0;
+ static mach_port_t device_master = MACH_PORT_NULL;
+
+ *dev = malloc(sizeof (struct dev));
+
+ if (!(flags & DEV_SERIAL))
+ flags |= DEV_SEEKABLE;
+
+ if (*dev == NULL)
+ return ENOMEM;
+
+ (*dev)->port = MACH_PORT_NULL;
+
+ if (device_master == MACH_PORT_NULL)
+ err = get_privileged_ports(NULL, &device_master);
+
+#ifdef FAKE
+ (*dev)->port = (mach_port_t)
+ open(name, ((flags & DEV_READONLY) ? O_RDONLY : O_RDWR));
+ err = ((int)(*dev)->port == -1 ? errno : 0);
+ if (!err)
+ {
+ struct stat stat;
+ if (fstat((int)(*dev)->port, &stat) == 0)
+ {
+ (*dev)->size = stat.st_size;
+ (*dev)->dev_block_size = stat.st_blksize;
+ }
+ else
+ err = errno;
+ }
+#else
+ if (!err)
+ err = device_open(device_master,
+ D_READ | (flags & DEV_READONLY ? D_WRITE : 0),
+ name, &(*dev)->port);
+
+ if (!err)
+ {
+ int count = DEV_GET_SIZE_COUNT;
+ int sizes[DEV_GET_SIZE_COUNT];
+
+ /* Get info about the device: total size (in bytes) and block size (in
+ bytes). Block size is unit in which device addressing is done. */
+ err = device_get_status((*dev)->port, DEV_GET_SIZE, sizes, &count);
+ if (!err)
+ {
+ (*dev)->size = sizes[DEV_GET_SIZE_DEVICE_SIZE];
+ (*dev)->dev_block_size = sizes[DEV_GET_SIZE_RECORD_SIZE];
+ }
+ }
+#endif
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "dev_open(`%s', 0x%x, %d) => %s\n",
+ name, flags, block_size, err ? strerror(err) : "OK");
+ if (!err)
+ fprintf(debug, " size = %d, dev_block_size = %d\n",
+ (*dev)->size, (*dev)->dev_block_size);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ if (!err)
+ {
+ if (block_size > 0)
+ if (block_size > (*dev)->dev_block_size
+ && block_size % (*dev)->dev_block_size == 0)
+ (*dev)->block_size = block_size;
+ else
+ err = EINVAL;
+ else
+ (*dev)->block_size = (*dev)->dev_block_size;
+
+ if ((*dev)->dev_block_size == 1)
+ flags |= DEV_SERIAL;
+
+ (*dev)->flags = flags;
+ (*dev)->owner = 0;
+ }
+
+ if (!err)
+ err = io_state_init(&(*dev)->io_state, *dev);
+
+ if (err)
+ {
+ if ((*dev)->port != MACH_PORT_NULL)
+ mach_port_deallocate(mach_task_self(), (*dev)->port);
+ free(*dev);
+ }
+
+ return err;
+}
+
+/* Free DEV and any resources it consumes. */
+void
+dev_close(struct dev *dev)
+{
+ if (!dev_is(dev, DEV_READONLY))
+ {
+ if (dev->pager != NULL)
+ pager_shutdown(dev->pager);
+ io_state_sync(&dev->io_state, dev);
+ }
+
+ device_close(dev->port);
+ io_state_finalize(&dev->io_state);
+
+ free(dev);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Try and write out any pending writes to DEV. If WAIT is true, will wait
+ for any paging activity to cease. */
+error_t
+dev_sync(struct dev *dev, int wait)
+{
+ error_t err = 0;
+
+ if (!dev_is(dev, DEV_READONLY))
+ {
+ struct io_state *ios = &dev->io_state;
+
+ io_state_lock(ios);
+
+ /* Sync any paged backing store. */
+ if (dev->pager != NULL)
+ pager_sync(dev->pager, wait);
+
+ /* Write out any stuff buffered in our io_state. */
+ err = io_state_sync(ios, dev);
+
+ io_state_unlock(ios);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+#ifdef MSG
+char *
+brep(vm_address_t buf, vm_size_t len)
+{
+ static char rep[200];
+ char *prep(char *buf, char *str, int len)
+ {
+ *buf++ = '`';
+ while (len-- > 0)
+ {
+ int ch = *str++;
+ if (isprint(ch) && ch != '\'' && ch != '\\')
+ *buf++ = ch;
+ else
+ {
+ *buf++ = '\\';
+ switch (ch)
+ {
+ case '\n': *buf++ = 'n'; break;
+ case '\t': *buf++ = 't'; break;
+ case '\b': *buf++ = 'b'; break;
+ case '\f': *buf++ = 'f'; break;
+ default: sprintf(buf, "%03o", ch); buf += 3; break;
+ }
+ }
+ }
+ *buf++ = '\'';
+ *buf = '\0';
+ return buf;
+ }
+
+ if (len < 40)
+ prep(rep, (char *)buf, (int)len);
+ else
+ {
+ char *end = prep(rep, (char *)buf, 20);
+ *end++ = '.'; *end++ = '.'; *end++ = '.';
+ prep(end, (char *)buf + len - 20, 20);
+ }
+
+ return rep;
+}
+#endif
+
+/* Writes AMOUNT bytes from the buffer pointed to by BUF to the device DEV.
+ *OFFS is incremented to reflect the amount read/written. Both AMOUNT and
+ *OFFS must be multiples of DEV's block size, and either BUF must be
+ page-aligned, or dev_write_valid() must return true for these arguments.
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t
+dev_write(struct dev *dev,
+ vm_address_t buf, vm_size_t amount, vm_offset_t *offs)
+{
+ int bsize = dev->dev_block_size;
+ vm_offset_t written = 0;
+ vm_offset_t block = (bsize == 1 ? *offs : *offs / bsize);
+ error_t err;
+
+ assert(dev_write_valid(dev, buf, amount, *offs));
+ assert(*offs % bsize == 0);
+ assert(amount % bsize == 0);
+
+ if (amount < IO_INBAND_MAX)
+ {
+#ifdef FAKE
+ if (*offs != dev->io_state.location)
+ {
+ err = lseek((int)dev->port, SEEK_SET, *offs);
+ if (err == -1)
+ err = errno;
+ else
+ dev->io_state.location = *offs;
+ }
+ written = write((int)dev->port, (char *)buf, amount);
+ err = (written < 1 ? errno : 0);
+ if (!err)
+ dev->io_state.location += written;
+#else
+ err =
+ device_write_inband(dev->port, 0, block,
+ (io_buf_ptr_t)buf, amount, &written);
+#endif
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_write_inband(%d, %s, %d) => %s, %d\n",
+ block, brep(buf, amount), amount, err ? strerror(err) : "OK", written);
+ fprintf(debug, " offset = %d => %d\n", *offs, *offs + written);
+#endif
+ mutex_unlock(&debug_lock);
+ }
+ }
+ else
+ {
+#ifdef FAKE
+ if (*offs != dev->io_state.location)
+ {
+ err = lseek((int)dev->port, SEEK_SET, *offs);
+ if (err == -1)
+ err = errno;
+ else
+ dev->io_state.location = *offs;
+ }
+ written = write((int)dev->port, (char *)buf, amount);
+ err = (written < 1 ? errno : 0);
+ if (!err)
+ dev->io_state.location += written;
+#else
+ err =
+ device_write(dev->port, 0, block, (io_buf_ptr_t)buf, amount, &written);
+#endif
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_write(%d, %s, %d) => %s, %d\n",
+ block, brep(buf, amount), amount,
+ err ? strerror(err) : "OK", written);
+ fprintf(debug, " offset = %d => %d\n", *offs, *offs + written);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ }
+
+ if (!err)
+ *offs += written;
+
+ return err;
+}
+
+/* Reads AMOUNT bytes from DEV and returns it in BUF and BUF_LEN (using the
+ standard mach out-array convention). *OFFS is incremented to reflect the
+ amount read/written. Both LEN and *OFFS must be multiples of DEV's block
+ size. If an error occurs, the error code is returned, otherwise 0. */
+error_t
+dev_read(struct dev *dev,
+ vm_address_t *buf, vm_size_t *buf_len, vm_size_t amount,
+ vm_offset_t *offs)
+{
+ error_t err = 0;
+ int bsize = dev->dev_block_size;
+ vm_offset_t read = 0;
+ vm_offset_t block = (bsize == 1 ? *offs : *offs / bsize);
+
+ assert(*offs % bsize == 0);
+ assert(amount % bsize == 0);
+
+ if (amount < IO_INBAND_MAX)
+ {
+ if (*buf_len < amount)
+ err = vm_allocate(mach_task_self(), buf, amount, 1);
+#ifdef FAKE
+ if (*offs != dev->io_state.location)
+ {
+ err = lseek((int)dev->port, SEEK_SET, *offs);
+ if (err == -1)
+ err = errno;
+ else
+ dev->io_state.location = *offs;
+ }
+ err = vm_allocate(mach_task_self(), buf, amount, 1);
+ if (!err)
+ {
+ read = __read((int)dev->port, (char *)*buf, amount);
+ err = (read == -1 ? errno : 0);
+ if (!err)
+ dev->io_state.location += read;
+ }
+#else
+ if (!err)
+ err =
+ device_read_inband(dev->port, 0, block,
+ amount, (io_buf_ptr_t)*buf, &read);
+#endif
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_read_inband(%d, %d) => %s, %s, %d\n",
+ block, amount,
+ err ? strerror(err) : "OK", err ? "-" : brep(*buf, read),
+ read);
+ if (!err)
+ fprintf(debug, " offset = %d => %d\n", *offs, *offs + read);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ }
+ else
+ {
+#ifdef FAKE
+ if (*offs != dev->io_state.location)
+ {
+ err = lseek((int)dev->port, SEEK_SET, *offs);
+ if (err == -1)
+ err = errno;
+ else
+ dev->io_state.location = *offs;
+ }
+ err = vm_allocate(mach_task_self(), buf, amount, 1);
+ if (!err)
+ {
+ read = __read((int)dev->port, (char *)*buf, amount);
+ err = (read == -1 ? errno : 0);
+ if (!err)
+ dev->io_state.location += read;
+ }
+#else
+ err =
+ device_read(dev->port, 0, block, amount, (io_buf_ptr_t *)buf, &read);
+#endif
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_read(%d, %d) => %s, %s, %d\n",
+ block, amount,
+ err ? strerror(err) : "OK", err ? "-" : brep(*buf, read),
+ read);
+ if (!err)
+ fprintf(debug, " offset = %d => %d\n", *offs, *offs + read);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ }
+
+ if (!err)
+ {
+ *offs += read;
+ *buf_len = read;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Returns in MEMOBJ the port for a memory object backed by the storage on
+ DEV. Returns 0 or the error code if an error occurred. */
+error_t
+dev_get_memory_object(struct dev *dev, memory_object_t *memobj)
+{
+ if (dev_is(dev, DEV_SERIAL))
+ return ENODEV;
+
+ io_state_lock(&dev->io_state);
+ if (dev->pager == NULL)
+ dev->pager =
+ pager_create((struct user_pager_info *)dev, 1, MEMORY_OBJECT_COPY_DELAY);
+ io_state_unlock(&dev->io_state);
+
+ if (dev->pager == NULL)
+ return ENODEV; /* XXX ??? */
+
+ *memobj = pager_get_port(dev->pager);
+ if (*memobj != MACH_PORT_NULL)
+ return
+ mach_port_insert_right(mach_task_self(),
+ *memobj, *memobj,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ return 0;
+}
diff --git a/devio/dev.h b/devio/dev.h
new file mode 100644
index 00000000..7244c91e
--- /dev/null
+++ b/devio/dev.h
@@ -0,0 +1,138 @@
+/* A handle on a mach device.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __DEV_H__
+#define __DEV_H__
+
+#include <mach.h>
+#include <device/device.h>
+
+#include "iostate.h"
+
+/* #define FAKE */
+#define MSG
+
+#ifdef MSG
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+extern FILE *debug;
+extern struct mutex debug_lock;
+#endif
+
+/* ---------------------------------------------------------------- */
+
+/* Information about a kernel device. */
+struct dev
+{
+ /* The device port for the kernel device we're doing paging on. */
+ device_t port;
+
+ /* The total size of DEV. */
+ vm_size_t size;
+
+ /* The block size of DEV. I/O to DEV must occur in multiples of
+ block_size. */
+ int dev_block_size;
+ /* The block size in which we will do I/O; this must be a multiple of
+ DEV_BLOCK_SIZE. */
+ int block_size;
+
+ /* Various attributes of this device (see below for the DEV_ flag bits).
+ This field is constant. */
+ int flags;
+
+ /* Current state of our output stream -- location and the buffer used to do
+ buffered i/o. */
+ struct io_state io_state;
+
+ /* The pager we're using to do disk i/o for us. If NULL, a pager hasn't
+ been allocated yet. Lock the lock in IO_STATE if you want to update
+ this field. */
+ struct pager *pager;
+
+ /* The current owner of the open device. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all objects
+ this affects old-style async IO. Negative values represent pgrps. This
+ has nothing to do with the owner of a file (as returned by io_stat, and
+ as used for various permission checks by filesystems). An owner of 0
+ indicates that there is no owner. */
+ pid_t owner;
+};
+
+/* Various bits to be set in the flags field. */
+
+/* True if this device should be used in `block' mode, with buffering of
+ sub-block-size i/o. */
+#define DEV_BUFFERED 0x1
+/* True if this device only supports serial i/o (that is, there's only one
+ read/write location, which must explicitly be moved to do i/o elsewhere.*/
+#define DEV_SERIAL 0x2
+/* True if we can change the current i/o location of a serial device. */
+#define DEV_SEEKABLE 0x4
+/* True if a device cannot be written on. */
+#define DEV_READONLY 0x8
+
+/* Returns TRUE if any of the flags in BITS are set for DEV. */
+#define dev_is(dev, bits) ((dev)->flags & (bits))
+
+/* Returns true if it's ok to call dev_write on these arguments, without
+ first copying BUF to a page-aligned buffer. */
+#define dev_write_valid(dev, buf, len, offs) \
+ ((len) <= IO_INBAND_MAX || (buf) % vm_page_size == 0)
+
+/* Returns a pointer to a new device structure in DEV for the kernel device
+ NAME, with the given FLAGS. If BLOCK_SIZE is non-zero, it should be the
+ desired block size, and must be a multiple of the device block size.
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t dev_open(char *name, int flags, int block_size, struct dev **dev);
+
+/* Free DEV and any resources it consumes. */
+void dev_close(struct dev *dev);
+
+/* Reads AMOUNT bytes from DEV and returns it in BUF and BUF_LEN (using the
+ standard mach out-array convention). *OFFS is incremented to reflect the
+ amount read/written. Both LEN and *OFFS must be multiples of DEV's block
+ size. If an error occurs, the error code is returned, otherwise 0. */
+error_t dev_read(struct dev *dev,
+ vm_address_t *buf, vm_size_t *buf_len, vm_size_t amount,
+ vm_offset_t *offs);
+
+/* Writes AMOUNT bytes from the buffer pointed to by BUF to the device DEV.
+ *OFFS is incremented to reflect the amount read/written. Both AMOUNT and
+ *OFFS must be multiples of DEV's block size, and either BUF must be
+ page-aligned, or dev_write_valid() must return true for these arguments.
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t dev_write(struct dev *dev,
+ vm_address_t buf, vm_size_t amount, vm_offset_t *offs);
+
+/* Returns in MEMOBJ the port for a memory object backed by the storage on
+ DEV. Returns 0 or the error code if an error occurred. */
+error_t dev_get_memory_object(struct dev *dev, memory_object_t *memobj);
+
+/* Try and write out any pending writes to DEV. If WAIT is true, will wait
+ for any paging activity to cease. */
+error_t dev_sync(struct dev *dev, int wait);
+
+#ifdef MSG
+char *brep(vm_address_t buf, vm_size_t len);
+#endif
+
+#endif /* !__DEV_H__ */
diff --git a/devio/devio.c b/devio/devio.c
new file mode 100644
index 00000000..05343b7e
--- /dev/null
+++ b/devio/devio.c
@@ -0,0 +1,451 @@
+/* A translator for doing I/O to mach kernel devices.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/pager.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+
+#include <stdio.h>
+#include <error.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+
+#include "open.h"
+#include "dev.h"
+#include "ptypes.h"
+
+/* ---------------------------------------------------------------- */
+
+#define USAGE "Usage: %s [OPTION...] DEVICE\n"
+
+static void
+usage(int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ program_invocation_name);
+ else
+ {
+ printf(USAGE, program_invocation_name);
+ printf("\
+\n\
+ -d, --devnum=NUM Give DEVICE a device number NUM\n\
+ -r, --readonly Disable writing to DEVICE\n\
+ -p, --seekable Enable seeking if DEVICE is serial\n\
+ -s, --serial Indicate that DEVICE has a single R/W point\n\
+ -b, --buffered, --block Open DEVICE in `block' mode, which allows reads\n\
+ or writes less than a single block and buffers\n\
+ I/O to the actual device. By default, all reads\n\
+ and writes are made directly to the device,\n\
+ with no buffering, and any sub-block-size I/O\n\
+ is padded to the nearest full block.\n\
+ -B NUM, --block-size=NUM Use a block size of NUM, which must be an integer\n\
+ multiple of DEVICE's real block size\n\
+");
+ }
+
+ exit(status);
+}
+
+#define SHORT_OPTIONS "bB:d:D:?rpsu"
+
+static struct option options[] =
+{
+ {"block-size", required_argument, 0, 'B'},
+ {"debug", required_argument, 0, 'D'},
+ {"help", no_argument, 0, '?'},
+ {"devnum", required_argument, 0, 'm'},
+ {"block", no_argument, 0, 'b'},
+ {"buffered", no_argument, 0, 'b'},
+ {"readonly", no_argument, 0, 'r'},
+ {"seekable", no_argument, 0, 'p'},
+ {"serial", no_argument, 0, 's'},
+ {0, 0, 0, 0}
+};
+
+
+/* ---------------------------------------------------------------- */
+
+/* A struct dev for the open kernel device. */
+static struct dev *device = NULL;
+
+/* Desired device parameters specified by the user. */
+static char *device_name = NULL;
+static int device_flags = 0;
+static int device_block_size = 0;
+
+/* A unixy device number to return when the device is stat'd. */
+static int device_number = 0;
+
+/* A stream on which we can print debugging message. */
+FILE *debug = NULL;
+/* A lock to use while doing so. */
+struct mutex debug_lock;
+
+void main(int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ int opt;
+ struct trivfs_control *trivfs_control;
+ mach_port_t realnode, control;
+
+ while ((opt = getopt_long(argc, argv, SHORT_OPTIONS, options, 0)) != EOF)
+ switch (opt)
+ {
+ case 'r': device_flags |= DEV_READONLY; break;
+ case 's': device_flags |= DEV_SERIAL; break;
+ case 'b': device_flags |= DEV_BUFFERED; break;
+ case 'p': device_flags |= DEV_SEEKABLE; break;
+ case 'B': device_block_size = atoi(optarg); break;
+ case 'd': device_number = atoi(optarg); break;
+ case 'D': debug = fopen(optarg, "w+"); setlinebuf(debug); break;
+ case '?': usage(0);
+ default: usage(1);
+ }
+
+ mutex_init(&debug_lock);
+
+ if (device_flags & DEV_READONLY)
+ /* Catch illegal writes at the point of open. */
+ trivfs_allow_open &= ~O_WRITE;
+
+ if (argv[optind] == NULL || argv[optind + 1] != NULL)
+ {
+ fprintf(stderr, USAGE, program_invocation_name);
+ usage(1);
+ }
+
+ device_name = argv[optind];
+
+ _libports_initialize();
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(2, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ control = trivfs_handle_port (MACH_PORT_NULL, PT_FSYS, PT_NODE);
+ err = fsys_startup (bootstrap, control, MACH_MSG_TYPE_MAKE_SEND, &realnode);
+
+ /* Install the returned realnode for trivfs's use */
+ trivfs_control = ports_check_port_type (control, PT_FSYS);
+ assert (trivfs_control);
+ ports_change_hardsoft (trivfs_control, 1);
+ trivfs_control->underlying = realnode;
+ ports_done_with_port (trivfs_control);
+
+ /* Open the device only when necessary. */
+ device = NULL;
+
+ /* Launch. */
+ ports_manage_port_operations_multithread ();
+
+ exit(0);
+}
+
+/* Called whenever someone tries to open our node (even for a stat). We
+ delay opening the kernel device until this point, as we can usefully
+ return errors from here. */
+static error_t
+check_open_hook (struct trivfs_control *trivfs_control,
+ uid_t *uids, u_int nuids,
+ gid_t *gids, u_int ngids,
+ int flags)
+{
+ error_t err = 0;
+
+ if (device == NULL)
+ /* Try and open the device. */
+ {
+ err = dev_open(device_name, device_flags, device_block_size, &device);
+ if (err)
+ device = NULL;
+ if (err && (flags & (O_READ|O_WRITE)) == 0)
+ /* If we're not opening for read or write, then just ignore the
+ error, as this allows stat to word correctly. XXX */
+ err = 0;
+ }
+
+ return err;
+}
+
+static void
+open_hook(struct trivfs_peropen *peropen)
+{
+ if (device)
+ open_create(device, (struct open **)&peropen->hook);
+}
+
+static void
+close_hook(struct trivfs_peropen *peropen)
+{
+ if (peropen->hook)
+ open_free(peropen->hook);
+}
+
+static void
+clean_exit(int status)
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "cleaning up and exiting (status = %d)...\n", status);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device)
+ {
+ dev_close(device);
+ device = NULL;
+ }
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "Bye!\n");
+ fclose(debug);
+ debug = NULL;
+ mutex_unlock(&debug_lock);
+ }
+#endif
+}
+
+/* ---------------------------------------------------------------- */
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0; /* ??? */
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+int trivfs_protid_porttypes[] = {PT_NODE};
+int trivfs_cntl_porttypes[] = {PT_FSYS};
+int trivfs_protid_nporttypes = 1;
+int trivfs_cntl_nporttypes = 1;
+
+void
+trivfs_modify_stat (struct stat *st)
+{
+ if (device)
+ {
+ vm_size_t size = device->size;
+
+ if (device->block_size > 1)
+ st->st_blksize = device->block_size;
+
+ st->st_size = size;
+ st->st_blocks = size / 512;
+
+ if (dev_is(device, DEV_READONLY))
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= dev_is(device, DEV_BUFFERED) ? S_IFBLK : S_IFCHR;
+ }
+ else
+ /* Try and do things without an open device... */
+ {
+ st->st_blksize = device_block_size;
+ st->st_size = 0;
+ st->st_blocks = 0;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= (device_flags & DEV_BUFFERED) ? S_IFBLK : S_IFCHR;
+ if (device_flags & DEV_READONLY)
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ st->st_fstype = FSTYPE_DEV;
+ st->st_rdev = device_number;
+}
+
+error_t
+trivfs_goaway (int flags, mach_port_t realnode, int ctltype, int pitype)
+{
+ if (device != NULL && !(flags & FSYS_GOAWAY_FORCE))
+ /* By default, don't go away if there are still opens on this device. */
+ return EBUSY;
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "trivfs_goaway(0x%x, %d, %d, %d)\n",
+ flags, realnode, ctltype, pitype);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ if (flags & FSYS_GOAWAY_NOSYNC)
+ exit(0);
+ else
+ clean_exit(0);
+}
+
+/* If this variable is set, it is called every time an open happens.
+ UIDS, GIDS, and FLAGS are from the open; CNTL identifies the
+ node being opened. This call need not check permissions on the underlying
+ node. If the open call should block, then return EWOULDBLOCK. Other
+ errors are immediately reflected to the user. If O_NONBLOCK
+ is not set in FLAGS and EWOULDBLOCK is returned, then call
+ trivfs_complete_open when all pending open requests for this
+ file can complete. */
+error_t (*trivfs_check_open_hook)(struct trivfs_control *trivfs_control,
+ uid_t *uids, u_int nuids,
+ gid_t *gids, u_int ngids,
+ int flags)
+ = check_open_hook;
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+void (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Sync this filesystem. */
+kern_return_t
+trivfs_S_fsys_syncfs (struct trivfs_control *cntl,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int wait, int dochildren)
+{
+#ifdef MSG
+ if (debug && device)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "syncing filesystem...\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device)
+ return dev_sync(device, wait);
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Ports hooks */
+
+void (*ports_cleanroutines[])(void *) =
+{
+ [PT_FSYS] = trivfs_clean_cntl,
+ [PT_NODE] = trivfs_clean_protid,
+};
+
+int
+ports_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ error_t err;
+#ifdef MSG
+ static int next_msg_num = 0;
+ int msg_num;
+
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ msg_num = next_msg_num++;
+ fprintf(debug, "port_demuxer(%d) [%d]\n", inp->msgh_id, msg_num);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ err = pager_demuxer(inp, outp) || trivfs_demuxer(inp, outp);
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "port_demuxer(%d) [%d] done!\n", inp->msgh_id, msg_num);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ return err;
+}
+
+/* This will be called whenever there have been no requests to the server for
+ a significant period of time. NHARD is the number of live hard ports;
+ NSOFT is the number of live soft ports. This function is called while an
+ internal lock is held, so it cannot reliably call any other functions of
+ the ports library. */
+void
+ports_notice_idle (int nhard, int nsoft)
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_notice_idle(%d, %d)\n", nhard, nsoft);
+ mutex_unlock(&debug_lock);
+ }
+ else
+#endif
+ if (nhard == 0)
+ clean_exit(0);
+}
+
+/* This will be called whenever there are no hard ports or soft ports
+ allocated. This function is called while an internal lock is held, so it
+ cannot reliably call any other functions of the ports library. */
+void
+ports_no_live_ports ()
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_no_live_ports()\n");
+ mutex_unlock(&debug_lock);
+ }
+ else
+#endif
+ clean_exit(0);
+}
+
+/* This will be called whenever there are no hard ports allocated but there
+ are still some soft ports. This function is called while an internal lock
+ is held, so it cannot reliably call any other functions of the ports
+ library. */
+void
+ports_no_hard_ports ()
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_no_hard_ports()\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device != NULL)
+ {
+ dev_close(device);
+ device = NULL;
+ }
+}
diff --git a/devio/devpager.c b/devio/devpager.c
new file mode 100644
index 00000000..9ed5bd3e
--- /dev/null
+++ b/devio/devpager.c
@@ -0,0 +1,138 @@
+/* A pager interface for raw mach devices.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/pager.h>
+#include <device/device.h>
+#include <assert.h>
+
+#include "dev.h"
+#include "ptypes.h"
+
+/* ---------------------------------------------------------------- */
+/* Pager library callbacks; see <hurd/pager.h> for more info. */
+
+/* This will be the type used in calls to allocate_port by the pager system.
+ */
+int pager_port_type = PT_MEMOBJ;
+
+/* For pager PAGER, read one page from offset PAGE. Set *BUF to be the
+ address of the page, and set *WRITE_LOCK if the page must be provided
+ read-only. The only permissable error returns are EIO, EDQUOT, and
+ ENOSPC. */
+error_t
+pager_read_page(struct user_pager_info *upi,
+ vm_offset_t page, vm_address_t *buf, int *writelock)
+{
+ int read; /* bytes actually read */
+ struct dev *dev = (struct dev *)upi;
+ error_t err =
+ device_read(dev->port,
+ 0, page / dev->dev_block_size, vm_page_size,
+ (io_buf_ptr_t *)buf, &read);
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_read(%d, %d) [pager] => %s, %s, %d\n",
+ page / dev->dev_block_size, vm_page_size,
+ strerror(err), err ? "-" : brep(*buf, read), read);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ *writelock = (dev->flags & DEV_READONLY);
+
+ if (err || read < vm_page_size)
+ return EIO;
+ else
+ return 0;
+}
+
+/* For pager PAGER, synchronously write one page from BUF to offset PAGE. In
+ addition, vm_deallocate (or equivalent) BUF. The only permissable error
+ returns are EIO, EDQUOT, and ENOSPC. */
+error_t
+pager_write_page(struct user_pager_info *upi,
+ vm_offset_t page, vm_address_t buf)
+{
+ struct dev *dev = (struct dev *)upi;
+
+ if (dev->flags & DEV_READONLY)
+ return EROFS;
+ else
+ {
+ int written;
+ error_t err =
+ device_write(dev->port,
+ 0, page / dev->dev_block_size,
+ (io_buf_ptr_t)buf, vm_page_size,
+ &written);
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "device_write(%d, %s, %d) [pager] => %s, %d\n",
+ page / dev->dev_block_size,
+ brep(buf, vm_page_size), vm_page_size,
+ strerror(err), written);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ vm_deallocate(mach_task_self(), buf, vm_page_size);
+
+ if (err || written < vm_page_size)
+ return EIO;
+ else
+ return 0;
+ }
+}
+
+/* A page should be made writable. */
+error_t
+pager_unlock_page(struct user_pager_info *upi, vm_offset_t address)
+{
+ struct dev *dev = (struct dev *)upi;
+
+ if (dev->flags & DEV_READONLY)
+ return EROFS;
+ else
+ return 0;
+}
+
+/* The user must define this function. It should report back (in
+ *OFFSET and *SIZE the minimum valid address the pager will accept
+ and the size of the object. */
+error_t
+pager_report_extent(struct user_pager_info *upi,
+ vm_address_t *offset, vm_size_t *size)
+{
+ *offset = 0;
+ *size = ((struct dev *)upi)->size;
+ return 0;
+}
+
+/* This is called when a pager is being deallocated after all extant send
+ rights have been destroyed. */
+void
+pager_clear_user_data(struct user_pager_info *upi)
+{
+}
diff --git a/devio/io.c b/devio/io.c
new file mode 100644
index 00000000..855cf158
--- /dev/null
+++ b/devio/io.c
@@ -0,0 +1,358 @@
+/* Implements the hurd io interface to devio.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "open.h"
+#include "dev.h"
+#include "iostate.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+kern_return_t
+trivfs_S_io_map(struct trivfs_protid *cred,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ mach_port_t memobj;
+ struct open *open = (struct open *)cred->po->hook;
+ error_t err = dev_get_memory_object(open->dev, &memobj);
+
+ if (!err)
+ {
+ if (cred->po->openmodes & O_READ)
+ {
+ *rdobj = memobj;
+ *rdtype = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ else
+ *rdobj = MACH_PORT_NULL;
+
+ if (cred->po->openmodes & O_WRITE)
+ {
+ *wrobj = memobj;
+ *wrtype = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ else
+ *wrobj = MACH_PORT_NULL;
+ }
+
+ return err;
+ }
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+kern_return_t
+trivfs_S_io_read(struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ vm_address_t *data,
+ mach_msg_type_number_t *datalen,
+ off_t offs,
+ mach_msg_type_number_t amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+ else
+ return open_read((struct open *)cred->po->hook, data, datalen, amt, offs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ struct dev *dev = open->dev;
+ vm_offset_t location = open_get_io_state(open)->location;
+ *amount = dev->size - location;
+ return 0;
+ }
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+kern_return_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ error_t err = 0;
+ struct open *open = (struct open *)cred->po->hook;
+ struct io_state *io_state = open_get_io_state(open);
+
+ if (!dev_is(open->dev, DEV_SEEKABLE))
+ return ESPIPE;
+
+ io_state_lock(io_state);
+ switch(whence)
+ {
+ case SEEK_SET:
+ io_state->location = offset; break;
+ case SEEK_CUR:
+ io_state->location += offset; break;
+ case SEEK_END:
+ io_state->location = open->dev->size - offset; break;
+ default:
+ err = EINVAL;
+ }
+ *new_offset = io_state->location;
+ io_state_unlock(io_state);
+
+ return err;
+ }
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type, int *tag)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+ else
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ vm_address_t data, mach_msg_type_number_t datalen,
+ off_t offs, mach_msg_type_number_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+ else
+ return open_write((struct open *)cred->po->hook, data, datalen, amt, offs);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Truncate file. */
+kern_return_t
+trivfs_S_file_truncate (struct trivfs_protid *cred, off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (dev_is(((struct open *)cred->po->hook)->dev, DEV_SERIAL))
+ return 0;
+ else
+ return EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+kern_return_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ *owner = open->dev->owner;
+ return 0;
+ }
+}
+
+kern_return_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ struct open *open = (struct open *)cred->po->hook;
+ open->dev->owner = owner;
+ return 0;
+ }
+}
+
+
+/* ---------------------------------------------------------------- */
+/* File syncing operations; these all do the same thing, sync the underlying
+ device. */
+
+kern_return_t
+trivfs_S_file_sync (struct trivfs_protid *cred, int wait)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "syncing file...\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ return dev_sync(((struct open *)cred->po->hook)->dev, wait);
+ }
+}
+
+kern_return_t
+trivfs_S_file_syncfs (struct trivfs_protid *cred, int wait, int dochildren)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "syncing filesystem (through file)...\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ return dev_sync(((struct open *)cred->po->hook)->dev, wait);
+ }
+}
diff --git a/devio/iostate.c b/devio/iostate.c
new file mode 100644
index 00000000..2f6863bc
--- /dev/null
+++ b/devio/iostate.c
@@ -0,0 +1,76 @@
+/* State for an I/O stream.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+
+#include "iostate.h"
+#include "dev.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Initialize the io_state structure IOS to be used with the device DEV. If
+ a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t
+io_state_init(struct io_state *ios, struct dev *dev)
+{
+ error_t err =
+ vm_allocate(mach_task_self(),
+ (vm_address_t *)&ios->buffer, dev->block_size, 1);
+
+ ios->location = 0;
+ ios->buffer_size = dev->block_size;
+ ios->buffer_use = 0;
+ mutex_init(&ios->lock);
+
+ return err;
+}
+
+/* Frees all resources used by IOS. */
+void
+io_state_finalize(struct io_state *ios)
+{
+ vm_deallocate(mach_task_self(), (vm_address_t)ios->buffer, ios->buffer_size);
+}
+
+/* If IOS's location isn't block aligned because writes have been buffered
+ there, then sync the whole buffer out to the device. Any error that
+ occurs while writing is returned, otherwise 0. */
+error_t
+io_state_sync(struct io_state *ios, struct dev *dev)
+{
+ error_t err = 0;
+
+ if (ios->buffer_use == IO_STATE_BUFFERED_WRITE)
+ {
+ vm_offset_t pos = ios->location;
+ int block_offs = pos % dev->block_size;
+
+ if (block_offs > 0)
+ {
+ bzero((char *)ios->buffer + block_offs,
+ dev->block_size - block_offs);
+ ios->location -= block_offs;
+ err =
+ dev_write(dev, ios->buffer, dev->block_size, &ios->location);
+ }
+ }
+
+ return err;
+}
diff --git a/devio/iostate.h b/devio/iostate.h
new file mode 100644
index 00000000..1795e153
--- /dev/null
+++ b/devio/iostate.h
@@ -0,0 +1,67 @@
+/* State for an I/O stream.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __IOSTATE_H__
+#define __IOSTATE_H__
+
+/* ---------------------------------------------------------------- */
+
+enum io_state_buffer_use
+{
+ /* 0 means nothing */
+ IO_STATE_BUFFERED_WRITE = 1, IO_STATE_BUFFERED_READ = 2
+};
+
+struct io_state {
+ /* What we think the current position is. */
+ vm_offset_t location;
+
+ /* The buffer in which we accumulate buffered i/o. */
+ vm_address_t buffer;
+ /* The size of BUFFER. */
+ vm_size_t buffer_size;
+
+ /* If LOCATION is not a multiple of the block size (and so points somewhere
+ in the middle of BUFFER), this indicates why. */
+ enum io_state_buffer_use buffer_use;
+
+ /* Lock this if you want to read/modify LOCATION or BUFFER. */
+ struct mutex lock;
+};
+
+#define io_state_lock(ios) mutex_lock(&(ios)->lock)
+#define io_state_unlock(ios) mutex_unlock(&(ios)->lock)
+
+/* Declare this to keep the parameter scope below sane. */
+struct dev;
+
+/* Initialize the io_state structure IOS to be used with the device DEV. If
+ a memory allocation error occurs, ENOMEM is returned, otherwise 0. */
+error_t io_state_init(struct io_state *ios, struct dev *dev);
+
+/* Frees all resources used by IOS. */
+void io_state_finalize(struct io_state *ios);
+
+/* If IOS's location isn't block aligned because writes have been buffered
+ there, then sync the whole buffer out to the device. Any error that
+ occurs while writing is returned, otherwise 0. */
+error_t io_state_sync(struct io_state *ios, struct dev *dev);
+
+#endif /* !__IOSTATE_H__ */
diff --git a/devio/mem.c b/devio/mem.c
new file mode 100644
index 00000000..65caa19f
--- /dev/null
+++ b/devio/mem.c
@@ -0,0 +1,57 @@
+/* Some random handy ops for dealing with rpcs that return memory.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <string.h>
+
+/* ---------------------------------------------------------------- */
+
+/* Makes sure that BUF, points to a buffer with AMOUNT bytes available.
+ *BUF_LEN should be the current length of *BUF, and if this isn't enough to
+ hold AMOUNT bytes, then more is allocated and the new buffer is returned
+ in *BUF and *BUF_LEN. If a memory allocation error occurs, the error code
+ is returned, otherwise 0. */
+error_t
+allocate(vm_address_t *buf, vm_size_t *buf_len, vm_size_t amount)
+{
+ if (*buf_len < amount)
+ {
+ error_t err = vm_allocate(mach_task_self(), buf, amount, 1);
+ if (err)
+ return err;
+ }
+
+ *buf_len = amount;
+ return 0;
+}
+
+/* Deallocates any pages entirely within the last EXCESS bytes of the BUF_LEN
+ long buffer, BUF. */
+error_t
+deallocate_excess(vm_address_t buf, vm_size_t buf_len, vm_size_t excess)
+{
+ vm_size_t excess_pages = buf_len - round_page(buf_len - excess);
+ if (excess_pages > 0)
+ return
+ vm_deallocate(mach_task_self(),
+ round_page(buf + buf_len - excess), excess_pages);
+ else
+ return 0;
+}
diff --git a/devio/mem.h b/devio/mem.h
new file mode 100644
index 00000000..e19b590c
--- /dev/null
+++ b/devio/mem.h
@@ -0,0 +1,33 @@
+/* Some random handy memory ops that know about VM.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* For bcopy &c. */
+#include <string.h>
+
+/* Makes sure that BUF, points to a buffer with AMOUNT bytes available.
+ *BUF_LEN should be the current length of *BUF, and if this isn't enough to
+ hold AMOUNT bytes, then more is allocated and the new buffer is returned
+ in *BUF and *BUF_LEN. If a memory allocation error occurs, the error code
+ is returned, otherwise 0. */
+error_t allocate(vm_address_t *buf, vm_size_t *buf_len, vm_size_t amount);
+
+/* Deallocates any pages entirely within the last EXCESS bytes of the BUF_LEN
+ long buffer, BUF. */
+error_t deallocate_excess(vm_address_t buf, vm_size_t buf_len, vm_size_t excess);
diff --git a/devio/open.c b/devio/open.c
new file mode 100644
index 00000000..64d62bfe
--- /dev/null
+++ b/devio/open.c
@@ -0,0 +1,90 @@
+/* Per-open information for devio.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+
+#include "open.h"
+#include "window.h"
+#include "dev.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Returns a new per-open structure for the device DEV in OPEN. If an error
+ occurs, the error-code is returned, otherwise 0. */
+error_t
+open_create(struct dev *dev, struct open **open)
+{
+ error_t err;
+
+ *open = malloc(sizeof(struct open));
+
+ if (*open == NULL)
+ return ENOMEM;
+
+ (*open)->dev = dev;
+
+ err = io_state_init(&(*open)->io_state, dev);
+
+ if (!err && dev_is(dev, DEV_BUFFERED) && !dev_is(dev, DEV_SERIAL))
+ /* A random-access buffered device -- use a pager to do i/o to it. */
+ {
+ mach_port_t memobj;
+ err = dev_get_memory_object(dev, &memobj);
+ if (!err)
+ err =
+ window_create(memobj, 0, 0, dev_is(dev, DEV_READONLY),
+ &(*open)->window); /* XXX sizes */
+ if (err)
+ {
+ mach_port_destroy(mach_task_self(), memobj);
+ io_state_finalize(&(*open)->io_state);
+ }
+ }
+ else
+ (*open)->window = NULL;
+
+ if (err)
+ free(*open);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Free OPEN and any resources it holds. */
+void
+open_free(struct open *open)
+{
+ io_state_finalize(&open->io_state);
+ window_free(open->window);
+ free(open);
+}
+
+
+/* ---------------------------------------------------------------- */
+
+/* Returns the appropiate io_state object for OPEN (which may be either
+ per-open or a per-device depending on the device). */
+struct io_state *
+open_get_io_state(struct open *open)
+{
+ return
+ dev_is(open->dev, DEV_SERIAL) ? &open->dev->io_state : &open->io_state;
+}
diff --git a/devio/open.h b/devio/open.h
new file mode 100644
index 00000000..c202d9ed
--- /dev/null
+++ b/devio/open.h
@@ -0,0 +1,67 @@
+/* Per-open information for devio.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __OPEN_H__
+#define __OPEN_H__
+
+#include "iostate.h"
+
+/* ---------------------------------------------------------------- */
+
+/* A structure describing a particular i/o stream on this device. */
+struct open
+{
+ /* Current state of our output stream -- location and the buffer used to do
+ buffered i/o. */
+ struct io_state io_state;
+
+ /* The memory window we're using to do i/o. This may be NULL, indicating
+ we're not doing buffered random access i/o. */
+ struct window *window;
+
+ /* The device that this an open on. */
+ struct dev *dev;
+};
+
+/* Returns a new per-open structure for the device DEV in OPEN. If an error
+ occurs, the error-code is returned, otherwise 0. */
+error_t open_create(struct dev *dev, struct open **open);
+
+/* Free OPEN and any resources it holds. */
+void open_free(struct open *open);
+
+/* Returns the appropiate io_state object for OPEN (which may be either
+ per-open or a per-device depending on the device). */
+struct io_state *open_get_io_state(struct open *open);
+
+/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
+ (which may be ignored if the device doesn't support random access),
+ and returns the number of bytes written in AMOUNT. If no error occurs,
+ zero is returned, otherwise the error code is returned. */
+error_t open_write(struct open *open, vm_address_t buf, vm_size_t len,
+ vm_size_t *amount, off_t offs);
+
+/* Reads up to AMOUNT bytes from the device into BUF and BUF_LEN using the
+ standard mach out-array convention. If no error occurs, zero is returned,
+ otherwise the error code is returned. */
+error_t open_read(struct open *open, vm_address_t *buf, vm_size_t *buf_len,
+ vm_size_t amount, off_t offs);
+
+#endif /* !__OPEN_H__ */
diff --git a/devio/ptypes.h b/devio/ptypes.h
new file mode 100644
index 00000000..64aa3b0d
--- /dev/null
+++ b/devio/ptypes.h
@@ -0,0 +1,23 @@
+/* Libports port types for devio.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define PT_FSYS 0
+#define PT_NODE 1
+#define PT_MEMOBJ 2
diff --git a/devio/rdwr.c b/devio/rdwr.c
new file mode 100644
index 00000000..99ba9414
--- /dev/null
+++ b/devio/rdwr.c
@@ -0,0 +1,473 @@
+/* Implements various types of I/O on top of raw devices.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <assert.h>
+#include <string.h>
+
+#include "open.h"
+#include "dev.h"
+#include "mem.h"
+#include "window.h"
+
+/* ---------------------------------------------------------------- */
+
+/* Writes BUF to DEV, copying through an intermediate buffer to page-align
+ it. If STAGING_BUF isn't 0, it is used as the copy buffer for
+ small-enough transfers (staging_buf is assumed to be one block in length).
+ AMOUNT is the actual amount written, and LEN is the amount of source
+ material actually in BUF; if LEN is smaller than AMOUNT, the remainder is
+ zero. */
+static error_t
+copying_block_write(struct dev *dev, vm_address_t staging_buf,
+ vm_address_t buf, vm_size_t len, vm_size_t amount,
+ vm_offset_t *offs)
+{
+ error_t err = 0;
+ vm_address_t copy_buf = staging_buf;
+ vm_size_t copy_buf_len = dev->block_size;
+
+ if (amount > dev->block_size || staging_buf == 0)
+ {
+ copy_buf_len = amount;
+ err = vm_allocate(mach_task_self(), &copy_buf, copy_buf_len, 1);
+ if (err)
+ return err;
+ }
+
+ bcopy((char *)buf, (char *)copy_buf, len);
+ if (len < amount && copy_buf == staging_buf)
+ /* We need to zero the rest of the bloc, but only if we didn't
+ vm_allocate it (in which case it will be zero-filled). */
+ bzero((char *)buf + len, amount - len);
+
+ err = dev_write(dev, copy_buf, amount, offs);
+
+ if (copy_buf != staging_buf)
+ vm_deallocate(mach_task_self(), copy_buf, copy_buf_len);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Copies LEN bytes from BUF to DEV, using STAGING_BUF to do buffering of
+ partial blocks, and returning the amount actually written in AMOUNT.
+ *OFFS is incremented to reflect the amount read/written. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t
+buffered_write(struct dev *dev, vm_address_t staging_buf,
+ vm_address_t buf, vm_size_t len, vm_size_t *amount,
+ vm_offset_t *offs)
+{
+ error_t err = 0;
+ int bsize = dev->block_size;
+ int staging_buf_loc = *offs % bsize;
+ int left_in_staging_buf = bsize - staging_buf_loc;
+ vm_offset_t start_offs = *offs;
+
+ if (left_in_staging_buf > 0)
+ /* Write what's buffered from the last I/O. */
+ {
+ /* Amount of the current i/o we can put in the staging buffer. */
+ int stage = (left_in_staging_buf > len ? len : left_in_staging_buf);
+
+ bcopy((char *)buf, (char *)staging_buf + staging_buf_loc, stage);
+
+ buf += stage;
+ len -= stage;
+ *offs += stage;
+
+ if (stage == left_in_staging_buf)
+ /* We've filled up STAGING_BUF so we can write it out now. */
+ {
+ /* Backup OFFS to reflect the beginning-of-block position. */
+ *offs -= bsize;
+ err = dev_write(dev, staging_buf, bsize, offs);
+ }
+ }
+
+ if (!err && len > bsize)
+ /* Enough i/o pending to do whole block transfers. */
+ {
+ /* The number of bytes at the end of the transfer that aren't a
+ multiple of the block-size. We have to deal with these separately
+ because device i/o must be in block multiples. */
+ int excess = len % bsize;
+ vm_size_t block_len = len - excess;
+
+ if (dev_write_valid(dev, buf, block_len, offs))
+ /* BUF is page-aligned, so we can do i/o directly to the device, or
+ it is small enough that it doesn't matter. */
+ err = dev_write(dev, buf, block_len, offs);
+ else
+ /* Argh! BUF isn't page aligned! We must filter the i/o though an
+ intermediate buffer... */
+ err = copying_block_write(dev, staging_buf,
+ buf, block_len, block_len, offs);
+
+ if (*offs - start_offs < left_in_staging_buf + block_len)
+ /* Didn't write out all the blocks, so suppress buffering the rest. */
+ len = 0;
+ else
+ len = excess;
+ }
+
+ /* At this point, LEN should be < BLOCK_SIZE, so we use buffering again. */
+ if (!err && len > 0)
+ {
+ bcopy((char *)staging_buf, (char *)buf, len);
+ *offs += len;
+ }
+
+ *amount = *offs - start_offs;
+ if (*amount > 0)
+ /* If an error occurred, but we successfully wrote *something*, then
+ pretend nothing bad happened; the error will probably get caught next
+ time. */
+ err = 0;
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Reads AMOUNT bytes from DEV and returns them in BUF and BUF_LEN (using the
+ standard mach out-array conventions), using STAGING_BUF to do buffering of
+ partial blocks. *OFFS is incremented to reflect the amount read/written.
+ If an error occurs, the error code is returned, otherwise 0. */
+error_t
+buffered_read (struct dev *dev, vm_address_t staging_buf,
+ vm_address_t *buf, vm_size_t *buf_len, vm_size_t amount,
+ vm_offset_t *offs)
+{
+ error_t err = 0;
+ int bsize = dev->block_size;
+ vm_offset_t start_offs = *offs;
+ int staging_buf_loc = *offs % bsize;
+ int from_staging_buf = bsize - staging_buf_loc;
+ vm_address_t block_buf = *buf;
+ vm_size_t block_buf_size = *buf_len;
+ vm_size_t block_amount = amount;
+
+ if (staging_buf_loc > 0)
+ {
+ /* Read into a temporary buffer. */
+ block_buf = 0;
+ block_buf_size = 0;
+
+ if (from_staging_buf > amount)
+ from_staging_buf = amount;
+
+ block_amount -= from_staging_buf;
+ }
+ else
+ from_staging_buf = 0;
+
+ /* Read any new block required. */
+ if (block_amount > 0)
+ {
+ /* We read enough to get every full block of BLOCK_AMOUNT, plus an
+ additional whole block if there's any more; we just copy any excess
+ from that last block into STAGING_BUF for next time. */
+ block_amount = ((block_amount + bsize - 1) / bsize) * bsize;
+
+ err = dev_read(dev, &block_buf, &block_buf_size, block_amount, offs);
+ if (err && staging_buf_loc > 0)
+ /* We got an error, but don't abort, since we did get the bit from
+ the buffer. */
+ {
+ err = 0;
+ amount = from_staging_buf;
+ block_amount = 0;
+ }
+
+ if (amount > *offs - start_offs)
+ /* If we read less than we hoped, reflect this down below. */
+ amount = *offs - start_offs;
+ }
+
+ if (staging_buf_loc > 0)
+ /* Coalesce what we have in STAGING_BUF with what we read. */
+ {
+ err = allocate(buf, buf_len, amount);
+ assert_perror(err);
+ bcopy((char *)staging_buf + staging_buf_loc, (char *)*buf,
+ from_staging_buf);
+
+ if (block_amount > 0)
+ bcopy((char *)block_buf, (char *)*buf + from_staging_buf,
+ amount - from_staging_buf);
+ }
+ else
+ /* Otherwise, BLOCK_BUF should already contain the correct data. */
+ {
+ *buf = block_buf;
+ *buf_len = block_buf_size;
+ }
+
+ if (*offs - start_offs > amount)
+ /* We've read too far, so put some amount from the end back into
+ STAGING_BUF. */
+ {
+ int excess = (*offs - start_offs) - amount;
+
+ bcopy((char *)block_buf + amount,
+ (char *)staging_buf + bsize - excess,
+ excess);
+ *offs -= excess;
+
+ if (excess >= vm_page_size)
+ deallocate_excess(*buf, *buf_len, excess);
+ *buf_len -= excess;
+ }
+
+ /* Deallocate any extra copy buffer if necessary. */
+ if (*buf != block_buf)
+ vm_deallocate(mach_task_self(), block_buf, block_buf_size);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write BUF_LEN bytes from BUF to DEV, padding with zeros as necessary to
+ write whole blocks, and returning the amount actually written in AMOUNT.
+ If successful, 0 is returned, otherwise an error code is returned. *OFFS
+ is incremented by the change in device location. */
+error_t
+raw_write(struct dev *dev,
+ vm_address_t buf, vm_size_t buf_len,
+ vm_size_t *amount, vm_offset_t *offs)
+{
+ error_t err;
+ int bsize = dev->block_size;
+ int block_amount = ((buf_len + bsize - 1) / bsize) * bsize;
+ vm_offset_t start_offs = *offs;
+
+ if (block_amount == buf_len && dev_write_valid(dev, buf, block_amount, offs))
+ /* BUF is page-aligned, so we can do i/o directly to the device, or
+ it is small enough that it doesn't matter. */
+ err = dev_write(dev, buf, block_amount, offs);
+ else
+ /* Argh! BUF isn't page aligned! We must filter the i/o though an
+ intermediate buffer... [We use DEV's io_state buffer, as we know
+ that the io_state is locked in open_rdwr, and it isn't otherwise
+ used...] */
+ err = copying_block_write(dev, dev->io_state.buffer,
+ buf, buf_len, block_amount, offs);
+
+ if (!err && *offs - start_offs < buf_len)
+ *amount = *offs - start_offs;
+ else
+ *amount = buf_len;
+
+ return err;
+}
+
+/* Read AMOUNT bytes from DEV into BUF and BUF_LEN; only whole blocks are
+ read, but anything greater than *AMOUNT bytes is discarded. The standard
+ mach out-array convention is used to return the data in BUF and BUF_LEN.
+ If successful, 0 is returned, otherwise an error code is returned. *OFFS
+ is incremented by the change in device location. */
+error_t
+raw_read(struct dev *dev,
+ vm_address_t *buf, vm_size_t *buf_len,
+ vm_size_t amount, vm_offset_t *offs)
+{
+ error_t err;
+ int bsize = dev->block_size;
+ int block_amount = ((amount + bsize - 1) / bsize) * bsize;
+ err = dev_read(dev, buf, buf_len, block_amount, offs);
+
+ if (!err)
+ {
+ int excess = *buf_len - amount;
+ if (excess > vm_page_size)
+ deallocate_excess(*buf, *buf_len, excess);
+ if (excess > 0)
+ *buf_len = amount;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+struct rdwr_state
+{
+ struct dev *dev;
+ off_t user_offs;
+ vm_offset_t *offs_p;
+ struct io_state *io_state;
+};
+
+/* Setup state needed for I/O to/from OPEN, putting it into STATE. OFFS
+ should be the original user-supplied offset. */
+static void
+rdwr_state_init(struct rdwr_state *state, struct open *open, off_t offs)
+{
+ state->dev = open->dev;
+ state->io_state = open_get_io_state(open);
+ state->user_offs = offs;
+
+ if (dev_is(state->dev, DEV_SERIAL))
+ /* For serial i/o, we always ignore the proffered offs, and use the
+ actual device offset. */
+ state->user_offs = -1;
+
+ if (state->user_offs == -1 || !dev_is(state->dev, DEV_BUFFERED))
+ /* If we're going to use some bit of IO_STATE, lock it first. This
+ should only not happen if we're going to used windowed i/o with an
+ explicit offset. */
+ io_state_lock(state->io_state);
+
+ if (state->user_offs == -1)
+ state->offs_p = &state->io_state->location;
+ else
+ state->offs_p = (vm_offset_t *)&state->user_offs;
+}
+
+/* Destroy any state created by rdwr_state_init. */
+static void
+rdwr_state_finalize(struct rdwr_state *state)
+{
+ if (state->user_offs == -1 || !dev_is(state->dev, DEV_BUFFERED))
+ io_state_unlock(state->io_state);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Writes up to LEN bytes from BUF to OPEN's device at device offset OFFS
+ (which may be ignored if the device doesn't support random access),
+ and returns the number of bytes written in AMOUNT. If no error occurs,
+ zero is returned, otherwise the error code is returned. */
+error_t
+open_write(struct open *open, vm_address_t buf, vm_size_t len,
+ vm_size_t *amount, off_t offs)
+{
+ error_t err;
+ struct rdwr_state state;
+ struct dev *dev = open->dev;
+#ifdef MSG
+ off_t start_offs;
+#endif
+
+ rdwr_state_init(&state, open, offs);
+
+#ifdef MSG
+ start_offs = *state.offs_p;
+#endif
+
+ if (!dev_is(dev, DEV_BUFFERED))
+ err = raw_write(dev, buf, len, amount, state.offs_p);
+ else if (dev_is(dev, DEV_SERIAL))
+ {
+ state.io_state->buffer_use = IO_STATE_BUFFERED_WRITE;
+ err = buffered_write(dev, state.io_state->buffer, buf, len,
+ amount, state.offs_p);
+ }
+ else
+ err = window_write(open->window, buf, len, amount, state.offs_p);
+
+#ifdef MSG
+ if (debug)
+ {
+ char *mode =
+ (dev_is(dev, DEV_BUFFERED)
+ ? dev_is(dev, DEV_SERIAL) ? "buffered" : "windowed" : "raw");
+ char *estr = err ? strerror(err) : "OK";
+ char *bstr = err ? "-" : brep(buf, len);
+
+ mutex_lock(&debug_lock);
+ fprintf(debug, "open_rdwr:\n using %s offset\n",
+ (offs == -1 || !dev_is(dev, DEV_BUFFERED))
+ ? (state.offs_p == &dev->io_state.location
+ ? "device" : "open")
+ : "msg");
+ fprintf(debug, " %s write(%s, %d, %d) => %s, %d\n",
+ mode, bstr, len, (int)start_offs, estr, *amount);
+ fprintf(debug, " offset = %d\n", (int)*state.offs_p);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ rdwr_state_finalize(&state);
+
+ return err;
+}
+
+/* Reads up to AMOUNT bytes from the device into BUF and BUF_LEN using the
+ standard mach out-array convention. If no error occurs, zero is returned,
+ otherwise the error code is returned. */
+error_t
+open_read(struct open *open, vm_address_t *buf, vm_size_t *buf_len,
+ vm_size_t amount, off_t offs)
+{
+ error_t err;
+ struct rdwr_state state;
+ struct dev *dev = open->dev;
+#ifdef MSG
+ off_t start_offs;
+#endif
+
+ rdwr_state_init(&state, open, offs);
+
+#ifdef MSG
+ start_offs = *state.offs_p;
+#endif
+
+ if (!dev_is(dev, DEV_BUFFERED))
+ err = raw_read(dev, buf, buf_len, amount, state.offs_p);
+ else if (dev_is(dev, DEV_SERIAL))
+ {
+ state.io_state->buffer_use = IO_STATE_BUFFERED_READ;
+ err = buffered_read(dev, state.io_state->buffer, buf, buf_len,
+ amount, state.offs_p);
+ }
+ else
+ err = window_read(open->window, buf, buf_len, amount, state.offs_p);
+
+#ifdef MSG
+ if (debug)
+ {
+ char *mode =
+ (dev_is(dev, DEV_BUFFERED)
+ ? dev_is(dev, DEV_SERIAL) ? "buffered" : "windowed" : "raw");
+ char *estr = err ? strerror(err) : "OK";
+ char *bstr = err ? "-" : brep(*buf, *buf_len);
+
+ mutex_lock(&debug_lock);
+ fprintf(debug, "open_rdwr:\n using %s offset\n",
+ (offs == -1 || !dev_is(dev, DEV_BUFFERED))
+ ? (state.offs_p == &dev->io_state.location
+ ? "device" : "open")
+ : "msg");
+ fprintf(debug, " %s read(%d, %d) => %s, %s, %d\n",
+ mode, amount, (int)start_offs, estr, bstr, *buf_len);
+ fprintf(debug, " offset = %d\n", (int)*state.offs_p);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ rdwr_state_finalize(&state);
+
+ return err;
+}
diff --git a/devio/window.c b/devio/window.c
new file mode 100644
index 00000000..26c6fd33
--- /dev/null
+++ b/devio/window.c
@@ -0,0 +1,200 @@
+/* Window management routines for buffered I/O using VM.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+
+#include "window.h"
+#include "mem.h"
+
+#include "dev.h" /* for MSG & debug */
+
+/* ---------------------------------------------------------------- */
+
+/* Create a VM window onto the memory object MEMOBJ, and return it in WIN.
+ MIN_SIZE and MAX_SIZE are the minimum and maximum sizes that the window
+ will shrink/grow to (a value of 0 will use some default). */
+error_t
+window_create(mach_port_t memobj,
+ vm_size_t min_size, vm_size_t max_size, int read_only,
+ struct window **win)
+{
+ *win = malloc(sizeof(struct window));
+ if (*win == NULL)
+ return ENOMEM;
+
+ if (min_size < max_size)
+ min_size = max_size;
+
+ (*win)->location = 0;
+ (*win)->size = 0;
+ (*win)->memobj = memobj;
+ (*win)->min_size = (min_size < vm_page_size ? vm_page_size : min_size);
+ (*win)->max_size = (max_size < vm_page_size ? vm_page_size : max_size);
+ (*win)->read_only = read_only;
+
+ return 0;
+}
+
+/* Free WIN and any resources it holds. */
+void
+window_free(struct window *win)
+{
+ if (win->size > 0)
+ vm_deallocate(mach_task_self(), win->buffer, win->size);
+ mach_port_destroy(mach_task_self(), win->memobj);
+ free(win);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Makes sure that WIN's memory window contains at least positions POS
+ through POS + LEN on the device WIN's mapping. If an error occurs in the
+ process, the error code is returned (and WIN may not map the desired
+ locations), otherwise 0. WIN is assumed to already be locked when this is
+ called. */
+static error_t
+position(struct window *win, vm_offset_t pos, vm_size_t len)
+{
+ vm_offset_t end = pos + len;
+ vm_offset_t win_beg = win->location;
+ vm_offset_t win_end = win_beg + win->size;
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "position: need window on 0x%x[%d]\n", pos, len);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ if (pos >= win_beg && end <= win_end)
+ /* The request is totally satisfied by our current position. */
+ return 0;
+ else
+#if 0 /* XXXXXXX */
+ if (end < win_beg || pos >= win_end)
+ /* The desired locations are entirely outside our current window, so just
+ trash it, and map a new buffer anywhere. */
+#endif
+ {
+ int prot = VM_PROT_READ | (win->read_only ? 0 : VM_PROT_WRITE);
+
+ if (win->size > 0)
+ {
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "position: deallocating window 0x%x[%d]\n",
+ win_beg, win->size);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ vm_deallocate(mach_task_self(), win->buffer, win->size);
+ }
+
+ win->location = trunc_page(pos);
+ win->size = round_page(len + (pos - win->location));
+ win->buffer = 0;
+
+ if (win->size < win->min_size)
+ win->size = win->min_size;
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "position: mapping window 0x%x[%d]\n",
+ win->location, win->size);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ return
+ vm_map(mach_task_self(), &win->buffer, win->size, 0, 1,
+ win->memobj, win->location, 0, prot, prot, VM_INHERIT_NONE);
+ }
+
+ return 0;
+}
+
+
+/* ---------------------------------------------------------------- */
+
+/* Write up to BUF_LEN bytes from BUF to the device that WIN is a window on,
+ at offset *OFFS, using memory-mapped buffered I/O. If successful, 0 is
+ returned, otherwise an error code is returned. *OFFS is incremented by
+ the amount sucessfully written. */
+error_t
+window_write(struct window *win,
+ vm_address_t buf, vm_size_t buf_len, vm_size_t *amount,
+ vm_offset_t *offs)
+{
+ error_t err;
+
+ mutex_lock(&win->lock);
+
+ err = position(win, *offs, buf_len);
+ if (!err)
+ {
+ bcopy((char *)buf,
+ (char *)win->buffer + (*offs - win->location),
+ buf_len);
+ *amount = buf_len;
+ *offs += buf_len;
+ }
+
+ mutex_unlock(&win->lock);
+
+ return err;
+}
+
+/* Read up to AMOUNT bytes from the device that WIN is a window on, at offset
+ *OFFS, into BUF and BUF_LEN (using the standard mach out-array
+ conventions), using memory-mapped buffered I/O. If successful, 0 is
+ returned, otherwise an error code is returned. *OFFS is incremented by
+ the amount sucessfully written. */
+error_t
+window_read(struct window *win,
+ vm_address_t *buf, vm_size_t *buf_len,
+ vm_size_t amount, vm_offset_t *offs)
+{
+ error_t err;
+
+ mutex_lock(&win->lock);
+
+ err = position(win, *offs, amount);
+ if (!err)
+ {
+ err = allocate(buf, buf_len, amount);
+ if (!err)
+ {
+ bcopy((char *)win->buffer + (*offs - win->location),
+ (char *)*buf,
+ amount);
+ *offs += amount;
+ }
+ }
+
+ mutex_unlock(&win->lock);
+
+ return err;
+}
diff --git a/devio/window.h b/devio/window.h
new file mode 100644
index 00000000..4bd6301c
--- /dev/null
+++ b/devio/window.h
@@ -0,0 +1,82 @@
+/* Window management routines for buffered I/O using VM.
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef __WINDOW_H__
+#define __WINDOW_H__
+
+#include <mach.h>
+
+/* ---------------------------------------------------------------- */
+
+/* A structure describing a memory window used to do buffered random access
+ device i/o through the device pager. */
+struct window
+{
+ /* The currently allocated vm window backed by the device pager. */
+ vm_address_t buffer;
+
+ /* The device offset of the window. */
+ vm_offset_t location;
+
+ /* The length of the window (should be a multiple of __vm_page_size). If
+ this is 0, this window isn't allocated. */
+ vm_size_t size;
+ /* If SIZE < MIN_SIZE we won't shrink the window. */
+ vm_size_t min_size;
+ /* If SIZE > MAX_SIZE, we'll try and shrink the window to fit. */
+ vm_size_t max_size;
+
+ /* The device pager providing backing store for this window. */
+ mach_port_t memobj;
+ /* True if the mapping should be read_only. */
+ int read_only;
+
+ /* Lock this if you want to read/write some field(s) here. */
+ struct mutex lock;
+};
+
+/* Create a VM window onto the memory object MEMOBJ, and return it in WIN.
+ MIN_SIZE and MAX_SIZE are the minimum and maximum sizes that the window
+ will shrink/grow to. */
+error_t window_create(mach_port_t memobj,
+ vm_size_t min_size, vm_size_t max_size, int read_only,
+ struct window **win);
+
+/* Free WIN and any resources it holds. */
+void window_free(struct window *win);
+
+/* Write up to BUF_LEN bytes from BUF to the device that WIN is a window on,
+ at offset *OFFS, using memory-mapped buffered I/O. If successful, 0 is
+ returned, otherwise an error code is returned. *OFFS is incremented by
+ the amount sucessfully written. */
+error_t window_write(struct window *win,
+ vm_address_t buf, vm_size_t buf_len, vm_size_t *amount,
+ vm_offset_t *offs);
+
+/* Read up to AMOUNT bytes from the device that WIN is a window on, at offset
+ *OFFS, into BUF and BUF_LEN (using the standard mach out-array
+ conventions), using memory-mapped buffered I/O. If successful, 0 is
+ returned, otherwise an error code is returned. *OFFS is incremented by
+ the amount sucessfully written. */
+error_t window_read(struct window *win,
+ vm_address_t *buf, vm_size_t *buf_len,
+ vm_size_t amount, vm_offset_t *offs);
+
+#endif /* !__WINDOW_H__ */