summaryrefslogtreecommitdiff
path: root/devio/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'devio/window.c')
-rw-r--r--devio/window.c200
1 files changed, 200 insertions, 0 deletions
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;
+}