2005-04-21 16:53:53 +02:00
|
|
|
/* pty.c - pseudo terminal driver Author: Kees J. Bot
|
|
|
|
* 30 Dec 1995
|
|
|
|
* PTYs can be seen as a bidirectional pipe with TTY
|
|
|
|
* input and output processing. For example a simple rlogin session:
|
|
|
|
*
|
|
|
|
* keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell
|
|
|
|
* shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen
|
|
|
|
*
|
|
|
|
* This file takes care of copying data between the tty/pty device pairs and
|
|
|
|
* the open/read/write/close calls on the pty devices. The TTY task takes
|
|
|
|
* care of the input and output processing (interrupt, backspace, raw I/O,
|
2013-09-11 12:48:10 +02:00
|
|
|
* etc.) using the pty_slave_read() and pty_slave_write() functions as the
|
|
|
|
* "keyboard" and "screen" functions of the ttypX devices.
|
2005-04-21 16:53:53 +02:00
|
|
|
* Be careful when reading this code, the terms "reading" and "writing" are
|
2013-09-11 12:48:10 +02:00
|
|
|
* used both for the tty (slave) and the pty (master) end of the pseudo tty.
|
|
|
|
* Writes to one end are to be read at the other end and vice-versa.
|
2005-04-21 16:53:53 +02:00
|
|
|
*/
|
|
|
|
|
2010-03-22 22:25:22 +01:00
|
|
|
#include <minix/drivers.h>
|
2005-04-21 16:53:53 +02:00
|
|
|
#include <termios.h>
|
2013-09-10 23:36:48 +02:00
|
|
|
#include <assert.h>
|
|
|
|
#include <sys/termios.h>
|
2005-04-21 16:53:53 +02:00
|
|
|
#include <signal.h>
|
|
|
|
#include "tty.h"
|
|
|
|
|
|
|
|
/* PTY bookkeeping structure, one per pty/tty pair. */
|
|
|
|
typedef struct pty {
|
|
|
|
tty_t *tty; /* associated TTY structure */
|
|
|
|
char state; /* flags: busy, closed, ... */
|
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
/* Read call on master (/dev/ptypX). */
|
|
|
|
endpoint_t rdcaller; /* process making the call, or NONE if none */
|
|
|
|
cdev_id_t rdid; /* ID of suspended read request */
|
|
|
|
cp_grant_id_t rdgrant; /* grant for reader's address space */
|
|
|
|
size_t rdleft; /* # bytes yet to be read */
|
|
|
|
size_t rdcum; /* # bytes written so far */
|
|
|
|
|
|
|
|
/* Write call to master (/dev/ptypX). */
|
|
|
|
endpoint_t wrcaller; /* process making the call, or NONE if none*/
|
|
|
|
cdev_id_t wrid; /* ID of suspended write request */
|
|
|
|
cp_grant_id_t wrgrant; /* grant for writer's address space */
|
|
|
|
size_t wrleft; /* # bytes yet to be written */
|
|
|
|
size_t wrcum; /* # bytes written so far */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Output buffer. */
|
|
|
|
int ocount; /* # characters in the buffer */
|
|
|
|
char *ohead, *otail; /* head and tail of the circular buffer */
|
2006-06-20 11:02:54 +02:00
|
|
|
char obuf[2048]; /* buffer for bytes going to the pty reader */
|
2005-08-05 15:50:58 +02:00
|
|
|
|
|
|
|
/* select() data. */
|
2013-09-11 12:48:10 +02:00
|
|
|
unsigned int select_ops; /* Which operations do we want to know about? */
|
|
|
|
endpoint_t select_proc; /* Who wants to know about it? */
|
2005-04-21 16:53:53 +02:00
|
|
|
} pty_t;
|
|
|
|
|
2013-08-31 16:08:25 +02:00
|
|
|
#define TTY_ACTIVE 0x01 /* tty is open/active */
|
|
|
|
#define PTY_ACTIVE 0x02 /* pty is open/active */
|
|
|
|
#define TTY_CLOSED 0x04 /* tty side has closed down */
|
|
|
|
#define PTY_CLOSED 0x08 /* pty side has closed down */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2012-03-25 20:25:53 +02:00
|
|
|
static pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
|
|
|
|
|
|
|
|
static void pty_start(pty_t *pp);
|
|
|
|
static void pty_finish(pty_t *pp);
|
2013-09-11 12:48:10 +02:00
|
|
|
|
|
|
|
static int pty_master_open(devminor_t minor, int access,
|
|
|
|
endpoint_t user_endpt);
|
|
|
|
static int pty_master_close(devminor_t minor);
|
|
|
|
static ssize_t pty_master_read(devminor_t minor, u64_t position,
|
|
|
|
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
|
|
|
|
cdev_id_t id);
|
|
|
|
static ssize_t pty_master_write(devminor_t minor, u64_t position,
|
|
|
|
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
|
|
|
|
cdev_id_t id);
|
|
|
|
static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
|
|
|
|
static int pty_master_select(devminor_t minor, unsigned int ops,
|
|
|
|
endpoint_t endpt);
|
|
|
|
|
|
|
|
static struct chardriver pty_master_tab = {
|
|
|
|
.cdr_open = pty_master_open,
|
|
|
|
.cdr_close = pty_master_close,
|
|
|
|
.cdr_read = pty_master_read,
|
|
|
|
.cdr_write = pty_master_write,
|
|
|
|
.cdr_cancel = pty_master_cancel,
|
|
|
|
.cdr_select = pty_master_select
|
|
|
|
};
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_master_open *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_master_open(devminor_t minor, int UNUSED(access),
|
|
|
|
endpoint_t UNUSED(user_endpt))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2013-09-11 12:48:10 +02:00
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
|
|
|
|
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
|
|
|
|
|
|
|
if (pp->state & PTY_ACTIVE)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
pp->state |= PTY_ACTIVE;
|
|
|
|
pp->rdcum = 0;
|
|
|
|
pp->wrcum = 0;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_master_close *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int pty_master_close(devminor_t minor)
|
|
|
|
{
|
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
|
|
|
|
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
|
|
|
|
|
|
|
if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) {
|
|
|
|
pp->state = 0;
|
|
|
|
} else {
|
|
|
|
pp->state |= PTY_CLOSED;
|
|
|
|
sigchar(tp, SIGHUP, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_master_read *
|
|
|
|
*===========================================================================*/
|
|
|
|
static ssize_t pty_master_read(devminor_t minor, u64_t UNUSED(position),
|
|
|
|
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
|
|
|
|
cdev_id_t id)
|
|
|
|
{
|
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
|
|
|
|
|
|
|
/* Check, store information on the reader, do I/O. */
|
|
|
|
if (pp->state & TTY_CLOSED)
|
|
|
|
return 0; /* EOF */
|
|
|
|
|
|
|
|
if (pp->rdcaller != NONE || pp->rdleft != 0 || pp->rdcum != 0)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
if (size <= 0)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
pp->rdcaller = endpt;
|
|
|
|
pp->rdid = id;
|
|
|
|
pp->rdgrant = grant;
|
|
|
|
pp->rdleft = size;
|
|
|
|
pty_start(pp);
|
|
|
|
|
|
|
|
handle_events(tp);
|
|
|
|
|
|
|
|
if (pp->rdleft == 0) {
|
|
|
|
pp->rdcaller = NONE;
|
|
|
|
return EDONTREPLY; /* already done */
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:25:01 +02:00
|
|
|
if (flags & CDEV_NONBLOCK) {
|
2013-09-11 12:48:10 +02:00
|
|
|
r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
|
|
|
|
pp->rdleft = pp->rdcum = 0;
|
|
|
|
pp->rdcaller = NONE;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EDONTREPLY; /* do suspend */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_master_write *
|
|
|
|
*===========================================================================*/
|
|
|
|
static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position),
|
|
|
|
endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags,
|
|
|
|
cdev_id_t id)
|
|
|
|
{
|
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
|
|
|
|
|
|
|
/* Check, store information on the writer, do I/O. */
|
|
|
|
if (pp->state & TTY_CLOSED)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
if (pp->wrcaller != NONE || pp->wrleft != 0 || pp->wrcum != 0)
|
|
|
|
return EIO;
|
|
|
|
|
|
|
|
if (size <= 0)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
pp->wrcaller = endpt;
|
|
|
|
pp->wrid = id;
|
|
|
|
pp->wrgrant = grant;
|
|
|
|
pp->wrleft = size;
|
|
|
|
|
|
|
|
handle_events(tp);
|
|
|
|
|
|
|
|
if (pp->wrleft == 0) {
|
|
|
|
pp->wrcaller = NONE;
|
|
|
|
return EDONTREPLY; /* already done */
|
|
|
|
}
|
|
|
|
|
2013-09-10 20:25:01 +02:00
|
|
|
if (flags & CDEV_NONBLOCK) {
|
2013-09-11 12:48:10 +02:00
|
|
|
r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
|
|
|
|
pp->wrleft = pp->wrcum = 0;
|
|
|
|
pp->wrcaller = NONE;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EDONTREPLY; /* do suspend */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_master_cancel *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
|
|
|
|
{
|
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
2005-04-21 16:53:53 +02:00
|
|
|
int r;
|
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
if (pp->rdcaller == endpt && pp->rdid == id) {
|
|
|
|
/* Cancel a read from a PTY. */
|
|
|
|
r = pp->rdcum > 0 ? pp->rdcum : EINTR;
|
|
|
|
pp->rdleft = pp->rdcum = 0;
|
|
|
|
pp->rdcaller = NONE;
|
|
|
|
return r;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
if (pp->wrcaller == endpt && pp->wrid == id) {
|
|
|
|
/* Cancel a write to a PTY. */
|
|
|
|
r = pp->wrcum > 0 ? pp->wrcum : EINTR;
|
|
|
|
pp->wrleft = pp->wrcum = 0;
|
|
|
|
pp->wrcaller = NONE;
|
|
|
|
return r;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
/* Request not found. */
|
|
|
|
return EDONTREPLY;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-11 12:48:10 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* select_try_pty *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int select_try_pty(tty_t *tp, int ops)
|
|
|
|
{
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
int r = 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-10 20:25:01 +02:00
|
|
|
if (ops & CDEV_OP_WR) {
|
2013-09-11 12:48:10 +02:00
|
|
|
/* Write won't block on error. */
|
2013-09-10 20:25:01 +02:00
|
|
|
if (pp->state & TTY_CLOSED) r |= CDEV_OP_WR;
|
|
|
|
else if (pp->wrleft != 0 || pp->wrcum != 0) r |= CDEV_OP_WR;
|
2013-10-05 01:40:58 +02:00
|
|
|
else if (tp->tty_incount < buflen(tp->tty_inbuf)) r |= CDEV_OP_WR;
|
2013-09-11 12:48:10 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2013-09-10 20:25:01 +02:00
|
|
|
if (ops & CDEV_OP_RD) {
|
2013-09-11 12:48:10 +02:00
|
|
|
/* Read won't block on error. */
|
2013-09-10 20:25:01 +02:00
|
|
|
if (pp->state & TTY_CLOSED) r |= CDEV_OP_RD;
|
|
|
|
else if (pp->rdleft != 0 || pp->rdcum != 0) r |= CDEV_OP_RD;
|
|
|
|
else if (pp->ocount > 0) r |= CDEV_OP_RD; /* Actual data. */
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2013-09-11 12:48:10 +02:00
|
|
|
|
|
|
|
return r;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* select_retry_pty *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
void select_retry_pty(tty_t *tp)
|
|
|
|
{
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
devminor_t minor;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* See if the pty side of a pty is ready to return a select. */
|
|
|
|
if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) {
|
|
|
|
minor = PTYPX_MINOR + (int) (pp - pty_table);
|
|
|
|
chardriver_reply_select(pp->select_proc, minor, r);
|
|
|
|
pp->select_ops &= ~r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_master_select *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int pty_master_select(devminor_t minor, unsigned int ops,
|
|
|
|
endpoint_t endpt)
|
|
|
|
{
|
|
|
|
tty_t *tp;
|
|
|
|
pty_t *pp;
|
|
|
|
int ready_ops, watch;
|
|
|
|
|
|
|
|
if ((tp = line2tty(minor)) == NULL)
|
|
|
|
return ENXIO;
|
|
|
|
pp = tp->tty_priv;
|
|
|
|
|
2013-09-10 20:25:01 +02:00
|
|
|
watch = (ops & CDEV_NOTIFY);
|
|
|
|
ops &= (CDEV_OP_RD | CDEV_OP_WR | CDEV_OP_ERR);
|
2013-09-11 12:48:10 +02:00
|
|
|
|
|
|
|
ready_ops = select_try_pty(tp, ops);
|
|
|
|
|
|
|
|
ops &= ~ready_ops;
|
|
|
|
if (ops && watch) {
|
|
|
|
pp->select_ops |= ops;
|
|
|
|
pp->select_proc = endpt;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ready_ops;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* do_pty *
|
|
|
|
*===========================================================================*/
|
|
|
|
void do_pty(message *m_ptr, int ipc_status)
|
|
|
|
{
|
|
|
|
/* Process a request for a PTY master (/dev/ptypX) device. */
|
|
|
|
|
|
|
|
chardriver_process(&pty_master_tab, m_ptr, ipc_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*===========================================================================*
|
|
|
|
* pty_slave_write *
|
|
|
|
*===========================================================================*/
|
|
|
|
static int pty_slave_write(tty_t *tp, int try)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
|
|
|
|
* /dev/ttypX to the output buffer.
|
|
|
|
*/
|
|
|
|
pty_t *pp = tp->tty_priv;
|
2005-05-09 16:35:26 +02:00
|
|
|
int count, ocount, s;
|
2008-11-19 13:26:10 +01:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/* PTY closed down? */
|
|
|
|
if (pp->state & PTY_CLOSED) {
|
2005-09-11 19:09:11 +02:00
|
|
|
if (try) return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
if (tp->tty_outleft > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
|
2013-08-30 11:14:03 +02:00
|
|
|
tp->tty_outleft = tp->tty_outcum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
tp->tty_outcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2008-11-19 13:26:10 +01:00
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* While there is something to do. */
|
|
|
|
for (;;) {
|
|
|
|
ocount = buflen(pp->obuf) - pp->ocount;
|
2005-09-11 19:09:11 +02:00
|
|
|
if (try) return (ocount > 0);
|
2005-04-21 16:53:53 +02:00
|
|
|
count = bufend(pp->obuf) - pp->ohead;
|
|
|
|
if (count > ocount) count = ocount;
|
|
|
|
if (count > tp->tty_outleft) count = tp->tty_outleft;
|
2005-08-05 15:50:58 +02:00
|
|
|
if (count == 0 || tp->tty_inhibited)
|
2005-06-17 15:37:41 +02:00
|
|
|
break;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Copy from user space to the PTY output buffer. */
|
TTY: seperate hardware dependent parts + add new serial driver
.Split TTY in order to support both x86 and ARM.
.Add support for the TI 16750 UARTs on OMAP35x.
.Various other improvements:
.Kernel messages are printed using generic terminal write
functions. That is, they are no longer directly displayed
on the console.
.The console can now be displayed on any terminal. This
is configured by the "console={tty00,tty01,ttyc2,ttyc3,ttyc4}"
boot variable -- basically any valid /dev/tty* terminal.
.Cutify kernel messages with colors. Configured by
"kernelclr={1,2,3,4,5,6,7}" boot variable.
2012-10-17 16:07:53 +02:00
|
|
|
if (tp->tty_outcaller == KERNEL) {
|
|
|
|
/* We're trying to print on kernel's behalf */
|
2013-09-11 12:48:10 +02:00
|
|
|
memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
|
TTY: seperate hardware dependent parts + add new serial driver
.Split TTY in order to support both x86 and ARM.
.Add support for the TI 16750 UARTs on OMAP35x.
.Various other improvements:
.Kernel messages are printed using generic terminal write
functions. That is, they are no longer directly displayed
on the console.
.The console can now be displayed on any terminal. This
is configured by the "console={tty00,tty01,ttyc2,ttyc3,ttyc4}"
boot variable -- basically any valid /dev/tty* terminal.
.Cutify kernel messages with colors. Configured by
"kernelclr={1,2,3,4,5,6,7}" boot variable.
2012-10-17 16:07:53 +02:00
|
|
|
count);
|
|
|
|
} else {
|
|
|
|
if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
|
2013-09-11 12:48:10 +02:00
|
|
|
tp->tty_outcum, (vir_bytes) pp->ohead,
|
TTY: seperate hardware dependent parts + add new serial driver
.Split TTY in order to support both x86 and ARM.
.Add support for the TI 16750 UARTs on OMAP35x.
.Various other improvements:
.Kernel messages are printed using generic terminal write
functions. That is, they are no longer directly displayed
on the console.
.The console can now be displayed on any terminal. This
is configured by the "console={tty00,tty01,ttyc2,ttyc3,ttyc4}"
boot variable -- basically any valid /dev/tty* terminal.
.Cutify kernel messages with colors. Configured by
"kernelclr={1,2,3,4,5,6,7}" boot variable.
2012-10-17 16:07:53 +02:00
|
|
|
count)) != OK) {
|
|
|
|
break;
|
|
|
|
}
|
2005-05-09 16:35:26 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Perform output processing on the output buffer. */
|
|
|
|
out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
|
|
|
|
if (count == 0) break;
|
|
|
|
|
|
|
|
/* Assume echoing messed up by output. */
|
|
|
|
tp->tty_reprint = TRUE;
|
|
|
|
|
|
|
|
/* Bookkeeping. */
|
|
|
|
pp->ocount += ocount;
|
|
|
|
if ((pp->ohead += ocount) >= bufend(pp->obuf))
|
|
|
|
pp->ohead -= buflen(pp->obuf);
|
|
|
|
pty_start(pp);
|
2006-06-20 11:02:54 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
tp->tty_outcum += count;
|
|
|
|
if ((tp->tty_outleft -= count) == 0) {
|
|
|
|
/* Output is finished, reply to the writer. */
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
|
|
|
|
tp->tty_outcum);
|
2013-08-30 11:14:03 +02:00
|
|
|
tp->tty_outcum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
tp->tty_outcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
pty_finish(pp);
|
2005-06-17 15:37:41 +02:00
|
|
|
return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_echo *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static void pty_slave_echo(tty_t *tp, int c)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Echo one character. (Like pty_write, but only one character, optionally.) */
|
|
|
|
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
int count, ocount;
|
|
|
|
|
|
|
|
ocount = buflen(pp->obuf) - pp->ocount;
|
|
|
|
if (ocount == 0) return; /* output buffer full */
|
|
|
|
count = 1;
|
|
|
|
*pp->ohead = c; /* add one character */
|
|
|
|
|
|
|
|
out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount);
|
|
|
|
if (count == 0) return;
|
|
|
|
|
|
|
|
pp->ocount += ocount;
|
|
|
|
if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf);
|
|
|
|
pty_start(pp);
|
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* pty_start *
|
|
|
|
*===========================================================================*/
|
2012-03-25 20:25:53 +02:00
|
|
|
static void pty_start(pty_t *pp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Transfer bytes written to the output buffer to the PTY reader. */
|
|
|
|
int count;
|
|
|
|
|
|
|
|
/* While there are things to do. */
|
|
|
|
for (;;) {
|
2005-05-09 16:35:26 +02:00
|
|
|
int s;
|
2005-04-21 16:53:53 +02:00
|
|
|
count = bufend(pp->obuf) - pp->otail;
|
|
|
|
if (count > pp->ocount) count = pp->ocount;
|
|
|
|
if (count > pp->rdleft) count = pp->rdleft;
|
|
|
|
if (count == 0) break;
|
|
|
|
|
|
|
|
/* Copy from the output buffer to the readers address space. */
|
2013-09-11 12:48:10 +02:00
|
|
|
if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
|
|
|
|
(vir_bytes) pp->otail, count)) != OK) {
|
2006-06-20 11:02:54 +02:00
|
|
|
break;
|
2011-03-25 11:43:24 +01:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Bookkeeping. */
|
|
|
|
pp->ocount -= count;
|
|
|
|
if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf;
|
|
|
|
pp->rdcum += count;
|
|
|
|
pp->rdleft -= count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* pty_finish *
|
|
|
|
*===========================================================================*/
|
2012-03-25 20:25:53 +02:00
|
|
|
static void pty_finish(pty_t *pp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Finish the read request of a PTY reader if there is at least one byte
|
|
|
|
* transferred.
|
|
|
|
*/
|
2013-09-11 12:48:10 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
if (pp->rdcum > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
|
2013-08-30 11:14:03 +02:00
|
|
|
pp->rdleft = pp->rdcum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
pp->rdcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_read *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_slave_read(tty_t *tp, int try)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at
|
|
|
|
* a time, 99% of the writes will be for one byte, so no sense in being smart.)
|
|
|
|
*/
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
if (pp->state & PTY_CLOSED) {
|
2005-09-11 19:09:11 +02:00
|
|
|
if (try) return 1;
|
2005-04-21 16:53:53 +02:00
|
|
|
if (tp->tty_inleft > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
|
|
|
|
tp->tty_incum);
|
2013-08-30 11:14:03 +02:00
|
|
|
tp->tty_inleft = tp->tty_incum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
tp->tty_incaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2005-06-17 15:37:41 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
if (try) {
|
|
|
|
if (pp->wrleft > 0)
|
2005-06-17 15:37:41 +02:00
|
|
|
return 1;
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
while (pp->wrleft > 0) {
|
2005-05-09 16:35:26 +02:00
|
|
|
int s;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
/* Transfer one character to 'c'. */
|
2013-09-11 12:48:10 +02:00
|
|
|
if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
|
2012-06-16 03:46:15 +02:00
|
|
|
(vir_bytes) &c, 1)) != OK) {
|
2006-06-20 11:02:54 +02:00
|
|
|
printf("pty: safecopy failed (error %d)\n", s);
|
|
|
|
break;
|
2005-05-09 16:35:26 +02:00
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Input processing. */
|
Input infrastructure, INPUT server, PCKBD driver
This commit separates the low-level keyboard driver from TTY, putting
it in a separate driver (PCKBD). The commit also separates management
of raw input devices from TTY, and puts it in a separate server
(INPUT). All keyboard and mouse input from hardware is sent by drivers
to the INPUT server, which either sends it to a process that has
opened a raw input device, or otherwise forwards it to TTY for
standard processing.
Design by Dirk Vogt. Prototype by Uli Kastlunger.
Additional changes made to the prototype:
- the event communication is now based on USB HID codes; all input
drivers have to use USB codes to describe events;
- all TTY keymaps have been converted to USB format, with the effect
that a single keymap covers all keys; there is no (static) escaped
keymap anymore;
- further keymap tweaks now allow remapping of literally all keys;
- input device renumbering and protocol rewrite;
- INPUT server rewrite, with added support for cancel and select;
- PCKBD reimplementation, including PC/AT-to-USB translation;
- support for manipulating keyboard LEDs has been added;
- keyboard and mouse multiplexer devices have been added to INPUT,
primarily so that an X server need only open two devices;
- a new "libinputdriver" library abstracts away protocol details from
input drivers, and should be used by all future input drivers;
- both INPUT and PCKBD can be restarted;
- TTY is now scheduled by KERNEL, so that it won't be punished for
running a lot; without this, simply running "yes" on the console
kills the system;
- the KIOCBELL IOCTL has been moved to /dev/console;
- support for the SCANCODES termios setting has been removed;
- obsolete keymap compression has been removed;
- the obsolete Olivetti M24 keymap has been removed.
Change-Id: I3a672fb8c4fd566734e4b46d3994b4b7fc96d578
2013-09-28 14:46:21 +02:00
|
|
|
if (in_process(tp, &c, 1) == 0) break;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* PTY writer bookkeeping. */
|
|
|
|
pp->wrcum++;
|
|
|
|
if (--pp->wrleft == 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
|
2013-08-30 11:14:03 +02:00
|
|
|
pp->wrcum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
pp->wrcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
2008-11-19 13:26:10 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2013-08-31 16:08:25 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_open *
|
2013-08-31 16:08:25 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_slave_open(tty_t *tp, int UNUSED(try))
|
2013-08-31 16:08:25 +02:00
|
|
|
{
|
|
|
|
/* The tty side has been opened. */
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
|
|
|
|
/* TTY_ACTIVE may already be set, which would indicate that the slave is
|
|
|
|
* reopened after being fully closed while the master is still open. In that
|
|
|
|
* case TTY_CLOSED will also be set, so clear that one.
|
|
|
|
*/
|
|
|
|
pp->state |= TTY_ACTIVE;
|
|
|
|
pp->state &= ~TTY_CLOSED;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_close *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_slave_close(tty_t *tp, int UNUSED(try))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* The tty side has closed, so shut down the pty side. */
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
|
2008-11-19 13:26:10 +01:00
|
|
|
if (!(pp->state & PTY_ACTIVE)) return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
if (pp->rdleft > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
|
2013-08-30 11:14:03 +02:00
|
|
|
pp->rdleft = pp->rdcum = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
pp->rdcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pp->wrleft > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
|
|
|
|
pp->wrleft = pp->wrcum = 0;
|
|
|
|
pp->wrcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2013-08-31 16:08:25 +02:00
|
|
|
if (pp->state & PTY_CLOSED) pp->state = 0;
|
|
|
|
else pp->state |= TTY_CLOSED;
|
2008-11-19 13:26:10 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_icancel *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Discard waiting input. */
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
|
|
|
|
if (pp->wrleft > 0) {
|
2013-09-11 12:48:10 +02:00
|
|
|
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
|
2013-08-30 11:14:03 +02:00
|
|
|
pp->wrcum = pp->wrleft = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
pp->wrcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2008-11-19 13:26:10 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
2013-09-11 12:48:10 +02:00
|
|
|
* pty_slave_ocancel *
|
2005-09-11 19:09:11 +02:00
|
|
|
*===========================================================================*/
|
2013-09-11 12:48:10 +02:00
|
|
|
static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
/* Drain the output buffer. */
|
|
|
|
pty_t *pp = tp->tty_priv;
|
|
|
|
|
|
|
|
pp->ocount = 0;
|
|
|
|
pp->otail = pp->ohead;
|
2008-11-19 13:26:10 +01:00
|
|
|
|
|
|
|
return 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-11 19:09:11 +02:00
|
|
|
/*===========================================================================*
|
|
|
|
* pty_init *
|
|
|
|
*===========================================================================*/
|
2012-03-25 20:25:53 +02:00
|
|
|
void pty_init(tty_t *tp)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
pty_t *pp;
|
|
|
|
int line;
|
|
|
|
|
|
|
|
/* Associate PTY and TTY structures. */
|
2014-02-24 23:28:12 +01:00
|
|
|
line = tp - tty_table;
|
2005-04-21 16:53:53 +02:00
|
|
|
pp = tp->tty_priv = &pty_table[line];
|
|
|
|
pp->tty = tp;
|
2005-08-05 15:50:58 +02:00
|
|
|
pp->select_ops = 0;
|
2013-09-11 12:48:10 +02:00
|
|
|
pp->rdcaller = NONE;
|
|
|
|
pp->wrcaller = NONE;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/* Set up output queue. */
|
|
|
|
pp->ohead = pp->otail = pp->obuf;
|
|
|
|
|
|
|
|
/* Fill in TTY function hooks. */
|
2013-09-11 12:48:10 +02:00
|
|
|
tp->tty_devread = pty_slave_read;
|
|
|
|
tp->tty_devwrite = pty_slave_write;
|
|
|
|
tp->tty_echo = pty_slave_echo;
|
|
|
|
tp->tty_icancel = pty_slave_icancel;
|
|
|
|
tp->tty_ocancel = pty_slave_ocancel;
|
|
|
|
tp->tty_open = pty_slave_open;
|
|
|
|
tp->tty_close = pty_slave_close;
|
2005-08-05 15:50:58 +02:00
|
|
|
tp->tty_select_ops = 0;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|