diff options
author | Michael I. Bushnell <mib@gnu.org> | 1995-12-05 01:37:45 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1995-12-05 01:37:45 +0000 |
commit | f590b3b072ae7c0a98daf2489c4a1332c34ff5cf (patch) | |
tree | dbe27bec5f04be42d0b0c8b4f3b1ed1daa6ed706 /term/ptyio.c | |
parent | 99407d5e9431ada40c80bbbf64b67a00583699a3 (diff) |
Initial revision
Diffstat (limited to 'term/ptyio.c')
-rw-r--r-- | term/ptyio.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/term/ptyio.c b/term/ptyio.c new file mode 100644 index 00000000..4fb4e39e --- /dev/null +++ b/term/ptyio.c @@ -0,0 +1,473 @@ +/* + 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 <sys/ioctl.h> +#include <hurd/hurd_types.h> +#include <string.h> +#include <hurd/ports.h> +#include "term.h" + +/* Set if we need a wakeup when tty output has been done */ +static int pty_read_blocked = 0; + +/* Wake this up when tty output occurs and pty_read_blocked is set */ +static struct condition pty_read_wakeup = CONDITION_INITIALIZER; + +static struct condition pty_select_wakeup = CONDITION_INITIALIZER; + +/* Set if "dtr" is on. */ +static int dtr_on = 0; + +/* Set if packet mode is on. */ +static int packet_mode = 0; + +/* Set if user ioctl mode is on. */ +static int user_ioctl_mode = 0; + +/* Byte to send to user in packet mode or user ioctl mode. */ +static char control_byte = 0; + +static int output_stopped; + + +static void +ptyio_init () +{ + condition_implies (inputq->wait, &pty_select_wakeup); + condition_implies (&pty_read_wakeup, &pty_select_wakeup); +} + + +static inline void +wake_reader () +{ + if (pty_read_blocked) + { + pty_read_blocked = 0; + condition_broadcast (&pty_read_wakeup); + } +} + + +/* Lower half for tty node */ + +static void +ptyio_start_output () +{ + if (packet_mode && output_stopped && (!(termflags & USER_OUTPUT_SUSP))) + { + control_byte &= ~TIOCPKT_STOP; + control_byte |= TIOCPKT_START; + output_stopped = 0; + } + wake_reader (); +} + +static void +ptyio_abandon_physical_output () +{ + if (packet_mode) + { + control_byte |= TIOCPKT_FLUSHWRITE; + wake_reader (); + } +} + +static void +ptyio_suspend_physical_output () +{ + if (packet_mode) + { + control_byte &= ~TIOCPKT_START; + control_byte |= TIOCPKT_STOP; + output_stopped = 1; + wake_reader (); + } +} + +static int +ptyio_pending_output_size () +{ + /* We don't maintain any pending output buffer separate from the outputq. */ + return 0; +} + +static void +ptyio_notice_input_flushed () +{ + if (packet_mode) + { + control_byte |= TIOCPKT_FLUSHREAD; + wake_reader (); + } +} + +static error_t +ptyio_assert_dtr () +{ + dtr_on = 1; + return 0; +} + +static void +ptyio_desert_dtr () +{ + dtr_on = 0; + wake_reader (); +} + +static void +ptyio_set_bits () +{ + if (packet_mode && external_processing) + { + control_byte |= TIOCPKT_IOCTL; + wake_reader (); + } +} + +/* These do nothing. In BSD the associated ioctls get errors, but + I'd rather just ignore them. */ +static void +ptyio_set_break () +{ +} + +static void +ptyio_clear_break () +{ +} + +static void +ptyio_mdmctl (int a, int b) +{ +} + +static int +ptyio_mdmstate () +{ + return 0; +} + +struct bottomhalf ptyio_bottom = +{ + ptyio_start_output, + ptyio_set_break, + ptyio_clear_break, + ptyio_abandon_physical_output, + ptyio_suspend_physical_output, + ptyio_pending_output_size, + ptyio_notice_input_flushed, + ptyio_assert_dtr, + ptyio_desert_dtr, + ptyio_set_bits, + ptyio_mdmctl, + ptyio_mdmstate, +}; + + + + +/* I/O interface for pty master nodes */ + +/* Validation has already been done by trivfs_S_io_read. */ +error_t +pty_io_read (char **data, + mach_msg_type_number_t *datalen, + mach_msg_type_number_t amount) +{ + int size; + + mutex_lock (&global_lock); + + while (!control_byte + && (!qsize (outputq) || (termflags & USER_OUTPUT_SUSP))) + { + pty_read_blocked = 1; + condition_wait (&pty_read_wakeup, &global_lock); + } + + if (control_byte) + { + size = 1; + if (packet_mode && (control_byte & TIOCPKT_IOCTL)) + size += sizeof (struct termios); + } + else + { + size = qsize (outputq); + if (packet_mode || user_ioctl_mode) + size++; + } + + if (size > amount) + size = amount; + if (size > *datalen) + vm_allocate (mach_task_self (), (vm_address_t *) data, size, 1); + *datalen = size; + + if (control_byte) + { + **data = control_byte; + if (packet_mode && (control_byte & TIOCPKT_IOCTL)) + bcopy (&termstate, *data + 1, size - 1); + control_byte = 0; + } + else + { + char *cp = *data; + + if (packet_mode || user_ioctl_mode) + *cp++ = TIOCPKT_DATA; + while (size--) + *cp++ = dequeue (outputq); + } + + mutex_unlock (&global_lock); + return 0; +} + + +/* Validation has already been done by trivfs_S_io_write. */ +error_t +pty_io_write (char *data, + mach_msg_type_number_t datalen, + mach_msg_type_number_t *amount) +{ + int i, flush; + int cancel = 0; + + mutex_lock (&global_lock); + + if (remote_input_mode) + { + /* Wait for the queue to be empty */ + while (qsize (inputq) && !cancel) + cancel = hurd_condition_wait (inputq->wait, &global_lock); + if (cancel) + { + mutex_unlock (&global_lock); + return EINTR; + } + + for (i = 0; i < datalen; i++) + enqueue (&inputq, data[i]); + + /* Extra garbage charater */ + enqueue (&inputq, 0); + } + else if (termstate.c_cflag & CREAD) + for (i = 0; i < datalen; i++) + { + flush = input_character (data[i]); + if (flush) + { + if (packet_mode) + { + control_byte |= TIOCPKT_FLUSHREAD; + wake_reader (); + } + break; + } + } + mutex_unlock (&global_lock); + return 0; +} + +/* Validation has already been done by trivfs_S_io_readable */ +error_t +pty_io_readable (int *amt) +{ + mutex_lock (&global_lock); + if (control_byte) + { + *amt = 1; + if (packet_mode && (control_byte & TIOCPKT_IOCTL)) + *amt += sizeof (struct termios); + } + else + *amt = qsize (outputq); + mutex_unlock (&global_lock); + return 0; +} + +/* Validation has already been done by trivfs_S_io_select. */ +error_t +pty_io_select (int *type, + int *idtag) +{ + int avail = 0; + + if (*type == 0) + return 0; + + mutex_lock (&global_lock); + + while (1) + { + if ((*type & SELECT_READ) && (control_byte || qsize (outputq))) + avail |= SELECT_READ; + + if ((*type & SELECT_URG) && control_byte) + avail |= SELECT_URG; + + if ((*type & SELECT_WRITE) && (!remote_input_mode || !qsize (inputq))) + avail |= SELECT_WRITE; + + if (avail) + { + *type = avail; + mutex_unlock (&global_lock); + return 0; + } + + pty_read_blocked = 1; + if (hurd_condition_wait (&pty_select_wakeup, &global_lock)) + { + *type = 0; + mutex_unlock (&global_lock); + return EINTR; + } + } +} + +error_t +S_tioctl_tiocsig (io_t port, + int sig) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, pty_class); + if (!cred) + return EOPNOTSUPP; + + drop_output (); + clear_queue (inputq); + clear_queue (rawq); + ptyio_notice_input_flushed (); + send_signal (sig); + ports_port_deref (cred); + return 0; +} + +error_t +S_tioctl_tiocpkt (io_t port, + int mode) +{ + error_t err; + + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, pty_class); + if (!cred) + return EOPNOTSUPP; + + if (!!mode == !!packet_mode) + err = 0; + else if (mode && user_ioctl_mode) + err = EINVAL; + else + { + packet_mode = mode; + control_byte = 0; + err = 0; + } + ports_port_deref (cred); + return err; +} + +error_t +S_tioctl_tiocucntl (io_t port, + int mode) +{ + error_t err; + + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, pty_class); + if (!cred) + return EOPNOTSUPP; + + if (!!mode == !!user_ioctl_mode) + err = 0; + else if (mode && packet_mode) + err = EINVAL; + else + { + user_ioctl_mode = mode; + control_byte = 0; + err = 0; + } + ports_port_deref (cred); + return err; +} + +error_t +S_tioctl_tiocremote (io_t port, + int how) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, pty_class); + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + remote_input_mode = how; + drop_output (); + clear_queue (inputq); + clear_queue (rawq); + ptyio_notice_input_flushed (); + mutex_unlock (&global_lock); + ports_port_deref (cred); + return 0; +} + +error_t +S_tioctl_tiocext (io_t port, + int mode) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, pty_class); + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (mode && !external_processing) + { + if (packet_mode) + { + control_byte |= TIOCPKT_IOCTL; + wake_reader (); + } + external_processing = 1; + termstate.c_lflag |= EXTPROC; + } + else if (!mode && external_processing) + { + if (packet_mode) + { + control_byte |= TIOCPKT_IOCTL; + wake_reader (); + } + external_processing = 0; + termstate.c_lflag &= ~EXTPROC; + } + mutex_unlock (&global_lock); + ports_port_deref (cred); + return 0; +} |