summaryrefslogtreecommitdiff
path: root/device
diff options
context:
space:
mode:
Diffstat (limited to 'device')
-rw-r--r--device/blkio.c238
-rw-r--r--device/buf.h102
-rw-r--r--device/chario.c1089
-rw-r--r--device/cirbuf.c298
-rw-r--r--device/cirbuf.h62
-rw-r--r--device/conf.h114
-rw-r--r--device/cons.c267
-rw-r--r--device/cons.h50
-rw-r--r--device/dev_forward.defs44
-rw-r--r--device/dev_hdr.h108
-rw-r--r--device/dev_lookup.c409
-rw-r--r--device/dev_master.h60
-rw-r--r--device/dev_name.c237
-rw-r--r--device/dev_pager.c741
-rw-r--r--device/device.srv29
-rw-r--r--device/device_init.c73
-rw-r--r--device/device_pager.srv44
-rw-r--r--device/device_port.h41
-rw-r--r--device/device_reply.cli27
-rw-r--r--device/device_types_kernel.h44
-rw-r--r--device/dk_label.c98
-rw-r--r--device/ds_routines.c1820
-rw-r--r--device/ds_routines.h52
-rw-r--r--device/errno.h45
-rw-r--r--device/if_ether.h57
-rw-r--r--device/if_hdr.h150
-rw-r--r--device/io_req.h141
-rw-r--r--device/memory_object_reply.cli27
-rw-r--r--device/net_io.c2168
-rw-r--r--device/net_io.h80
-rw-r--r--device/param.h49
-rw-r--r--device/subrs.c140
-rw-r--r--device/tty.h203
33 files changed, 9107 insertions, 0 deletions
diff --git a/device/blkio.c b/device/blkio.c
new file mode 100644
index 0000000..26d4a75
--- /dev/null
+++ b/device/blkio.c
@@ -0,0 +1,238 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/89
+ *
+ * Block IO driven from generic kernel IO interface.
+ */
+#include <mach/kern_return.h>
+
+#include <device/param.h>
+#include <device/device_types.h>
+#include <device/io_req.h>
+#include <device/ds_routines.h>
+
+
+
+io_return_t block_io(strat, max_count, ior)
+ void (*strat)();
+ void (*max_count)();
+ register io_req_t ior;
+{
+ register kern_return_t rc;
+ boolean_t wait = FALSE;
+
+ /*
+ * Make sure the size is not too large by letting max_count
+ * change io_count. If we are doing a write, then io_alloc_size
+ * preserves the original io_count.
+ */
+ (*max_count)(ior);
+
+ /*
+ * If reading, allocate memory. If writing, wire
+ * down the incoming memory.
+ */
+ if (ior->io_op & IO_READ)
+ rc = device_read_alloc(ior, (vm_size_t)ior->io_count);
+ else
+ rc = device_write_get(ior, &wait);
+
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ /*
+ * Queue the operation for the device.
+ */
+ (*strat)(ior);
+
+ /*
+ * The io is now queued. Wait for it if needed.
+ */
+ if (wait) {
+ iowait(ior);
+ return(D_SUCCESS);
+ }
+
+ return (D_IO_QUEUED);
+}
+
+/*
+ * 'standard' max_count routine. VM continuations mean that this
+ * code can cope with arbitrarily-sized write operations (they won't be
+ * atomic, but any caller that cares will do the op synchronously).
+ */
+#define MAX_PHYS (256 * 1024)
+
+void minphys(ior)
+ register io_req_t ior;
+{
+ if ((ior->io_op & (IO_WRITE | IO_READ | IO_OPEN)) == IO_WRITE)
+ return;
+
+ if (ior->io_count > MAX_PHYS)
+ ior->io_count = MAX_PHYS;
+}
+
+/*
+ * Dummy routine placed in device switch entries to indicate that
+ * block device may be mapped.
+ */
+vm_offset_t block_io_mmap()
+{
+ return (0);
+}
+
+/*
+ * Disk sort routine.
+ *
+ * We order the disk request chain so that the disk head will sweep
+ * back and forth across the disk. The chain is divided into two
+ * pieces, with requests ordered in opposite directions. Assume that
+ * the first part of the chain holds increasing cylinder numbers.
+ * If a new request has a higher cylinder number than the head of
+ * the chain, the disk head has not yet reached it; the new request
+ * can go in the first part of the chain. If the new request has
+ * a lower cylinder number, the disk head has already passed it and
+ * must catch it on the way back; so the new request goes in the
+ * second (descending) part of the chain.
+ * When all of the requests in the ascending portion are filled,
+ * the descending chain becomes the first chain, and requests above
+ * the first now go in the second part of the chain (ascending).
+ */
+
+#define io_cylinder io_residual
+ /* Disk drivers put cylinder here */
+#define h_head io_next
+#define h_tail io_prev
+ /* IORs are chained here */
+
+void disksort(head, ior)
+ io_req_t head; /* (sort of) */
+ io_req_t ior;
+{
+ register int cylinder = ior->io_cylinder;
+ register io_req_t next, prev;
+
+ next = head->h_head;
+ if (next == 0) {
+ head->h_head = ior;
+ head->h_tail = ior;
+ ior->io_next = 0;
+ return;
+ }
+
+ do {
+ prev = next;
+ next = prev->io_next;
+ } while (next != 0 && prev->io_cylinder == next->io_cylinder);
+
+ if (next == 0) {
+ prev->io_next = ior;
+ head->h_tail = ior;
+ ior->io_next = 0;
+ return;
+ }
+
+ if (prev->io_cylinder < next->io_cylinder) {
+ /*
+ * Ascending list first.
+ */
+ if (prev->io_cylinder <= cylinder) {
+ /*
+ * Insert in ascending list.
+ */
+ while (next != 0 &&
+ next->io_cylinder <= cylinder &&
+ prev->io_cylinder <= next->io_cylinder)
+ {
+ prev = next;
+ next = prev->io_next;
+ }
+ }
+ else {
+ /*
+ * Insert in descending list
+ */
+ do {
+ prev = next;
+ next = prev->io_next;
+ } while (next != 0 &&
+ prev->io_cylinder <= next->io_cylinder);
+
+ while (next != 0 &&
+ next->io_cylinder >= cylinder)
+ {
+ prev = next;
+ next = prev->io_next;
+ }
+ }
+ }
+ else {
+ /*
+ * Descending first.
+ */
+ if (prev->io_cylinder >= cylinder) {
+ /*
+ * Insert in descending list.
+ */
+ while (next != 0 &&
+ next->io_cylinder >= cylinder &&
+ prev->io_cylinder >= next->io_cylinder)
+ {
+ prev = next;
+ next = prev->io_next;
+ }
+ }
+ else {
+ /*
+ * Insert in ascending list
+ */
+ do {
+ prev = next;
+ next = prev->io_next;
+ } while (next != 0 &&
+ prev->io_cylinder >= next->io_cylinder);
+ while (next != 0 &&
+ next->io_cylinder <= cylinder)
+ {
+ prev = next;
+ next = prev->io_next;
+ }
+ }
+ }
+ /*
+ * Insert between prev and next.
+ */
+ prev->io_next = ior;
+ ior->io_next = next;
+ if (next == 0) {
+ /* At tail of list. */
+ head->h_tail = ior;
+ }
+}
+
diff --git a/device/buf.h b/device/buf.h
new file mode 100644
index 0000000..cf34694
--- /dev/null
+++ b/device/buf.h
@@ -0,0 +1,102 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/90
+ *
+ * Definitions to make new IO structures look like old ones
+ */
+
+/*
+ * io_req and fields
+ */
+#include <device/io_req.h>
+
+#define buf io_req
+
+/*
+ * Redefine fields for drivers using old names
+ */
+#define b_flags io_op
+#define b_bcount io_count
+#define b_error io_error
+#define b_dev io_unit
+#define b_blkno io_recnum
+#define b_resid io_residual
+#define b_un io_un
+#define b_addr data
+#define av_forw io_next
+#define av_back io_prev
+#define b_physblock io_physrec
+#define b_blocktotal io_rectotal
+
+/*
+ * Redefine fields for driver request list heads, using old names.
+ */
+#define b_actf io_next
+#define b_actl io_prev
+#define b_forw io_link
+#define b_back io_rlink
+#define b_active io_count
+#define b_errcnt io_residual
+#define b_bufsize io_alloc_size
+
+/*
+ * Redefine flags
+ */
+#define B_WRITE IO_WRITE
+#define B_READ IO_READ
+#define B_OPEN IO_OPEN
+#define B_DONE IO_DONE
+#define B_ERROR IO_ERROR
+#define B_BUSY IO_BUSY
+#define B_WANTED IO_WANTED
+#define B_BAD IO_BAD
+#define B_CALL IO_CALL
+
+#define B_MD1 IO_SPARE_START
+
+/*
+ * Redefine uio structure
+ */
+#define uio io_req
+
+/*
+ * Redefine physio routine
+ */
+#define physio(strat, xbuf, dev, ops, minphys, ior) \
+ block_io(strat, minphys, ior)
+
+/*
+ * Export standard minphys routine.
+ */
+extern minphys();
+
+/*
+ * Alternate name for iodone
+ */
+#define biodone iodone
+#define biowait iowait
diff --git a/device/chario.c b/device/chario.c
new file mode 100644
index 0000000..b3994a5
--- /dev/null
+++ b/device/chario.c
@@ -0,0 +1,1089 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/88
+ *
+ * TTY io.
+ * Compatibility with old TTY device drivers.
+ */
+
+#include <mach/kern_return.h>
+#include <mach/mig_errors.h>
+#include <mach/vm_param.h>
+#include <machine/machspl.h> /* spl definitions */
+
+#include <ipc/ipc_port.h>
+
+#include <kern/lock.h>
+#include <kern/queue.h>
+
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+
+#include <device/device_types.h>
+#include <device/io_req.h>
+#include <device/ds_routines.h>
+#include "device_reply.h"
+
+#include <device/tty.h>
+
+/* If you change these, check that tty_outq_size and tty_inq_size
+ * is greater than largest tthiwat entry.
+ */
+short tthiwat[16] =
+ { 100,100,100,100,100,100,100,200,200,400,400,400,650,650,1300,2000 };
+short ttlowat[16] =
+ { 30, 30, 30, 30, 30, 30, 30, 50, 50,120,120,120,125,125, 125, 125 };
+
+/*
+ * forward declarations
+ */
+void queue_delayed_reply(
+ queue_t, io_req_t, boolean_t (*)(io_req_t));
+void tty_output(struct tty *);
+void tty_flush(struct tty *, int);
+boolean_t char_open_done(io_req_t);
+boolean_t char_read_done(io_req_t);
+boolean_t char_write_done(io_req_t);
+
+/*
+ * Fake 'line discipline' switch for the benefit of old code
+ * that wants to call through it.
+ */
+struct ldisc_switch linesw[] = {
+ {
+ char_read,
+ char_write,
+ ttyinput,
+ ttymodem,
+ tty_output
+ }
+};
+
+/*
+ * Sizes for input and output circular buffers.
+ */
+int tty_inq_size = 4096; /* big nuf */
+int tty_outq_size = 2048; /* Must be bigger that tthiwat */
+int pdma_default = 1; /* turn pseudo dma on by default */
+
+/*
+ * compute pseudo-dma tables
+ */
+
+int pdma_timeouts[NSPEEDS]; /* how many ticks in timeout */
+int pdma_water_mark[NSPEEDS];
+
+
+void chario_init(void)
+{
+ /* the basic idea with the timeouts is two allow enough
+ time for a character to show up if data is coming in at full data rate
+ plus a little slack. 2 ticks is considered slack
+ Below 300 baud we just glob a character at a time */
+#define _PR(x) ((hz/x) + 2)
+
+ int i;
+
+ for (i = B0; i < B300; i++)
+ pdma_timeouts[i] = 0;
+
+ pdma_timeouts[B300] = _PR(30);
+ pdma_timeouts[B600] = _PR(60);
+ pdma_timeouts[B1200] = _PR(120);
+ pdma_timeouts[B1800] = _PR(180);
+ pdma_timeouts[B2400] = _PR(240);
+ pdma_timeouts[B4800] = _PR(480);
+ pdma_timeouts[B9600] = _PR(960);
+ pdma_timeouts[EXTA] = _PR(1440); /* >14400 baud */
+ pdma_timeouts[EXTB] = _PR(1920); /* >19200 baud */
+
+ for (i = B0; i < B300; i++)
+ pdma_water_mark[i] = 0;
+
+ /* for the slow speeds, we try to buffer 0.02 of the baud rate
+ (20% of the character rate). For the faster lines,
+ we try to buffer 1/2 the input queue size */
+
+#undef _PR
+#define _PR(x) (0.20 * x)
+
+ pdma_water_mark[B300] = _PR(120);
+ pdma_water_mark[B600] = _PR(120);
+ pdma_water_mark[B1200] = _PR(120);
+ pdma_water_mark[B1800] = _PR(180);
+ pdma_water_mark[B2400] = _PR(240);
+ pdma_water_mark[B4800] = _PR(480);
+ i = tty_inq_size/2;
+ pdma_water_mark[B9600] = i;
+ pdma_water_mark[EXTA] = i; /* >14400 baud */
+ pdma_water_mark[EXTB] = i; /* >19200 baud */
+
+ return;
+}
+
+/*
+ * Open TTY, waiting for CARR_ON.
+ * No locks may be held.
+ * May run on any CPU.
+ */
+io_return_t char_open(
+ int dev,
+ struct tty * tp,
+ dev_mode_t mode,
+ io_req_t ior)
+{
+ spl_t s;
+ io_return_t rc = D_SUCCESS;
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+
+ tp->t_dev = dev;
+
+ if (tp->t_mctl)
+ (*tp->t_mctl)(tp, TM_DTR, DMSET);
+
+ if (pdma_default)
+ tp->t_state |= TS_MIN;
+
+ if ((tp->t_state & TS_CARR_ON) == 0) {
+ /*
+ * No carrier.
+ */
+ if (mode & D_NODELAY) {
+ tp->t_state |= TS_ONDELAY;
+ }
+ else {
+ /*
+ * Don`t return from open until carrier detected.
+ */
+ tp->t_state |= TS_WOPEN;
+
+ ior->io_dev_ptr = (char *)tp;
+
+ queue_delayed_reply(&tp->t_delayed_open, ior, char_open_done);
+ rc = D_IO_QUEUED;
+ goto out;
+ }
+ }
+ tp->t_state |= TS_ISOPEN;
+ if (tp->t_mctl)
+ (*tp->t_mctl)(tp, TM_RTS, DMBIS);
+out:
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ return rc;
+}
+
+/*
+ * Retry wait for CARR_ON for open.
+ * No locks may be held.
+ * May run on any CPU.
+ */
+boolean_t char_open_done(
+ io_req_t ior)
+{
+ register struct tty *tp = (struct tty *)ior->io_dev_ptr;
+ spl_t s = spltty();
+
+ simple_lock(&tp->t_lock);
+ if ((tp->t_state & TS_ISOPEN) == 0) {
+ queue_delayed_reply(&tp->t_delayed_open, ior, char_open_done);
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ return FALSE;
+ }
+
+ tp->t_state |= TS_ISOPEN;
+ tp->t_state &= ~TS_WOPEN;
+
+ if (tp->t_mctl)
+ (*tp->t_mctl)(tp, TM_RTS, DMBIS);
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ ior->io_error = D_SUCCESS;
+ (void) ds_open_done(ior);
+ return TRUE;
+}
+
+boolean_t tty_close_open_reply(
+ io_req_t ior)
+{
+ ior->io_error = D_DEVICE_DOWN;
+ (void) ds_open_done(ior);
+ return TRUE;
+}
+
+/*
+ * Write to TTY.
+ * No locks may be held.
+ * Calls device start routine; must already be on master if
+ * device needs to run on master.
+ */
+io_return_t char_write(
+ register struct tty * tp,
+ register io_req_t ior)
+{
+ spl_t s;
+ register int count;
+ register char *data;
+ vm_offset_t addr;
+ io_return_t rc = D_SUCCESS;
+
+ data = ior->io_data;
+ count = ior->io_count;
+ if (count == 0)
+ return rc;
+
+ if (!(ior->io_op & IO_INBAND)) {
+ /*
+ * Copy out-of-line data into kernel address space.
+ * Since data is copied as page list, it will be
+ * accessible.
+ */
+ vm_map_copy_t copy = (vm_map_copy_t) data;
+ kern_return_t kr;
+
+ kr = vm_map_copyout(device_io_map, &addr, copy);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ data = (char *) addr;
+ }
+
+ /*
+ * Check for tty operating.
+ */
+ s = spltty();
+ simple_lock(&tp->t_lock);
+
+ if ((tp->t_state & TS_CARR_ON) == 0) {
+
+ if ((tp->t_state & TS_ONDELAY) == 0) {
+ /*
+ * No delayed writes - tell caller that device is down
+ */
+ rc = D_IO_ERROR;
+ goto out;
+ }
+
+ if (ior->io_mode & D_NOWAIT) {
+ rc = D_WOULD_BLOCK;
+ goto out;
+ }
+ }
+
+ /*
+ * Copy data into the output buffer.
+ * Report the amount not copied.
+ */
+
+ ior->io_residual = b_to_q(data, count, &tp->t_outq);
+
+ /*
+ * Start hardware output.
+ */
+
+ tp->t_state &= ~TS_TTSTOP;
+ tty_output(tp);
+
+ if (tp->t_outq.c_cc > TTHIWAT(tp) ||
+ (tp->t_state & TS_CARR_ON) == 0) {
+
+ /*
+ * Do not send reply until some characters have been sent.
+ */
+ ior->io_dev_ptr = (char *)tp;
+ queue_delayed_reply(&tp->t_delayed_write, ior, char_write_done);
+
+ rc = D_IO_QUEUED;
+ }
+out:
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ if (!(ior->io_op & IO_INBAND))
+ (void) vm_deallocate(device_io_map, addr, ior->io_count);
+ return rc;
+}
+
+/*
+ * Retry wait for output queue emptied, for write.
+ * No locks may be held.
+ * May run on any CPU.
+ */
+boolean_t char_write_done(
+ register io_req_t ior)
+{
+ register struct tty *tp = (struct tty *)ior->io_dev_ptr;
+ register spl_t s = spltty();
+
+ simple_lock(&tp->t_lock);
+ if (tp->t_outq.c_cc > TTHIWAT(tp) ||
+ (tp->t_state & TS_CARR_ON) == 0) {
+
+ queue_delayed_reply(&tp->t_delayed_write, ior, char_write_done);
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ return FALSE;
+ }
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ if (IP_VALID(ior->io_reply_port)) {
+ (void) (*((ior->io_op & IO_INBAND) ?
+ ds_device_write_reply_inband :
+ ds_device_write_reply))(ior->io_reply_port,
+ ior->io_reply_port_type,
+ ior->io_error,
+ (int) (ior->io_total -
+ ior->io_residual));
+ }
+ mach_device_deallocate(ior->io_device);
+ return TRUE;
+}
+
+boolean_t tty_close_write_reply(
+ register io_req_t ior)
+{
+ ior->io_residual = ior->io_count;
+ ior->io_error = D_DEVICE_DOWN;
+ (void) ds_write_done(ior);
+ return TRUE;
+}
+
+/*
+ * Read from TTY.
+ * No locks may be held.
+ * May run on any CPU - does not talk to device driver.
+ */
+io_return_t char_read(
+ register struct tty *tp,
+ register io_req_t ior)
+{
+ spl_t s;
+ kern_return_t rc;
+
+ /*
+ * Allocate memory for read buffer.
+ */
+ rc = device_read_alloc(ior, (vm_size_t)ior->io_count);
+ if (rc != KERN_SUCCESS)
+ return rc;
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+ if ((tp->t_state & TS_CARR_ON) == 0) {
+
+ if ((tp->t_state & TS_ONDELAY) == 0) {
+ /*
+ * No delayed writes - tell caller that device is down
+ */
+ rc = D_IO_ERROR;
+ goto out;
+ }
+
+ if (ior->io_mode & D_NOWAIT) {
+ rc = D_WOULD_BLOCK;
+ goto out;
+ }
+
+ }
+
+ if (tp->t_inq.c_cc <= 0 ||
+ (tp->t_state & TS_CARR_ON) == 0) {
+
+ ior->io_dev_ptr = (char *)tp;
+ queue_delayed_reply(&tp->t_delayed_read, ior, char_read_done);
+ rc = D_IO_QUEUED;
+ goto out;
+ }
+
+ ior->io_residual = ior->io_count - q_to_b(&tp->t_inq,
+ ior->io_data,
+ (int)ior->io_count);
+ if (tp->t_state & TS_RTS_DOWN) {
+ (*tp->t_mctl)(tp, TM_RTS, DMBIS);
+ tp->t_state &= ~TS_RTS_DOWN;
+ }
+
+ out:
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ return rc;
+}
+
+/*
+ * Retry wait for characters, for read.
+ * No locks may be held.
+ * May run on any CPU - does not talk to device driver.
+ */
+boolean_t char_read_done(
+ register io_req_t ior)
+{
+ register struct tty *tp = (struct tty *)ior->io_dev_ptr;
+ register spl_t s = spltty();
+
+ simple_lock(&tp->t_lock);
+
+ if (tp->t_inq.c_cc <= 0 ||
+ (tp->t_state & TS_CARR_ON) == 0) {
+
+ queue_delayed_reply(&tp->t_delayed_read, ior, char_read_done);
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ return FALSE;
+ }
+
+ ior->io_residual = ior->io_count - q_to_b(&tp->t_inq,
+ ior->io_data,
+ (int)ior->io_count);
+ if (tp->t_state & TS_RTS_DOWN) {
+ (*tp->t_mctl)(tp, TM_RTS, DMBIS);
+ tp->t_state &= ~TS_RTS_DOWN;
+ }
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ (void) ds_read_done(ior);
+ return TRUE;
+}
+
+boolean_t tty_close_read_reply(
+ register io_req_t ior)
+{
+ ior->io_residual = ior->io_count;
+ ior->io_error = D_DEVICE_DOWN;
+ (void) ds_read_done(ior);
+ return TRUE;
+}
+
+/*
+ * Close the tty.
+ * Tty must be locked (at spltty).
+ * Iff modem control should run on master.
+ */
+void ttyclose(
+ register struct tty *tp)
+{
+ register io_req_t ior;
+
+ /*
+ * Flush the read and write queues. Signal
+ * the open queue so that those waiting for open
+ * to complete will see that the tty is closed.
+ */
+ while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_read)) != 0) {
+ ior->io_done = tty_close_read_reply;
+ iodone(ior);
+ }
+ while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_write)) != 0) {
+ ior->io_done = tty_close_write_reply;
+ iodone(ior);
+ }
+ while ((ior = (io_req_t)dequeue_head(&tp->t_delayed_open)) != 0) {
+ ior->io_done = tty_close_open_reply;
+ iodone(ior);
+ }
+
+ /* Close down modem */
+ if (tp->t_mctl) {
+ (*tp->t_mctl)(tp, TM_BRK|TM_RTS, DMBIC);
+ if ((tp->t_state&(TS_HUPCLS|TS_WOPEN)) || (tp->t_state&TS_ISOPEN)==0)
+ (*tp->t_mctl)(tp, TM_HUP, DMSET);
+ }
+
+ /* only save buffering bit, and carrier */
+ tp->t_state = tp->t_state & (TS_MIN|TS_CARR_ON);
+}
+
+/*
+ * Port-death routine to clean up reply messages.
+ */
+boolean_t
+tty_queue_clean(
+ queue_t q,
+ ipc_port_t port,
+ boolean_t (*routine)(io_req_t) )
+{
+ register io_req_t ior;
+
+ ior = (io_req_t)queue_first(q);
+ while (!queue_end(q, (queue_entry_t)ior)) {
+ if (ior->io_reply_port == port) {
+ remqueue(q, (queue_entry_t)ior);
+ ior->io_done = routine;
+ iodone(ior);
+ return TRUE;
+ }
+ ior = ior->io_next;
+ }
+ return FALSE;
+}
+
+/*
+ * Handle port-death (dead reply port) for tty.
+ * No locks may be held.
+ * May run on any CPU.
+ */
+boolean_t
+tty_portdeath(
+ struct tty * tp,
+ ipc_port_t port)
+{
+ register spl_t spl = spltty();
+ register boolean_t result;
+
+ simple_lock(&tp->t_lock);
+
+ /*
+ * The queues may never have been initialized
+ */
+ if (tp->t_delayed_read.next == 0) {
+ result = FALSE;
+ }
+ else {
+ result =
+ tty_queue_clean(&tp->t_delayed_read, port,
+ tty_close_read_reply)
+ || tty_queue_clean(&tp->t_delayed_write, port,
+ tty_close_write_reply)
+ || tty_queue_clean(&tp->t_delayed_open, port,
+ tty_close_open_reply);
+ }
+ simple_unlock(&tp->t_lock);
+ splx(spl);
+
+ return result;
+}
+
+/*
+ * Get TTY status.
+ * No locks may be held.
+ * May run on any CPU.
+ */
+io_return_t tty_get_status(
+ register struct tty *tp,
+ dev_flavor_t flavor,
+ int * data, /* pointer to OUT array */
+ natural_t *count) /* out */
+{
+ spl_t s;
+
+ switch (flavor) {
+ case TTY_STATUS:
+ {
+ register struct tty_status *tsp =
+ (struct tty_status *) data;
+
+ if (*count < TTY_STATUS_COUNT)
+ return (D_INVALID_OPERATION);
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+
+ tsp->tt_ispeed = tp->t_ispeed;
+ tsp->tt_ospeed = tp->t_ospeed;
+ tsp->tt_breakc = tp->t_breakc;
+ tsp->tt_flags = tp->t_flags;
+ if (tp->t_state & TS_HUPCLS)
+ tsp->tt_flags |= TF_HUPCLS;
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ *count = TTY_STATUS_COUNT;
+ break;
+
+ }
+ default:
+ return D_INVALID_OPERATION;
+ }
+ return D_SUCCESS;
+}
+
+/*
+ * Set TTY status.
+ * No locks may be held.
+ * Calls device start or stop routines; must already be on master if
+ * device needs to run on master.
+ */
+io_return_t tty_set_status(
+ register struct tty *tp,
+ dev_flavor_t flavor,
+ int * data,
+ natural_t count)
+{
+ int s;
+
+ switch (flavor) {
+ case TTY_FLUSH:
+ {
+ register int flags;
+ if (count < TTY_FLUSH_COUNT)
+ return D_INVALID_OPERATION;
+
+ flags = *data;
+ if (flags == 0)
+ flags = D_READ | D_WRITE;
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+ tty_flush(tp, flags);
+ simple_unlock(&tp->t_lock);
+ splx(s);
+
+ break;
+ }
+ case TTY_STOP:
+ /* stop output */
+ s = spltty();
+ simple_lock(&tp->t_lock);
+ if ((tp->t_state & TS_TTSTOP) == 0) {
+ tp->t_state |= TS_TTSTOP;
+ (*tp->t_stop)(tp, 0);
+ }
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ break;
+
+ case TTY_START:
+ /* start output */
+ s = spltty();
+ simple_lock(&tp->t_lock);
+ if (tp->t_state & TS_TTSTOP) {
+ tp->t_state &= ~TS_TTSTOP;
+ tty_output(tp);
+ }
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ break;
+
+ case TTY_STATUS:
+ /* set special characters and speed */
+ {
+ register struct tty_status *tsp;
+
+ if (count < TTY_STATUS_COUNT)
+ return D_INVALID_OPERATION;
+
+ tsp = (struct tty_status *)data;
+
+ if (tsp->tt_ispeed < 0 ||
+ tsp->tt_ispeed >= NSPEEDS ||
+ tsp->tt_ospeed < 0 ||
+ tsp->tt_ospeed >= NSPEEDS)
+ {
+ return D_INVALID_OPERATION;
+ }
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+
+ tp->t_ispeed = tsp->tt_ispeed;
+ tp->t_ospeed = tsp->tt_ospeed;
+ tp->t_breakc = tsp->tt_breakc;
+ tp->t_flags = tsp->tt_flags & ~TF_HUPCLS;
+ if (tsp->tt_flags & TF_HUPCLS)
+ tp->t_state |= TS_HUPCLS;
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ break;
+ }
+ default:
+ return D_INVALID_OPERATION;
+ }
+ return D_SUCCESS;
+}
+
+
+/*
+ * [internal]
+ * Queue IOR on reply queue, to wait for TTY operation.
+ * TTY must be locked (at spltty).
+ */
+void queue_delayed_reply(
+ queue_t qh,
+ io_req_t ior,
+ boolean_t (*io_done)(io_req_t) )
+{
+ ior->io_done = io_done;
+ enqueue_tail(qh, (queue_entry_t)ior);
+}
+
+/*
+ * Retry delayed IO operations for TTY.
+ * TTY containing queue must be locked (at spltty).
+ */
+void tty_queue_completion(
+ register queue_t qh)
+{
+ register io_req_t ior;
+
+ while ((ior = (io_req_t)dequeue_head(qh)) != 0) {
+ iodone(ior);
+ }
+}
+
+/*
+ * Set the default special characters.
+ * Since this routine is called whenever a tty has never been opened,
+ * we can initialize the queues here.
+ */
+void ttychars(
+ register struct tty *tp)
+{
+ if ((tp->t_flags & TS_INIT) == 0) {
+ /*
+ * Initialize queues
+ */
+ queue_init(&tp->t_delayed_open);
+ queue_init(&tp->t_delayed_read);
+ queue_init(&tp->t_delayed_write);
+
+ /*
+ * Initialize character buffers
+ */
+ cb_alloc(&tp->t_inq, tty_inq_size);
+
+ /* if we might do modem flow control */
+ if (tp->t_mctl && tp->t_inq.c_hog > 30)
+ tp->t_inq.c_hog -= 30;
+
+ cb_alloc(&tp->t_outq, tty_outq_size);
+
+ /*
+ * Mark initialized
+ */
+ tp->t_state |= TS_INIT;
+ }
+
+ tp->t_breakc = 0;
+}
+
+/*
+ * Flush all TTY queues.
+ * Called at spltty, tty already locked.
+ * Calls device STOP routine; must already be on master if
+ * device needs to run on master.
+ */
+void tty_flush(
+ register struct tty *tp,
+ int rw)
+{
+ if (rw & D_READ) {
+ cb_clear(&tp->t_inq);
+ tty_queue_completion(&tp->t_delayed_read);
+ }
+ if (rw & D_WRITE) {
+ tp->t_state &= ~TS_TTSTOP;
+ (*tp->t_stop)(tp, rw);
+ cb_clear(&tp->t_outq);
+ tty_queue_completion(&tp->t_delayed_write);
+ }
+}
+
+/*
+ * Restart character output after a delay timeout.
+ * Calls device start routine - must be on master CPU.
+ *
+ * Timeout routines are called only on master CPU.
+ * What if device runs on a different CPU?
+ */
+void ttrstrt(
+ register struct tty *tp)
+{
+ register spl_t s;
+
+ s = spltty();
+ simple_lock(&tp->t_lock);
+
+ tp->t_state &= ~TS_TIMEOUT;
+ ttstart (tp);
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+}
+
+/*
+ * Start output on the typewriter. It is used from the top half
+ * after some characters have been put on the output queue,
+ * from the interrupt routine to transmit the next
+ * character, and after a timeout has finished.
+ *
+ * Called at spltty, tty already locked.
+ * Must be on master CPU if device runs on master.
+ */
+void ttstart(tp)
+ register struct tty *tp;
+{
+ if ((tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) == 0) {
+ /*
+ * Start up the hardware again
+ */
+ (*tp->t_start)(tp);
+
+ /*
+ * Wake up those waiting for write completion.
+ */
+ if (tp->t_outq.c_cc <= TTLOWAT(tp))
+ tty_queue_completion(&tp->t_delayed_write);
+ }
+}
+
+/*
+ * Start character output, if the device is not busy or
+ * stopped or waiting for a timeout.
+ *
+ * Called at spltty, tty already locked.
+ * Must be on master CPU if device runs on master.
+ */
+void tty_output(
+ register struct tty *tp)
+{
+ if ((tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) == 0) {
+ /*
+ * Not busy. Start output.
+ */
+ (*tp->t_start)(tp);
+
+ /*
+ * Wake up those waiting for write completion.
+ */
+ if (tp->t_outq.c_cc <= TTLOWAT(tp))
+ tty_queue_completion(&tp->t_delayed_write);
+ }
+}
+
+/*
+ * Send any buffered recvd chars up to user
+ */
+void ttypush(
+ register struct tty *tp)
+{
+ spl_t s = spltty();
+ register int state;
+
+ simple_lock(&tp->t_lock);
+
+ /*
+ The pdma timeout has gone off.
+ If no character has been received since the timeout
+ was set, push any pending characters up.
+ If any characters were received in the last interval
+ then just reset the timeout and the character received bit.
+ */
+
+ state = tp->t_state;
+
+ if (state & TS_MIN_TO)
+ {
+ if (state & TS_MIN_TO_RCV)
+ { /* a character was received */
+ tp->t_state = state & ~TS_MIN_TO_RCV;
+ timeout(ttypush,tp,pdma_timeouts[tp->t_ispeed]);
+ }
+ else
+ {
+ tp->t_state = state & ~TS_MIN_TO;
+ if (tp->t_inq.c_cc) /* pending characters */
+ tty_queue_completion(&tp->t_delayed_read);
+ }
+ }
+ else
+ {
+ tp->t_state = state & ~TS_MIN_TO_RCV;/* sanity */
+ }
+
+ simple_unlock(&tp->t_lock);
+ splx(s);
+}
+
+/*
+ * Put input character on input queue.
+ *
+ * Called at spltty, tty already locked.
+ */
+void ttyinput(
+ unsigned int c,
+ struct tty *tp)
+{
+ if (tp->t_inq.c_cc >= tp->t_inq.c_hog) {
+ /*
+ * Do not want to overflow input queue
+ */
+ if (tp->t_mctl) {
+ (*tp->t_mctl)(tp, TM_RTS, DMBIC);
+ tp->t_state |= TS_RTS_DOWN;
+ }
+ tty_queue_completion(&tp->t_delayed_read);
+ return;
+
+ }
+
+ c &= 0xff;
+
+ (void) putc(c, &tp->t_inq);
+ if ((tp->t_state & TS_MIN) == 0 ||
+ tp->t_inq.c_cc > pdma_water_mark[tp->t_ispeed])
+ {
+ /*
+ * No input buffering, or input minimum exceeded.
+ * Grab a request from input queue and queue it
+ * to io_done thread.
+ */
+ if (tp->t_state & TS_MIN_TO) {
+ tp->t_state &= ~(TS_MIN_TO|TS_MIN_TO_RCV);
+ untimeout(ttypush, tp);
+ }
+ tty_queue_completion(&tp->t_delayed_read);
+ }
+ else {
+ /*
+ * Not enough characters.
+ * If no timeout is set, initiate the timeout
+ * Otherwise set the character received during timeout interval
+ * flag.
+ * One alternative approach would be just to reset the timeout
+ * into the future, but this involves making a timeout/untimeout
+ * call on every character.
+ */
+ register int ptime = pdma_timeouts[tp->t_ispeed];
+ if (ptime > 0)
+ {
+ if ((tp->t_state & TS_MIN_TO) == 0)
+ {
+ tp->t_state |= TS_MIN_TO;
+ timeout(ttypush, tp, ptime);
+ }
+ else
+ {
+ tp->t_state |= TS_MIN_TO_RCV;
+ }
+ }
+ }
+}
+
+/*
+ * Put many characters on input queue.
+ *
+ * Called at spltty, tty already locked.
+ */
+void ttyinput_many(
+ struct tty *tp,
+ unsigned char *chars,
+ int count)
+{
+ /*
+ * Do not want to overflow input queue
+ */
+ if (tp->t_inq.c_cc < tp->t_inq.c_hog)
+ count -= b_to_q( chars, count, &tp->t_inq);
+
+ tty_queue_completion(&tp->t_delayed_read);
+}
+
+
+/*
+ * Handle modem control transition on a tty.
+ * Flag indicates new state of carrier.
+ * Returns FALSE if the line should be turned off.
+ *
+ * Called at spltty, tty already locked.
+ */
+boolean_t ttymodem(
+ struct tty * tp,
+ boolean_t carrier_up)
+{
+ if ((tp->t_state&TS_WOPEN) == 0 && (tp->t_flags & MDMBUF)) {
+ /*
+ * Flow control by carrier. Carrier down stops
+ * output; carrier up restarts output.
+ */
+ if (carrier_up) {
+ tp->t_state &= ~TS_TTSTOP;
+ tty_output(tp);
+ }
+ else if ((tp->t_state&TS_TTSTOP) == 0) {
+ tp->t_state |= TS_TTSTOP;
+ (*tp->t_stop)(tp, 0);
+ }
+ }
+ else if (carrier_up) {
+ /*
+ * Carrier now on.
+ */
+ tp->t_state |= TS_CARR_ON;
+ tt_open_wakeup(tp);
+ }
+ else {
+ /*
+ * Lost carrier.
+ */
+ tp->t_state &= ~TS_CARR_ON;
+ if (tp->t_state & TS_ISOPEN &&
+ (tp->t_flags & NOHANG) == 0)
+ {
+ /*
+ * Hang up TTY if carrier drops.
+ * Need to alert users, somehow...
+ */
+ tty_flush(tp, D_READ|D_WRITE);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/*
+ * Similarly, handle transitions on the ClearToSend
+ * signal. Nowadays, it is used by many modems as
+ * a flow-control device: they turn it down to stop
+ * us from sending more chars. We do the same with
+ * the RequestToSend signal. [Yes, that is exactly
+ * why those signals are defined in the standard.]
+ *
+ * Tty must be locked and on master.
+ */
+tty_cts(
+ struct tty * tp,
+ boolean_t cts_up)
+{
+ if (tp->t_state & TS_ISOPEN){
+ if (cts_up) {
+ tp->t_state &= ~(TS_TTSTOP|TS_BUSY);
+ tty_output(tp);
+ } else {
+ tp->t_state |= (TS_TTSTOP|TS_BUSY);
+ (*tp->t_stop)(tp, D_WRITE);
+ }
+ }
+}
diff --git a/device/cirbuf.c b/device/cirbuf.c
new file mode 100644
index 0000000..9653168
--- /dev/null
+++ b/device/cirbuf.c
@@ -0,0 +1,298 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ *
+ * Circular buffers for TTY
+ */
+
+#include <device/cirbuf.h>
+#include <kern/kalloc.h>
+
+
+
+/* read at c_cf, write at c_cl */
+/* if c_cf == c_cl, buffer is empty */
+/* if c_cl == c_cf - 1, buffer is full */
+
+#if DEBUG
+int cb_check_enable = 0;
+#define CB_CHECK(cb) if (cb_check_enable) cb_check(cb)
+
+void
+cb_check(register struct cirbuf *cb)
+{
+ if (!(cb->c_cf >= cb->c_start && cb->c_cf < cb->c_end))
+ panic("cf %x out of range [%x..%x)",
+ cb->c_cf, cb->c_start, cb->c_end);
+ if (!(cb->c_cl >= cb->c_start && cb->c_cl < cb->c_end))
+ panic("cl %x out of range [%x..%x)",
+ cb->c_cl, cb->c_start, cb->c_end);
+ if (cb->c_cf <= cb->c_cl) {
+ if (!(cb->c_cc == cb->c_cl - cb->c_cf))
+ panic("cc %x should be %x",
+ cb->c_cc,
+ cb->c_cl - cb->c_cf);
+ }
+ else {
+ if (!(cb->c_cc == cb->c_end - cb->c_cf
+ + cb->c_cl - cb->c_start))
+ panic("cc %x should be %x",
+ cb->c_cc,
+ cb->c_end - cb->c_cf +
+ cb->c_cl - cb->c_start);
+ }
+}
+#else /* DEBUG */
+#define CB_CHECK(cb)
+#endif /* DEBUG */
+
+/*
+ * Put one character in circular buffer.
+ */
+int putc(
+ int c,
+ register struct cirbuf *cb)
+{
+ register char *ow, *nw;
+
+ ow = cb->c_cl;
+ nw = ow+1;
+ if (nw == cb->c_end)
+ nw = cb->c_start;
+ if (nw == cb->c_cf)
+ return 1; /* not entered */
+ *ow = c;
+ cb->c_cl = nw;
+
+ cb->c_cc++;
+
+ CB_CHECK(cb);
+
+ return 0;
+}
+
+/*
+ * Get one character from circular buffer.
+ */
+int getc(register struct cirbuf *cb)
+{
+ register unsigned char *nr;
+ register int c;
+
+ nr = (unsigned char *)cb->c_cf;
+ if (nr == (unsigned char *)cb->c_cl) {
+ CB_CHECK(cb);
+ return -1; /* empty */
+ }
+ c = *nr;
+ nr++;
+ if (nr == (unsigned char *)cb->c_end)
+ nr = (unsigned char *)cb->c_start;
+ cb->c_cf = (char *)nr;
+
+ cb->c_cc--;
+
+ CB_CHECK(cb);
+
+ return c;
+}
+
+/*
+ * Get lots of characters.
+ * Return number moved.
+ */
+int
+q_to_b( register struct cirbuf *cb,
+ register char *cp,
+ register int count)
+{
+ char * ocp = cp;
+ register int i;
+
+ while (count != 0) {
+ if (cb->c_cl == cb->c_cf)
+ break; /* empty */
+ if (cb->c_cl < cb->c_cf)
+ i = cb->c_end - cb->c_cf;
+ else
+ i = cb->c_cl - cb->c_cf;
+ if (i > count)
+ i = count;
+ bcopy(cb->c_cf, cp, i);
+ cp += i;
+ count -= i;
+ cb->c_cf += i;
+ cb->c_cc -= i;
+ if (cb->c_cf == cb->c_end)
+ cb->c_cf = cb->c_start;
+
+ CB_CHECK(cb);
+ }
+ CB_CHECK(cb);
+
+ return cp - ocp;
+}
+
+/*
+ * Add character array to buffer and return number of characters
+ * NOT entered.
+ */
+int
+b_to_q( register char * cp,
+ int count,
+ register struct cirbuf *cb)
+{
+ register int i;
+ register char *lim;
+
+ while (count != 0) {
+ lim = cb->c_cf - 1;
+ if (lim < cb->c_start)
+ lim = cb->c_end - 1;
+
+ if (cb->c_cl == lim)
+ break;
+ if (cb->c_cl < lim)
+ i = lim - cb->c_cl;
+ else
+ i = cb->c_end - cb->c_cl;
+
+ if (i > count)
+ i = count;
+ bcopy(cp, cb->c_cl, i);
+ cp += i;
+ count -= i;
+ cb->c_cc += i;
+ cb->c_cl += i;
+ if (cb->c_cl == cb->c_end)
+ cb->c_cl = cb->c_start;
+
+ CB_CHECK(cb);
+ }
+ CB_CHECK(cb);
+ return count;
+}
+
+/*
+ * Return number of contiguous characters up to a character
+ * that matches the mask.
+ */
+int
+ndqb( register struct cirbuf *cb,
+ register int mask)
+{
+ register char *cp, *lim;
+
+ if (cb->c_cl < cb->c_cf)
+ lim = cb->c_end;
+ else
+ lim = cb->c_cl;
+ if (mask == 0)
+ return (lim - cb->c_cf);
+ cp = cb->c_cf;
+ while (cp < lim) {
+ if (*cp & mask)
+ break;
+ cp++;
+ }
+ return (cp - cb->c_cf);
+}
+
+/*
+ * Flush characters from circular buffer.
+ */
+void
+ndflush(register struct cirbuf *cb,
+ register int count)
+{
+ register int i;
+
+ while (count != 0) {
+ if (cb->c_cl == cb->c_cf)
+ break; /* empty */
+ if (cb->c_cl < cb->c_cf)
+ i = cb->c_end - cb->c_cf;
+ else
+ i = cb->c_cl - cb->c_cf;
+ if (i > count)
+ i = count;
+ count -= i;
+ cb->c_cf += i;
+ cb->c_cc -= i;
+ if (cb->c_cf == cb->c_end)
+ cb->c_cf = cb->c_start;
+ CB_CHECK(cb);
+ }
+
+ CB_CHECK(cb);
+}
+
+/*
+ * Empty a circular buffer.
+ */
+void cb_clear(struct cirbuf *cb)
+{
+ cb->c_cf = cb->c_start;
+ cb->c_cl = cb->c_start;
+ cb->c_cc = 0;
+}
+
+/*
+ * Allocate character space for a circular buffer.
+ */
+void
+cb_alloc(
+ register struct cirbuf *cb,
+ int buf_size)
+{
+ register char *buf;
+
+ buf = (char *)kalloc(buf_size);
+
+ cb->c_start = buf;
+ cb->c_end = buf + buf_size;
+ cb->c_cf = buf;
+ cb->c_cl = buf;
+ cb->c_cc = 0;
+ cb->c_hog = buf_size - 1;
+
+ CB_CHECK(cb);
+}
+
+/*
+ * Free character space for a circular buffer.
+ */
+void
+cb_free(register struct cirbuf *cb)
+{
+ int size;
+
+ size = cb->c_end - cb->c_start;
+ kfree((vm_offset_t)cb->c_start, size);
+}
+
diff --git a/device/cirbuf.h b/device/cirbuf.h
new file mode 100644
index 0000000..a3f50ce
--- /dev/null
+++ b/device/cirbuf.h
@@ -0,0 +1,62 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#ifndef _DEVICE_CIRBUF_H_
+#define _DEVICE_CIRBUF_H_
+
+/*
+ * Circular buffers for TTY
+ */
+
+struct cirbuf {
+ char * c_start; /* start of buffer */
+ char * c_end; /* end of buffer + 1*/
+ char * c_cf; /* read pointer */
+ char * c_cl; /* write pointer */
+ short c_cc; /* current number of characters
+ (compatibility) */
+ short c_hog; /* max ever */
+};
+
+/*
+ * Exported routines
+ */
+extern int putc(int, struct cirbuf *);
+extern int getc(struct cirbuf *);
+extern int q_to_b(struct cirbuf *, char *, int);
+extern int b_to_q(char *, int, struct cirbuf *);
+extern int nqdb(struct cirbuf *, int);
+extern void ndflush(struct cirbuf *, int);
+extern void cb_clear(struct cirbuf *);
+
+extern void cb_alloc(struct cirbuf *, int);
+extern void cb_free(struct cirbuf *);
+
+#endif /* _DEVICE_CIRBUF_H_ */
diff --git a/device/conf.h b/device/conf.h
new file mode 100644
index 0000000..7ca5dd6
--- /dev/null
+++ b/device/conf.h
@@ -0,0 +1,114 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/88
+ */
+
+#ifndef _DEVICE_CONF_H_
+#define _DEVICE_CONF_H_
+
+#include <mach/machine/vm_types.h>
+
+/*
+ * Operations list for major device types.
+ */
+struct dev_ops {
+ char * d_name; /* name for major device */
+ int (*d_open)(); /* open device */
+ int (*d_close)(); /* close device */
+ int (*d_read)(); /* read */
+ int (*d_write)(); /* write */
+ int (*d_getstat)(); /* get status/control */
+ int (*d_setstat)(); /* set status/control */
+ vm_offset_t (*d_mmap)(); /* map memory */
+ int (*d_async_in)();/* asynchronous input setup */
+ int (*d_reset)(); /* reset device */
+ int (*d_port_death)();
+ /* clean up reply ports */
+ int d_subdev; /* number of sub-devices per
+ unit */
+ int (*d_dev_info)(); /* driver info for kernel */
+};
+typedef struct dev_ops *dev_ops_t;
+
+/*
+ * Routines for null entries.
+ */
+extern int nulldev(); /* no operation - OK */
+extern int nodev(); /* no operation - error */
+extern vm_offset_t nomap(); /* no operation - error */
+
+/*
+ * Flavor constants for d_dev_info routine
+ */
+#define D_INFO_BLOCK_SIZE 1
+
+/*
+ * Head of list of attached devices
+ */
+extern struct dev_ops dev_name_list[];
+extern int dev_name_count;
+
+/*
+ * Macro to search device list
+ */
+#define dev_search(dp) \
+ for (dp = dev_name_list; \
+ dp < &dev_name_list[dev_name_count]; \
+ dp++)
+
+/*
+ * Indirection vectors for certain devices.
+ */
+struct dev_indirect {
+ char * d_name; /* name for device */
+ dev_ops_t d_ops; /* operations (major device) */
+ int d_unit; /* and unit number */
+};
+typedef struct dev_indirect *dev_indirect_t;
+
+/*
+ * List of indirect devices.
+ */
+extern struct dev_indirect dev_indirect_list[];
+extern int dev_indirect_count;
+
+/*
+ * Macro to search indirect list
+ */
+#define dev_indirect_search(di) \
+ for (di = dev_indirect_list; \
+ di < &dev_indirect_list[dev_indirect_count]; \
+ di++)
+
+/*
+ * Exported routine to set indirection.
+ */
+extern void dev_set_indirect();
+
+#endif /* _DEVICE_CONF_H_ */
+
diff --git a/device/cons.c b/device/cons.c
new file mode 100644
index 0000000..954f527
--- /dev/null
+++ b/device/cons.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1988-1994, The University of Utah and
+ * the Computer Systems Laboratory (CSL). All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Utah $Hdr: cons.c 1.14 94/12/14$
+ */
+
+#ifdef MACH_KERNEL
+#include <sys/types.h>
+#include <device/conf.h>
+#include <mach/boolean.h>
+#include <cons.h>
+#else
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/file.h>
+#include <sys/conf.h>
+#include <hpdev/cons.h>
+#endif
+
+static int cn_inited = 0;
+static struct consdev *cn_tab = 0; /* physical console device info */
+#ifndef MACH_KERNEL
+static struct tty *constty = 0; /* virtual console output device */
+#endif
+
+/*
+ * ROM getc/putc primitives.
+ * On some architectures, the boot ROM provides basic character input/output
+ * routines that can be used before devices are configured or virtual memory
+ * is enabled. This can be useful to debug (or catch panics from) code early
+ * in the bootstrap procedure.
+ */
+int (*romgetc)() = 0;
+void (*romputc)() = 0;
+
+#if CONSBUFSIZE > 0
+/*
+ * Temporary buffer to store console output before a console is selected.
+ * This is statically allocated so it can be called before malloc/kmem_alloc
+ * have been initialized. It is initialized so it won't be clobbered as
+ * part of the zeroing of BSS (on PA/Mach).
+ */
+static char consbuf[CONSBUFSIZE] = { 0 };
+static char *consbp = consbuf;
+static int consbufused = 0;
+#endif
+
+cninit()
+{
+ struct consdev *cp;
+#ifdef MACH_KERNEL
+ dev_ops_t cn_ops;
+ int x;
+#endif
+
+ if (cn_inited)
+ return;
+
+ /*
+ * Collect information about all possible consoles
+ * and find the one with highest priority
+ */
+ for (cp = constab; cp->cn_probe; cp++) {
+ (*cp->cn_probe)(cp);
+ if (cp->cn_pri > CN_DEAD &&
+ (cn_tab == NULL || cp->cn_pri > cn_tab->cn_pri))
+ cn_tab = cp;
+ }
+ /*
+ * Found a console, initialize it.
+ */
+ if (cp = cn_tab) {
+ /*
+ * Initialize as console
+ */
+ (*cp->cn_init)(cp);
+#ifdef MACH_KERNEL
+ /*
+ * Look up its dev_ops pointer in the device table and
+ * place it in the device indirection table.
+ */
+ if (dev_name_lookup(cp->cn_name, &cn_ops, &x) == FALSE)
+ panic("cninit: dev_name_lookup failed");
+ dev_set_indirection("console", cn_ops, minor(cp->cn_dev));
+#endif
+#if CONSBUFSIZE > 0
+ /*
+ * Now that the console is initialized, dump any chars in
+ * the temporary console buffer.
+ */
+ if (consbufused) {
+ char *cbp = consbp;
+ do {
+ if (*cbp)
+ cnputc(*cbp);
+ if (++cbp == &consbuf[CONSBUFSIZE])
+ cbp = consbuf;
+ } while (cbp != consbp);
+ consbufused = 0;
+ }
+#endif
+ cn_inited = 1;
+ return;
+ }
+ /*
+ * No console device found, not a problem for BSD, fatal for Mach
+ */
+#ifdef MACH_KERNEL
+ panic("can't find a console device");
+#endif
+}
+
+#ifndef MACH_KERNEL
+cnopen(dev, flag)
+ dev_t dev;
+{
+ if (cn_tab == NULL)
+ return(0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_open)(dev, flag));
+}
+
+cnclose(dev, flag)
+ dev_t dev;
+{
+ if (cn_tab == NULL)
+ return(0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_close)(dev, flag));
+}
+
+cnread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ if (cn_tab == NULL)
+ return(0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_read)(dev, uio));
+}
+
+cnwrite(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ if (cn_tab == NULL)
+ return(0);
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_write)(dev, uio));
+}
+
+cnioctl(dev, cmd, data, flag)
+ dev_t dev;
+ caddr_t data;
+{
+ if (cn_tab == NULL)
+ return(0);
+ /*
+ * Superuser can always use this to wrest control of console
+ * output from the "virtual" console.
+ */
+ if (cmd == TIOCCONS && constty) {
+ if (!suser())
+ return(EPERM);
+ constty = NULL;
+ return(0);
+ }
+ dev = cn_tab->cn_dev;
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, cmd, data, flag));
+}
+
+cnselect(dev, rw)
+ dev_t dev;
+ int rw;
+{
+ if (cn_tab == NULL)
+ return(1);
+ return(ttselect(cn_tab->cn_dev, rw));
+}
+
+#ifndef hp300
+/*
+ * XXX Should go away when the new CIO MUX driver is in place
+ */
+#define d_control d_mmap
+cncontrol(dev, cmd, data)
+ dev_t dev;
+ int cmd;
+ int data;
+{
+ if (cn_tab == NULL)
+ return(0);
+ dev = cn_tab->cn_dev;
+ return((*cdevsw[major(dev)].d_control)(dev, cmd, data));
+}
+#undef d_control
+#endif
+#endif
+
+cngetc()
+{
+ if (cn_tab)
+ return ((*cn_tab->cn_getc)(cn_tab->cn_dev, 1));
+ if (romgetc)
+ return ((*romgetc)(1));
+ return (0);
+}
+
+#ifdef MACH_KERNEL
+cnmaygetc()
+{
+ if (cn_tab)
+ return((*cn_tab->cn_getc)(cn_tab->cn_dev, 0));
+ if (romgetc)
+ return ((*romgetc)(0));
+ return (0);
+}
+#endif
+
+cnputc(c)
+ int c;
+{
+ if (c == 0)
+ return;
+
+ if (cn_tab) {
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, c);
+ if (c == '\n')
+ (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r');
+ } else if (romputc) {
+ (*romputc)(c);
+ if (c == '\n')
+ (*romputc)('\r');
+ }
+#if CONSBUFSIZE > 0
+ else {
+ if (consbufused == 0) {
+ consbp = consbuf;
+ consbufused = 1;
+ bzero(consbuf, CONSBUFSIZE);
+ }
+ *consbp++ = c;
+ if (consbp >= &consbuf[CONSBUFSIZE])
+ consbp = consbuf;
+ }
+#endif
+}
diff --git a/device/cons.h b/device/cons.h
new file mode 100644
index 0000000..bcaeffc
--- /dev/null
+++ b/device/cons.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1988-1994, The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Utah $Hdr: cons.h 1.10 94/12/14$
+ */
+
+struct consdev {
+#ifdef MACH_KERNEL
+ char *cn_name; /* name of device in dev_name_list */
+#endif
+ int (*cn_probe)(); /* probe hardware and fill in consdev info */
+ int (*cn_init)(); /* turn on as console */
+ int (*cn_getc)(); /* kernel getchar interface */
+ int (*cn_putc)(); /* kernel putchar interface */
+ dev_t cn_dev; /* major/minor of device */
+ short cn_pri; /* pecking order; the higher the better */
+};
+
+/* values for cn_pri - reflect our policy for console selection */
+#define CN_DEAD 0 /* device doesn't exist */
+#define CN_NORMAL 1 /* device exists but is nothing special */
+#define CN_INTERNAL 2 /* "internal" bit-mapped display */
+#define CN_REMOTE 3 /* serial interface with remote bit set */
+
+/* XXX */
+#define CONSMAJOR 0
+
+#define CONSBUFSIZE 1024
+
+#ifdef KERNEL
+extern struct consdev constab[];
+extern struct consdev *cn_tab;
+#endif
diff --git a/device/dev_forward.defs b/device/dev_forward.defs
new file mode 100644
index 0000000..a237bb8
--- /dev/null
+++ b/device/dev_forward.defs
@@ -0,0 +1,44 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: Joseph S. Barrera, Carnegie Mellon University
+ * Date: 12/90
+ */
+
+subsystem KernelUser dev_forward 2800;
+
+#include <mach/std_types.defs>
+#include <mach/mach_types.defs>
+#include <device/device_types.defs>
+
+type reply_port_t = MACH_MSG_TYPE_MOVE_SEND_ONCE | polymorphic
+ ctype: mach_port_t;
+
+simpleroutine forward_device_open_send(
+ master_port : mach_port_t;
+ ureplyport reply_port : reply_port_t;
+ mode : dev_mode_t;
+ name : dev_name_t);
diff --git a/device/dev_hdr.h b/device/dev_hdr.h
new file mode 100644
index 0000000..b976caf
--- /dev/null
+++ b/device/dev_hdr.h
@@ -0,0 +1,108 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/89
+ */
+
+#ifndef _DEVICE_DEV_HDR_H_
+#define _DEVICE_DEV_HDR_H_
+
+#include <mach/port.h>
+#include <kern/lock.h>
+#include <kern/queue.h>
+
+#include <device/conf.h>
+
+#ifdef i386
+#include <i386at/dev_hdr.h>
+#else
+#define mach_device device
+#define mach_device_t device_t
+#define MACH_DEVICE_NULL DEVICE_NULL
+#define mach_device_reference device_reference
+#define mach_device_deallocate device_deallocate
+#define mach_convert_device_to_port convert_device_to_port
+#endif
+
+/*
+ * Generic device header. May be allocated with the device,
+ * or built when the device is opened.
+ */
+struct mach_device {
+ decl_simple_lock_data(,ref_lock)/* lock for reference count */
+ int ref_count; /* reference count */
+ decl_simple_lock_data(, lock) /* lock for rest of state */
+ short state; /* state: */
+#define DEV_STATE_INIT 0 /* not open */
+#define DEV_STATE_OPENING 1 /* being opened */
+#define DEV_STATE_OPEN 2 /* open */
+#define DEV_STATE_CLOSING 3 /* being closed */
+ short flag; /* random flags: */
+#define D_EXCL_OPEN 0x0001 /* open only once */
+ short open_count; /* number of times open */
+ short io_in_progress; /* number of IOs in progress */
+ boolean_t io_wait; /* someone waiting for IO to finish */
+
+ struct ipc_port *port; /* open port */
+ queue_chain_t number_chain; /* chain for lookup by number */
+ int dev_number; /* device number */
+ int bsize; /* replacement for DEV_BSIZE */
+ struct dev_ops *dev_ops; /* and operations vector */
+#ifdef i386
+ struct device dev; /* the real device structure */
+#endif
+};
+typedef struct mach_device *mach_device_t;
+#define MACH_DEVICE_NULL ((mach_device_t)0)
+
+/*
+ * To find and remove device entries
+ */
+mach_device_t device_lookup(); /* by name */
+
+void mach_device_reference();
+void mach_device_deallocate();
+
+/*
+ * To find and remove port-to-device mappings
+ */
+device_t dev_port_lookup();
+void dev_port_enter();
+void dev_port_remove();
+
+/*
+ * To call a routine on each device
+ */
+boolean_t dev_map();
+
+/*
+ * To lock and unlock state and open-count
+ */
+#define device_lock(device) simple_lock(&(device)->lock)
+#define device_unlock(device) simple_unlock(&(device)->lock)
+
+#endif /* _DEVICE_DEV_HDR_H_ */
diff --git a/device/dev_lookup.c b/device/dev_lookup.c
new file mode 100644
index 0000000..746c394
--- /dev/null
+++ b/device/dev_lookup.c
@@ -0,0 +1,409 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/89
+ */
+
+#include <mach/port.h>
+#include <mach/vm_param.h>
+
+#include <kern/queue.h>
+#include <kern/zalloc.h>
+
+#include <device/device_types.h>
+#include <device/dev_hdr.h>
+#include <device/conf.h>
+#include <device/param.h> /* DEV_BSIZE, as default */
+
+#include <ipc/ipc_port.h>
+#include <kern/ipc_kobject.h>
+
+#ifdef i386
+#include <i386at/device_emul.h>
+#endif
+
+/*
+ * Device structure routines: reference counting, port->device.
+ */
+
+/*
+ * Lookup/enter by device number.
+ */
+#define NDEVHASH 8
+#define DEV_NUMBER_HASH(dev) ((dev) & (NDEVHASH-1))
+queue_head_t dev_number_hash_table[NDEVHASH];
+
+/*
+ * Lock for device-number to device lookup.
+ * Must be held before device-ref_count lock.
+ */
+decl_simple_lock_data(,
+ dev_number_lock)
+
+zone_t dev_hdr_zone;
+
+/*
+ * Enter device in the number lookup table.
+ * The number table lock must be held.
+ */
+void
+dev_number_enter(device)
+ register mach_device_t device;
+{
+ register queue_t q;
+
+ q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)];
+ queue_enter(q, device, mach_device_t, number_chain);
+}
+
+/*
+ * Remove device from the device-number lookup table.
+ * The device-number table lock must be held.
+ */
+void
+dev_number_remove(device)
+ register mach_device_t device;
+{
+ register queue_t q;
+
+ q = &dev_number_hash_table[DEV_NUMBER_HASH(device->dev_number)];
+ queue_remove(q, device, mach_device_t, number_chain);
+}
+
+/*
+ * Lookup a device by device operations and minor number.
+ * The number table lock must be held.
+ */
+mach_device_t
+dev_number_lookup(ops, devnum)
+ dev_ops_t ops;
+ int devnum;
+{
+ register queue_t q;
+ register mach_device_t device;
+
+ q = &dev_number_hash_table[DEV_NUMBER_HASH(devnum)];
+ queue_iterate(q, device, mach_device_t, number_chain) {
+ if (device->dev_ops == ops && device->dev_number == devnum) {
+ return (device);
+ }
+ }
+ return (MACH_DEVICE_NULL);
+}
+
+/*
+ * Look up a device by name, and create the device structure
+ * if it does not exist. Enter it in the dev_number lookup
+ * table.
+ */
+mach_device_t
+device_lookup(name)
+ char * name;
+{
+ dev_ops_t dev_ops;
+ int dev_minor;
+ register mach_device_t device;
+ register mach_device_t new_device;
+
+ /*
+ * Get the device and unit number from the name.
+ */
+ if (!dev_name_lookup(name, &dev_ops, &dev_minor))
+ return (MACH_DEVICE_NULL);
+
+ /*
+ * Look up the device in the hash table. If it is
+ * not there, enter it.
+ */
+ new_device = MACH_DEVICE_NULL;
+ simple_lock(&dev_number_lock);
+ while ((device = dev_number_lookup(dev_ops, dev_minor))
+ == MACH_DEVICE_NULL) {
+ /*
+ * Must unlock to allocate the structure. If
+ * the structure has appeared after we have allocated,
+ * release the new structure.
+ */
+ if (new_device != MACH_DEVICE_NULL)
+ break; /* allocated */
+
+ simple_unlock(&dev_number_lock);
+
+ new_device = (mach_device_t) zalloc(dev_hdr_zone);
+ simple_lock_init(&new_device->ref_lock);
+ new_device->ref_count = 1;
+ simple_lock_init(&new_device->lock);
+ new_device->state = DEV_STATE_INIT;
+ new_device->flag = 0;
+ new_device->open_count = 0;
+ new_device->io_in_progress = 0;
+ new_device->io_wait = FALSE;
+ new_device->port = IP_NULL;
+ new_device->dev_ops = dev_ops;
+ new_device->dev_number = dev_minor;
+ new_device->bsize = DEV_BSIZE; /* change later */
+
+ simple_lock(&dev_number_lock);
+ }
+
+ if (device == MACH_DEVICE_NULL) {
+ /*
+ * No existing device structure. Insert the
+ * new one.
+ */
+ assert(new_device != MACH_DEVICE_NULL);
+ device = new_device;
+
+ dev_number_enter(device);
+ simple_unlock(&dev_number_lock);
+ }
+ else {
+ /*
+ * Have existing device.
+ */
+ mach_device_reference(device);
+ simple_unlock(&dev_number_lock);
+
+ if (new_device != MACH_DEVICE_NULL)
+ zfree(dev_hdr_zone, (vm_offset_t)new_device);
+ }
+
+ return (device);
+}
+
+/*
+ * Add a reference to the device.
+ */
+void
+mach_device_reference(device)
+ register mach_device_t device;
+{
+ simple_lock(&device->ref_lock);
+ device->ref_count++;
+ simple_unlock(&device->ref_lock);
+}
+
+/*
+ * Remove a reference to the device, and deallocate the
+ * structure if no references are left.
+ */
+void
+mach_device_deallocate(device)
+ register mach_device_t device;
+{
+ simple_lock(&device->ref_lock);
+ if (--device->ref_count > 0) {
+ simple_unlock(&device->ref_lock);
+ return;
+ }
+ device->ref_count = 1;
+ simple_unlock(&device->ref_lock);
+
+ simple_lock(&dev_number_lock);
+ simple_lock(&device->ref_lock);
+ if (--device->ref_count > 0) {
+ simple_unlock(&device->ref_lock);
+ simple_unlock(&dev_number_lock);
+ return;
+ }
+
+ dev_number_remove(device);
+ simple_unlock(&device->ref_lock);
+ simple_unlock(&dev_number_lock);
+
+ zfree(dev_hdr_zone, (vm_offset_t)device);
+}
+
+/*
+
+ */
+/*
+ * port-to-device lookup routines.
+ */
+decl_simple_lock_data(,
+ dev_port_lock)
+
+/*
+ * Enter a port-to-device mapping.
+ */
+void
+dev_port_enter(device)
+ register mach_device_t device;
+{
+ mach_device_reference(device);
+#ifdef i386
+ ipc_kobject_set(device->port,
+ (ipc_kobject_t) &device->dev, IKOT_DEVICE);
+ device->dev.emul_data = device;
+ {
+ extern struct device_emulation_ops mach_device_emulation_ops;
+
+ device->dev.emul_ops = &mach_device_emulation_ops;
+ }
+#else
+ ipc_kobject_set(device->port, (ipc_kobject_t) device, IKOT_DEVICE);
+#endif
+}
+
+/*
+ * Remove a port-to-device mapping.
+ */
+void
+dev_port_remove(device)
+ register mach_device_t device;
+{
+ ipc_kobject_set(device->port, IKO_NULL, IKOT_NONE);
+ mach_device_deallocate(device);
+}
+
+/*
+ * Lookup a device by its port.
+ * Doesn't consume the naked send right; produces a device reference.
+ */
+device_t
+dev_port_lookup(port)
+ ipc_port_t port;
+{
+ register device_t device;
+
+ if (!IP_VALID(port))
+ return (DEVICE_NULL);
+
+ ip_lock(port);
+ if (ip_active(port) && (ip_kotype(port) == IKOT_DEVICE)) {
+ device = (device_t) port->ip_kobject;
+#ifdef i386
+ if (device->emul_ops->reference)
+ (*device->emul_ops->reference)(device->emul_data);
+#else
+ mach_device_reference(device);
+#endif
+ }
+ else
+ device = DEVICE_NULL;
+
+ ip_unlock(port);
+ return (device);
+}
+
+/*
+ * Get the port for a device.
+ * Consumes a device reference; produces a naked send right.
+ */
+ipc_port_t
+convert_device_to_port(device)
+ register device_t device;
+{
+#ifndef i386
+ register ipc_port_t port;
+#endif
+
+ if (device == DEVICE_NULL)
+ return IP_NULL;
+
+#ifdef i386
+ return (*device->emul_ops->dev_to_port) (device->emul_data);
+#else
+ device_lock(device);
+ if (device->state == DEV_STATE_OPEN)
+ port = ipc_port_make_send(device->port);
+ else
+ port = IP_NULL;
+ device_unlock(device);
+
+ mach_device_deallocate(device);
+ return port;
+#endif
+}
+
+/*
+ * Call a supplied routine on each device, passing it
+ * the port as an argument. If the routine returns TRUE,
+ * stop the search and return TRUE. If none returns TRUE,
+ * return FALSE.
+ */
+boolean_t
+dev_map(routine, port)
+ boolean_t (*routine)();
+ mach_port_t port;
+{
+ register int i;
+ register queue_t q;
+ register mach_device_t dev, prev_dev;
+
+ for (i = 0, q = &dev_number_hash_table[0];
+ i < NDEVHASH;
+ i++, q++) {
+ prev_dev = MACH_DEVICE_NULL;
+ simple_lock(&dev_number_lock);
+ queue_iterate(q, dev, mach_device_t, number_chain) {
+ mach_device_reference(dev);
+ simple_unlock(&dev_number_lock);
+ if (prev_dev != MACH_DEVICE_NULL)
+ mach_device_deallocate(prev_dev);
+
+ if ((*routine)(dev, port)) {
+ /*
+ * Done
+ */
+ mach_device_deallocate(dev);
+ return (TRUE);
+ }
+
+ simple_lock(&dev_number_lock);
+ prev_dev = dev;
+ }
+ simple_unlock(&dev_number_lock);
+ if (prev_dev != MACH_DEVICE_NULL)
+ mach_device_deallocate(prev_dev);
+ }
+ return (FALSE);
+}
+
+/*
+ * Initialization
+ */
+#define NDEVICES 256
+
+void
+dev_lookup_init()
+{
+ register int i;
+
+ simple_lock_init(&dev_number_lock);
+
+ for (i = 0; i < NDEVHASH; i++)
+ queue_init(&dev_number_hash_table[i]);
+
+ simple_lock_init(&dev_port_lock);
+
+ dev_hdr_zone = zinit(sizeof(struct mach_device),
+ sizeof(struct mach_device) * NDEVICES,
+ PAGE_SIZE,
+ FALSE,
+ "open device entry");
+}
diff --git a/device/dev_master.h b/device/dev_master.h
new file mode 100644
index 0000000..964ae82
--- /dev/null
+++ b/device/dev_master.h
@@ -0,0 +1,60 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 11/89
+ *
+ * Bind an IO operation to the master CPU.
+ */
+
+#include <cpus.h>
+
+#if NCPUS > 1
+
+#include <kern/macro_help.h>
+#include <kern/cpu_number.h>
+#include <kern/sched_prim.h>
+#include <kern/thread.h>
+#include <kern/processor.h>
+
+#define io_grab_master() \
+ MACRO_BEGIN \
+ thread_bind(current_thread(), master_processor); \
+ if (current_processor() != master_processor) \
+ thread_block((void (*)()) 0); \
+ MACRO_END
+
+#define io_release_master() \
+ MACRO_BEGIN \
+ thread_bind(current_thread(), PROCESSOR_NULL); \
+ MACRO_END
+
+#else NCPUS > 1
+
+#define io_grab_master()
+#define io_release_master()
+
+#endif NCPUS > 1
diff --git a/device/dev_name.c b/device/dev_name.c
new file mode 100644
index 0000000..99d9227
--- /dev/null
+++ b/device/dev_name.c
@@ -0,0 +1,237 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ */
+
+#include <device/device_types.h>
+#include <device/dev_hdr.h>
+#include <device/conf.h>
+
+
+
+/*
+ * Routines placed in empty entries in the device tables
+ */
+int nulldev()
+{
+ return (D_SUCCESS);
+}
+
+int nodev()
+{
+ return (D_INVALID_OPERATION);
+}
+
+vm_offset_t
+nomap()
+{
+ return (D_INVALID_OPERATION);
+}
+
+/*
+ * Name comparison routine.
+ * Compares first 'len' characters of 'src'
+ * with 'target', which is zero-terminated.
+ * Returns TRUE if strings are equal:
+ * src and target are equal in first 'len' characters
+ * next character of target is 0 (end of string).
+ */
+boolean_t
+name_equal(src, len, target)
+ register char * src;
+ register int len;
+ register char * target;
+{
+ while (--len >= 0)
+ if (*src++ != *target++)
+ return FALSE;
+ return *target == 0;
+}
+
+/*
+ * device name lookup
+ */
+boolean_t dev_name_lookup(name, ops, unit)
+ char * name;
+ dev_ops_t *ops; /* out */
+ int *unit; /* out */
+{
+ /*
+ * Assume that block device names are of the form
+ *
+ * <device_name><unit_number>[[<slice num>]<partition>]
+ *
+ * where
+ * <device_name> is the name in the device table
+ * <unit_number> is an integer
+ * <slice num> * is 's' followed by a number (disks only!)
+ * <partition> is a letter in [a-h] (disks only?)
+ */
+
+ register char * cp = name;
+ int len;
+ register int j = 0;
+ register int c;
+ dev_ops_t dev;
+ register boolean_t found;
+
+ int slice_num=0;
+
+#if 0
+ printf("lookup on name %s\n",name);
+#endif 0
+
+ /*
+ * Find device type name (characters before digit)
+ */
+ while ((c = *cp) != '\0' &&
+ !(c >= '0' && c <= '9'))
+ cp++;
+
+ len = cp - name;
+ if (c != '\0') {
+ /*
+ * Find unit number
+ */
+ while ((c = *cp) != '\0' &&
+ c >= '0' && c <= '9') {
+ j = j * 10 + (c - '0');
+ cp++;
+ }
+ }
+
+ found = FALSE;
+ dev_search(dev) {
+ if (name_equal(name, len, dev->d_name)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ /* name not found - try indirection list */
+ register dev_indirect_t di;
+
+ dev_indirect_search(di) {
+ if (name_equal(name, len, di->d_name)) {
+ /*
+ * Return device and unit from indirect vector.
+ */
+ *ops = di->d_ops;
+ *unit = di->d_unit;
+ return (TRUE);
+ }
+ }
+ /* Not found in either list. */
+ return (FALSE);
+ }
+
+ *ops = dev;
+ *unit = j;
+
+ /*
+ * Find sub-device number
+ */
+
+ j = dev->d_subdev;
+ if (j > 0) {
+ /* if no slice string, slice num = 0 */
+
+ /* <subdev_count>*unit + <slice_number>*16 -- I know it's bad */
+ *unit *= j;
+
+ /* find slice ? */
+ if (c=='s') {
+ cp++;
+ while ((c = *cp) != '\0' &&
+ c >= '0' && c <= '9') {
+ slice_num = slice_num * 10 + (c - '0');
+ cp++;
+ }
+ }
+
+ *unit += (slice_num <<4);
+ /* if slice==0, it is either compatability or whole device */
+
+ if (c >= 'a' && c < 'a' + j) { /* note: w/o this -> whole slice */
+ /*
+ * Minor number is <subdev_count>*unit + letter.
+ * NOW it is slice result + letter
+ */
+#if 0
+ *unit = *unit * j + (c - 'a' +1); /* +1 to start 'a' at 1 */
+#endif 0
+ *unit += (c - 'a' +1);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Change an entry in the indirection list.
+ */
+void
+dev_set_indirection(name, ops, unit)
+ char *name;
+ dev_ops_t ops;
+ int unit;
+{
+ register dev_indirect_t di;
+
+ dev_indirect_search(di) {
+ if (!strcmp(di->d_name, name)) {
+ di->d_ops = ops;
+ di->d_unit = unit;
+ break;
+ }
+ }
+}
+
+boolean_t dev_change_indirect(iname, dname, unit)
+char *iname,*dname;
+int unit;
+{
+ struct dev_ops *dp;
+ struct dev_indirect *di;
+ int found = FALSE;
+
+ dev_search(dp) {
+ if (!strcmp(dp->d_name,dname)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) return FALSE;
+ dev_indirect_search(di) {
+ if (!strcmp(di->d_name,iname)) {
+ di->d_ops = dp;
+ di->d_unit = unit;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/device/dev_pager.c b/device/dev_pager.c
new file mode 100644
index 0000000..007942d
--- /dev/null
+++ b/device/dev_pager.c
@@ -0,0 +1,741 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/89
+ *
+ * Device pager.
+ */
+#include <norma_vm.h>
+
+#include <mach/boolean.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <mach/std_types.h>
+#include <mach/mach_types.h>
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+
+#include <kern/queue.h>
+#include <kern/zalloc.h>
+#include <kern/kalloc.h>
+
+#include <vm/vm_page.h>
+#include <vm/vm_kern.h>
+
+#include <device/device_types.h>
+#include <device/ds_routines.h>
+#include <device/dev_hdr.h>
+#include <device/io_req.h>
+
+extern vm_offset_t block_io_mmap(); /* dummy routine to allow
+ mmap for block devices */
+
+/*
+ * The device pager routines are called directly from the message
+ * system (via mach_msg), and thus run in the kernel-internal
+ * environment. All ports are in internal form (ipc_port_t),
+ * and must be correctly reference-counted in order to be saved
+ * in other data structures. Kernel routines may be called
+ * directly. Kernel types are used for data objects (tasks,
+ * memory objects, ports). The only IPC routines that may be
+ * called are ones that masquerade as the kernel task (via
+ * msg_send_from_kernel).
+ *
+ * Port rights and references are maintained as follows:
+ * Memory object port:
+ * The device_pager task has all rights.
+ * Memory object control port:
+ * The device_pager task has only send rights.
+ * Memory object name port:
+ * The device_pager task has only send rights.
+ * The name port is not even recorded.
+ * Regardless how the object is created, the control and name
+ * ports are created by the kernel and passed through the memory
+ * management interface.
+ *
+ * The device_pager assumes that access to its memory objects
+ * will not be propagated to more that one host, and therefore
+ * provides no consistency guarantees beyond those made by the
+ * kernel.
+ *
+ * In the event that more than one host attempts to use a device
+ * memory object, the device_pager will only record the last set
+ * of port names. [This can happen with only one host if a new
+ * mapping is being established while termination of all previous
+ * mappings is taking place.] Currently, the device_pager assumes
+ * that its clients adhere to the initialization and termination
+ * protocols in the memory management interface; otherwise, port
+ * rights or out-of-line memory from erroneous messages may be
+ * allowed to accumulate.
+ *
+ * [The phrase "currently" has been used above to denote aspects of
+ * the implementation that could be altered without changing the rest
+ * of the basic documentation.]
+ */
+
+/*
+ * Basic device pager structure.
+ */
+struct dev_pager {
+ decl_simple_lock_data(, lock) /* lock for reference count */
+ int ref_count; /* reference count */
+ int client_count; /* How many memory_object_create
+ * calls have we received */
+ ipc_port_t pager; /* pager port */
+ ipc_port_t pager_request; /* Known request port */
+ ipc_port_t pager_name; /* Known name port */
+ mach_device_t device; /* Device handle */
+ int type; /* to distinguish */
+#define DEV_PAGER_TYPE 0
+#define CHAR_PAGER_TYPE 1
+ /* char pager specifics */
+ int prot;
+ vm_size_t size;
+};
+typedef struct dev_pager *dev_pager_t;
+#define DEV_PAGER_NULL ((dev_pager_t)0)
+
+
+zone_t dev_pager_zone;
+
+void dev_pager_reference(register dev_pager_t ds)
+{
+ simple_lock(&ds->lock);
+ ds->ref_count++;
+ simple_unlock(&ds->lock);
+}
+
+void dev_pager_deallocate(register dev_pager_t ds)
+{
+ simple_lock(&ds->lock);
+ if (--ds->ref_count > 0) {
+ simple_unlock(&ds->lock);
+ return;
+ }
+
+ simple_unlock(&ds->lock);
+ zfree(dev_pager_zone, (vm_offset_t)ds);
+}
+
+/*
+ * A hash table of ports for device_pager backed objects.
+ */
+
+#define DEV_PAGER_HASH_COUNT 127
+
+struct dev_pager_entry {
+ queue_chain_t links;
+ ipc_port_t name;
+ dev_pager_t pager_rec;
+};
+typedef struct dev_pager_entry *dev_pager_entry_t;
+
+queue_head_t dev_pager_hashtable[DEV_PAGER_HASH_COUNT];
+zone_t dev_pager_hash_zone;
+decl_simple_lock_data(,
+ dev_pager_hash_lock)
+
+#define dev_pager_hash(name_port) \
+ (((natural_t)(name_port) & 0xffffff) % DEV_PAGER_HASH_COUNT)
+
+void dev_pager_hash_init(void)
+{
+ register int i;
+ register vm_size_t size;
+
+ size = sizeof(struct dev_pager_entry);
+ dev_pager_hash_zone = zinit(
+ size,
+ size * 1000,
+ PAGE_SIZE,
+ FALSE,
+ "dev_pager port hash");
+ for (i = 0; i < DEV_PAGER_HASH_COUNT; i++)
+ queue_init(&dev_pager_hashtable[i]);
+ simple_lock_init(&dev_pager_hash_lock);
+}
+
+void dev_pager_hash_insert(
+ ipc_port_t name_port,
+ dev_pager_t rec)
+{
+ register dev_pager_entry_t new_entry;
+
+ new_entry = (dev_pager_entry_t) zalloc(dev_pager_hash_zone);
+ new_entry->name = name_port;
+ new_entry->pager_rec = rec;
+
+ simple_lock(&dev_pager_hash_lock);
+ queue_enter(&dev_pager_hashtable[dev_pager_hash(name_port)],
+ new_entry, dev_pager_entry_t, links);
+ simple_unlock(&dev_pager_hash_lock);
+}
+
+void dev_pager_hash_delete(ipc_port_t name_port)
+{
+ register queue_t bucket;
+ register dev_pager_entry_t entry;
+
+ bucket = &dev_pager_hashtable[dev_pager_hash(name_port)];
+
+ simple_lock(&dev_pager_hash_lock);
+ for (entry = (dev_pager_entry_t)queue_first(bucket);
+ !queue_end(bucket, &entry->links);
+ entry = (dev_pager_entry_t)queue_next(&entry->links)) {
+ if (entry->name == name_port) {
+ queue_remove(bucket, entry, dev_pager_entry_t, links);
+ break;
+ }
+ }
+ simple_unlock(&dev_pager_hash_lock);
+ if (entry)
+ zfree(dev_pager_hash_zone, (vm_offset_t)entry);
+}
+
+dev_pager_t dev_pager_hash_lookup(ipc_port_t name_port)
+{
+ register queue_t bucket;
+ register dev_pager_entry_t entry;
+ register dev_pager_t pager;
+
+ bucket = &dev_pager_hashtable[dev_pager_hash(name_port)];
+
+ simple_lock(&dev_pager_hash_lock);
+ for (entry = (dev_pager_entry_t)queue_first(bucket);
+ !queue_end(bucket, &entry->links);
+ entry = (dev_pager_entry_t)queue_next(&entry->links)) {
+ if (entry->name == name_port) {
+ pager = entry->pager_rec;
+ dev_pager_reference(pager);
+ simple_unlock(&dev_pager_hash_lock);
+ return (pager);
+ }
+ }
+ simple_unlock(&dev_pager_hash_lock);
+ return (DEV_PAGER_NULL);
+}
+
+kern_return_t device_pager_setup(
+ mach_device_t device,
+ int prot,
+ vm_offset_t offset,
+ vm_size_t size,
+ mach_port_t *pager)
+{
+ register dev_pager_t d;
+
+ /*
+ * Verify the device is indeed mappable
+ */
+ if (!device->dev_ops->d_mmap || (device->dev_ops->d_mmap == nomap))
+ return (D_INVALID_OPERATION);
+
+ /*
+ * Allocate a structure to hold the arguments
+ * and port to represent this object.
+ */
+
+ d = dev_pager_hash_lookup((ipc_port_t)device); /* HACK */
+ if (d != DEV_PAGER_NULL) {
+ *pager = (mach_port_t) ipc_port_make_send(d->pager);
+ dev_pager_deallocate(d);
+ return (D_SUCCESS);
+ }
+
+ d = (dev_pager_t) zalloc(dev_pager_zone);
+ if (d == DEV_PAGER_NULL)
+ return (KERN_RESOURCE_SHORTAGE);
+
+ simple_lock_init(&d->lock);
+ d->ref_count = 1;
+
+ /*
+ * Allocate the pager port.
+ */
+ d->pager = ipc_port_alloc_kernel();
+ if (d->pager == IP_NULL) {
+ dev_pager_deallocate(d);
+ return (KERN_RESOURCE_SHORTAGE);
+ }
+
+ d->client_count = 0;
+ d->pager_request = IP_NULL;
+ d->pager_name = IP_NULL;
+ d->device = device;
+ mach_device_reference(device);
+ d->prot = prot;
+ d->size = round_page(size);
+ if (device->dev_ops->d_mmap == block_io_mmap) {
+ d->type = DEV_PAGER_TYPE;
+ } else {
+ d->type = CHAR_PAGER_TYPE;
+ }
+
+ dev_pager_hash_insert(d->pager, d);
+ dev_pager_hash_insert((ipc_port_t)device, d); /* HACK */
+
+ *pager = (mach_port_t) ipc_port_make_send(d->pager);
+ return (KERN_SUCCESS);
+}
+
+/*
+ * Routine: device_pager_release
+ * Purpose:
+ * Relinquish any references or rights that were
+ * associated with the result of a call to
+ * device_pager_setup.
+ */
+void device_pager_release(memory_object_t object)
+{
+ if (MACH_PORT_VALID(object))
+ ipc_port_release_send((ipc_port_t) object);
+}
+
+boolean_t device_pager_debug = FALSE;
+
+boolean_t device_pager_data_request_done(); /* forward */
+boolean_t device_pager_data_write_done(); /* forward */
+
+
+kern_return_t device_pager_data_request(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t protection_required)
+{
+ register dev_pager_t ds;
+
+#ifdef lint
+ protection_required++;
+#endif lint
+
+ if (device_pager_debug)
+ printf("(device_pager)data_request: pager=%d, offset=0x%x, length=0x%x\n",
+ pager, offset, length);
+
+ ds = dev_pager_hash_lookup((ipc_port_t)pager);
+ if (ds == DEV_PAGER_NULL)
+ panic("(device_pager)data_request: lookup failed");
+
+ if (ds->pager_request != pager_request)
+ panic("(device_pager)data_request: bad pager_request");
+
+ if (ds->type == CHAR_PAGER_TYPE) {
+ register vm_object_t object;
+ vm_offset_t device_map_page(void *,vm_offset_t);
+
+#if NORMA_VM
+ object = vm_object_lookup(pager);
+#else NORMA_VM
+ object = vm_object_lookup(pager_request);
+#endif NORMA_VM
+ if (object == VM_OBJECT_NULL) {
+ (void) r_memory_object_data_error(pager_request,
+ offset, length,
+ KERN_FAILURE);
+ dev_pager_deallocate(ds);
+ return (KERN_SUCCESS);
+ }
+
+ vm_object_page_map(object,
+ offset, length,
+ device_map_page, (char *)ds);
+
+ vm_object_deallocate(object);
+ }
+ else {
+ register io_req_t ior;
+ register mach_device_t device;
+ io_return_t result;
+
+ panic("(device_pager)data_request: dev pager");
+
+ device = ds->device;
+ mach_device_reference(device);
+ dev_pager_deallocate(ds);
+
+ /*
+ * Package the read for the device driver.
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_READ | IO_CALL;
+ ior->io_mode = 0;
+ ior->io_recnum = offset / device->bsize;
+ ior->io_data = 0; /* driver must allocate */
+ ior->io_count = length;
+ ior->io_alloc_size = 0; /* no data allocated yet */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = device_pager_data_request_done;
+ ior->io_reply_port = pager_request;
+ ior->io_reply_port_type = MACH_MSG_TYPE_PORT_SEND;
+
+ result = (*device->dev_ops->d_read)(device->dev_number, ior);
+ if (result == D_IO_QUEUED)
+ return (KERN_SUCCESS);
+
+ /*
+ * Return by queuing IOR for io_done thread, to reply in
+ * correct environment (kernel).
+ */
+ ior->io_error = result;
+ iodone(ior);
+ }
+
+ dev_pager_deallocate(ds);
+
+ return (KERN_SUCCESS);
+}
+
+/*
+ * Always called by io_done thread.
+ */
+boolean_t device_pager_data_request_done(register io_req_t ior)
+{
+ vm_offset_t start_alloc, end_alloc;
+ vm_size_t size_read;
+
+ if (ior->io_error == D_SUCCESS) {
+ size_read = ior->io_count;
+ if (ior->io_residual) {
+ if (device_pager_debug)
+ printf("(device_pager)data_request_done: r: 0x%x\n",ior->io_residual);
+ bzero( (char *) (&ior->io_data[ior->io_count -
+ ior->io_residual]),
+ (unsigned) ior->io_residual);
+ }
+ } else {
+ size_read = ior->io_count - ior->io_residual;
+ }
+
+ start_alloc = trunc_page((vm_offset_t)ior->io_data);
+ end_alloc = start_alloc + round_page(ior->io_alloc_size);
+
+ if (ior->io_error == D_SUCCESS) {
+ vm_map_copy_t copy;
+ kern_return_t kr;
+
+ kr = vm_map_copyin(kernel_map, (vm_offset_t)ior->io_data,
+ size_read, TRUE, &copy);
+ if (kr != KERN_SUCCESS)
+ panic("device_pager_data_request_done");
+
+ (void) r_memory_object_data_provided(
+ ior->io_reply_port,
+ ior->io_recnum * ior->io_device->bsize,
+ (vm_offset_t)copy,
+ size_read,
+ VM_PROT_NONE);
+ }
+ else {
+ (void) r_memory_object_data_error(
+ ior->io_reply_port,
+ ior->io_recnum * ior->io_device->bsize,
+ (vm_size_t)ior->io_count,
+ ior->io_error);
+ }
+
+ (void)vm_deallocate(kernel_map,
+ start_alloc,
+ end_alloc - start_alloc);
+ mach_device_deallocate(ior->io_device);
+ return (TRUE);
+}
+
+kern_return_t device_pager_data_write(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ register vm_offset_t offset,
+ register pointer_t addr,
+ vm_size_t data_count)
+{
+ register dev_pager_t ds;
+ register mach_device_t device;
+ register io_req_t ior;
+ kern_return_t result;
+
+ panic("(device_pager)data_write: called");
+
+ ds = dev_pager_hash_lookup((ipc_port_t)pager);
+ if (ds == DEV_PAGER_NULL)
+ panic("(device_pager)data_write: lookup failed");
+
+ if (ds->pager_request != pager_request)
+ panic("(device_pager)data_write: bad pager_request");
+
+ if (ds->type == CHAR_PAGER_TYPE)
+ panic("(device_pager)data_write: char pager");
+
+ device = ds->device;
+ mach_device_reference(device);
+ dev_pager_deallocate(ds);
+
+ /*
+ * Package the write request for the device driver.
+ */
+ io_req_alloc(ior, data_count);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL;
+ ior->io_mode = 0;
+ ior->io_recnum = offset / device->bsize;
+ ior->io_data = (io_buf_ptr_t)addr;
+ ior->io_count = data_count;
+ ior->io_alloc_size = data_count; /* amount to deallocate */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = device_pager_data_write_done;
+ ior->io_reply_port = IP_NULL;
+
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ if (result != D_IO_QUEUED) {
+ device_write_dealloc(ior);
+ io_req_free((vm_offset_t)ior);
+ mach_device_deallocate(device);
+ }
+
+ return (KERN_SUCCESS);
+}
+
+boolean_t device_pager_data_write_done(ior)
+ register io_req_t ior;
+{
+ device_write_dealloc(ior);
+ mach_device_deallocate(ior->io_device);
+
+ return (TRUE);
+}
+
+kern_return_t device_pager_copy(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ register vm_offset_t offset,
+ register vm_size_t length,
+ ipc_port_t new_pager)
+{
+ panic("(device_pager)copy: called");
+}
+
+kern_return_t
+device_pager_supply_completed(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ vm_offset_t offset,
+ vm_size_t length,
+ kern_return_t result,
+ vm_offset_t error_offset)
+{
+ panic("(device_pager)supply_completed: called");
+}
+
+kern_return_t
+device_pager_data_return(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ vm_offset_t offset,
+ register pointer_t addr,
+ vm_size_t data_cnt,
+ boolean_t dirty,
+ boolean_t kernel_copy)
+{
+ panic("(device_pager)data_return: called");
+}
+
+kern_return_t
+device_pager_change_completed(
+ ipc_port_t pager,
+ boolean_t may_cache,
+ memory_object_copy_strategy_t copy_strategy)
+{
+ panic("(device_pager)change_completed: called");
+}
+
+/*
+ * The mapping function takes a byte offset, but returns
+ * a machine-dependent page frame number. We convert
+ * that into something that the pmap module will
+ * accept later.
+ */
+vm_offset_t device_map_page(
+ void *dsp,
+ vm_offset_t offset)
+{
+ register dev_pager_t ds = (dev_pager_t) dsp;
+
+ return pmap_phys_address(
+ (*(ds->device->dev_ops->d_mmap))
+ (ds->device->dev_number, offset, ds->prot));
+}
+
+kern_return_t device_pager_init_pager(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ ipc_port_t pager_name,
+ vm_size_t pager_page_size)
+{
+ register dev_pager_t ds;
+
+ if (device_pager_debug)
+ printf("(device_pager)init: pager=%d, request=%d, name=%d\n",
+ pager, pager_request, pager_name);
+
+ assert(pager_page_size == PAGE_SIZE);
+ assert(IP_VALID(pager_request));
+ assert(IP_VALID(pager_name));
+
+ ds = dev_pager_hash_lookup(pager);
+ assert(ds != DEV_PAGER_NULL);
+
+ assert(ds->client_count == 0);
+ assert(ds->pager_request == IP_NULL);
+ assert(ds->pager_name == IP_NULL);
+
+ ds->client_count = 1;
+
+ /*
+ * We save the send rights for the request and name ports.
+ */
+
+ ds->pager_request = pager_request;
+ ds->pager_name = pager_name;
+
+ if (ds->type == CHAR_PAGER_TYPE) {
+ /*
+ * Reply that the object is ready
+ */
+ (void) r_memory_object_set_attributes(pager_request,
+ TRUE, /* ready */
+ FALSE, /* do not cache */
+ MEMORY_OBJECT_COPY_NONE);
+ } else {
+ (void) r_memory_object_set_attributes(pager_request,
+ TRUE, /* ready */
+ TRUE, /* cache */
+ MEMORY_OBJECT_COPY_DELAY);
+ }
+
+ dev_pager_deallocate(ds);
+ return (KERN_SUCCESS);
+}
+
+kern_return_t device_pager_terminate(
+ ipc_port_t pager,
+ ipc_port_t pager_request,
+ ipc_port_t pager_name)
+{
+ register dev_pager_t ds;
+
+ assert(IP_VALID(pager_request));
+ assert(IP_VALID(pager_name));
+
+ ds = dev_pager_hash_lookup(pager);
+ assert(ds != DEV_PAGER_NULL);
+
+ assert(ds->client_count == 1);
+ assert(ds->pager_request == pager_request);
+ assert(ds->pager_name == pager_name);
+
+ dev_pager_hash_delete(ds->pager);
+ dev_pager_hash_delete((ipc_port_t)ds->device); /* HACK */
+ mach_device_deallocate(ds->device);
+
+ /* release the send rights we have saved from the init call */
+
+ ipc_port_release_send(pager_request);
+ ipc_port_release_send(pager_name);
+
+ /* release the naked receive rights we just acquired */
+
+ ipc_port_release_receive(pager_request);
+ ipc_port_release_receive(pager_name);
+
+ /* release the kernel's receive right for the pager port */
+
+ ipc_port_dealloc_kernel(pager);
+
+ /* once for ref from lookup, once to make it go away */
+ dev_pager_deallocate(ds);
+ dev_pager_deallocate(ds);
+
+ return (KERN_SUCCESS);
+}
+
+kern_return_t device_pager_data_unlock(
+ ipc_port_t memory_object,
+ ipc_port_t memory_control_port,
+ vm_offset_t offset,
+ vm_size_t length,
+ vm_prot_t desired_access)
+{
+#ifdef lint
+ memory_object++; memory_control_port++; offset++; length++; desired_access++;
+#endif lint
+
+ panic("(device_pager)data_unlock: called");
+ return (KERN_FAILURE);
+}
+
+kern_return_t device_pager_lock_completed(
+ ipc_port_t memory_object,
+ ipc_port_t pager_request_port,
+ vm_offset_t offset,
+ vm_size_t length)
+{
+#ifdef lint
+ memory_object++; pager_request_port++; offset++; length++;
+#endif lint
+
+ panic("(device_pager)lock_completed: called");
+ return (KERN_FAILURE);
+}
+
+void device_pager_init(void)
+{
+ register vm_size_t size;
+
+ /*
+ * Initialize zone of paging structures.
+ */
+ size = sizeof(struct dev_pager);
+ dev_pager_zone = zinit(size,
+ (vm_size_t) size * 1000,
+ PAGE_SIZE,
+ FALSE,
+ "device pager structures");
+
+ /*
+ * Initialize the name port hashing stuff.
+ */
+ dev_pager_hash_init();
+}
diff --git a/device/device.srv b/device/device.srv
new file mode 100644
index 0000000..06aa0be
--- /dev/null
+++ b/device/device.srv
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+/* This is a server presentation file. */
+
+#define KERNEL_SERVER 1
+
+simport <kern/compat_xxx_defs.h>; /* for obsolete routines */
+
+#include <device/device.defs>
diff --git a/device/device_init.c b/device/device_init.c
new file mode 100644
index 0000000..4eef63d
--- /dev/null
+++ b/device/device_init.c
@@ -0,0 +1,73 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ *
+ * Initialize device service as part of kernel task.
+ */
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+#include <kern/task.h>
+
+#include <device/device_types.h>
+#include <device/device_port.h>
+
+
+
+extern void ds_init();
+extern void dev_lookup_init();
+extern void net_io_init();
+extern void device_pager_init();
+extern void chario_init(void);
+#ifdef FIPC
+extern void fipc_init();
+#endif
+
+extern void io_done_thread();
+extern void net_thread();
+
+ipc_port_t master_device_port;
+
+void
+device_service_create()
+{
+ master_device_port = ipc_port_alloc_kernel();
+ if (master_device_port == IP_NULL)
+ panic("can't allocate master device port");
+
+ ds_init();
+ dev_lookup_init();
+ net_io_init();
+ device_pager_init();
+ chario_init();
+#ifdef FIPC
+ fipc_init();
+#endif
+
+ (void) kernel_thread(kernel_task, io_done_thread, 0);
+ (void) kernel_thread(kernel_task, net_thread, 0);
+}
diff --git a/device/device_pager.srv b/device/device_pager.srv
new file mode 100644
index 0000000..e4e52ea
--- /dev/null
+++ b/device/device_pager.srv
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+/* This is a server presentation file. */
+
+#define KERNEL_SERVER 1
+
+#define memory_object device_pager
+
+/*
+ * Rename all of the functions in the pager interface, to avoid
+ * confusing them with the kernel interface.
+ */
+#define memory_object_init device_pager_init_pager
+#define memory_object_terminate device_pager_terminate
+#define memory_object_copy device_pager_copy
+#define memory_object_data_request device_pager_data_request
+#define memory_object_data_unlock device_pager_data_unlock
+#define memory_object_data_write device_pager_data_write
+#define memory_object_lock_completed device_pager_lock_completed
+#define memory_object_supply_completed device_pager_supply_completed
+#define memory_object_data_return device_pager_data_return
+#define memory_object_change_completed device_pager_change_completed
+
+#include <mach/memory_object.defs>
diff --git a/device/device_port.h b/device/device_port.h
new file mode 100644
index 0000000..980a709
--- /dev/null
+++ b/device/device_port.h
@@ -0,0 +1,41 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ */
+
+#ifndef _DEVICE_DEVICE_PORT_H_
+#define _DEVICE_DEVICE_PORT_H_
+
+#include <ipc/ipc_port.h>
+
+/*
+ * Master privileged port for this host's device service
+ */
+extern ipc_port_t master_device_port;
+
+#endif _DEVICE_DEVICE_PORT_H_
diff --git a/device/device_reply.cli b/device/device_reply.cli
new file mode 100644
index 0000000..956540c
--- /dev/null
+++ b/device/device_reply.cli
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+/* This is a client presentation file. */
+
+#define KERNEL_USER 1
+
+#include <device/device_reply.defs>
diff --git a/device/device_types_kernel.h b/device/device_types_kernel.h
new file mode 100644
index 0000000..dd7b63e
--- /dev/null
+++ b/device/device_types_kernel.h
@@ -0,0 +1,44 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ */
+
+#ifndef _DEVICE_DEVICE_TYPES_KERNEL_H_
+#define _DEVICE_DEVICE_TYPES_KERNEL_H_
+
+/*
+ * Kernel-only type definitions for device server.
+ */
+
+#include <mach/port.h>
+#include <device/dev_hdr.h>
+
+extern device_t dev_port_lookup(/* struct ipc_port * */);
+extern struct ipc_port *convert_device_to_port(/* device_t */);
+
+#endif _DEVICE_DEVICE_TYPES_KERNEL_H_
diff --git a/device/dk_label.c b/device/dk_label.c
new file mode 100644
index 0000000..8639e33
--- /dev/null
+++ b/device/dk_label.c
@@ -0,0 +1,98 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: rz_disk.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 10/90
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <device/device_types.h>
+#include <device/disk_status.h>
+
+/* Checksum a disk label */
+unsigned
+dkcksum(lp)
+ struct disklabel *lp;
+{
+ register unsigned short *start, *end, sum = 0;
+
+ start = (unsigned short *)lp;
+ end = (unsigned short*)&lp->d_partitions[lp->d_npartitions];
+ while (start < end) sum ^= *start++;
+ return sum;
+}
+
+/* Perform some checks and then copy a disk label */
+setdisklabel(lp, nlp)
+ struct disklabel *lp, *nlp;
+{
+ if (nlp->d_magic != DISKMAGIC || nlp->d_magic2 != DISKMAGIC ||
+ (dkcksum(nlp) != 0))
+ return D_INVALID_OPERATION;
+ *lp = *nlp;
+ return D_SUCCESS;
+}
+
+dkgetlabel(lp, flavor, data, count)
+ struct disklabel *lp;
+ int flavor;
+ int * data; /* pointer to OUT array */
+ unsigned int *count; /* OUT */
+{
+
+ switch (flavor) {
+ /* get */
+ case DIOCGDINFO:
+ *(struct disklabel *)data = *lp;
+ *count = sizeof(struct disklabel)/sizeof(int);
+ break;
+ case DIOCGDINFO - (0x10<<16):
+ *(struct disklabel *)data = *lp;
+ *count = sizeof(struct disklabel)/sizeof(int) - 4;
+ break;
+ }
+}
+
+print_bsd_label(lp, str)
+struct disklabel *lp;
+char *str;
+{
+int i;
+ printf("%s sectors %d, tracks %d, cylinders %d\n",
+ str, lp->d_nsectors, lp->d_ntracks, lp->d_ncylinders);
+ printf("%s secpercyl %d, secperunit %d, npartitions %d\n",
+ str, lp->d_secpercyl, lp->d_secperunit, lp->d_npartitions);
+
+ for (i = 0; i < lp->d_npartitions; i++) {
+ printf("%s %c: size = %d, offset = %d\n",
+ str, 'a'+i,
+ lp->d_partitions[i].p_size,
+ lp->d_partitions[i].p_offset);
+ }
+}
diff --git a/device/ds_routines.c b/device/ds_routines.c
new file mode 100644
index 0000000..9880f7e
--- /dev/null
+++ b/device/ds_routines.c
@@ -0,0 +1,1820 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/89
+ */
+
+#include <norma_device.h>
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/mig_errors.h>
+#include <mach/port.h>
+#include <mach/vm_param.h>
+#include <mach/notify.h>
+#include <machine/machspl.h> /* spl definitions */
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+
+#include <kern/ast.h>
+#include <kern/counters.h>
+#include <kern/queue.h>
+#include <kern/zalloc.h>
+#include <kern/thread.h>
+#include <kern/task.h>
+#include <kern/sched_prim.h>
+
+#include <vm/memory_object.h>
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+
+#include <device/device_types.h>
+#include <device/dev_hdr.h>
+#include <device/conf.h>
+#include <device/io_req.h>
+#include <device/ds_routines.h>
+#include <device/net_status.h>
+#include <device/device_port.h>
+#include "device_reply.h"
+
+#include <machine/machspl.h>
+
+#ifdef i386
+#include <i386at/device_emul.h>
+#endif
+
+#ifdef i386
+ipc_port_t
+mach_convert_device_to_port (void *d)
+{
+ ipc_port_t port;
+ mach_device_t device = d;
+
+ if (! device)
+ return IP_NULL;
+ device_lock(device);
+ if (device->state == DEV_STATE_OPEN)
+ port = ipc_port_make_send(device->port);
+ else
+ port = IP_NULL;
+ device_unlock(device);
+ mach_device_deallocate(device);
+ return port;
+}
+#endif /* i386 */
+
+#ifdef i386
+static io_return_t
+device_open(reply_port, reply_port_type, mode, name, device_p)
+#else
+io_return_t
+ds_device_open(open_port, reply_port, reply_port_type,
+ mode, name, device_p)
+ ipc_port_t open_port;
+#endif
+ ipc_port_t reply_port;
+ mach_msg_type_name_t reply_port_type;
+ dev_mode_t mode;
+ char * name;
+ device_t *device_p; /* out */
+{
+ register mach_device_t device;
+ register kern_return_t result;
+ register io_req_t ior;
+ char namebuf[64];
+ ipc_port_t notify;
+
+#ifndef i386
+ /*
+ * Open must be called on the master device port.
+ */
+ if (open_port != master_device_port)
+ return (D_INVALID_OPERATION);
+
+ /*
+ * There must be a reply port.
+ */
+ if (!IP_VALID(reply_port)) {
+ printf("ds_* invalid reply port\n");
+ Debugger("ds_* reply_port");
+ return (MIG_NO_REPLY); /* no sense in doing anything */
+ }
+
+#if NORMA_DEVICE
+ /*
+ * Map global device name to <node> + local device name.
+ */
+ if (name[0] != '<') {
+ extern char *dev_forward_name();
+
+ name = dev_forward_name(name, namebuf, sizeof(namebuf));
+ }
+ /*
+ * Look for explicit node specifier, e.g., <2>sd0a.
+ * If found, then forward request to correct device server.
+ * If not found, then remove '<n>' and process locally.
+ *
+ * XXX should handle send-right reply_port as well as send-once XXX
+ */
+ if (name[0] == '<') {
+ char *n;
+ int node = 0;
+
+ for (n = &name[1]; *n != '>'; n++) {
+ if (*n >= '0' && *n <= '9') {
+ node = 10 * node + (*n - '0');
+ } else {
+ return (D_NO_SUCH_DEVICE);
+ }
+ }
+ if (node == node_self()) {
+ name = &n[1]; /* skip trailing '>' */
+ } else {
+ forward_device_open_send(remote_device(node),
+ reply_port, mode, name);
+ return (MIG_NO_REPLY);
+ }
+ }
+#endif NORMA_DEVICE
+#endif /* ! i386 */
+
+ /*
+ * Find the device.
+ */
+ device = device_lookup(name);
+ if (device == MACH_DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+
+ /*
+ * If the device is being opened or closed,
+ * wait for that operation to finish.
+ */
+ device_lock(device);
+ while (device->state == DEV_STATE_OPENING ||
+ device->state == DEV_STATE_CLOSING) {
+ device->io_wait = TRUE;
+ thread_sleep((event_t)device, simple_lock_addr(device->lock), TRUE);
+ device_lock(device);
+ }
+
+ /*
+ * If the device is already open, increment the open count
+ * and return.
+ */
+ if (device->state == DEV_STATE_OPEN) {
+
+ if (device->flag & D_EXCL_OPEN) {
+ /*
+ * Cannot open a second time.
+ */
+ device_unlock(device);
+ mach_device_deallocate(device);
+ return (D_ALREADY_OPEN);
+ }
+
+ device->open_count++;
+ device_unlock(device);
+#ifdef i386
+ *device_p = &device->dev;
+#else
+ *device_p = device;
+#endif
+ return (D_SUCCESS);
+ /*
+ * Return deallocates device reference while acquiring
+ * port.
+ */
+ }
+
+ /*
+ * Allocate the device port and register the device before
+ * opening it.
+ */
+ device->state = DEV_STATE_OPENING;
+ device_unlock(device);
+
+ /*
+ * Allocate port, keeping a reference for it.
+ */
+ device->port = ipc_port_alloc_kernel();
+ if (device->port == IP_NULL) {
+ device_lock(device);
+ device->state = DEV_STATE_INIT;
+ device->port = IP_NULL;
+ if (device->io_wait) {
+ device->io_wait = FALSE;
+ thread_wakeup((event_t)device);
+ }
+ device_unlock(device);
+ mach_device_deallocate(device);
+ return (KERN_RESOURCE_SHORTAGE);
+ }
+
+ dev_port_enter(device);
+
+ /*
+ * Request no-senders notifications on device port.
+ */
+ notify = ipc_port_make_sonce(device->port);
+ ip_lock(device->port);
+ ipc_port_nsrequest(device->port, 1, notify, &notify);
+ assert(notify == IP_NULL);
+
+ /*
+ * Open the device.
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_OPEN | IO_CALL;
+ ior->io_mode = mode;
+ ior->io_error = 0;
+ ior->io_done = ds_open_done;
+ ior->io_reply_port = reply_port;
+ ior->io_reply_port_type = reply_port_type;
+
+ result = (*device->dev_ops->d_open)(device->dev_number, (int)mode, ior);
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Return result via ds_open_done.
+ */
+ ior->io_error = result;
+ (void) ds_open_done(ior);
+
+ io_req_free(ior);
+
+ return (MIG_NO_REPLY); /* reply already sent */
+}
+
+boolean_t
+ds_open_done(ior)
+ register io_req_t ior;
+{
+ kern_return_t result;
+ register mach_device_t device;
+
+ device = ior->io_device;
+ result = ior->io_error;
+
+ if (result != D_SUCCESS) {
+ /*
+ * Open failed. Deallocate port and device.
+ */
+ dev_port_remove(device);
+ ipc_port_dealloc_kernel(device->port);
+ device->port = IP_NULL;
+
+ device_lock(device);
+ device->state = DEV_STATE_INIT;
+ if (device->io_wait) {
+ device->io_wait = FALSE;
+ thread_wakeup((event_t)device);
+ }
+ device_unlock(device);
+
+ mach_device_deallocate(device);
+ device = MACH_DEVICE_NULL;
+ }
+ else {
+ /*
+ * Open succeeded.
+ */
+ device_lock(device);
+ device->state = DEV_STATE_OPEN;
+ device->open_count = 1;
+ if (device->io_wait) {
+ device->io_wait = FALSE;
+ thread_wakeup((event_t)device);
+ }
+ device_unlock(device);
+
+ /* donate device reference to get port */
+ }
+ /*
+ * Must explicitly convert device to port, since
+ * device_reply interface is built as 'user' side
+ * (thus cannot get translation).
+ */
+ if (IP_VALID(ior->io_reply_port)) {
+ (void) ds_device_open_reply(ior->io_reply_port,
+ ior->io_reply_port_type,
+ result,
+#ifdef i386
+ (mach_convert_device_to_port
+ (device)));
+#else
+ convert_device_to_port(device));
+#endif
+ } else
+ mach_device_deallocate(device);
+
+ return (TRUE);
+}
+
+#ifdef i386
+static io_return_t
+device_close(device)
+#else
+io_return_t
+ds_device_close(device)
+#endif
+ register mach_device_t device;
+{
+#ifndef i386
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ device_lock(device);
+
+ /*
+ * If device will remain open, do nothing.
+ */
+ if (--device->open_count > 0) {
+ device_unlock(device);
+ return (D_SUCCESS);
+ }
+
+ /*
+ * If device is being closed, do nothing.
+ */
+ if (device->state == DEV_STATE_CLOSING) {
+ device_unlock(device);
+ return (D_SUCCESS);
+ }
+
+ /*
+ * Mark device as closing, to prevent new IO.
+ * Outstanding IO will still be in progress.
+ */
+ device->state = DEV_STATE_CLOSING;
+ device_unlock(device);
+
+ /*
+ * ? wait for IO to end ?
+ * only if device wants to
+ */
+
+ /*
+ * Remove the device-port association.
+ */
+ dev_port_remove(device);
+ ipc_port_dealloc_kernel(device->port);
+
+ /*
+ * Close the device
+ */
+ (*device->dev_ops->d_close)(device->dev_number);
+
+ /*
+ * Finally mark it closed. If someone else is trying
+ * to open it, the open can now proceed.
+ */
+ device_lock(device);
+ device->state = DEV_STATE_INIT;
+ if (device->io_wait) {
+ device->io_wait = FALSE;
+ thread_wakeup((event_t)device);
+ }
+ device_unlock(device);
+
+ return (D_SUCCESS);
+}
+
+/*
+ * Write to a device.
+ */
+#ifdef i386
+static io_return_t
+device_write(d, reply_port, reply_port_type, mode, recnum,
+ data, data_count, bytes_written)
+ void *d;
+#else
+io_return_t
+ds_device_write(device, reply_port, reply_port_type, mode, recnum,
+ data, data_count, bytes_written)
+ register mach_device_t device;
+#endif
+ ipc_port_t reply_port;
+ mach_msg_type_name_t reply_port_type;
+ dev_mode_t mode;
+ recnum_t recnum;
+ io_buf_ptr_t data;
+ unsigned int data_count;
+ int *bytes_written; /* out */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+ register io_req_t ior;
+ register io_return_t result;
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+#ifndef i386
+ if (data == 0)
+ return (D_INVALID_SIZE);
+#endif
+
+ /*
+ * XXX Need logic to reject ridiculously big requests.
+ */
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * Package the write request for the device driver
+ */
+ io_req_alloc(ior, data_count);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = data;
+ ior->io_count = data_count;
+ ior->io_total = data_count;
+ ior->io_alloc_size = 0;
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_write_done;
+ ior->io_reply_port = reply_port;
+ ior->io_reply_port_type = reply_port_type;
+ ior->io_copy = VM_MAP_COPY_NULL;
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * And do the write ...
+ *
+ * device_write_dealoc returns false if there's more
+ * to do; it has updated the ior appropriately and expects
+ * its caller to reinvoke it on the device.
+ */
+
+ do {
+
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Discard the local mapping of the data.
+ */
+
+ } while (!device_write_dealloc(ior));
+
+ /*
+ * Return the number of bytes actually written.
+ */
+ *bytes_written = ior->io_total - ior->io_residual;
+
+ /*
+ * Remove the extra reference.
+ */
+ mach_device_deallocate(device);
+
+ io_req_free(ior);
+ return (result);
+}
+
+/*
+ * Write to a device, but memory is in message.
+ */
+#ifdef i386
+static io_return_t
+device_write_inband(d, reply_port, reply_port_type, mode, recnum,
+ data, data_count, bytes_written)
+ void *d;
+#else
+io_return_t
+ds_device_write_inband(device, reply_port, reply_port_type, mode, recnum,
+ data, data_count, bytes_written)
+ register mach_device_t device;
+#endif
+ ipc_port_t reply_port;
+ mach_msg_type_name_t reply_port_type;
+ dev_mode_t mode;
+ recnum_t recnum;
+ io_buf_ptr_inband_t data;
+ unsigned int data_count;
+ int *bytes_written; /* out */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+ register io_req_t ior;
+ register io_return_t result;
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+#ifndef i386
+ if (data == 0)
+ return (D_INVALID_SIZE);
+#endif
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * Package the write request for the device driver.
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL | IO_INBAND;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = data;
+ ior->io_count = data_count;
+ ior->io_total = data_count;
+ ior->io_alloc_size = 0;
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_write_done;
+ ior->io_reply_port = reply_port;
+ ior->io_reply_port_type = reply_port_type;
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * And do the write.
+ */
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Return the number of bytes actually written.
+ */
+ *bytes_written = ior->io_total - ior->io_residual;
+
+ /*
+ * Remove the extra reference.
+ */
+ mach_device_deallocate(device);
+
+ io_req_free(ior);
+ return (result);
+}
+
+/*
+ * Wire down incoming memory to give to device.
+ */
+kern_return_t
+device_write_get(ior, wait)
+ register io_req_t ior;
+ boolean_t *wait;
+{
+ vm_map_copy_t io_copy;
+ vm_offset_t new_addr;
+ register kern_return_t result;
+ int bsize;
+ vm_size_t min_size;
+
+ /*
+ * By default, caller does not have to wait.
+ */
+ *wait = FALSE;
+
+ /*
+ * Nothing to do if no data.
+ */
+ if (ior->io_count == 0)
+ return (KERN_SUCCESS);
+
+ /*
+ * Loaned iors already have valid data.
+ */
+ if (ior->io_op & IO_LOANED)
+ return (KERN_SUCCESS);
+
+ /*
+ * Inband case.
+ */
+ if (ior->io_op & IO_INBAND) {
+ assert(ior->io_count <= sizeof (io_buf_ptr_inband_t));
+ new_addr = zalloc(io_inband_zone);
+ bcopy((void*)ior->io_data, (void*)new_addr, ior->io_count);
+ ior->io_data = (io_buf_ptr_t)new_addr;
+ ior->io_alloc_size = sizeof (io_buf_ptr_inband_t);
+
+ return (KERN_SUCCESS);
+ }
+
+ /*
+ * Figure out how much data to move this time. If the device
+ * won't return a block size, then we have to do the whole
+ * request in one shot (ditto if this is a block fragment),
+ * otherwise, move at least one block's worth.
+ */
+ result = (*ior->io_device->dev_ops->d_dev_info)(
+ ior->io_device->dev_number,
+ D_INFO_BLOCK_SIZE,
+ &bsize);
+
+ if (result != KERN_SUCCESS || ior->io_count < (vm_size_t) bsize)
+ min_size = (vm_size_t) ior->io_count;
+ else
+ min_size = (vm_size_t) bsize;
+
+ /*
+ * Map the pages from this page list into memory.
+ * io_data records location of data.
+ * io_alloc_size is the vm size of the region to deallocate.
+ */
+ io_copy = (vm_map_copy_t) ior->io_data;
+ result = kmem_io_map_copyout(device_io_map,
+ (vm_offset_t*)&ior->io_data, &new_addr,
+ &ior->io_alloc_size, io_copy, min_size);
+ if (result != KERN_SUCCESS)
+ return (result);
+
+ if ((ior->io_data + ior->io_count) >
+ (((char *)new_addr) + ior->io_alloc_size)) {
+
+ /*
+ * Operation has to be split. Reset io_count for how
+ * much we can do this time.
+ */
+ assert(vm_map_copy_has_cont(io_copy));
+ assert(ior->io_count == io_copy->size);
+ ior->io_count = ior->io_alloc_size -
+ (ior->io_data - ((char *)new_addr));
+
+ /*
+ * Caller must wait synchronously.
+ */
+ ior->io_op &= ~IO_CALL;
+ *wait = TRUE;
+ }
+
+ ior->io_copy = io_copy; /* vm_map_copy to discard */
+ return (KERN_SUCCESS);
+}
+
+/*
+ * Clean up memory allocated for IO.
+ */
+boolean_t
+device_write_dealloc(ior)
+ register io_req_t ior;
+{
+ vm_map_copy_t new_copy = VM_MAP_COPY_NULL;
+ register
+ vm_map_copy_t io_copy;
+ kern_return_t result;
+ vm_offset_t size_to_do;
+ int bsize;
+
+ if (ior->io_alloc_size == 0)
+ return (TRUE);
+
+ /*
+ * Inband case.
+ */
+ if (ior->io_op & IO_INBAND) {
+ zfree(io_inband_zone, (vm_offset_t)ior->io_data);
+
+ return (TRUE);
+ }
+
+ if ((io_copy = ior->io_copy) == VM_MAP_COPY_NULL)
+ return (TRUE);
+
+ /*
+ * To prevent a possible deadlock with the default pager,
+ * we have to release space in the device_io_map before
+ * we allocate any memory. (Which vm_map_copy_invoke_cont
+ * might do.) See the discussion in ds_init.
+ */
+
+ kmem_io_map_deallocate(device_io_map,
+ trunc_page(ior->io_data),
+ (vm_size_t) ior->io_alloc_size);
+
+ if (vm_map_copy_has_cont(io_copy)) {
+
+ /*
+ * Remember how much is left, then
+ * invoke or abort the continuation.
+ */
+ size_to_do = io_copy->size - ior->io_count;
+ if (ior->io_error == 0) {
+ vm_map_copy_invoke_cont(io_copy, &new_copy, &result);
+ }
+ else {
+ vm_map_copy_abort_cont(io_copy);
+ result = KERN_FAILURE;
+ }
+
+ if (result == KERN_SUCCESS && new_copy != VM_MAP_COPY_NULL) {
+ register int res;
+
+ /*
+ * We have a new continuation, reset the ior to
+ * represent the remainder of the request. Must
+ * adjust the recnum because drivers assume
+ * that the residual is zero.
+ */
+ ior->io_op &= ~IO_DONE;
+ ior->io_op |= IO_CALL;
+
+ res = (*ior->io_device->dev_ops->d_dev_info)(
+ ior->io_device->dev_number,
+ D_INFO_BLOCK_SIZE,
+ &bsize);
+
+ if (res != D_SUCCESS)
+ panic("device_write_dealloc: No block size");
+
+ ior->io_recnum += ior->io_count/bsize;
+ ior->io_count = new_copy->size;
+ }
+ else {
+
+ /*
+ * No continuation. Add amount we didn't get
+ * to into residual.
+ */
+ ior->io_residual += size_to_do;
+ }
+ }
+
+ /*
+ * Clean up the state for the IO that just completed.
+ */
+ vm_map_copy_discard(ior->io_copy);
+ ior->io_copy = VM_MAP_COPY_NULL;
+ ior->io_data = (char *) new_copy;
+
+ /*
+ * Return FALSE if there's more IO to do.
+ */
+
+ return(new_copy == VM_MAP_COPY_NULL);
+}
+
+/*
+ * Send write completion message to client, and discard the data.
+ */
+boolean_t
+ds_write_done(ior)
+ register io_req_t ior;
+{
+ /*
+ * device_write_dealloc discards the data that has been
+ * written, but may decide that there is more to write.
+ */
+ while (!device_write_dealloc(ior)) {
+ register io_return_t result;
+ register mach_device_t device;
+
+ /*
+ * More IO to do -- invoke it.
+ */
+ device = ior->io_device;
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, return FALSE -- not done yet.
+ */
+ if (result == D_IO_QUEUED)
+ return (FALSE);
+ }
+
+ /*
+ * Now the write is really complete. Send reply.
+ */
+
+ if (IP_VALID(ior->io_reply_port)) {
+ (void) (*((ior->io_op & IO_INBAND) ?
+ ds_device_write_reply_inband :
+ ds_device_write_reply))(ior->io_reply_port,
+ ior->io_reply_port_type,
+ ior->io_error,
+ (int) (ior->io_total -
+ ior->io_residual));
+ }
+ mach_device_deallocate(ior->io_device);
+
+ return (TRUE);
+}
+
+/*
+ * Read from a device.
+ */
+#ifdef i386
+static io_return_t
+device_read(d, reply_port, reply_port_type, mode, recnum,
+ bytes_wanted, data, data_count)
+ void *d;
+#else
+io_return_t
+ds_device_read(device, reply_port, reply_port_type, mode, recnum,
+ bytes_wanted, data, data_count)
+ register mach_device_t device;
+#endif
+ ipc_port_t reply_port;
+ mach_msg_type_name_t reply_port_type;
+ dev_mode_t mode;
+ recnum_t recnum;
+ int bytes_wanted;
+ io_buf_ptr_t *data; /* out */
+ unsigned int *data_count; /* out */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+ register io_req_t ior;
+ register io_return_t result;
+
+#ifdef lint
+ *data = *data;
+ *data_count = *data_count;
+#endif lint
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * There must be a reply port.
+ */
+ if (!IP_VALID(reply_port)) {
+ printf("ds_* invalid reply port\n");
+ Debugger("ds_* reply_port");
+ return (MIG_NO_REPLY); /* no sense in doing anything */
+ }
+
+ /*
+ * Package the read request for the device driver
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_READ | IO_CALL;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = 0; /* driver must allocate data */
+ ior->io_count = bytes_wanted;
+ ior->io_alloc_size = 0; /* no data allocated yet */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_read_done;
+ ior->io_reply_port = reply_port;
+ ior->io_reply_port_type = reply_port_type;
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * And do the read.
+ */
+ result = (*device->dev_ops->d_read)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Return result via ds_read_done.
+ */
+ ior->io_error = result;
+ (void) ds_read_done(ior);
+ io_req_free(ior);
+
+ return (MIG_NO_REPLY); /* reply has already been sent. */
+}
+
+/*
+ * Read from a device, but return the data 'inband.'
+ */
+#ifdef i386
+static io_return_t
+device_read_inband(d, reply_port, reply_port_type, mode, recnum,
+ bytes_wanted, data, data_count)
+ void *d;
+#else
+io_return_t
+ds_device_read_inband(device, reply_port, reply_port_type, mode, recnum,
+ bytes_wanted, data, data_count)
+ register mach_device_t device;
+#endif
+ ipc_port_t reply_port;
+ mach_msg_type_name_t reply_port_type;
+ dev_mode_t mode;
+ recnum_t recnum;
+ int bytes_wanted;
+ char *data; /* pointer to OUT array */
+ unsigned int *data_count; /* out */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+ register io_req_t ior;
+ register io_return_t result;
+
+#ifdef lint
+ *data = *data;
+ *data_count = *data_count;
+#endif lint
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * There must be a reply port.
+ */
+ if (!IP_VALID(reply_port)) {
+ printf("ds_* invalid reply port\n");
+ Debugger("ds_* reply_port");
+ return (MIG_NO_REPLY); /* no sense in doing anything */
+ }
+
+ /*
+ * Package the read for the device driver
+ */
+ io_req_alloc(ior, 0);
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_READ | IO_CALL | IO_INBAND;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = 0; /* driver must allocate data */
+ ior->io_count =
+ ((bytes_wanted < sizeof(io_buf_ptr_inband_t)) ?
+ bytes_wanted : sizeof(io_buf_ptr_inband_t));
+ ior->io_alloc_size = 0; /* no data allocated yet */
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_read_done;
+ ior->io_reply_port = reply_port;
+ ior->io_reply_port_type = reply_port_type;
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * Do the read.
+ */
+ result = (*device->dev_ops->d_read)(device->dev_number, ior);
+
+ /*
+ * If the io was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Return result, via ds_read_done.
+ */
+ ior->io_error = result;
+ (void) ds_read_done(ior);
+ io_req_free(ior);
+
+ return (MIG_NO_REPLY); /* reply has already been sent. */
+}
+
+
+/*
+ * Allocate wired-down memory for device read.
+ */
+kern_return_t device_read_alloc(ior, size)
+ register io_req_t ior;
+ register vm_size_t size;
+{
+ vm_offset_t addr;
+ kern_return_t kr;
+
+ /*
+ * Nothing to do if no data.
+ */
+ if (ior->io_count == 0)
+ return (KERN_SUCCESS);
+
+ if (ior->io_op & IO_INBAND) {
+ ior->io_data = (io_buf_ptr_t) zalloc(io_inband_zone);
+ ior->io_alloc_size = sizeof(io_buf_ptr_inband_t);
+ } else {
+ size = round_page(size);
+ kr = kmem_alloc(kernel_map, &addr, size);
+ if (kr != KERN_SUCCESS)
+ return (kr);
+
+ ior->io_data = (io_buf_ptr_t) addr;
+ ior->io_alloc_size = size;
+ }
+
+ return (KERN_SUCCESS);
+}
+
+boolean_t ds_read_done(ior)
+ io_req_t ior;
+{
+ vm_offset_t start_data, end_data;
+ vm_offset_t start_sent, end_sent;
+ register vm_size_t size_read;
+
+ if (ior->io_error)
+ size_read = 0;
+ else
+ size_read = ior->io_count - ior->io_residual;
+
+ start_data = (vm_offset_t)ior->io_data;
+ end_data = start_data + size_read;
+
+ start_sent = (ior->io_op & IO_INBAND) ? start_data :
+ trunc_page(start_data);
+ end_sent = (ior->io_op & IO_INBAND) ?
+ start_data + ior->io_alloc_size : round_page(end_data);
+
+ /*
+ * Zero memory that the device did not fill.
+ */
+ if (start_sent < start_data)
+ bzero((char *)start_sent, start_data - start_sent);
+ if (end_sent > end_data)
+ bzero((char *)end_data, end_sent - end_data);
+
+
+ /*
+ * Touch the data being returned, to mark it dirty.
+ * If the pages were filled by DMA, the pmap module
+ * may think that they are clean.
+ */
+ {
+ register vm_offset_t touch;
+ register int c;
+
+ for (touch = start_sent; touch < end_sent; touch += PAGE_SIZE) {
+ c = *(char *)touch;
+ *(char *)touch = c;
+ }
+ }
+
+ /*
+ * Send the data to the reply port - this
+ * unwires and deallocates it.
+ */
+ if (ior->io_op & IO_INBAND) {
+ (void)ds_device_read_reply_inband(ior->io_reply_port,
+ ior->io_reply_port_type,
+ ior->io_error,
+ (char *) start_data,
+ size_read);
+ } else {
+ vm_map_copy_t copy;
+ kern_return_t kr;
+
+ kr = vm_map_copyin_page_list(kernel_map, start_data,
+ size_read, TRUE, TRUE,
+ &copy, FALSE);
+
+ if (kr != KERN_SUCCESS)
+ panic("read_done: vm_map_copyin_page_list failed");
+
+ (void)ds_device_read_reply(ior->io_reply_port,
+ ior->io_reply_port_type,
+ ior->io_error,
+ (char *) copy,
+ size_read);
+ }
+
+ /*
+ * Free any memory that was allocated but not sent.
+ */
+ if (ior->io_count != 0) {
+ if (ior->io_op & IO_INBAND) {
+ if (ior->io_alloc_size > 0)
+ zfree(io_inband_zone, (vm_offset_t)ior->io_data);
+ } else {
+ register vm_offset_t end_alloc;
+
+ end_alloc = start_sent + round_page(ior->io_alloc_size);
+ if (end_alloc > end_sent)
+ (void) vm_deallocate(kernel_map,
+ end_sent,
+ end_alloc - end_sent);
+ }
+ }
+
+ mach_device_deallocate(ior->io_device);
+
+ return (TRUE);
+}
+
+#ifdef i386
+static io_return_t
+device_set_status(d, flavor, status, status_count)
+ void *d;
+#else
+io_return_t
+ds_device_set_status(device, flavor, status, status_count)
+ register mach_device_t device;
+#endif
+ dev_flavor_t flavor;
+ dev_status_t status;
+ mach_msg_type_number_t status_count;
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ return ((*device->dev_ops->d_setstat)(device->dev_number,
+ flavor,
+ status,
+ status_count));
+}
+
+#ifdef i386
+io_return_t
+mach_device_get_status(d, flavor, status, status_count)
+ void *d;
+#else
+io_return_t
+ds_device_get_status(device, flavor, status, status_count)
+ register mach_device_t device;
+#endif
+ dev_flavor_t flavor;
+ dev_status_t status; /* pointer to OUT array */
+ mach_msg_type_number_t *status_count; /* out */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ return ((*device->dev_ops->d_getstat)(device->dev_number,
+ flavor,
+ status,
+ status_count));
+}
+
+#ifdef i386
+static io_return_t
+device_set_filter(d, receive_port, priority, filter, filter_count)
+ void *d;
+#else
+io_return_t
+ds_device_set_filter(device, receive_port, priority, filter, filter_count)
+ register mach_device_t device;
+#endif
+ ipc_port_t receive_port;
+ int priority;
+ filter_t filter[]; /* pointer to IN array */
+ unsigned int filter_count;
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * Request is absurd if no receive port is specified.
+ */
+ if (!IP_VALID(receive_port))
+ return (D_INVALID_OPERATION);
+
+ return ((*device->dev_ops->d_async_in)(device->dev_number,
+ receive_port,
+ priority,
+ filter,
+ filter_count));
+}
+
+#ifdef i386
+static io_return_t
+device_map(d, protection, offset, size, pager, unmap)
+ void *d;
+#else
+io_return_t
+ds_device_map(device, protection, offset, size, pager, unmap)
+ register mach_device_t device;
+#endif
+ vm_prot_t protection;
+ vm_offset_t offset;
+ vm_size_t size;
+ ipc_port_t *pager; /* out */
+ boolean_t unmap; /* ? */
+{
+#ifdef i386
+ register mach_device_t device = d;
+#endif
+
+#ifdef lint
+ unmap = unmap;
+#endif lint
+ if (protection & ~VM_PROT_ALL)
+ return (KERN_INVALID_ARGUMENT);
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ return (device_pager_setup(device, protection, offset, size,
+ (mach_port_t*)pager));
+}
+
+/*
+ * Doesn't do anything (yet).
+ */
+#ifdef i386
+static void
+#else
+void
+#endif
+ds_no_senders(notification)
+ mach_no_senders_notification_t *notification;
+{
+ printf("ds_no_senders called! device_port=0x%x count=%d\n",
+ notification->not_header.msgh_remote_port,
+ notification->not_count);
+}
+
+#ifndef i386
+boolean_t
+ds_notify(msg)
+ mach_msg_header_t *msg;
+{
+ switch (msg->msgh_id) {
+ case MACH_NOTIFY_NO_SENDERS:
+ ds_no_senders((mach_no_senders_notification_t *) msg);
+ return TRUE;
+
+ default:
+ printf("ds_notify: strange notification %d\n", msg->msgh_id);
+ return FALSE;
+ }
+}
+#endif
+
+queue_head_t io_done_list;
+decl_simple_lock_data(, io_done_list_lock)
+
+#define splio splsched /* XXX must block ALL io devices */
+
+void iodone(ior)
+ register io_req_t ior;
+{
+ register spl_t s;
+
+ /*
+ * If this ior was loaned to us, return it directly.
+ */
+ if (ior->io_op & IO_LOANED) {
+ (*ior->io_done)(ior);
+ return;
+ }
+ /*
+ * If !IO_CALL, some thread is waiting for this. Must lock
+ * structure to interlock correctly with iowait(). Else can
+ * toss on queue for io_done thread to call completion.
+ */
+ s = splio();
+ if ((ior->io_op & IO_CALL) == 0) {
+ ior_lock(ior);
+ ior->io_op |= IO_DONE;
+ ior->io_op &= ~IO_WANTED;
+ ior_unlock(ior);
+ thread_wakeup((event_t)ior);
+ } else {
+ ior->io_op |= IO_DONE;
+ simple_lock(&io_done_list_lock);
+ enqueue_tail(&io_done_list, (queue_entry_t)ior);
+ thread_wakeup((event_t)&io_done_list);
+ simple_unlock(&io_done_list_lock);
+ }
+ splx(s);
+}
+
+void io_done_thread_continue()
+{
+ for (;;) {
+ register spl_t s;
+ register io_req_t ior;
+
+#if defined (i386) && defined (LINUX_DEV)
+ free_skbuffs ();
+#endif
+ s = splio();
+ simple_lock(&io_done_list_lock);
+ while ((ior = (io_req_t)dequeue_head(&io_done_list)) != 0) {
+ simple_unlock(&io_done_list_lock);
+ (void) splx(s);
+
+ if ((*ior->io_done)(ior)) {
+ /*
+ * IO done - free io_req_elt
+ */
+ io_req_free(ior);
+ }
+ /* else routine has re-queued it somewhere */
+
+ s = splio();
+ simple_lock(&io_done_list_lock);
+ }
+
+ assert_wait(&io_done_list, FALSE);
+ simple_unlock(&io_done_list_lock);
+ (void) splx(s);
+ counter(c_io_done_thread_block++);
+ thread_block(io_done_thread_continue);
+ }
+}
+
+void io_done_thread()
+{
+ /*
+ * Set thread privileges and highest priority.
+ */
+ current_thread()->vm_privilege = TRUE;
+ stack_privilege(current_thread());
+ thread_set_own_priority(0);
+
+ io_done_thread_continue();
+ /*NOTREACHED*/
+}
+
+#define DEVICE_IO_MAP_SIZE (2 * 1024 * 1024)
+
+extern void ds_trap_init(void); /* forward */
+
+void ds_init()
+{
+ vm_offset_t device_io_min, device_io_max;
+
+ queue_init(&io_done_list);
+ simple_lock_init(&io_done_list_lock);
+
+ device_io_map = kmem_suballoc(kernel_map,
+ &device_io_min,
+ &device_io_max,
+ DEVICE_IO_MAP_SIZE,
+ FALSE);
+ /*
+ * If the kernel receives many device_write requests, the
+ * device_io_map might run out of space. To prevent
+ * device_write_get from failing in this case, we enable
+ * wait_for_space on the map. This causes kmem_io_map_copyout
+ * to block until there is sufficient space.
+ * (XXX Large writes may be starved by small writes.)
+ *
+ * There is a potential deadlock problem with this solution,
+ * if a device_write from the default pager has to wait
+ * for the completion of a device_write which needs to wait
+ * for memory allocation. Hence, once device_write_get
+ * allocates space in device_io_map, no blocking memory
+ * allocations should happen until device_write_dealloc
+ * frees the space. (XXX A large write might starve
+ * a small write from the default pager.)
+ */
+ device_io_map->wait_for_space = TRUE;
+
+ io_inband_zone = zinit(sizeof(io_buf_ptr_inband_t),
+ 1000 * sizeof(io_buf_ptr_inband_t),
+ 10 * sizeof(io_buf_ptr_inband_t),
+ FALSE,
+ "io inband read buffers");
+
+ ds_trap_init();
+}
+
+void iowait(ior)
+ io_req_t ior;
+{
+ spl_t s;
+
+ s = splio();
+ ior_lock(ior);
+ while ((ior->io_op&IO_DONE)==0) {
+ assert_wait((event_t)ior, FALSE);
+ ior_unlock(ior);
+ thread_block((void (*)()) 0);
+ ior_lock(ior);
+ }
+ ior_unlock(ior);
+ splx(s);
+}
+
+
+/*
+ * Device trap support.
+ */
+
+/*
+ * Memory Management
+ *
+ * This currently has a single pool of 2k wired buffers
+ * since we only handle writes to an ethernet device.
+ * Should be more general.
+ */
+#define IOTRAP_REQSIZE 2048
+
+zone_t io_trap_zone;
+
+/*
+ * Initialization. Called from ds_init().
+ */
+void
+ds_trap_init(void)
+{
+ io_trap_zone = zinit(IOTRAP_REQSIZE,
+ 256 * IOTRAP_REQSIZE,
+ 16 * IOTRAP_REQSIZE,
+ FALSE,
+ "wired device trap buffers");
+}
+
+/*
+ * Allocate an io_req_t.
+ * Currently zalloc's from io_trap_zone.
+ *
+ * Could have lists of different size zones.
+ * Could call a device-specific routine.
+ */
+io_req_t
+ds_trap_req_alloc(mach_device_t device, vm_size_t data_size)
+{
+ return (io_req_t) zalloc(io_trap_zone);
+}
+
+/*
+ * Called by iodone to release ior.
+ */
+boolean_t
+ds_trap_write_done(io_req_t ior)
+{
+ register mach_device_t dev;
+
+ dev = ior->io_device;
+
+ /*
+ * Should look at reply port and maybe send a message.
+ */
+ zfree(io_trap_zone, ior);
+
+ /*
+ * Give up device reference from ds_write_trap.
+ */
+ mach_device_deallocate(dev);
+ return TRUE;
+}
+
+/*
+ * Like device_write except that data is in user space.
+ */
+#ifdef i386
+static io_return_t
+device_write_trap (void *d, dev_mode_t mode,
+ recnum_t recnum, vm_offset_t data, vm_size_t data_count)
+#else
+io_return_t
+ds_device_write_trap(device_t device,
+ dev_mode_t mode,
+ recnum_t recnum,
+ vm_offset_t data,
+ vm_size_t data_count)
+#endif
+{
+#ifdef i386
+ mach_device_t device = d;
+#endif
+ io_req_t ior;
+ io_return_t result;
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * Get a buffer to hold the ioreq.
+ */
+ ior = ds_trap_req_alloc(device, data_count);
+
+ /*
+ * Package the write request for the device driver.
+ */
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL | IO_LOANED;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = (io_buf_ptr_t)
+ (vm_offset_t)ior + sizeof(struct io_req);
+ ior->io_count = data_count;
+ ior->io_total = data_count;
+ ior->io_alloc_size = 0;
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_trap_write_done;
+ ior->io_reply_port = IP_NULL; /* XXX */
+ ior->io_reply_port_type = 0; /* XXX */
+
+ /*
+ * Copy the data from user space.
+ */
+ if (data_count > 0)
+ copyin((char *)data, (char *)ior->io_data, data_count);
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * And do the write.
+ */
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Remove the extra reference.
+ */
+ mach_device_deallocate(device);
+
+ zfree(io_trap_zone, ior);
+ return (result);
+}
+
+#ifdef i386
+static io_return_t
+device_writev_trap (void *d, dev_mode_t mode,
+ recnum_t recnum, io_buf_vec_t *iovec, vm_size_t iocount)
+#else
+io_return_t
+ds_device_writev_trap(device_t device,
+ dev_mode_t mode,
+ recnum_t recnum,
+ io_buf_vec_t *iovec,
+ vm_size_t iocount)
+#endif
+{
+#ifdef i386
+ mach_device_t device = d;
+#endif
+ io_req_t ior;
+ io_return_t result;
+ io_buf_vec_t stack_iovec[16]; /* XXX */
+ vm_size_t data_count;
+ int i;
+
+#ifndef i386
+ /*
+ * Refuse if device is dead or not completely open.
+ */
+ if (device == DEVICE_NULL)
+ return (D_NO_SUCH_DEVICE);
+#endif
+
+ if (device->state != DEV_STATE_OPEN)
+ return (D_NO_SUCH_DEVICE);
+
+ /* XXX note that a CLOSE may proceed at any point */
+
+ /*
+ * Copyin user addresses.
+ */
+ if (iocount > 16)
+ return KERN_INVALID_VALUE; /* lame */
+ copyin((char *)iovec,
+ (char *)stack_iovec,
+ iocount * sizeof(io_buf_vec_t));
+ for (data_count = 0, i = 0; i < iocount; i++)
+ data_count += stack_iovec[i].count;
+
+ /*
+ * Get a buffer to hold the ioreq.
+ */
+ ior = ds_trap_req_alloc(device, data_count);
+
+ /*
+ * Package the write request for the device driver.
+ */
+
+ ior->io_device = device;
+ ior->io_unit = device->dev_number;
+ ior->io_op = IO_WRITE | IO_CALL | IO_LOANED;
+ ior->io_mode = mode;
+ ior->io_recnum = recnum;
+ ior->io_data = (io_buf_ptr_t)
+ (vm_offset_t)ior + sizeof(struct io_req);
+ ior->io_count = data_count;
+ ior->io_total = data_count;
+ ior->io_alloc_size = 0;
+ ior->io_residual = 0;
+ ior->io_error = 0;
+ ior->io_done = ds_trap_write_done;
+ ior->io_reply_port = IP_NULL; /* XXX */
+ ior->io_reply_port_type = 0; /* XXX */
+
+ /*
+ * Copy the data from user space.
+ */
+ if (data_count > 0) {
+ vm_offset_t p;
+
+ p = (vm_offset_t) ior->io_data;
+ for (i = 0; i < iocount; i++) {
+ copyin((char *) stack_iovec[i].data,
+ (char *) p,
+ stack_iovec[i].count);
+ p += stack_iovec[i].count;
+ }
+ }
+
+ /*
+ * The ior keeps an extra reference for the device.
+ */
+ mach_device_reference(device);
+
+ /*
+ * And do the write.
+ */
+ result = (*device->dev_ops->d_write)(device->dev_number, ior);
+
+ /*
+ * If the IO was queued, delay reply until it is finished.
+ */
+ if (result == D_IO_QUEUED)
+ return (MIG_NO_REPLY);
+
+ /*
+ * Remove the extra reference.
+ */
+ mach_device_deallocate(device);
+
+ zfree(io_trap_zone, ior);
+ return (result);
+}
+
+#ifdef i386
+struct device_emulation_ops mach_device_emulation_ops =
+{
+ mach_device_reference,
+ mach_device_deallocate,
+ mach_convert_device_to_port,
+ device_open,
+ device_close,
+ device_write,
+ device_write_inband,
+ device_read,
+ device_read_inband,
+ device_set_status,
+ mach_device_get_status,
+ device_set_filter,
+ device_map,
+ ds_no_senders,
+ device_write_trap,
+ device_writev_trap
+};
+#endif
diff --git a/device/ds_routines.h b/device/ds_routines.h
new file mode 100644
index 0000000..fff21fb
--- /dev/null
+++ b/device/ds_routines.h
@@ -0,0 +1,52 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ *
+ * Device service utility routines.
+ */
+
+#ifndef DS_ROUTINES_H
+#define DS_ROUTINES_H
+
+#include <vm/vm_map.h>
+#include <device/device_types.h>
+
+/*
+ * Map for device IO memory.
+ */
+vm_map_t device_io_map;
+
+kern_return_t device_read_alloc();
+kern_return_t device_write_get();
+boolean_t device_write_dealloc();
+
+boolean_t ds_open_done();
+boolean_t ds_read_done();
+boolean_t ds_write_done();
+
+#endif DS_ROUTINES_H
diff --git a/device/errno.h b/device/errno.h
new file mode 100644
index 0000000..35e31e5
--- /dev/null
+++ b/device/errno.h
@@ -0,0 +1,45 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ *
+ * Old names for new error codes, for compatibility.
+ */
+
+#ifndef _ERRNO_
+#define _ERRNO_
+
+#include <device/device_types.h> /* the real error names */
+
+#define EIO D_IO_ERROR
+#define ENXIO D_NO_SUCH_DEVICE
+#define EINVAL D_INVALID_SIZE /* XXX */
+#define EBUSY D_ALREADY_OPEN
+#define ENOTTY D_INVALID_OPERATION
+#define ENOMEM D_NO_MEMORY
+
+#endif _ERRNO_
diff --git a/device/if_ether.h b/device/if_ether.h
new file mode 100644
index 0000000..da83fcf
--- /dev/null
+++ b/device/if_ether.h
@@ -0,0 +1,57 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Ethernet definitions.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 8/89
+ */
+
+#ifndef _DEVICE_IF_ETHER_H_
+#define _DEVICE_IF_ETHER_H_
+
+#include <sys/types.h>
+
+/*
+ * Structure of a 10Mb/s Ethernet header.
+ */
+struct ether_header {
+ u_char ether_dhost[6];
+ u_char ether_shost[6];
+ u_short ether_type;
+};
+
+#define ETHERMTU 1500
+#define ETHERMIN (60-14)
+
+#ifdef KERNEL
+u_char etherbroadcastaddr[6];
+
+extern char * ether_sprintf();
+#endif KERNEL
+
+#endif /*_DEVICE_IF_ETHER_H_*/
diff --git a/device/if_hdr.h b/device/if_hdr.h
new file mode 100644
index 0000000..64defb7
--- /dev/null
+++ b/device/if_hdr.h
@@ -0,0 +1,150 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Taken from (bsd)net/if.h. Modified for MACH kernel.
+ */
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * @(#)if.h 7.3 (Berkeley) 6/27/88
+ */
+
+#ifndef _IF_HDR_
+#define _IF_HDR_
+
+#include <kern/lock.h>
+#include <kern/queue.h>
+
+/*
+ * Queue for network output and filter input.
+ */
+struct ifqueue {
+ queue_head_t ifq_head; /* queue of io_req_t */
+ int ifq_len; /* length of queue */
+ int ifq_maxlen; /* maximum length of queue */
+ int ifq_drops; /* number of packets dropped
+ because queue full */
+ decl_simple_lock_data(,
+ ifq_lock) /* lock for queue and counters */
+};
+
+/*
+ * Header for network interface drivers.
+ */
+struct ifnet {
+ short if_unit; /* unit number */
+ short if_flags; /* up/down, broadcast, etc. */
+ short if_timer; /* time until if_watchdog called */
+ short if_mtu; /* maximum transmission unit */
+ short if_header_size; /* length of header */
+ short if_header_format; /* format of hardware header */
+ short if_address_size; /* length of hardware address */
+ short if_alloc_size; /* size of read buffer to allocate */
+ char *if_address; /* pointer to hardware address */
+ struct ifqueue if_snd; /* output queue */
+ queue_head_t if_rcv_port_list; /* input filter list */
+ decl_simple_lock_data(,
+ if_rcv_port_list_lock) /* lock for filter list */
+/* statistics */
+ int if_ipackets; /* packets received */
+ int if_ierrors; /* input errors */
+ int if_opackets; /* packets sent */
+ int if_oerrors; /* output errors */
+ int if_collisions; /* collisions on csma interfaces */
+ int if_rcvdrops; /* packets received but dropped */
+};
+
+#define IFF_UP 0x0001 /* interface is up */
+#define IFF_BROADCAST 0x0002 /* interface can broadcast */
+#define IFF_DEBUG 0x0004 /* turn on debugging */
+#define IFF_LOOPBACK 0x0008 /* is a loopback net */
+#define IFF_POINTOPOINT 0x0010 /* point-to-point link */
+#define IFF_RUNNING 0x0040 /* resources allocated */
+#define IFF_NOARP 0x0080 /* no address resolution protocol */
+#define IFF_PROMISC 0x0100 /* receive all packets */
+#define IFF_ALLMULTI 0x0200 /* receive all multicast packets */
+#define IFF_BRIDGE 0x0100 /* support token ring routing field */
+#define IFF_SNAP 0x0200 /* support extended sap header */
+
+/* internal flags only: */
+#define IFF_CANTCHANGE (IFF_BROADCAST | IFF_POINTOPOINT | IFF_RUNNING)
+
+/*
+ * Output queues (ifp->if_snd)
+ * have queues of messages stored on ifqueue structures. Entries
+ * are added to and deleted from these structures by these macros, which
+ * should be called with ipl raised to splimp().
+ * XXX locking XXX
+ */
+
+#define IF_QFULL(ifq) ((ifq)->ifq_len >= (ifq)->ifq_maxlen)
+#define IF_DROP(ifq) ((ifq)->ifq_drops++)
+#define IF_ENQUEUE(ifq, ior) { \
+ simple_lock(&(ifq)->ifq_lock); \
+ enqueue_tail(&(ifq)->ifq_head, (queue_entry_t)ior); \
+ (ifq)->ifq_len++; \
+ simple_unlock(&(ifq)->ifq_lock); \
+}
+#define IF_PREPEND(ifq, ior) { \
+ simple_lock(&(ifq)->ifq_lock); \
+ enqueue_head(&(ifq)->ifq_head, (queue_entry_t)ior); \
+ (ifq)->ifq_len++; \
+ simple_unlock(&(ifq)->ifq_lock); \
+}
+
+#define IF_DEQUEUE(ifq, ior) { \
+ simple_lock(&(ifq)->ifq_lock); \
+ if (((ior) = (io_req_t)dequeue_head(&(ifq)->ifq_head)) != 0) \
+ (ifq)->ifq_len--; \
+ simple_unlock(&(ifq)->ifq_lock); \
+}
+
+#define IFQ_MAXLEN 50
+
+#define IFQ_INIT(ifq) { \
+ queue_init(&(ifq)->ifq_head); \
+ simple_lock_init(&(ifq)->ifq_lock); \
+ (ifq)->ifq_len = 0; \
+ (ifq)->ifq_maxlen = IFQ_MAXLEN; \
+ (ifq)->ifq_drops = 0; \
+}
+
+#define IFNET_SLOWHZ 1 /* granularity is 1 second */
+
+#endif _IF_HDR_
diff --git a/device/io_req.h b/device/io_req.h
new file mode 100644
index 0000000..76e55b6
--- /dev/null
+++ b/device/io_req.h
@@ -0,0 +1,141 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 10/88
+ */
+
+#ifndef _IO_REQ_
+#define _IO_REQ_
+
+#include <mach/boolean.h>
+#include <mach/port.h>
+#include <mach/message.h>
+#include <mach/vm_param.h>
+#include <kern/kalloc.h>
+#include <kern/lock.h>
+#include <vm/vm_page.h>
+#include <device/device_types.h>
+#include <device/dev_hdr.h>
+
+#include <kern/macro_help.h>
+
+/*
+ * IO request element, queued on device for delayed replies.
+ */
+struct io_req {
+ struct io_req * io_next; /* next, ... */
+ struct io_req * io_prev; /* prev pointers: link in done,
+ defered, or in-progress list */
+ mach_device_t io_device; /* pointer to open-device structure */
+ char * io_dev_ptr; /* pointer to driver structure -
+ filled in by driver if necessary */
+ int io_unit; /* unit number ('minor') of device */
+ int io_op; /* IO operation */
+ dev_mode_t io_mode; /* operation mode (wait, truncate) */
+ recnum_t io_recnum; /* starting record number for
+ random-access devices */
+
+ union io_un {
+ io_buf_ptr_t data; /* data, for IO requests */
+ } io_un;
+#define io_data io_un.data
+
+ long io_count; /* amount requested */
+ long io_alloc_size; /* amount allocated */
+ long io_residual; /* amount NOT done */
+ io_return_t io_error; /* error code */
+ boolean_t (*io_done)(); /* call when done - returns TRUE
+ if IO really finished */
+ struct ipc_port *io_reply_port; /* reply port, for asynchronous
+ messages */
+ mach_msg_type_name_t io_reply_port_type;
+ /* send or send-once right? */
+ struct io_req * io_link; /* forward link (for driver header) */
+ struct io_req * io_rlink; /* reverse link (for driver header) */
+ vm_map_copy_t io_copy; /* vm_map_copy obj. for this op. */
+ long io_total; /* total op size, for write */
+ decl_simple_lock_data(,io_req_lock)
+ /* Lock for this structure */
+ long io_physrec; /* mapping to the physical block
+ number */
+ long io_rectotal; /* total number of blocks to move */
+};
+typedef struct io_req * io_req_t;
+
+/*
+ * LOCKING NOTE: Operations on io_req's are in general single threaded by
+ * the invoking code, obviating the need for a lock. The usual IO_CALL
+ * path through the code is: Initiating thread hands io_req to device driver,
+ * driver passes it to io_done thread, io_done thread sends reply message. No
+ * locking is needed in this sequence. Unfortunately, a synchronous wait
+ * for a buffer requires a lock to avoid problems if the wait and interrupt
+ * happen simultaneously on different processors.
+ */
+
+#define ior_lock(ior) simple_lock(&(ior)->io_req_lock)
+#define ior_unlock(ior) simple_unlock(&(ior)->io_req_lock)
+
+/*
+ * Flags and operations
+ */
+
+#define IO_WRITE 0x00000000 /* operation is write */
+#define IO_READ 0x00000001 /* operation is read */
+#define IO_OPEN 0x00000002 /* operation is open */
+#define IO_DONE 0x00000100 /* operation complete */
+#define IO_ERROR 0x00000200 /* error on operation */
+#define IO_BUSY 0x00000400 /* operation in progress */
+#define IO_WANTED 0x00000800 /* wakeup when no longer BUSY */
+#define IO_BAD 0x00001000 /* bad disk block */
+#define IO_CALL 0x00002000 /* call io_done_thread when done */
+#define IO_INBAND 0x00004000 /* mig call was inband */
+#define IO_INTERNAL 0x00008000 /* internal, device-driver specific */
+#define IO_LOANED 0x00010000 /* ior loaned by another module */
+
+#define IO_SPARE_START 0x00020000 /* start of spare flags */
+
+/*
+ * Standard completion routine for io_requests.
+ */
+void iodone(/* io_req_t */);
+
+/*
+ * Macros to allocate and free IORs - will convert to zones later.
+ */
+#define io_req_alloc(ior,size) \
+ MACRO_BEGIN \
+ (ior) = (io_req_t)kalloc(sizeof(struct io_req)); \
+ simple_lock_init(&(ior)->io_req_lock); \
+ MACRO_END
+
+#define io_req_free(ior) \
+ (kfree((vm_offset_t)(ior), sizeof(struct io_req)))
+
+
+zone_t io_inband_zone; /* for inband reads */
+
+#endif _IO_REQ_
diff --git a/device/memory_object_reply.cli b/device/memory_object_reply.cli
new file mode 100644
index 0000000..f2cd480
--- /dev/null
+++ b/device/memory_object_reply.cli
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+/* This is a client presentation file. */
+
+#define KERNEL_USER 1
+
+#include <mach/mach.defs>
diff --git a/device/net_io.c b/device/net_io.c
new file mode 100644
index 0000000..7714ebe
--- /dev/null
+++ b/device/net_io.c
@@ -0,0 +1,2168 @@
+ /*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 3/98
+ *
+ * Network IO.
+ *
+ * Packet filter code taken from vaxif/enet.c written
+ * CMU and Stanford.
+ */
+
+/*
+ * Note: don't depend on anything in this file.
+ * It may change a lot real soon. -cmaeda 11 June 1993
+ */
+
+#include <norma_ether.h>
+#include <mach_ttd.h>
+
+#include <sys/types.h>
+#include <device/net_status.h>
+#include <machine/machspl.h> /* spl definitions */
+#include <device/net_io.h>
+#include <device/if_hdr.h>
+#include <device/io_req.h>
+#include <device/ds_routines.h>
+
+#include <mach/boolean.h>
+#include <mach/vm_param.h>
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_kmsg.h>
+#include <ipc/ipc_mqueue.h>
+
+#include <kern/counters.h>
+#include <kern/lock.h>
+#include <kern/queue.h>
+#include <kern/sched_prim.h>
+#include <kern/thread.h>
+
+#if NORMA_ETHER
+#include <norma/ipc_ether.h>
+#endif /*NORMA_ETHER*/
+
+#include <machine/machspl.h>
+
+#if MACH_TTD
+#include <ttd/ttd_stub.h>
+#endif /* MACH_TTD */
+
+#if MACH_TTD
+int kttd_async_counter= 0;
+#endif /* MACH_TTD */
+
+
+/*
+ * Packet Buffer Management
+ *
+ * This module manages a private pool of kmsg buffers.
+ */
+
+/*
+ * List of net kmsgs queued to be sent to users.
+ * Messages can be high priority or low priority.
+ * The network thread processes high priority messages first.
+ */
+decl_simple_lock_data(,net_queue_lock)
+boolean_t net_thread_awake = FALSE;
+struct ipc_kmsg_queue net_queue_high;
+int net_queue_high_size = 0;
+int net_queue_high_max = 0; /* for debugging */
+struct ipc_kmsg_queue net_queue_low;
+int net_queue_low_size = 0;
+int net_queue_low_max = 0; /* for debugging */
+
+/*
+ * List of net kmsgs that can be touched at interrupt level.
+ * If it is empty, we will also steal low priority messages.
+ */
+decl_simple_lock_data(,net_queue_free_lock)
+struct ipc_kmsg_queue net_queue_free;
+int net_queue_free_size = 0; /* on free list */
+int net_queue_free_max = 0; /* for debugging */
+
+/*
+ * This value is critical to network performance.
+ * At least this many buffers should be sitting in net_queue_free.
+ * If this is set too small, we will drop network packets.
+ * Even a low drop rate (<1%) can cause severe network throughput problems.
+ * We add one to net_queue_free_min for every filter.
+ */
+int net_queue_free_min = 3;
+
+int net_queue_free_hits = 0; /* for debugging */
+int net_queue_free_steals = 0; /* for debugging */
+int net_queue_free_misses = 0; /* for debugging */
+
+int net_kmsg_send_high_hits = 0; /* for debugging */
+int net_kmsg_send_low_hits = 0; /* for debugging */
+int net_kmsg_send_high_misses = 0; /* for debugging */
+int net_kmsg_send_low_misses = 0; /* for debugging */
+
+int net_thread_awaken = 0; /* for debugging */
+int net_ast_taken = 0; /* for debugging */
+
+decl_simple_lock_data(,net_kmsg_total_lock)
+int net_kmsg_total = 0; /* total allocated */
+int net_kmsg_max; /* initialized below */
+
+vm_size_t net_kmsg_size; /* initialized below */
+
+/*
+ * We want more buffers when there aren't enough in the free queue
+ * and the low priority queue. However, we don't want to allocate
+ * more than net_kmsg_max.
+ */
+
+#define net_kmsg_want_more() \
+ (((net_queue_free_size + net_queue_low_size) < net_queue_free_min) && \
+ (net_kmsg_total < net_kmsg_max))
+
+ipc_kmsg_t
+net_kmsg_get(void)
+{
+ register ipc_kmsg_t kmsg;
+ spl_t s;
+
+ /*
+ * First check the list of free buffers.
+ */
+ s = splimp();
+ simple_lock(&net_queue_free_lock);
+ kmsg = ipc_kmsg_queue_first(&net_queue_free);
+ if (kmsg != IKM_NULL) {
+ ipc_kmsg_rmqueue_first_macro(&net_queue_free, kmsg);
+ net_queue_free_size--;
+ net_queue_free_hits++;
+ }
+ simple_unlock(&net_queue_free_lock);
+
+ if (kmsg == IKM_NULL) {
+ /*
+ * Try to steal from the low priority queue.
+ */
+ simple_lock(&net_queue_lock);
+ kmsg = ipc_kmsg_queue_first(&net_queue_low);
+ if (kmsg != IKM_NULL) {
+ ipc_kmsg_rmqueue_first_macro(&net_queue_low, kmsg);
+ net_queue_low_size--;
+ net_queue_free_steals++;
+ }
+ simple_unlock(&net_queue_lock);
+ }
+
+ if (kmsg == IKM_NULL)
+ net_queue_free_misses++;
+ (void) splx(s);
+
+ if (net_kmsg_want_more() || (kmsg == IKM_NULL)) {
+ boolean_t awake;
+
+ s = splimp();
+ simple_lock(&net_queue_lock);
+ awake = net_thread_awake;
+ net_thread_awake = TRUE;
+ simple_unlock(&net_queue_lock);
+ (void) splx(s);
+
+ if (!awake)
+ thread_wakeup((event_t) &net_thread_awake);
+ }
+
+ return kmsg;
+}
+
+void
+net_kmsg_put(register ipc_kmsg_t kmsg)
+{
+ spl_t s;
+
+ s = splimp();
+ simple_lock(&net_queue_free_lock);
+ ipc_kmsg_enqueue_macro(&net_queue_free, kmsg);
+ if (++net_queue_free_size > net_queue_free_max)
+ net_queue_free_max = net_queue_free_size;
+ simple_unlock(&net_queue_free_lock);
+ (void) splx(s);
+}
+
+void
+net_kmsg_collect(void)
+{
+ register ipc_kmsg_t kmsg;
+ spl_t s;
+
+ s = splimp();
+ simple_lock(&net_queue_free_lock);
+ while (net_queue_free_size > net_queue_free_min) {
+ kmsg = ipc_kmsg_dequeue(&net_queue_free);
+ net_queue_free_size--;
+ simple_unlock(&net_queue_free_lock);
+ (void) splx(s);
+
+ net_kmsg_free(kmsg);
+ simple_lock(&net_kmsg_total_lock);
+ net_kmsg_total--;
+ simple_unlock(&net_kmsg_total_lock);
+
+ s = splimp();
+ simple_lock(&net_queue_free_lock);
+ }
+ simple_unlock(&net_queue_free_lock);
+ (void) splx(s);
+}
+
+void
+net_kmsg_more(void)
+{
+ register ipc_kmsg_t kmsg;
+
+ /*
+ * Replenish net kmsg pool if low. We don't have the locks
+ * necessary to look at these variables, but that's OK because
+ * misread values aren't critical. The danger in this code is
+ * that while we allocate buffers, interrupts are happening
+ * which take buffers out of the free list. If we are not
+ * careful, we will sit in the loop and allocate a zillion
+ * buffers while a burst of packets arrives. So we count
+ * buffers in the low priority queue as available, because
+ * net_kmsg_get will make use of them, and we cap the total
+ * number of buffers we are willing to allocate.
+ */
+
+ while (net_kmsg_want_more()) {
+ simple_lock(&net_kmsg_total_lock);
+ net_kmsg_total++;
+ simple_unlock(&net_kmsg_total_lock);
+ kmsg = net_kmsg_alloc();
+ net_kmsg_put(kmsg);
+ }
+}
+
+/*
+ * Packet Filter Data Structures
+ *
+ * Each network interface has a set of packet filters
+ * that are run on incoming packets.
+ *
+ * Each packet filter may represent a single network
+ * session or multiple network sessions. For example,
+ * all application level TCP sessions would be represented
+ * by a single packet filter data structure.
+ *
+ * If a packet filter has a single session, we use a
+ * struct net_rcv_port to represent it. If the packet
+ * filter represents multiple sessions, we use a
+ * struct net_hash_header to represent it.
+ */
+
+/*
+ * Each interface has a write port and a set of read ports.
+ * Each read port has one or more filters to determine what packets
+ * should go to that port.
+ */
+
+/*
+ * Receive port for net, with packet filter.
+ * This data structure by itself represents a packet
+ * filter for a single session.
+ */
+struct net_rcv_port {
+ queue_chain_t chain; /* list of open_descriptors */
+ ipc_port_t rcv_port; /* port to send packet to */
+ int rcv_qlimit; /* port's qlimit */
+ int rcv_count; /* number of packets received */
+ int priority; /* priority for filter */
+ filter_t *filter_end; /* pointer to end of filter */
+ filter_t filter[NET_MAX_FILTER];
+ /* filter operations */
+};
+typedef struct net_rcv_port *net_rcv_port_t;
+
+zone_t net_rcv_zone; /* zone of net_rcv_port structs */
+
+
+#define NET_HASH_SIZE 256
+#define N_NET_HASH 4
+#define N_NET_HASH_KEYS 4
+
+unsigned int bpf_hash (int, unsigned int *);
+
+/*
+ * A single hash entry.
+ */
+struct net_hash_entry {
+ queue_chain_t chain; /* list of entries with same hval */
+#define he_next chain.next
+#define he_prev chain.prev
+ ipc_port_t rcv_port; /* destination port */
+ int rcv_qlimit; /* qlimit for the port */
+ unsigned int keys[N_NET_HASH_KEYS];
+};
+typedef struct net_hash_entry *net_hash_entry_t;
+
+zone_t net_hash_entry_zone;
+
+/*
+ * This structure represents a packet filter with multiple sessions.
+ *
+ * For example, all application level TCP sessions might be
+ * represented by one of these structures. It looks like a
+ * net_rcv_port struct so that both types can live on the
+ * same packet filter queues.
+ */
+struct net_hash_header {
+ struct net_rcv_port rcv;
+ int n_keys; /* zero if not used */
+ int ref_count; /* reference count */
+ net_hash_entry_t table[NET_HASH_SIZE];
+} filter_hash_header[N_NET_HASH];
+
+typedef struct net_hash_header *net_hash_header_t;
+
+decl_simple_lock_data(,net_hash_header_lock)
+
+#define HASH_ITERATE(head, elt) (elt) = (net_hash_entry_t) (head); do {
+#define HASH_ITERATE_END(head, elt) \
+ (elt) = (net_hash_entry_t) queue_next((queue_entry_t) (elt)); \
+ } while ((elt) != (head));
+
+
+#define FILTER_ITERATE(ifp, fp, nextfp) \
+ for ((fp) = (net_rcv_port_t) queue_first(&(ifp)->if_rcv_port_list);\
+ !queue_end(&(ifp)->if_rcv_port_list, (queue_entry_t)(fp)); \
+ (fp) = (nextfp)) { \
+ (nextfp) = (net_rcv_port_t) queue_next(&(fp)->chain);
+#define FILTER_ITERATE_END }
+
+/* entry_p must be net_rcv_port_t or net_hash_entry_t */
+#define ENQUEUE_DEAD(dead, entry_p) { \
+ queue_next(&(entry_p)->chain) = (queue_entry_t) (dead); \
+ (dead) = (queue_entry_t)(entry_p); \
+}
+
+extern boolean_t net_do_filter(); /* CSPF */
+extern int bpf_do_filter(); /* BPF */
+
+
+/*
+ * ethernet_priority:
+ *
+ * This function properly belongs in the ethernet interfaces;
+ * it should not be called by this module. (We get packet
+ * priorities as an argument to net_filter.) It is here
+ * to avoid massive code duplication.
+ *
+ * Returns TRUE for high-priority packets.
+ */
+
+boolean_t ethernet_priority(kmsg)
+ ipc_kmsg_t kmsg;
+{
+ register unsigned char *addr =
+ (unsigned char *) net_kmsg(kmsg)->header;
+
+ /*
+ * A simplistic check for broadcast packets.
+ */
+
+ if ((addr[0] == 0xff) && (addr[1] == 0xff) &&
+ (addr[2] == 0xff) && (addr[3] == 0xff) &&
+ (addr[4] == 0xff) && (addr[5] == 0xff))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+mach_msg_type_t header_type = {
+ MACH_MSG_TYPE_BYTE,
+ 8,
+ NET_HDW_HDR_MAX,
+ TRUE,
+ FALSE,
+ FALSE,
+ 0
+};
+
+mach_msg_type_t packet_type = {
+ MACH_MSG_TYPE_BYTE, /* name */
+ 8, /* size */
+ 0, /* number */
+ TRUE, /* inline */
+ FALSE, /* longform */
+ FALSE /* deallocate */
+};
+
+/*
+ * net_deliver:
+ *
+ * Called and returns holding net_queue_lock, at splimp.
+ * Dequeues a message and delivers it at spl0.
+ * Returns FALSE if no messages.
+ */
+boolean_t net_deliver(nonblocking)
+ boolean_t nonblocking;
+{
+ register ipc_kmsg_t kmsg;
+ boolean_t high_priority;
+ struct ipc_kmsg_queue send_list;
+
+ /*
+ * Pick up a pending network message and deliver it.
+ * Deliver high priority messages before low priority.
+ */
+
+ if ((kmsg = ipc_kmsg_dequeue(&net_queue_high)) != IKM_NULL) {
+ net_queue_high_size--;
+ high_priority = TRUE;
+ } else if ((kmsg = ipc_kmsg_dequeue(&net_queue_low)) != IKM_NULL) {
+ net_queue_low_size--;
+ high_priority = FALSE;
+ } else
+ return FALSE;
+ simple_unlock(&net_queue_lock);
+ (void) spl0();
+
+ /*
+ * Run the packet through the filters,
+ * getting back a queue of packets to send.
+ */
+ net_filter(kmsg, &send_list);
+
+ if (!nonblocking) {
+ /*
+ * There is a danger of running out of available buffers
+ * because they all get moved into the high priority queue
+ * or a port queue. In particular, we might need to
+ * allocate more buffers as we pull (previously available)
+ * buffers out of the low priority queue. But we can only
+ * allocate if we are allowed to block.
+ */
+ net_kmsg_more();
+ }
+
+ while ((kmsg = ipc_kmsg_dequeue(&send_list)) != IKM_NULL) {
+ int count;
+
+ /*
+ * Fill in the rest of the kmsg.
+ */
+ count = net_kmsg(kmsg)->net_rcv_msg_packet_count;
+
+ ikm_init_special(kmsg, IKM_SIZE_NETWORK);
+
+ kmsg->ikm_header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_PORT_SEND, 0);
+ /* remember message sizes must be rounded up */
+ kmsg->ikm_header.msgh_size =
+ ((mach_msg_size_t) (sizeof(struct net_rcv_msg)
+ - NET_RCV_MAX + count))+3 &~ 3;
+ kmsg->ikm_header.msgh_local_port = MACH_PORT_NULL;
+ kmsg->ikm_header.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ kmsg->ikm_header.msgh_id = NET_RCV_MSG_ID;
+
+ net_kmsg(kmsg)->header_type = header_type;
+ net_kmsg(kmsg)->packet_type = packet_type;
+ net_kmsg(kmsg)->net_rcv_msg_packet_count = count;
+
+ /*
+ * Send the packet to the destination port. Drop it
+ * if the destination port is over its backlog.
+ */
+
+ if (ipc_mqueue_send(kmsg, MACH_SEND_TIMEOUT, 0) ==
+ MACH_MSG_SUCCESS) {
+ if (high_priority)
+ net_kmsg_send_high_hits++;
+ else
+ net_kmsg_send_low_hits++;
+ /* the receiver is responsible for the message now */
+ } else {
+ if (high_priority)
+ net_kmsg_send_high_misses++;
+ else
+ net_kmsg_send_low_misses++;
+ ipc_kmsg_destroy(kmsg);
+ }
+ }
+
+ (void) splimp();
+ simple_lock(&net_queue_lock);
+ return TRUE;
+}
+
+/*
+ * We want to deliver packets using ASTs, so we can avoid the
+ * thread_wakeup/thread_block needed to get to the network
+ * thread. However, we can't allocate memory in the AST handler,
+ * because memory allocation might block. Hence we have the
+ * network thread to allocate memory. The network thread also
+ * delivers packets, so it can be allocating and delivering for a
+ * burst. net_thread_awake is protected by net_queue_lock
+ * (instead of net_queue_free_lock) so that net_packet and
+ * net_ast can safely determine if the network thread is running.
+ * This prevents a race that might leave a packet sitting without
+ * being delivered. It is possible for net_kmsg_get to think
+ * the network thread is awake, and so avoid a wakeup, and then
+ * have the network thread sleep without allocating. The next
+ * net_kmsg_get will do a wakeup.
+ */
+
+void net_ast()
+{
+ spl_t s;
+
+ net_ast_taken++;
+
+ /*
+ * If the network thread is awake, then we would
+ * rather deliver messages from it, because
+ * it can also allocate memory.
+ */
+
+ s = splimp();
+ simple_lock(&net_queue_lock);
+ while (!net_thread_awake && net_deliver(TRUE))
+ continue;
+
+ /*
+ * Prevent an unnecessary AST. Either the network
+ * thread will deliver the messages, or there are
+ * no messages left to deliver.
+ */
+
+ simple_unlock(&net_queue_lock);
+ (void) splsched();
+ ast_off(cpu_number(), AST_NETWORK);
+ (void) splx(s);
+}
+
+void net_thread_continue()
+{
+ for (;;) {
+ spl_t s;
+
+ net_thread_awaken++;
+
+ /*
+ * First get more buffers.
+ */
+ net_kmsg_more();
+
+ s = splimp();
+ simple_lock(&net_queue_lock);
+ while (net_deliver(FALSE))
+ continue;
+
+ net_thread_awake = FALSE;
+ assert_wait(&net_thread_awake, FALSE);
+ simple_unlock(&net_queue_lock);
+ (void) splx(s);
+ counter(c_net_thread_block++);
+ thread_block(net_thread_continue);
+ }
+}
+
+void net_thread()
+{
+ spl_t s;
+
+ /*
+ * We should be very high priority.
+ */
+
+ thread_set_own_priority(0);
+
+ /*
+ * We sleep initially, so that we don't allocate any buffers
+ * unless the network is really in use and they are needed.
+ */
+
+ s = splimp();
+ simple_lock(&net_queue_lock);
+ net_thread_awake = FALSE;
+ assert_wait(&net_thread_awake, FALSE);
+ simple_unlock(&net_queue_lock);
+ (void) splx(s);
+ counter(c_net_thread_block++);
+ thread_block(net_thread_continue);
+ net_thread_continue();
+ /*NOTREACHED*/
+}
+
+void
+reorder_queue(first, last)
+ register queue_t first, last;
+{
+ register queue_entry_t prev, next;
+
+ prev = first->prev;
+ next = last->next;
+
+ prev->next = last;
+ next->prev = first;
+
+ last->prev = prev;
+ last->next = first;
+
+ first->next = next;
+ first->prev = last;
+}
+
+/*
+ * Incoming packet. Header has already been moved to proper place.
+ * We are already at splimp.
+ */
+void
+net_packet(ifp, kmsg, count, priority)
+ register struct ifnet *ifp;
+ register ipc_kmsg_t kmsg;
+ unsigned int count;
+ boolean_t priority;
+{
+ boolean_t awake;
+
+#if NORMA_ETHER
+ if (netipc_net_packet(kmsg, count)) {
+ return;
+ }
+#endif NORMA_ETHER
+
+#if MACH_TTD
+ /*
+ * Do a quick check to see if it is a kernel TTD packet.
+ *
+ * Only check if KernelTTD is enabled, ie. the current
+ * device driver supports TTD, and the bootp succeded.
+ */
+ if (kttd_enabled && kttd_handle_async(kmsg)) {
+ /*
+ * Packet was a valid ttd packet and
+ * doesn't need to be passed up to filter.
+ * The ttd code put the used kmsg buffer
+ * back onto the free list.
+ */
+ if (kttd_debug)
+ printf("**%x**", kttd_async_counter++);
+ return;
+ }
+#endif /* MACH_TTD */
+
+ kmsg->ikm_header.msgh_remote_port = (mach_port_t) ifp;
+ net_kmsg(kmsg)->net_rcv_msg_packet_count = count;
+
+ simple_lock(&net_queue_lock);
+ if (priority) {
+ ipc_kmsg_enqueue(&net_queue_high, kmsg);
+ if (++net_queue_high_size > net_queue_high_max)
+ net_queue_high_max = net_queue_high_size;
+ } else {
+ ipc_kmsg_enqueue(&net_queue_low, kmsg);
+ if (++net_queue_low_size > net_queue_low_max)
+ net_queue_low_max = net_queue_low_size;
+ }
+ /*
+ * If the network thread is awake, then we don't
+ * need to take an AST, because the thread will
+ * deliver the packet.
+ */
+ awake = net_thread_awake;
+ simple_unlock(&net_queue_lock);
+
+ if (!awake) {
+ spl_t s = splsched();
+ ast_on(cpu_number(), AST_NETWORK);
+ (void) splx(s);
+ }
+}
+
+int net_filter_queue_reorder = 0; /* non-zero to enable reordering */
+
+/*
+ * Run a packet through the filters, returning a list of messages.
+ * We are *not* called at interrupt level.
+ */
+void
+net_filter(kmsg, send_list)
+ register ipc_kmsg_t kmsg;
+ ipc_kmsg_queue_t send_list;
+{
+ register struct ifnet *ifp;
+ register net_rcv_port_t infp, nextfp;
+ register ipc_kmsg_t new_kmsg;
+
+ net_hash_entry_t entp, *hash_headp;
+ ipc_port_t dest;
+ queue_entry_t dead_infp = (queue_entry_t) 0;
+ queue_entry_t dead_entp = (queue_entry_t) 0;
+ unsigned int ret_count;
+
+ int count = net_kmsg(kmsg)->net_rcv_msg_packet_count;
+ ifp = (struct ifnet *) kmsg->ikm_header.msgh_remote_port;
+ ipc_kmsg_queue_init(send_list);
+
+ /*
+ * Unfortunately we can't allocate or deallocate memory
+ * while holding this lock. And we can't drop the lock
+ * while examining the filter list.
+ */
+ simple_lock(&ifp->if_rcv_port_list_lock);
+ FILTER_ITERATE(ifp, infp, nextfp)
+ {
+ entp = (net_hash_entry_t) 0;
+ if (infp->filter[0] == NETF_BPF) {
+ ret_count = bpf_do_filter(infp, net_kmsg(kmsg)->packet, count,
+ net_kmsg(kmsg)->header,
+ &hash_headp, &entp);
+ if (entp == (net_hash_entry_t) 0)
+ dest = infp->rcv_port;
+ else
+ dest = entp->rcv_port;
+ } else {
+ ret_count = net_do_filter(infp, net_kmsg(kmsg)->packet, count,
+ net_kmsg(kmsg)->header);
+ if (ret_count)
+ ret_count = count;
+ dest = infp->rcv_port;
+ }
+
+ if (ret_count) {
+
+ /*
+ * Make a send right for the destination.
+ */
+
+ dest = ipc_port_copy_send(dest);
+ if (!IP_VALID(dest)) {
+ /*
+ * This filter is dead. We remove it from the
+ * filter list and set it aside for deallocation.
+ */
+
+ if (entp == (net_hash_entry_t) 0) {
+ queue_remove(&ifp->if_rcv_port_list, infp,
+ net_rcv_port_t, chain);
+ ENQUEUE_DEAD(dead_infp, infp);
+ continue;
+ } else {
+ hash_ent_remove (ifp,
+ (net_hash_header_t)infp,
+ FALSE, /* no longer used */
+ hash_headp,
+ entp,
+ &dead_entp);
+ continue;
+ }
+ }
+
+ /*
+ * Deliver copy of packet to this channel.
+ */
+ if (ipc_kmsg_queue_empty(send_list)) {
+ /*
+ * Only receiver, so far
+ */
+ new_kmsg = kmsg;
+ } else {
+ /*
+ * Other receivers - must allocate message and copy.
+ */
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ ipc_port_release_send(dest);
+ break;
+ }
+
+ bcopy(
+ net_kmsg(kmsg)->packet,
+ net_kmsg(new_kmsg)->packet,
+ ret_count);
+ bcopy(
+ net_kmsg(kmsg)->header,
+ net_kmsg(new_kmsg)->header,
+ NET_HDW_HDR_MAX);
+ }
+ net_kmsg(new_kmsg)->net_rcv_msg_packet_count = ret_count;
+ new_kmsg->ikm_header.msgh_remote_port = (mach_port_t) dest;
+ ipc_kmsg_enqueue(send_list, new_kmsg);
+
+ {
+ register net_rcv_port_t prevfp;
+ int rcount = ++infp->rcv_count;
+
+ /*
+ * See if ordering of filters is wrong
+ */
+ if (infp->priority >= NET_HI_PRI) {
+ prevfp = (net_rcv_port_t) queue_prev(&infp->chain);
+ /*
+ * If infp is not the first element on the queue,
+ * and the previous element is at equal priority
+ * but has a lower count, then promote infp to
+ * be in front of prevfp.
+ */
+ if ((queue_t)prevfp != &ifp->if_rcv_port_list &&
+ infp->priority == prevfp->priority) {
+ /*
+ * Threshold difference to prevent thrashing
+ */
+ if (net_filter_queue_reorder
+ && (100 + prevfp->rcv_count < rcount))
+ reorder_queue(&prevfp->chain, &infp->chain);
+ }
+ /*
+ * High-priority filter -> no more deliveries
+ */
+ break;
+ }
+ }
+ }
+ }
+ FILTER_ITERATE_END
+
+ simple_unlock(&ifp->if_rcv_port_list_lock);
+
+ /*
+ * Deallocate dead filters.
+ */
+ if (dead_infp != 0)
+ net_free_dead_infp(dead_infp);
+ if (dead_entp != 0)
+ net_free_dead_entp(dead_entp);
+
+ if (ipc_kmsg_queue_empty(send_list)) {
+ /* Not sent - recycle */
+ net_kmsg_put(kmsg);
+ }
+}
+
+boolean_t
+net_do_filter(infp, data, data_count, header)
+ net_rcv_port_t infp;
+ char * data;
+ unsigned int data_count;
+ char * header;
+{
+ int stack[NET_FILTER_STACK_DEPTH+1];
+ register int *sp;
+ register filter_t *fp, *fpe;
+ register unsigned int op, arg;
+
+ /*
+ * The filter accesses the header and data
+ * as unsigned short words.
+ */
+ data_count /= sizeof(unsigned short);
+
+#define data_word ((unsigned short *)data)
+#define header_word ((unsigned short *)header)
+
+ sp = &stack[NET_FILTER_STACK_DEPTH];
+ fp = &infp->filter[0];
+ fpe = infp->filter_end;
+
+ *sp = TRUE;
+
+ while (fp < fpe) {
+ arg = *fp++;
+ op = NETF_OP(arg);
+ arg = NETF_ARG(arg);
+
+ switch (arg) {
+ case NETF_NOPUSH:
+ arg = *sp++;
+ break;
+ case NETF_PUSHZERO:
+ arg = 0;
+ break;
+ case NETF_PUSHLIT:
+ arg = *fp++;
+ break;
+ case NETF_PUSHIND:
+ arg = *sp++;
+ if (arg >= data_count)
+ return FALSE;
+ arg = data_word[arg];
+ break;
+ case NETF_PUSHHDRIND:
+ arg = *sp++;
+ if (arg >= NET_HDW_HDR_MAX/sizeof(unsigned short))
+ return FALSE;
+ arg = header_word[arg];
+ break;
+ default:
+ if (arg >= NETF_PUSHSTK) {
+ arg = sp[arg - NETF_PUSHSTK];
+ }
+ else if (arg >= NETF_PUSHHDR) {
+ arg = header_word[arg - NETF_PUSHHDR];
+ }
+ else {
+ arg -= NETF_PUSHWORD;
+ if (arg >= data_count)
+ return FALSE;
+ arg = data_word[arg];
+ }
+ break;
+
+ }
+ switch (op) {
+ case NETF_OP(NETF_NOP):
+ *--sp = arg;
+ break;
+ case NETF_OP(NETF_AND):
+ *sp &= arg;
+ break;
+ case NETF_OP(NETF_OR):
+ *sp |= arg;
+ break;
+ case NETF_OP(NETF_XOR):
+ *sp ^= arg;
+ break;
+ case NETF_OP(NETF_EQ):
+ *sp = (*sp == arg);
+ break;
+ case NETF_OP(NETF_NEQ):
+ *sp = (*sp != arg);
+ break;
+ case NETF_OP(NETF_LT):
+ *sp = (*sp < arg);
+ break;
+ case NETF_OP(NETF_LE):
+ *sp = (*sp <= arg);
+ break;
+ case NETF_OP(NETF_GT):
+ *sp = (*sp > arg);
+ break;
+ case NETF_OP(NETF_GE):
+ *sp = (*sp >= arg);
+ break;
+ case NETF_OP(NETF_COR):
+ if (*sp++ == arg)
+ return (TRUE);
+ break;
+ case NETF_OP(NETF_CAND):
+ if (*sp++ != arg)
+ return (FALSE);
+ break;
+ case NETF_OP(NETF_CNOR):
+ if (*sp++ == arg)
+ return (FALSE);
+ break;
+ case NETF_OP(NETF_CNAND):
+ if (*sp++ != arg)
+ return (TRUE);
+ break;
+ case NETF_OP(NETF_LSH):
+ *sp <<= arg;
+ break;
+ case NETF_OP(NETF_RSH):
+ *sp >>= arg;
+ break;
+ case NETF_OP(NETF_ADD):
+ *sp += arg;
+ break;
+ case NETF_OP(NETF_SUB):
+ *sp -= arg;
+ break;
+ }
+ }
+ return ((*sp) ? TRUE : FALSE);
+
+#undef data_word
+#undef header_word
+}
+
+/*
+ * Check filter for invalid operations or stack over/under-flow.
+ */
+boolean_t
+parse_net_filter(filter, count)
+ register filter_t *filter;
+ unsigned int count;
+{
+ register int sp;
+ register filter_t *fpe = &filter[count];
+ register filter_t op, arg;
+
+ sp = NET_FILTER_STACK_DEPTH;
+
+ for (; filter < fpe; filter++) {
+ op = NETF_OP(*filter);
+ arg = NETF_ARG(*filter);
+
+ switch (arg) {
+ case NETF_NOPUSH:
+ break;
+ case NETF_PUSHZERO:
+ sp--;
+ break;
+ case NETF_PUSHLIT:
+ filter++;
+ if (filter >= fpe)
+ return (FALSE); /* literal value not in filter */
+ sp--;
+ break;
+ case NETF_PUSHIND:
+ case NETF_PUSHHDRIND:
+ break;
+ default:
+ if (arg >= NETF_PUSHSTK) {
+ if (arg - NETF_PUSHSTK + sp > NET_FILTER_STACK_DEPTH)
+ return FALSE;
+ }
+ else if (arg >= NETF_PUSHHDR) {
+ if (arg - NETF_PUSHHDR >=
+ NET_HDW_HDR_MAX/sizeof(unsigned short))
+ return FALSE;
+ }
+ /* else... cannot check for packet bounds
+ without packet */
+ sp--;
+ break;
+ }
+ if (sp < 2) {
+ return (FALSE); /* stack overflow */
+ }
+ if (op == NETF_OP(NETF_NOP))
+ continue;
+
+ /*
+ * all non-NOP operators are binary.
+ */
+ if (sp > NET_MAX_FILTER-2)
+ return (FALSE);
+
+ sp++;
+ switch (op) {
+ case NETF_OP(NETF_AND):
+ case NETF_OP(NETF_OR):
+ case NETF_OP(NETF_XOR):
+ case NETF_OP(NETF_EQ):
+ case NETF_OP(NETF_NEQ):
+ case NETF_OP(NETF_LT):
+ case NETF_OP(NETF_LE):
+ case NETF_OP(NETF_GT):
+ case NETF_OP(NETF_GE):
+ case NETF_OP(NETF_COR):
+ case NETF_OP(NETF_CAND):
+ case NETF_OP(NETF_CNOR):
+ case NETF_OP(NETF_CNAND):
+ case NETF_OP(NETF_LSH):
+ case NETF_OP(NETF_RSH):
+ case NETF_OP(NETF_ADD):
+ case NETF_OP(NETF_SUB):
+ break;
+ default:
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Set a filter for a network interface.
+ *
+ * We are given a naked send right for the rcv_port.
+ * If we are successful, we must consume that right.
+ */
+io_return_t
+net_set_filter(ifp, rcv_port, priority, filter, filter_count)
+ struct ifnet *ifp;
+ ipc_port_t rcv_port;
+ int priority;
+ filter_t *filter;
+ unsigned int filter_count;
+{
+ int filter_bytes;
+ bpf_insn_t match;
+ register net_rcv_port_t infp, my_infp;
+ net_rcv_port_t nextfp;
+ net_hash_header_t hhp;
+ register net_hash_entry_t entp, hash_entp;
+ net_hash_entry_t *head, nextentp;
+ queue_entry_t dead_infp, dead_entp;
+ int i;
+ int ret, is_new_infp;
+ io_return_t rval;
+
+ /*
+ * Check the filter syntax.
+ */
+
+ filter_bytes = CSPF_BYTES(filter_count);
+ match = (bpf_insn_t) 0;
+
+ if (filter_count > 0 && filter[0] == NETF_BPF) {
+ ret = bpf_validate((bpf_insn_t)filter, filter_bytes, &match);
+ if (!ret)
+ return (D_INVALID_OPERATION);
+ } else {
+ if (!parse_net_filter(filter, filter_count))
+ return (D_INVALID_OPERATION);
+ }
+
+ rval = D_SUCCESS; /* default return value */
+ dead_infp = dead_entp = 0;
+
+ if (match == (bpf_insn_t) 0) {
+ /*
+ * If there is no match instruction, we allocate
+ * a normal packet filter structure.
+ */
+ my_infp = (net_rcv_port_t) zalloc(net_rcv_zone);
+ my_infp->rcv_port = rcv_port;
+ is_new_infp = TRUE;
+ } else {
+ /*
+ * If there is a match instruction, we assume there will
+ * multiple session with a common substructure and allocate
+ * a hash table to deal with them.
+ */
+ my_infp = 0;
+ hash_entp = (net_hash_entry_t) zalloc(net_hash_entry_zone);
+ is_new_infp = FALSE;
+ }
+
+ /*
+ * Look for an existing filter on the same reply port.
+ * Look for filters with dead ports (for GC).
+ * Look for a filter with the same code except KEY insns.
+ */
+
+ simple_lock(&ifp->if_rcv_port_list_lock);
+
+ FILTER_ITERATE(ifp, infp, nextfp)
+ {
+ if (infp->rcv_port == MACH_PORT_NULL) {
+ if (match != 0
+ && infp->priority == priority
+ && my_infp == 0
+ && (infp->filter_end - infp->filter) == filter_count
+ && bpf_eq((bpf_insn_t)infp->filter,
+ filter, filter_bytes))
+ {
+ my_infp = infp;
+ }
+
+ for (i = 0; i < NET_HASH_SIZE; i++) {
+ head = &((net_hash_header_t) infp)->table[i];
+ if (*head == 0)
+ continue;
+
+ /*
+ * Check each hash entry to make sure the
+ * destination port is still valid. Remove
+ * any invalid entries.
+ */
+ entp = *head;
+ do {
+ nextentp = (net_hash_entry_t) entp->he_next;
+
+ /* checked without
+ ip_lock(entp->rcv_port) */
+ if (entp->rcv_port == rcv_port
+ || !IP_VALID(entp->rcv_port)
+ || !ip_active(entp->rcv_port)) {
+
+ ret = hash_ent_remove (ifp,
+ (net_hash_header_t)infp,
+ (my_infp == infp),
+ head,
+ entp,
+ &dead_entp);
+ if (ret)
+ goto hash_loop_end;
+ }
+
+ entp = nextentp;
+ /* While test checks head since hash_ent_remove
+ might modify it.
+ */
+ } while (*head != 0 && entp != *head);
+ }
+ hash_loop_end:
+ ;
+
+ } else if (infp->rcv_port == rcv_port
+ || !IP_VALID(infp->rcv_port)
+ || !ip_active(infp->rcv_port)) {
+ /* Remove the old filter from list */
+ remqueue(&ifp->if_rcv_port_list, (queue_entry_t)infp);
+ ENQUEUE_DEAD(dead_infp, infp);
+ }
+ }
+ FILTER_ITERATE_END
+
+ if (my_infp == 0) {
+ /* Allocate a dummy infp */
+ simple_lock(&net_hash_header_lock);
+ for (i = 0; i < N_NET_HASH; i++) {
+ if (filter_hash_header[i].n_keys == 0)
+ break;
+ }
+ if (i == N_NET_HASH) {
+ simple_unlock(&net_hash_header_lock);
+ simple_unlock(&ifp->if_rcv_port_list_lock);
+
+ ipc_port_release_send(rcv_port);
+ if (match != 0)
+ zfree (net_hash_entry_zone, (vm_offset_t)hash_entp);
+
+ rval = D_NO_MEMORY;
+ goto clean_and_return;
+ }
+
+ hhp = &filter_hash_header[i];
+ hhp->n_keys = match->jt;
+ simple_unlock(&net_hash_header_lock);
+
+ hhp->ref_count = 0;
+ for (i = 0; i < NET_HASH_SIZE; i++)
+ hhp->table[i] = 0;
+
+ my_infp = (net_rcv_port_t)hhp;
+ my_infp->rcv_port = MACH_PORT_NULL; /* indication of dummy */
+ is_new_infp = TRUE;
+ }
+
+ if (is_new_infp) {
+ my_infp->priority = priority;
+ my_infp->rcv_count = 0;
+
+ /* Copy filter program. */
+ bcopy ((vm_offset_t)filter, (vm_offset_t)my_infp->filter,
+ filter_bytes);
+ my_infp->filter_end =
+ (filter_t *)((char *)my_infp->filter + filter_bytes);
+
+ if (match == 0) {
+ my_infp->rcv_qlimit = net_add_q_info(rcv_port);
+ } else {
+ my_infp->rcv_qlimit = 0;
+ }
+
+ /* Insert my_infp according to priority */
+ queue_iterate(&ifp->if_rcv_port_list, infp, net_rcv_port_t, chain)
+ if (priority > infp->priority)
+ break;
+ enqueue_tail((queue_t)&infp->chain, (queue_entry_t)my_infp);
+ }
+
+ if (match != 0)
+ { /* Insert to hash list */
+ net_hash_entry_t *p;
+ int j;
+
+ hash_entp->rcv_port = rcv_port;
+ for (i = 0; i < match->jt; i++) /* match->jt is n_keys */
+ hash_entp->keys[i] = match[i+1].k;
+ p = &((net_hash_header_t)my_infp)->
+ table[bpf_hash(match->jt, hash_entp->keys)];
+
+ /* Not checking for the same key values */
+ if (*p == 0) {
+ queue_init ((queue_t) hash_entp);
+ *p = hash_entp;
+ } else {
+ enqueue_tail((queue_t)*p, hash_entp);
+ }
+
+ ((net_hash_header_t)my_infp)->ref_count++;
+ hash_entp->rcv_qlimit = net_add_q_info(rcv_port);
+
+ }
+
+ simple_unlock(&ifp->if_rcv_port_list_lock);
+
+clean_and_return:
+ /* No locks are held at this point. */
+
+ if (dead_infp != 0)
+ net_free_dead_infp(dead_infp);
+ if (dead_entp != 0)
+ net_free_dead_entp(dead_entp);
+
+ return (rval);
+}
+
+/*
+ * Other network operations
+ */
+io_return_t
+net_getstat(ifp, flavor, status, count)
+ struct ifnet *ifp;
+ dev_flavor_t flavor;
+ dev_status_t status; /* pointer to OUT array */
+ natural_t *count; /* OUT */
+{
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ register struct net_status *ns = (struct net_status *)status;
+
+ if (*count < NET_STATUS_COUNT)
+ return (D_INVALID_OPERATION);
+
+ ns->min_packet_size = ifp->if_header_size;
+ ns->max_packet_size = ifp->if_header_size + ifp->if_mtu;
+ ns->header_format = ifp->if_header_format;
+ ns->header_size = ifp->if_header_size;
+ ns->address_size = ifp->if_address_size;
+ ns->flags = ifp->if_flags;
+ ns->mapped_size = 0;
+
+ *count = NET_STATUS_COUNT;
+ break;
+ }
+ case NET_ADDRESS:
+ {
+ register int addr_byte_count;
+ register int addr_int_count;
+ register int i;
+
+ addr_byte_count = ifp->if_address_size;
+ addr_int_count = (addr_byte_count + (sizeof(int)-1))
+ / sizeof(int);
+
+ if (*count < addr_int_count)
+ {
+/* XXX debug hack. */
+printf ("net_getstat: count: %d, addr_int_count: %d\n",
+ *count, addr_int_count);
+ return (D_INVALID_OPERATION);
+ }
+
+ bcopy((char *)ifp->if_address,
+ (char *)status,
+ (unsigned) addr_byte_count);
+ if (addr_byte_count < addr_int_count * sizeof(int))
+ bzero((char *)status + addr_byte_count,
+ (unsigned) (addr_int_count * sizeof(int)
+ - addr_byte_count));
+
+ for (i = 0; i < addr_int_count; i++) {
+ register int word;
+
+ word = status[i];
+ status[i] = htonl(word);
+ }
+ *count = addr_int_count;
+ break;
+ }
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+io_return_t
+net_write(ifp, start, ior)
+ register struct ifnet *ifp;
+ int (*start)();
+ io_req_t ior;
+{
+ spl_t s;
+ kern_return_t rc;
+ boolean_t wait;
+
+ /*
+ * Reject the write if the interface is down.
+ */
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ return (D_DEVICE_DOWN);
+
+ /*
+ * Reject the write if the packet is too large or too small.
+ */
+ if (ior->io_count < ifp->if_header_size ||
+ ior->io_count > ifp->if_header_size + ifp->if_mtu)
+ return (D_INVALID_SIZE);
+
+ /*
+ * Wire down the memory.
+ */
+
+ rc = device_write_get(ior, &wait);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+
+ /*
+ * Network interfaces can't cope with VM continuations.
+ * If wait is set, just panic.
+ */
+ if (wait) {
+ panic("net_write: VM continuation");
+ }
+
+ /*
+ * Queue the packet on the output queue, and
+ * start the device.
+ */
+ s = splimp();
+ IF_ENQUEUE(&ifp->if_snd, ior);
+ (*start)(ifp->if_unit);
+ splx(s);
+
+ return (D_IO_QUEUED);
+}
+
+#ifdef FIPC
+/* This gets called by nefoutput for dev_ops->d_port_death ... */
+
+io_return_t
+net_fwrite(ifp, start, ior)
+ register struct ifnet *ifp;
+ int (*start)();
+ io_req_t ior;
+{
+ spl_t s;
+ kern_return_t rc;
+ boolean_t wait;
+
+ /*
+ * Reject the write if the interface is down.
+ */
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
+ return (D_DEVICE_DOWN);
+
+ /*
+ * Reject the write if the packet is too large or too small.
+ */
+ if (ior->io_count < ifp->if_header_size ||
+ ior->io_count > ifp->if_header_size + ifp->if_mtu)
+ return (D_INVALID_SIZE);
+
+ /*
+ * DON'T Wire down the memory.
+ */
+#if 0
+ rc = device_write_get(ior, &wait);
+ if (rc != KERN_SUCCESS)
+ return (rc);
+#endif
+ /*
+ * Network interfaces can't cope with VM continuations.
+ * If wait is set, just panic.
+ */
+ /* I'll have to figure out who was setting wait...*/
+#if 0
+ if (wait) {
+ panic("net_write: VM continuation");
+ }
+#endif
+ /*
+ * Queue the packet on the output queue, and
+ * start the device.
+ */
+ s = splimp();
+ IF_ENQUEUE(&ifp->if_snd, ior);
+ (*start)(ifp->if_unit);
+ splx(s);
+
+ return (D_IO_QUEUED);
+}
+#endif /* FIPC */
+
+/*
+ * Initialize the whole package.
+ */
+void
+net_io_init()
+{
+ register vm_size_t size;
+
+ size = sizeof(struct net_rcv_port);
+ net_rcv_zone = zinit(size,
+ size * 1000,
+ PAGE_SIZE,
+ FALSE,
+ "net_rcv_port");
+
+ size = sizeof(struct net_hash_entry);
+ net_hash_entry_zone = zinit(size,
+ size * 100,
+ PAGE_SIZE,
+ FALSE,
+ "net_hash_entry");
+
+ size = ikm_plus_overhead(sizeof(struct net_rcv_msg));
+ net_kmsg_size = round_page(size);
+
+ /*
+ * net_kmsg_max caps the number of buffers
+ * we are willing to allocate. By default,
+ * we allow for net_queue_free_min plus
+ * the queue limit for each filter.
+ * (Added as the filters are added.)
+ */
+
+ simple_lock_init(&net_kmsg_total_lock);
+ if (net_kmsg_max == 0)
+ net_kmsg_max = net_queue_free_min;
+
+ simple_lock_init(&net_queue_free_lock);
+ ipc_kmsg_queue_init(&net_queue_free);
+
+ simple_lock_init(&net_queue_lock);
+ ipc_kmsg_queue_init(&net_queue_high);
+ ipc_kmsg_queue_init(&net_queue_low);
+
+ simple_lock_init(&net_hash_header_lock);
+}
+
+
+/* ======== BPF: Berkeley Packet Filter ======== */
+
+/*-
+ * Copyright (c) 1990-1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from the Stanford/CMU enet packet filter,
+ * (net/enet.c) distributed as part of 4.3BSD, and code contributed
+ * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence
+ * Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)bpf.c 7.5 (Berkeley) 7/15/91
+ */
+
+#if defined(sparc) || defined(mips) || defined(ibm032) || defined(alpha)
+#define BPF_ALIGN
+#endif
+
+#ifndef BPF_ALIGN
+#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p))
+#define EXTRACT_LONG(p) (ntohl(*(u_long *)p))
+#else
+#define EXTRACT_SHORT(p)\
+ ((u_short)\
+ ((u_short)*((u_char *)p+0)<<8|\
+ (u_short)*((u_char *)p+1)<<0))
+#define EXTRACT_LONG(p)\
+ ((u_long)*((u_char *)p+0)<<24|\
+ (u_long)*((u_char *)p+1)<<16|\
+ (u_long)*((u_char *)p+2)<<8|\
+ (u_long)*((u_char *)p+3)<<0)
+#endif
+
+/*
+ * Execute the filter program starting at pc on the packet p
+ * wirelen is the length of the original packet
+ * buflen is the amount of data present
+ */
+
+int
+bpf_do_filter(infp, p, wirelen, header, hash_headpp, entpp)
+ net_rcv_port_t infp;
+ char * p; /* packet data */
+ unsigned int wirelen; /* data_count (in bytes) */
+ char * header;
+ net_hash_entry_t **hash_headpp, *entpp; /* out */
+{
+ register bpf_insn_t pc, pc_end;
+ register unsigned int buflen;
+
+ register unsigned long A, X;
+ register int k;
+ long mem[BPF_MEMWORDS];
+
+ pc = ((bpf_insn_t) infp->filter) + 1;
+ /* filter[0].code is BPF_BEGIN */
+ pc_end = (bpf_insn_t)infp->filter_end;
+ buflen = NET_RCV_MAX;
+ *entpp = 0; /* default */
+
+#ifdef lint
+ A = 0;
+ X = 0;
+#endif
+ for (; pc < pc_end; ++pc) {
+ switch (pc->code) {
+
+ default:
+#ifdef KERNEL
+ return 0;
+#else
+ abort();
+#endif
+ case BPF_RET|BPF_K:
+ if (infp->rcv_port == MACH_PORT_NULL &&
+ *entpp == 0) {
+ return 0;
+ }
+ return ((u_int)pc->k <= wirelen) ?
+ pc->k : wirelen;
+
+ case BPF_RET|BPF_A:
+ if (infp->rcv_port == MACH_PORT_NULL &&
+ *entpp == 0) {
+ return 0;
+ }
+ return ((u_int)A <= wirelen) ?
+ A : wirelen;
+
+ case BPF_RET|BPF_MATCH_IMM:
+ if (bpf_match ((net_hash_header_t)infp, pc->jt, mem,
+ hash_headpp, entpp)) {
+ return ((u_int)pc->k <= wirelen) ?
+ pc->k : wirelen;
+ }
+ return 0;
+
+ case BPF_LD|BPF_W|BPF_ABS:
+ k = pc->k;
+ if ((u_int)k + sizeof(long) <= buflen) {
+#ifdef BPF_ALIGN
+ if (((int)(p + k) & 3) != 0)
+ A = EXTRACT_LONG(&p[k]);
+ else
+#endif
+ A = ntohl(*(long *)(p + k));
+ continue;
+ }
+
+ k -= BPF_DLBASE;
+ if ((u_int)k + sizeof(long) <= NET_HDW_HDR_MAX) {
+#ifdef BPF_ALIGN
+ if (((int)(header + k) & 3) != 0)
+ A = EXTRACT_LONG(&header[k]);
+ else
+#endif
+ A = ntohl(*(long *)(header + k));
+ continue;
+ } else {
+ return 0;
+ }
+
+ case BPF_LD|BPF_H|BPF_ABS:
+ k = pc->k;
+ if ((u_int)k + sizeof(short) <= buflen) {
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+ }
+
+ k -= BPF_DLBASE;
+ if ((u_int)k + sizeof(short) <= NET_HDW_HDR_MAX) {
+ A = EXTRACT_SHORT(&header[k]);
+ continue;
+ } else {
+ return 0;
+ }
+
+ case BPF_LD|BPF_B|BPF_ABS:
+ k = pc->k;
+ if ((u_int)k < buflen) {
+ A = p[k];
+ continue;
+ }
+
+ k -= BPF_DLBASE;
+ if ((u_int)k < NET_HDW_HDR_MAX) {
+ A = header[k];
+ continue;
+ } else {
+ return 0;
+ }
+
+ case BPF_LD|BPF_W|BPF_LEN:
+ A = wirelen;
+ continue;
+
+ case BPF_LDX|BPF_W|BPF_LEN:
+ X = wirelen;
+ continue;
+
+ case BPF_LD|BPF_W|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(long) > buflen)
+ return 0;
+#ifdef BPF_ALIGN
+ if (((int)(p + k) & 3) != 0)
+ A = EXTRACT_LONG(&p[k]);
+ else
+#endif
+ A = ntohl(*(long *)(p + k));
+ continue;
+
+ case BPF_LD|BPF_H|BPF_IND:
+ k = X + pc->k;
+ if (k + sizeof(short) > buflen)
+ return 0;
+ A = EXTRACT_SHORT(&p[k]);
+ continue;
+
+ case BPF_LD|BPF_B|BPF_IND:
+ k = X + pc->k;
+ if (k >= buflen)
+ return 0;
+ A = p[k];
+ continue;
+
+ case BPF_LDX|BPF_MSH|BPF_B:
+ k = pc->k;
+ if (k >= buflen)
+ return 0;
+ X = (p[pc->k] & 0xf) << 2;
+ continue;
+
+ case BPF_LD|BPF_IMM:
+ A = pc->k;
+ continue;
+
+ case BPF_LDX|BPF_IMM:
+ X = pc->k;
+ continue;
+
+ case BPF_LD|BPF_MEM:
+ A = mem[pc->k];
+ continue;
+
+ case BPF_LDX|BPF_MEM:
+ X = mem[pc->k];
+ continue;
+
+ case BPF_ST:
+ mem[pc->k] = A;
+ continue;
+
+ case BPF_STX:
+ mem[pc->k] = X;
+ continue;
+
+ case BPF_JMP|BPF_JA:
+ pc += pc->k;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_K:
+ pc += (A > pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_K:
+ pc += (A >= pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_K:
+ pc += (A == pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_K:
+ pc += (A & pc->k) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGT|BPF_X:
+ pc += (A > X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JGE|BPF_X:
+ pc += (A >= X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JEQ|BPF_X:
+ pc += (A == X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_JMP|BPF_JSET|BPF_X:
+ pc += (A & X) ? pc->jt : pc->jf;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_X:
+ A += X;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_X:
+ A -= X;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_X:
+ A *= X;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_X:
+ if (X == 0)
+ return 0;
+ A /= X;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_X:
+ A &= X;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_X:
+ A |= X;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_X:
+ A <<= X;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_X:
+ A >>= X;
+ continue;
+
+ case BPF_ALU|BPF_ADD|BPF_K:
+ A += pc->k;
+ continue;
+
+ case BPF_ALU|BPF_SUB|BPF_K:
+ A -= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_MUL|BPF_K:
+ A *= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_DIV|BPF_K:
+ A /= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_AND|BPF_K:
+ A &= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_OR|BPF_K:
+ A |= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_LSH|BPF_K:
+ A <<= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_RSH|BPF_K:
+ A >>= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_NEG:
+ A = -A;
+ continue;
+
+ case BPF_MISC|BPF_TAX:
+ X = A;
+ continue;
+
+ case BPF_MISC|BPF_TXA:
+ A = X;
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Return 1 if the 'f' is a valid filter program without a MATCH
+ * instruction. Return 2 if it is a valid filter program with a MATCH
+ * instruction. Otherwise, return 0.
+ * The constraints are that each jump be forward and to a valid
+ * code. The code must terminate with either an accept or reject.
+ * 'valid' is an array for use by the routine (it must be at least
+ * 'len' bytes long).
+ *
+ * The kernel needs to be able to verify an application's filter code.
+ * Otherwise, a bogus program could easily crash the system.
+ */
+int
+bpf_validate(f, bytes, match)
+ bpf_insn_t f;
+ int bytes;
+ bpf_insn_t *match;
+{
+ register int i, j, len;
+ register bpf_insn_t p;
+
+ len = BPF_BYTES2LEN(bytes);
+ /* f[0].code is already checked to be BPF_BEGIN. So skip f[0]. */
+
+ for (i = 1; i < len; ++i) {
+ /*
+ * Check that that jumps are forward, and within
+ * the code block.
+ */
+ p = &f[i];
+ if (BPF_CLASS(p->code) == BPF_JMP) {
+ register int from = i + 1;
+
+ if (BPF_OP(p->code) == BPF_JA) {
+ if (from + p->k >= len)
+ return 0;
+ }
+ else if (from + p->jt >= len || from + p->jf >= len)
+ return 0;
+ }
+ /*
+ * Check that memory operations use valid addresses.
+ */
+ if ((BPF_CLASS(p->code) == BPF_ST ||
+ (BPF_CLASS(p->code) == BPF_LD &&
+ (p->code & 0xe0) == BPF_MEM)) &&
+ (p->k >= BPF_MEMWORDS || p->k < 0))
+ return 0;
+ /*
+ * Check for constant division by 0.
+ */
+ if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0)
+ return 0;
+ /*
+ * Check for match instruction.
+ * Only one match instruction per filter is allowed.
+ */
+ if (p->code == (BPF_RET|BPF_MATCH_IMM)) {
+ if (*match != 0 ||
+ p->jt == 0 ||
+ p->jt > N_NET_HASH_KEYS)
+ return 0;
+ i += p->jt; /* skip keys */
+ if (i + 1 > len)
+ return 0;
+
+ for (j = 1; j <= p->jt; j++) {
+ if (p[j].code != (BPF_MISC|BPF_KEY))
+ return 0;
+ }
+
+ *match = p;
+ }
+ }
+ if (BPF_CLASS(f[len - 1].code) == BPF_RET)
+ return ((*match == 0) ? 1 : 2);
+ else
+ return 0;
+}
+
+int
+bpf_eq (f1, f2, bytes)
+ register bpf_insn_t f1, f2;
+ register int bytes;
+{
+ register int count;
+
+ count = BPF_BYTES2LEN(bytes);
+ for (; count--; f1++, f2++) {
+ if (!BPF_INSN_EQ(f1, f2)) {
+ if ( f1->code == (BPF_MISC|BPF_KEY) &&
+ f2->code == (BPF_MISC|BPF_KEY) )
+ continue;
+ return FALSE;
+ }
+ };
+ return TRUE;
+}
+
+unsigned int
+bpf_hash (n, keys)
+ register int n;
+ register unsigned int *keys;
+{
+ register unsigned int hval = 0;
+
+ while (n--) {
+ hval += *keys++;
+ }
+ return (hval % NET_HASH_SIZE);
+}
+
+
+int
+bpf_match (hash, n_keys, keys, hash_headpp, entpp)
+ net_hash_header_t hash;
+ register int n_keys;
+ register unsigned int *keys;
+ net_hash_entry_t **hash_headpp, *entpp;
+{
+ register net_hash_entry_t head, entp;
+ register int i;
+
+ if (n_keys != hash->n_keys)
+ return FALSE;
+
+ *hash_headpp = &hash->table[bpf_hash(n_keys, keys)];
+ head = **hash_headpp;
+
+ if (head == 0)
+ return FALSE;
+
+ HASH_ITERATE (head, entp)
+ {
+ for (i = 0; i < n_keys; i++) {
+ if (keys[i] != entp->keys[i])
+ break;
+ }
+ if (i == n_keys) {
+ *entpp = entp;
+ return TRUE;
+ }
+ }
+ HASH_ITERATE_END (head, entp)
+ return FALSE;
+}
+
+
+/*
+ * Removes a hash entry (ENTP) from its queue (HEAD).
+ * If the reference count of filter (HP) becomes zero and not USED,
+ * HP is removed from ifp->if_rcv_port_list and is freed.
+ */
+
+int
+hash_ent_remove (ifp, hp, used, head, entp, dead_p)
+ struct ifnet *ifp;
+ net_hash_header_t hp;
+ int used;
+ net_hash_entry_t *head, entp;
+ queue_entry_t *dead_p;
+{
+ hp->ref_count--;
+
+ if (*head == entp) {
+
+ if (queue_empty((queue_t) entp)) {
+ *head = 0;
+ ENQUEUE_DEAD(*dead_p, entp);
+ if (hp->ref_count == 0 && !used) {
+ remqueue((queue_t) &ifp->if_rcv_port_list,
+ (queue_entry_t)hp);
+ hp->n_keys = 0;
+ return TRUE;
+ }
+ return FALSE;
+ } else {
+ *head = (net_hash_entry_t)queue_next((queue_t) entp);
+ }
+ }
+
+ remqueue((queue_t)*head, (queue_entry_t)entp);
+ ENQUEUE_DEAD(*dead_p, entp);
+ return FALSE;
+}
+
+int
+net_add_q_info (rcv_port)
+ ipc_port_t rcv_port;
+{
+ mach_port_msgcount_t qlimit = 0;
+
+ /*
+ * We use a new port, so increase net_queue_free_min
+ * and net_kmsg_max to allow for more queued messages.
+ */
+
+ if (IP_VALID(rcv_port)) {
+ ip_lock(rcv_port);
+ if (ip_active(rcv_port))
+ qlimit = rcv_port->ip_qlimit;
+ ip_unlock(rcv_port);
+ }
+
+ simple_lock(&net_kmsg_total_lock);
+ net_queue_free_min++;
+ net_kmsg_max += qlimit + 1;
+ simple_unlock(&net_kmsg_total_lock);
+
+ return (int)qlimit;
+}
+
+net_del_q_info (qlimit)
+ int qlimit;
+{
+ simple_lock(&net_kmsg_total_lock);
+ net_queue_free_min--;
+ net_kmsg_max -= qlimit + 1;
+ simple_unlock(&net_kmsg_total_lock);
+}
+
+
+/*
+ * net_free_dead_infp (dead_infp)
+ * queue_entry_t dead_infp; list of dead net_rcv_port_t.
+ *
+ * Deallocates dead net_rcv_port_t.
+ * No locks should be held when called.
+ */
+net_free_dead_infp (dead_infp)
+ queue_entry_t dead_infp;
+{
+ register net_rcv_port_t infp, nextfp;
+
+ for (infp = (net_rcv_port_t) dead_infp; infp != 0; infp = nextfp)
+ {
+ nextfp = (net_rcv_port_t) queue_next(&infp->chain);
+ ipc_port_release_send(infp->rcv_port);
+ net_del_q_info(infp->rcv_qlimit);
+ zfree(net_rcv_zone, (vm_offset_t) infp);
+ }
+}
+
+/*
+ * net_free_dead_entp (dead_entp)
+ * queue_entry_t dead_entp; list of dead net_hash_entry_t.
+ *
+ * Deallocates dead net_hash_entry_t.
+ * No locks should be held when called.
+ */
+net_free_dead_entp (dead_entp)
+ queue_entry_t dead_entp;
+{
+ register net_hash_entry_t entp, nextentp;
+
+ for (entp = (net_hash_entry_t)dead_entp; entp != 0; entp = nextentp)
+ {
+ nextentp = (net_hash_entry_t) queue_next(&entp->chain);
+
+ ipc_port_release_send(entp->rcv_port);
+ net_del_q_info(entp->rcv_qlimit);
+ zfree(net_hash_entry_zone, (vm_offset_t) entp);
+ }
+}
+
diff --git a/device/net_io.h b/device/net_io.h
new file mode 100644
index 0000000..2228e72
--- /dev/null
+++ b/device/net_io.h
@@ -0,0 +1,80 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: ll/89
+ */
+
+#ifndef _DEVICE_NET_IO_H_
+#define _DEVICE_NET_IO_H_
+
+/*
+ * Utilities for playing with network messages.
+ */
+
+#include <mach/machine/vm_types.h>
+#include <ipc/ipc_kmsg.h>
+
+#include <kern/macro_help.h>
+#include <kern/lock.h>
+#include <kern/kalloc.h>
+
+#include <device/net_status.h>
+
+/*
+ * A network packet is wrapped in a kernel message while in
+ * the kernel.
+ */
+
+#define net_kmsg(kmsg) ((net_rcv_msg_t)&(kmsg)->ikm_header)
+
+/*
+ * Interrupt routines may allocate and free net_kmsgs with these
+ * functions. net_kmsg_get may return IKM_NULL.
+ */
+
+extern ipc_kmsg_t net_kmsg_get();
+extern void net_kmsg_put();
+
+/*
+ * Network utility routines.
+ */
+
+extern void net_packet();
+extern void net_filter();
+extern io_return_t net_getstat();
+extern io_return_t net_write();
+
+/*
+ * Non-interrupt code may allocate and free net_kmsgs with these functions.
+ */
+
+extern vm_size_t net_kmsg_size;
+
+#define net_kmsg_alloc() ((ipc_kmsg_t) kalloc(net_kmsg_size))
+#define net_kmsg_free(kmsg) kfree((vm_offset_t) (kmsg), net_kmsg_size)
+
+#endif _DEVICE_NET_IO_H_
diff --git a/device/param.h b/device/param.h
new file mode 100644
index 0000000..41b4793
--- /dev/null
+++ b/device/param.h
@@ -0,0 +1,49 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ */
+
+#ifndef _DEVICE_PARAM_H_
+#define _DEVICE_PARAM_H_
+
+/*
+ * Compatibility definitions for disk IO.
+ */
+
+/*
+ * Disk devices do all IO in 512-byte blocks.
+ */
+#define DEV_BSIZE 512
+
+/*
+ * Conversion between bytes and disk blocks.
+ */
+#define btodb(byte_offset) ((byte_offset) >> 9)
+#define dbtob(block_number) ((block_number) << 9)
+
+#endif /* _DEVICE_PARAM_H_ */
diff --git a/device/subrs.c b/device/subrs.c
new file mode 100644
index 0000000..9d59020
--- /dev/null
+++ b/device/subrs.c
@@ -0,0 +1,140 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989,1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Random device subroutines and stubs.
+ */
+
+#include <vm/vm_kern.h>
+#include <device/buf.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+
+
+
+/*
+ * Print out disk name and block number for hard disk errors.
+ */
+void harderr(bp, cp)
+ struct buf *bp;
+ char * cp;
+{
+ printf("%s%d%c: hard error sn%d ",
+ cp,
+ minor(bp->b_dev) >> 3,
+ 'a' + (minor(bp->b_dev) & 0x7),
+ bp->b_blkno);
+}
+
+/*
+ * Ethernet support routines.
+ */
+u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+/*
+ * Convert Ethernet address to printable (loggable) representation.
+ */
+char *
+ether_sprintf(ap)
+ register u_char *ap;
+{
+ register i;
+ static char etherbuf[18];
+ register char *cp = etherbuf;
+ static char digits[] = "0123456789abcdef";
+
+ for (i = 0; i < 6; i++) {
+ *cp++ = digits[*ap >> 4];
+ *cp++ = digits[*ap++ & 0xf];
+ *cp++ = ':';
+ }
+ *--cp = 0;
+ return (etherbuf);
+}
+
+/*
+ * Initialize send and receive queues on an interface.
+ */
+void if_init_queues(ifp)
+ register struct ifnet *ifp;
+{
+ IFQ_INIT(&ifp->if_snd);
+ queue_init(&ifp->if_rcv_port_list);
+ simple_lock_init(&ifp->if_rcv_port_list_lock);
+}
+
+
+/*
+ * Compatibility with BSD device drivers.
+ */
+void sleep(channel, priority)
+ vm_offset_t channel;
+ int priority;
+{
+ assert_wait((event_t) channel, FALSE); /* not interruptible XXX */
+ thread_block((void (*)()) 0);
+}
+
+void wakeup(channel)
+ vm_offset_t channel;
+{
+ thread_wakeup((event_t) channel);
+}
+
+struct buf *
+geteblk(size)
+ int size;
+{
+ register io_req_t ior;
+
+ io_req_alloc(ior, 0);
+ ior->io_device = (device_t)0;
+ ior->io_unit = 0;
+ ior->io_op = 0;
+ ior->io_mode = 0;
+ ior->io_recnum = 0;
+ ior->io_count = size;
+ ior->io_residual = 0;
+ ior->io_error = 0;
+
+ size = round_page(size);
+ ior->io_alloc_size = size;
+ if (kmem_alloc(kernel_map, (vm_offset_t *)&ior->io_data, size)
+ != KERN_SUCCESS)
+ panic("geteblk");
+
+ return (ior);
+}
+
+void brelse(bp)
+ struct buf *bp;
+{
+ register io_req_t ior = bp;
+
+ (void) vm_deallocate(kernel_map,
+ (vm_offset_t) ior->io_data,
+ (vm_size_t) ior->io_alloc_size);
+ io_req_free(ior);
+}
diff --git a/device/tty.h b/device/tty.h
new file mode 100644
index 0000000..9422996
--- /dev/null
+++ b/device/tty.h
@@ -0,0 +1,203 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Author: David B. Golub, Carnegie Mellon University
+ * Date: 7/90
+ *
+ * Compatibility TTY structure for existing TTY device drivers.
+ */
+
+#ifndef _DEVICE_TTY_H_
+#define _DEVICE_TTY_H_
+
+#include <kern/lock.h>
+#include <kern/queue.h>
+#include <mach/port.h>
+
+#include <device/device_types.h>
+#include <device/tty_status.h>
+#include <device/cirbuf.h>
+#include <device/io_req.h>
+
+#ifdef luna88k
+#include <luna88k/jtermio.h>
+#endif
+
+struct tty {
+ decl_simple_lock_data(,t_lock)
+ struct cirbuf t_inq; /* input buffer */
+ struct cirbuf t_outq; /* output buffer */
+ char * t_addr; /* device pointer */
+ int t_dev; /* device number */
+ int (*t_start)(struct tty *);
+ /* routine to start output */
+#define t_oproc t_start
+ int (*t_stop)(struct tty *, int);
+ /* routine to stop output */
+ int (*t_mctl)(struct tty *, int, int);
+ /* (optional) routine to control
+ modem signals */
+ char t_ispeed; /* input speed */
+ char t_ospeed; /* output speed */
+ char t_breakc; /* character to deliver when 'break'
+ condition received */
+ int t_flags; /* mode flags */
+ int t_state; /* current state */
+ int t_line; /* fake line discipline number,
+ for old drivers - always 0 */
+ queue_head_t t_delayed_read; /* pending read requests */
+ queue_head_t t_delayed_write;/* pending write requests */
+ queue_head_t t_delayed_open; /* pending open requests */
+
+/*
+ * Items beyond this point should be removed to device-specific
+ * extension structures.
+ */
+ int (*t_getstat)(); /* routine to get status */
+ int (*t_setstat)(); /* routine to set status */
+ dev_ops_t t_tops; /* another device to possibly
+ push through */
+};
+typedef struct tty *tty_t;
+
+/*
+ * Common TTY service routines
+ */
+extern io_return_t char_open(
+ int dev,
+ struct tty * tp,
+ dev_mode_t mode,
+ io_req_t ior);
+
+extern io_return_t char_read(
+ struct tty * tp,
+ io_req_t ior);
+
+extern io_return_t char_write(
+ struct tty * tp,
+ io_req_t ior);
+
+extern void ttyinput(
+ unsigned int c,
+ struct tty * tp);
+
+extern boolean_t ttymodem(
+ struct tty * tp,
+ boolean_t carrier_up);
+
+extern void tty_queue_completion(
+ queue_t queue);
+#define tt_open_wakeup(tp) \
+ (tty_queue_completion(&(tp)->t_delayed_open))
+#define tt_write_wakeup(tp) \
+ (tty_queue_completion(&(tp)->t_delayed_write))
+
+extern void ttychars(
+ struct tty * tp);
+
+#define TTMINBUF 90
+
+short tthiwat[NSPEEDS], ttlowat[NSPEEDS];
+#define TTHIWAT(tp) tthiwat[(tp)->t_ospeed]
+#define TTLOWAT(tp) ttlowat[(tp)->t_ospeed]
+
+/* internal state bits */
+#define TS_INIT 0x00000001 /* tty structure initialized */
+#define TS_TIMEOUT 0x00000002 /* delay timeout in progress */
+#define TS_WOPEN 0x00000004 /* waiting for open to complete */
+#define TS_ISOPEN 0x00000008 /* device is open */
+#define TS_FLUSH 0x00000010 /* outq has been flushed during DMA */
+#define TS_CARR_ON 0x00000020 /* software copy of carrier-present */
+#define TS_BUSY 0x00000040 /* output in progress */
+#define TS_ASLEEP 0x00000080 /* wakeup when output done */
+
+#define TS_TTSTOP 0x00000100 /* output stopped by ctl-s */
+#define TS_HUPCLS 0x00000200 /* hang up upon last close */
+#define TS_TBLOCK 0x00000400 /* tandem queue blocked */
+
+#define TS_NBIO 0x00001000 /* tty in non-blocking mode */
+#define TS_ONDELAY 0x00002000 /* device is open; software copy of
+ * carrier is not present */
+#define TS_MIN 0x00004000 /* buffer input chars, if possible */
+#define TS_MIN_TO 0x00008000 /* timeout for the above is active */
+
+#define TS_OUT 0x00010000 /* tty in use for dialout only */
+#define TS_RTS_DOWN 0x00020000 /* modem pls stop */
+
+#define TS_TRANSLATE 0x00100000 /* translation device enabled */
+#define TS_KDB 0x00200000 /* should enter kdb on ALT */
+
+#define TS_MIN_TO_RCV 0x00400000 /* character recived during
+ receive timeout interval */
+
+/* flags - old names defined in terms of new ones */
+
+#define TANDEM TF_TANDEM
+#define ODDP TF_ODDP
+#define EVENP TF_EVENP
+#define ANYP (ODDP|EVENP)
+#define MDMBUF TF_MDMBUF
+#define LITOUT TF_LITOUT
+#define NOHANG TF_NOHANG
+
+#define ECHO TF_ECHO
+#define CRMOD TF_CRMOD
+#define XTABS TF_XTABS
+
+/* these are here only to let old code compile - they are never set */
+#define RAW LITOUT
+#define PASS8 LITOUT
+
+/*
+ * Hardware bits.
+ * SHOULD NOT BE HERE.
+ */
+#define DONE 0200
+#define IENABLE 0100
+
+/*
+ * Modem control commands.
+ */
+#define DMSET 0
+#define DMBIS 1
+#define DMBIC 2
+#define DMGET 3
+
+/*
+ * Fake 'line discipline' switch, for the benefit of old code
+ * that wants to call through it.
+ */
+struct ldisc_switch {
+ int (*l_read) (struct tty *, io_req_t); /* read */
+ int (*l_write)(struct tty *, io_req_t); /* write */
+ void (*l_rint) (unsigned int, struct tty *); /* character input */
+ boolean_t (*l_modem)(struct tty *, boolean_t); /* modem change */
+ void (*l_start)(struct tty *); /* start output */
+};
+
+extern struct ldisc_switch linesw[];
+
+#endif /* _DEVICE_TTY_H_ */