diff options
-rw-r--r-- | devio/dev.c | 454 | ||||
-rw-r--r-- | devio/dev.h | 138 | ||||
-rw-r--r-- | devio/devio.c | 451 | ||||
-rw-r--r-- | devio/devpager.c | 138 | ||||
-rw-r--r-- | devio/io.c | 358 | ||||
-rw-r--r-- | devio/iostate.c | 76 | ||||
-rw-r--r-- | devio/iostate.h | 67 | ||||
-rw-r--r-- | devio/mem.c | 57 | ||||
-rw-r--r-- | devio/mem.h | 33 | ||||
-rw-r--r-- | devio/open.c | 90 | ||||
-rw-r--r-- | devio/open.h | 67 | ||||
-rw-r--r-- | devio/ptypes.h | 23 | ||||
-rw-r--r-- | devio/rdwr.c | 473 | ||||
-rw-r--r-- | devio/window.c | 200 | ||||
-rw-r--r-- | devio/window.h | 82 |
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(), ©_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__ */ |