summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Brinkmann <marcus@gnu.org>2002-02-10 18:05:56 +0000
committerMarcus Brinkmann <marcus@gnu.org>2002-02-10 18:05:56 +0000
commit15395557e2aaf3ec9c86ba15d14e0c08a9a86405 (patch)
tree67094453cf3eb3139d0bea4fb30f03fcb330b39d
parentaf107a198dce152da74590f2eec5a320002c0f82 (diff)
2002-02-10 Marcus Brinkmann <marcus@gnu.org>
* hurdio.c: New file. * Makefile (SRCS): Add hurdio.c. * term.h: Add hurdio_bottom declaration. * main.c: Add T_HURDIO to enum of tty_type. (parse_opt): Add case for hurdio bottom handler. (term_argp): Likewise. (main): Likewise.
-rw-r--r--term/ChangeLog10
-rw-r--r--term/Makefile2
-rw-r--r--term/hurdio.c590
-rw-r--r--term/main.c15
-rw-r--r--term/term.h2
5 files changed, 616 insertions, 3 deletions
diff --git a/term/ChangeLog b/term/ChangeLog
index 84431c6a..2ef3d89b 100644
--- a/term/ChangeLog
+++ b/term/ChangeLog
@@ -1,5 +1,15 @@
2002-02-10 Marcus Brinkmann <marcus@gnu.org>
+ * hurdio.c: New file.
+ * Makefile (SRCS): Add hurdio.c.
+ * term.h: Add hurdio_bottom declaration.
+ * main.c: Add T_HURDIO to enum of tty_type.
+ (parse_opt): Add case for hurdio bottom handler.
+ (term_argp): Likewise.
+ (main): Likewise.
+
+2002-02-10 Marcus Brinkmann <marcus@gnu.org>
+
* main.c: Include `argp.h' and `version.h'.
(argp_program_version): New global variable.
(tty_name, tty_type, tty_arg): Likewise.
diff --git a/term/Makefile b/term/Makefile
index 63a75d82..ff842036 100644
--- a/term/Makefile
+++ b/term/Makefile
@@ -22,7 +22,7 @@ dir := term
makemode := server
target = term
-SRCS = devio.c munge.c users.c main.c ptyio.c
+SRCS = devio.c munge.c users.c main.c ptyio.c hurdio.c
LCLHDRS = term.h
DIST_FILES = ourmsg.defs
diff --git a/term/hurdio.c b/term/hurdio.c
new file mode 100644
index 00000000..682b03bc
--- /dev/null
+++ b/term/hurdio.c
@@ -0,0 +1,590 @@
+/*
+ Copyright (C) 1995,96,98,99,2000,01,02 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG and Marcus Brinkmann.
+
+ 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. */
+
+/* Handle carrier dropped (at least EIO errors in read, write) correctly. */
+
+#include <termios.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+
+#include <cthreads.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/io.h>
+#include <hurd/tioctl.h>
+
+#include "term.h"
+
+
+/* The thread asserting the DTR and performing all reads. Only
+ different from MACH_PORT_NULL if thread is live and blocked. */
+thread_t reader_thread = MACH_PORT_NULL;
+
+/* The Hurd file_t representing the terminal. If this is not
+ MACH_PORT_NULL, it has the additional meaning that the DTR is
+ asserted. */
+static file_t ioport = MACH_PORT_NULL;
+
+/* Each bit represents a supported tioctl call in the underlying node.
+ If we detect that a tioctl is not supported, we clear the bit in
+ tioc_caps (which is initialized at every open). */
+#define TIOC_CAP_OUTQ 0x001
+#define TIOC_CAP_START 0x002
+#define TIOC_CAP_STOP 0x004
+#define TIOC_CAP_FLUSH 0x008
+#define TIOC_CAP_CBRK 0x010
+#define TIOC_CAP_SBRK 0x020
+#define TIOC_CAP_MODG 0x040
+#define TIOC_CAP_MODS 0x080
+#define TIOC_CAP_GETA 0x100
+#define TIOC_CAP_SETA 0x200
+int tioc_caps;
+
+/* The thread performing all writes. Only different from
+ MACH_PORT_NULL if thread is live and blocked. */
+thread_t writer_thread = MACH_PORT_NULL;
+
+/* This flag is set if the output was suspended. */
+static int output_stopped;
+static struct condition hurdio_writer_condition;
+
+/* Hold the amount of bytes that are currently in the progress of
+ being written. May be set to zero while you hold the global lock
+ to drain the pending output buffer. */
+size_t npending_output;
+
+/* True if we should assert the dtr. */
+int assert_dtr;
+static struct condition hurdio_assert_dtr_condition;
+
+
+/* Forward */
+static error_t hurdio_desert_dtr ();
+static any_t hurdio_reader_loop (any_t arg);
+static any_t hurdio_writer_loop (any_t arg);
+static error_t hurdio_set_bits (struct termios *state);
+
+
+static error_t
+hurdio_init (void)
+{
+ condition_init (&hurdio_writer_condition);
+ condition_init (&hurdio_assert_dtr_condition);
+
+ cthread_detach (cthread_fork (hurdio_reader_loop, 0));
+ cthread_detach (cthread_fork (hurdio_writer_loop, 0));
+ return 0;
+}
+
+
+/* Assert the DTR if necessary. Must be called with global lock held. */
+static void
+wait_for_dtr (void)
+{
+ while (!assert_dtr)
+ hurd_condition_wait (&hurdio_assert_dtr_condition, &global_lock);
+ assert_dtr = 0;
+
+ /* Open the file in blocking mode, so that the carrier is
+ established as well. */
+ ioport = file_name_lookup (tty_arg, O_READ|O_WRITE, 0);
+ if (ioport == MACH_PORT_NULL)
+ report_carrier_error (errno);
+ else
+ {
+ error_t err;
+ struct termios state = termstate;
+
+ /* Assume that we have a full blown terminal initially. */
+ tioc_caps = ~0;
+
+ /* Set terminal in raw mode etc. */
+ err = hurdio_set_bits (&state);
+ if (err)
+ report_carrier_error (err);
+ else
+ {
+ termstate = state;
+
+ /* Signal that we have a carrier. */
+ report_carrier_on ();
+
+ /* Signal that the writer thread should resume its work. */
+ condition_broadcast (&hurdio_writer_condition);
+ }
+ }
+}
+
+
+/* Read and enqueue input characters. Is also responsible to assert
+ the DTR if necessary. */
+static any_t
+hurdio_reader_loop (any_t arg)
+{
+ /* XXX The input buffer has 256 bytes. */
+#define BUFFER_SIZE 256
+ char buffer[BUFFER_SIZE];
+ char *data;
+ size_t datalen;
+ error_t err;
+
+ mutex_lock (&global_lock);
+ reader_thread = mach_thread_self ();
+
+ while (1)
+ {
+ /* We can only start when the DTR has been asserted. */
+ while (!ioport)
+ wait_for_dtr ();
+ mutex_unlock (&global_lock);
+
+ data = buffer;
+ datalen = BUFFER_SIZE;
+
+ err = io_read (ioport, &data, &datalen, -1, BUFFER_SIZE);
+
+ mutex_lock (&global_lock);
+ /* EIO or EOF can mean the carrier has been dropped. */
+ if (err == EIO || !datalen)
+ hurdio_desert_dtr ();
+ else if (!err)
+ {
+ if (termstate.c_cflag & CREAD)
+ {
+ int i;
+
+ for (i = 0; i < datalen; i++)
+ if (input_character (data[i]))
+ break;
+ }
+
+ if (data != buffer)
+ vm_deallocate (mach_task_self(), (vm_address_t) data, datalen);
+ }
+ }
+#undef BUFFER_SIZE
+}
+
+
+/* Output characters. */
+static any_t
+hurdio_writer_loop (any_t arg)
+{
+ /* XXX The output buffer has 256 bytes. */
+#define BUFFER_SIZE 256
+ char *bufp;
+ char pending_output[BUFFER_SIZE];
+ size_t amount;
+ error_t err;
+ int size;
+ int npending_output_copy;
+ mach_port_t ioport_copy;
+
+ mutex_lock (&global_lock);
+ writer_thread = mach_thread_self ();
+
+ while (1)
+ {
+ while (!ioport || !qsize (outputq)
+ || (termflags & USER_OUTPUT_SUSP))
+ hurd_condition_wait (&hurdio_writer_condition, &global_lock);
+
+ /* If the output was suspended earlier, we have to tell the
+ underlying port to resume it. */
+ if (output_stopped)
+ {
+ if (tioc_caps & TIOC_CAP_START)
+ {
+ err = tioctl_tiocstart (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_START;
+ /* XXX Handle the error. */
+ err = 0;
+ }
+ output_stopped = 0;
+ }
+
+ /* Copy characters onto PENDING_OUTPUT, not bothering
+ those already there. */
+ size = qsize (outputq);
+
+ if (size + npending_output > BUFFER_SIZE)
+ size = BUFFER_SIZE - npending_output;
+
+ bufp = pending_output + npending_output;
+ npending_output += size;
+ /* We need to save these values, as otherwise there are races
+ with hurdio_abandon_physical_output or hurdio_desert_dtr,
+ which might overwrite the static variables. */
+ npending_output_copy = npending_output;
+ ioport_copy = ioport;
+ mach_port_mod_refs (mach_task_self (), ioport_copy,
+ MACH_PORT_RIGHT_SEND, 1);
+
+ while (size--)
+ *bufp++ = dequeue (outputq);
+
+ /* Submit all the outstanding characters to the I/O port. */
+ mutex_unlock (&global_lock);
+ err = io_write (ioport_copy, pending_output, npending_output_copy,
+ -1, &amount);
+ mutex_lock (&global_lock);
+
+ mach_port_mod_refs (mach_task_self (), ioport_copy,
+ MACH_PORT_RIGHT_SEND, -1);
+ /* XXX Handle errors correctly. */
+ if (err == EIO)
+ hurdio_desert_dtr ();
+ else if (!err)
+ {
+ /* Note that npending_output might be set to null in the
+ meantime by hurdio_abandon_physical_output. */
+ 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);
+ }
+ }
+ }
+#undef BUFFER_SIZE
+}
+
+
+/* If there are characters on the output queue, then send them. Is
+ called with global lock held. */
+static error_t
+hurdio_start_output ()
+{
+ condition_broadcast (&hurdio_writer_condition);
+ return 0;
+}
+
+
+/* Stop carrier on the line. Is called with global lock held. */
+static error_t
+hurdio_set_break ()
+{
+ if (tioc_caps & TIOC_CAP_SBRK)
+ {
+ error_t err = tioctl_tiocsbrk (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_SBRK;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Reassert carrier on the line. Is called with global lock held. */
+static error_t
+hurdio_clear_break ()
+{
+ if (tioc_caps & TIOC_CAP_CBRK)
+ {
+ error_t err = tioctl_tioccbrk (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_CBRK;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* This is called when output queues are being flushed. But there may
+ be pending output which is sitting in a device buffer or other
+ place lower down than the terminal's output queue; so this is
+ called to flush whatever other such things may be going on. Is
+ called with global lock held. */
+static error_t
+hurdio_abandon_physical_output ()
+{
+ if (tioc_caps & TIOC_CAP_FLUSH)
+ {
+ error_t err = tioctl_tiocflush (ioport, O_WRITE);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_FLUSH;
+ else if (err)
+ return err;
+ }
+
+ /* Make sure that an incomplete write will not be finished.
+ hurdio_writer_loop must take care that meddling with
+ npending_output here does not introduce any races. */
+ npending_output = 0;
+ return 0;
+}
+
+
+/* Tell the underlying port to suspend all pending output, and stop
+ output in the bottom handler as well. Is called with the global
+ lock held. */
+static error_t
+hurdio_suspend_physical_output ()
+{
+ if (!output_stopped)
+ {
+ if (tioc_caps & TIOC_CAP_STOP)
+ {
+ error_t err = tioctl_tiocstop (ioport);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_STOP;
+ else if (err)
+ return err;
+ }
+ output_stopped = 1;
+ }
+ return 0;
+}
+
+/* This is called to notify the bottom half when an input flush has
+ occurred. It is necessary to support pty packet mode. */
+static error_t
+hurdio_notice_input_flushed ()
+{
+ if (tioc_caps & TIOC_CAP_FLUSH)
+ {
+ error_t err = tioctl_tiocflush (ioport, O_READ);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_FLUSH;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+/* Determine the number of bytes of output pending. */
+static int
+hurdio_pending_output_size ()
+{
+ int queue_size = 0;
+
+ if (tioc_caps & TIOC_CAP_OUTQ)
+ {
+ error_t err = tioctl_tiocoutq (ioport, &queue_size);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_OUTQ;
+ else if (err)
+ queue_size = 0;
+ }
+ /* We can not get the correct number, so let's try a guess. */
+ return queue_size + npending_output;
+}
+
+
+/* Desert the DTR. Is called with global lock held. */
+static error_t
+hurdio_desert_dtr ()
+{
+ if (ioport)
+ {
+ mach_port_deallocate (mach_task_self (), ioport);
+ ioport = MACH_PORT_NULL;
+ if (writer_thread != MACH_PORT_NULL)
+ hurd_thread_cancel (writer_thread);
+ if (reader_thread != MACH_PORT_NULL)
+ hurd_thread_cancel (reader_thread);
+ }
+ /* If we are called after hurdio_assert_dtr before the reader thread
+ had a chance to wake up and open the port, we can prevent it from
+ doing so by clearing this flag. */
+ assert_dtr = 0;
+ report_carrier_off ();
+ return 0;
+}
+
+
+static error_t
+hurdio_assert_dtr ()
+{
+ if (ioport == MACH_PORT_NULL)
+ {
+ assert_dtr = 1;
+ condition_signal (&hurdio_assert_dtr_condition);
+ }
+
+ return 0;
+}
+
+
+/* Adjust physical state on the basis of the terminal state.
+ Where it isn't possible, mutate terminal state to match
+ reality. */
+static error_t
+hurdio_set_bits (struct termios *state)
+{
+ error_t err;
+ struct termios ttystat;
+ /* This structure equals how the Hurd tioctl_tiocgeta/seta split up
+ a termios structure into RPC arguments. */
+ struct hurd_termios
+ {
+ modes_t modes;
+ ccs_t ccs;
+ speeds_t speeds;
+ } *hurd_ttystat = (struct hurd_termios *) &ttystat;
+
+ if (!(state->c_cflag & CIGNORE) && ioport != MACH_PORT_NULL)
+ {
+
+ /* If we can not get the terminal state, it doesn't make sense
+ to attempt to change it. Even if we could change it we
+ wouldn't know what changes took effect. */
+ if (!(tioc_caps & TIOC_CAP_GETA))
+ /* XXX Maybe return an error here, but then we must do the
+ right thing in users.c. */
+ return 0;
+
+ err = tioctl_tiocgeta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ {
+ tioc_caps &= ~TIOC_CAP_GETA;
+ /* XXX Maybe return an error here, but then we must do the
+ right thing in users.c. */
+ return 0;
+ }
+ else if (err)
+ return err;
+
+ /* If possible, change the state. Otherwise we will just make
+ termstate match reality below. */
+ if (tioc_caps & TIOC_CAP_SETA)
+ {
+ if (state->__ispeed)
+ hurd_ttystat->speeds[0] = state->__ispeed;
+ if (state->__ospeed)
+ hurd_ttystat->speeds[1] = state->__ospeed;
+ cfmakeraw (&ttystat);
+ ttystat.c_cflag = state->c_cflag &~ HUPCL;
+
+ err = tioctl_tiocseta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_SETA;
+ else if (err)
+ return err;
+
+ /* Refetch the terminal state. */
+ err = tioctl_tiocgeta (ioport, hurd_ttystat->modes,
+ hurd_ttystat->ccs, hurd_ttystat->speeds);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_GETA;
+ else if (err)
+ return err;
+ }
+
+ /* And now make termstate match reality. */
+ *state = ttystat;
+ }
+
+ return 0;
+}
+
+/* Diddle the modem control bits. If HOW is MDMCTL_BIC, the bits set
+ in BITS should be cleared. If HOW is MDMCTL_BIS, the bits in BITS
+ should be set. Otherwise, bits that are set in BITS should be set,
+ and the others cleared. */
+static error_t
+hurdio_mdmctl (int how, int bits)
+{
+ error_t err;
+ int oldbits, newbits;
+
+ if (tioc_caps & TIOC_CAP_MODS)
+ {
+ if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC))
+ {
+ if (tioc_caps & TIOC_CAP_MODG)
+ {
+ error_t err = tioctl_tiocmodg (ioport, &oldbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_MODG;
+ else if (err)
+ return err;
+ }
+ }
+
+ if (how == MDMCTL_BIS)
+ newbits = (oldbits | bits);
+ else if (how == MDMCTL_BIC)
+ newbits = (oldbits &= ~bits);
+ else
+ newbits = bits;
+
+ err = tioctl_tiocmods (ioport, oldbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_MODS;
+ else if (err)
+ return err;
+ }
+ return 0;
+}
+
+
+static int
+hurdio_mdmstate ()
+{
+ int oldbits;
+
+ if (tioc_caps & TIOC_CAP_MODG)
+ {
+ error_t err = tioctl_tiocmodg (ioport, &oldbits);
+ if (err && (err == EMIG_BAD_ID || err == EOPNOTSUPP))
+ tioc_caps &= ~TIOC_CAP_MODG;
+ else if (err)
+ return 0; /* XXX What else can we do? */
+ }
+ return 0;
+}
+
+
+
+const struct bottomhalf hurdio_bottom =
+{
+ TERM_ON_HURDIO,
+ hurdio_init,
+ hurdio_start_output,
+ hurdio_set_break,
+ hurdio_clear_break,
+ hurdio_abandon_physical_output,
+ hurdio_suspend_physical_output,
+ hurdio_pending_output_size,
+ hurdio_notice_input_flushed,
+ hurdio_assert_dtr,
+ hurdio_desert_dtr,
+ hurdio_set_bits,
+ hurdio_mdmctl,
+ hurdio_mdmstate,
+};
diff --git a/term/main.c b/term/main.c
index 5e90e28e..b0a80658 100644
--- a/term/main.c
+++ b/term/main.c
@@ -41,7 +41,7 @@ int trivfs_allow_open = O_READ|O_WRITE;
/* The argument line options. */
char *tty_name;
-enum { T_NONE = 0, T_DEVICE, T_PTYMASTER, T_PTYSLAVE } tty_type;
+enum { T_NONE = 0, T_DEVICE, T_HURDIO, T_PTYMASTER, T_PTYSLAVE } tty_type;
char *tty_arg;
int
@@ -76,6 +76,8 @@ parse_opt (int opt, char *arg, struct argp_state *state)
{
if (!strcmp (arg, "device"))
tty_type = T_DEVICE;
+ else if (!strcmp (arg, "hurdio"))
+ tty_type = T_HURDIO;
else if (!strcmp (arg, "pty-master"))
tty_type = T_PTYMASTER;
else if (!strcmp (arg, "pty-slave"))
@@ -100,6 +102,7 @@ static struct argp term_argp =
{ 0, parse_opt, "NAME TYPE ARG", "A translator that emulates a terminal.\v"\
"Possible values for TYPE:\n"\
" device Use Mach device ARG as bottom handler.\n"\
+ " hurdio Use file port ARG as bottom handler.\n"\
" pty-master Master for slave at ARG.\n"\
" pty-slave Slave for master at ARG.\n"\
"\n"\
@@ -140,6 +143,16 @@ main (int argc, char **argv)
peercntl = 0;
break;
+ case T_HURDIO:
+ bottom = &hurdio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = 0;
+ peercntlclass = 0;
+ peercntl = 0;
+ break;
+
case T_PTYMASTER:
bottom = &ptyio_bottom;
ourclass = pty_class;
diff --git a/term/term.h b/term/term.h
index 694c598e..cb22e159 100644
--- a/term/term.h
+++ b/term/term.h
@@ -155,7 +155,7 @@ struct bottomhalf
};
const struct bottomhalf *bottom;
-extern const struct bottomhalf devio_bottom, ptyio_bottom;
+extern const struct bottomhalf devio_bottom, hurdio_bottom, ptyio_bottom;
/* Character queues */