summaryrefslogtreecommitdiff
path: root/term/devio.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-08-31 20:01:44 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-08-31 20:01:44 +0000
commit941c81aa67cdaf9c63f4835e61d33c182db5237b (patch)
tree05f62a9fc70a5eb5cf58d54bd98ffe11ee7cb8f7 /term/devio.c
parent8832237df37b394f990e0746f2702f6400d1359e (diff)
Initial revision
Diffstat (limited to 'term/devio.c')
-rw-r--r--term/devio.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/term/devio.c b/term/devio.c
new file mode 100644
index 00000000..e24e34fb
--- /dev/null
+++ b/term/devio.c
@@ -0,0 +1,554 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <device/device.h>
+#include <device/device_request.h>
+#include <hurd/ports.h>
+#include <string.h>
+#include <sys/types.h>
+#include <hurd.h>
+#include <stdio.h>
+
+#undef B50
+#undef B75
+#undef B110
+#undef B134
+#undef B150
+#undef B200
+#undef B300
+#undef B600
+#undef B1200
+#undef B1800
+#undef B2400
+#undef B4800
+#undef B9600
+#undef EXTA
+#undef EXTB
+#include <device/tty_status.h>
+
+/* This flag is set if there is an outstanding device_write. */
+static int output_pending;
+
+/* This flag is set if there is an outstanding device_read. */
+static int input_pending;
+
+/* This flag is set if there is an outstanding device_open. */
+static int open_pending;
+
+static char pending_output[IO_INBAND_MAX];
+static int npending_output;
+
+static struct port_class *phys_reply_class;
+
+/* The Mach device_t representing the terminal. */
+static device_t phys_device = MACH_PORT_NULL;
+
+/* The ports we get replies on for device calls. */
+static mach_port_t phys_reply_writes = MACH_PORT_NULL;
+static mach_port_t phys_reply = MACH_PORT_NULL;
+
+/* The port-info structs. */
+static struct port_info *phys_reply_writes_pi;
+static struct port_info *phys_reply_pi;
+
+static device_t device_master;
+
+static void init_devio (void) __attribute__ ((constructor));
+static void
+init_devio ()
+{
+ mach_port_t host_priv;
+ errno = get_privileged_ports (&host_priv, &device_master);
+ if (errno)
+ {
+ perror ("Getting priviliged ports");
+ exit (1);
+ }
+ mach_port_deallocate (mach_task_self (), host_priv);
+ phys_reply_class = ports_create_class (0, 0);
+}
+
+/* XXX Convert a real speed to a bogus Mach speed. Return
+ -1 if the real speed was bogus, else 0. */
+static int
+real_speed_to_bogus_speed (int rspeed, int *bspeed)
+{
+ switch (rspeed)
+ {
+ case 0:
+ *bspeed = B0;
+ break;
+ case 50:
+ *bspeed = B50;
+ break;
+ case 75:
+ *bspeed = B75;
+ break;
+ case 110:
+ *bspeed = B110;
+ break;
+ case 134:
+ *bspeed = B134;
+ break;
+ case 150:
+ *bspeed = B150;
+ break;
+ case 200:
+ *bspeed = B200;
+ break;
+ case 300:
+ *bspeed = B300;
+ break;
+ case 600:
+ *bspeed = B600;
+ break;
+ case 1200:
+ *bspeed = B1200;
+ break;
+ case 1800:
+ *bspeed = B1800;
+ break;
+ case 2400:
+ *bspeed = B2400;
+ break;
+ case 4800:
+ *bspeed = B4800;
+ break;
+ case 9600:
+ *bspeed = B9600;
+ break;
+ case 19200:
+ *bspeed = EXTA;
+ break;
+ case 28400:
+ *bspeed = EXTB;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* XXX Convert a bogus speed to a real speed. */
+static int
+bogus_speed_to_real_speed (int bspeed)
+{
+ switch (bspeed)
+ {
+ case B0:
+ default:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+ case EXTA:
+ return 19200;
+ case EXTB:
+ return 38400;
+ }
+}
+
+
+/* If there are characters on the output queue and no
+ pending output requests, then send them. */
+void
+start_output ()
+{
+ char *cp;
+ int size;
+ error_t err;
+
+ size = qsize (outputq);
+
+ if (!size || output_pending)
+ return;
+
+ /* Copy characters onto PENDING_OUTPUT, not bothering
+ those already there. */
+
+ if (size + npending_output > IO_INBAND_MAX)
+ size = IO_INBAND_MAX - npending_output;
+
+ cp = pending_output + npending_output;
+ npending_output += size;
+
+ while (size--)
+ *cp++ = dequeue (outputq);
+
+ /* Submit all the outstanding characters to the device. */
+ /* The D_NOWAIT flag does not, in fact, prevent blocks. Instead,
+ it merely causes D_WOULD_BLOCK errors when carrier is down...
+ whee.... */
+ err = device_write_request_inband (phys_device, phys_reply_writes, D_NOWAIT,
+ 0, pending_output, npending_output);
+
+ if (err == MACH_SEND_INVALID_DEST)
+ desert_dtr ();
+ else if (!err)
+ output_pending = 1;
+}
+
+error_t
+device_write_reply_inband (mach_port_t replypt,
+ error_t return_code,
+ int amount)
+{
+ if (replypt != phys_reply_writes)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ output_pending = 0;
+
+ if (return_code == 0)
+ {
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ condition_broadcast (outputq->wait);
+ }
+ else
+ {
+ /* Copy the characters that didn't get output
+ to the front of the array. */
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount, npending_output);
+ }
+ start_output ();
+ }
+ else if (return_code == D_WOULD_BLOCK)
+ /* Carrier has dropped. */
+ desert_dtr ();
+ else
+ start_output ();
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+device_read_reply_inband (mach_port_t replypt,
+ error_t error_code,
+ char *data,
+ u_int datalen)
+{
+ int i, flush;
+ error_t err;
+
+ if (replypt != phys_reply)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ input_pending = 0;
+
+ if (!error_code && (termstate.c_cflag & CREAD))
+ for (i = 0; i < datalen; i++)
+ {
+ flush = input_character (data[i]);
+ if (flush)
+ break;
+ }
+ else if (error_code == D_WOULD_BLOCK)
+ {
+ desert_dtr ();
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ /* D_NOWAIT does not actually prevent blocks; it merely causes
+ D_WOULD_BLOCK errors when carrier drops. */
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+ if (err)
+ desert_dtr ();
+ else
+ input_pending = 1;
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+void
+set_break ()
+{
+ device_set_status (phys_device, TTY_SET_BREAK, 0, 0);
+}
+
+void
+clear_break ()
+{
+ device_set_status (phys_device, TTY_CLEAR_BREAK, 0, 0);
+}
+
+void
+abandon_physical_output ()
+{
+ int val = D_WRITE;
+
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ ports_reallocate_port (phys_reply_writes_pi);
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+
+ device_set_status (phys_device, TTY_FLUSH, &val, TTY_FLUSH_COUNT);
+ npending_output = 0;
+ output_pending = 0;
+}
+
+int
+pending_output_size ()
+{
+ /* Unfortunately, there's no way to get the amount back from Mach
+ that has actually been written from this... */
+ return npending_output;
+}
+
+void
+desert_dtr ()
+{
+ /* This will work, because we set the TF_HUPCLS bit earlier. */
+ device_close (phys_device);
+ mach_port_deallocate (mach_task_self (), phys_device);
+ phys_device = MACH_PORT_NULL;
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply = phys_reply_writes = MACH_PORT_NULL;
+
+ ports_port_deref (phys_reply_pi);
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_pi = phys_reply_writes_pi = 0;
+
+ report_carrier_off ();
+}
+
+error_t
+assert_dtr ()
+{
+ error_t err;
+
+ if (open_pending || (phys_device != MACH_PORT_NULL))
+ return 0;
+
+ assert (phys_reply == MACH_PORT_NULL);
+ assert (phys_reply_pi == 0);
+ phys_reply_pi = ports_allocate_port (term_bucket, sizeof (struct port_info),
+ phys_reply_class);
+ phys_reply = ports_get_right (phys_reply_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply, phys_reply,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ err = device_open_request (device_master, phys_reply,
+ D_READ|D_WRITE, pterm_name);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ }
+ return err;
+}
+
+
+kern_return_t
+device_open_reply (mach_port_t replyport,
+ int returncode,
+ mach_port_t device)
+{
+ struct tty_status ttystat;
+ int count = TTY_STATUS_COUNT;
+ error_t err;
+
+ if (replyport != phys_reply)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ open_pending = 0;
+
+ if (returncode != 0)
+ {
+ /* Bogus. */
+ report_carrier_on ();
+ report_carrier_off ();
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply_writes == MACH_PORT_NULL);
+ assert (phys_reply_writes_pi == 0);
+ phys_device = device;
+ phys_reply_writes_pi = ports_allocate_port (term_bucket,
+ sizeof (struct port_info),
+ phys_reply_class);
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+
+ device_get_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, &count);
+ ttystat.tt_breakc = 0;
+ ttystat.tt_flags = TF_ANYP | TF_LITOUT | TF_NOHANG | TF_HUPCLS;
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, TTY_STATUS_COUNT);
+
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+ input_pending = 1;
+ report_carrier_on ();
+ if (err)
+ desert_dtr ();
+
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* Adjust physical state on the basis of the terminal state.
+ Where it isn't possible, mutate terminal state to match
+ reality. */
+void
+set_bits ()
+{
+ struct tty_status ttystat;
+ int cnt = TTY_STATUS_COUNT;
+
+ assert (!(termstate.c_cflag & CIGNORE));
+
+ if (phys_device == MACH_PORT_NULL)
+ return;
+
+ /* Find the current state. */
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ if (termstate.__ispeed)
+ real_speed_to_bogus_speed (termstate.__ispeed, &ttystat.tt_ispeed);
+ if (termstate.__ospeed)
+ real_speed_to_bogus_speed (termstate.__ospeed, &ttystat.tt_ospeed);
+
+ /* Try and set it. */
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t) &ttystat, TTY_STATUS_COUNT);
+
+ /* And now make termstate match reality. */
+ cnt = TTY_STATUS_COUNT;
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ termstate.__ispeed = bogus_speed_to_real_speed (ttystat.tt_ispeed);
+ termstate.__ospeed = bogus_speed_to_real_speed (ttystat.tt_ospeed);
+
+ /* Mach forces us to use the normal stop bit convention:
+ two bits at 110 bps; 1 bit otherwise. */
+ if (termstate.__ispeed == 110)
+ termstate.c_cflag |= CSTOPB;
+ else
+ termstate.c_cflag &= ~CSTOPB;
+
+ /* Mach only supports 8 bit channels. So wark the CSIZE to conform. */
+ termstate.c_cflag = ((termstate.c_cflag & ~CSIZE)
+ | ((termstate.c_cflag & PARENB) ? CS7 : CS8));
+}
+
+void
+mdmctl (int how, int bits)
+{
+ int oldbits, newbits;
+ int cnt;
+ if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC))
+ {
+ cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM,
+ (dev_status_t) &oldbits, &cnt);
+ if (cnt < TTY_MODEM_COUNT)
+ oldbits = 0; /* what else can we do? */
+ }
+
+ if (how == MDMCTL_BIS)
+ newbits = (oldbits | bits);
+ else if (how == MDMCTL_BIC)
+ newbits = (oldbits &= ~bits);
+ else
+ newbits = bits;
+
+ device_set_status (phys_device, TTY_MODEM,
+ (dev_status_t) &newbits, TTY_MODEM_COUNT);
+}
+
+int
+mdmstate ()
+{
+ int bits, cnt;
+
+ cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM, (dev_status_t) &bits, &cnt);
+ if (cnt != TTY_MODEM_COUNT)
+ return 0;
+ else
+ return bits;
+}
+
+/* Unused stubs */
+kern_return_t
+device_read_reply (mach_port_t port,
+ kern_return_t retcode,
+ io_buf_ptr_t data,
+ mach_msg_type_number_t cnt)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+device_write_reply (mach_port_t replyport,
+ kern_return_t retcode,
+ int written)
+{
+ return EOPNOTSUPP;
+}