TTY: use libchardriver; clean up

- writing to a PTY master side blocks if there is not already a
  blocked reader on the slave side, and select now reflects this;
- internally, TTY now uses a test based on "caller != NONE" rather
  than "grant != GRANT_INVALID" to identify whether a call is
  currently ongoing;
- "offset" fields have been removed as they equal the corresponding
  "cum" fields;
- improved variable typing and function naming here and there;
- various other small fixes.

Change-Id: I6b51452888942e864b4e034e8c8490576184a23e
This commit is contained in:
David van Moolenbroek 2013-09-11 12:48:10 +02:00 committed by Lionel Sambuc
parent 701f2b4dd5
commit 3d697930dd
15 changed files with 997 additions and 1060 deletions

View file

@ -1221,7 +1221,7 @@
./usr/include/minix/sysutil.h minix-sys
./usr/include/minix/termios.h minix-sys
./usr/include/minix/timers.h minix-sys
./usr/include/minix/tty.h minix-sys
./usr/include/minix/tty.h minix-sys obsolete
./usr/include/minix/type.h minix-sys
./usr/include/minix/types.h minix-sys obsolete
./usr/include/minix/u64.h minix-sys

View file

@ -6,8 +6,8 @@ PROG= tty
SRCS += tty.c pty.c
DPADD+= ${LIBSYS} ${LIBTIMERS}
LDADD+= -lsys -ltimers
DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS}
LDADD+= -lchardriver -lsys -ltimers
MAN=

View file

@ -4,7 +4,7 @@
#include "tty.h"
void
do_video(message *m)
do_video(message *m, int ipc_status)
{
}
@ -19,7 +19,7 @@ cons_stop(void)
}
int
con_loadfont(message *m)
con_loadfont(endpoint_t endpt, cp_grant_id_t grant)
{
return 0;
}

View file

@ -19,12 +19,7 @@ do_kb_inject(message *m)
}
void
do_kbd(message *m)
{
}
void
do_kbdaux(message *m)
do_kbd(message *m, int ipc_status)
{
}
@ -34,7 +29,7 @@ kb_init_once(void)
}
int
kbd_loadmap(message *m)
kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant)
{
return 0;
}

View file

@ -237,11 +237,11 @@ rs_write(register tty_t *tp, int try)
if (tp->tty_outcaller == KERNEL) {
/* We're trying to print on kernel's behalf */
memcpy(rs->ohead,
(void *) tp->tty_outgrant + tp->tty_outoffset,
(char *) tp->tty_outgrant + tp->tty_outcum,
count);
} else {
if ((r = sys_safecopyfrom(tp->tty_outcaller,
tp->tty_outgrant, tp->tty_outoffset,
tp->tty_outgrant, tp->tty_outcum,
(vir_bytes) rs->ohead, count)) != OK) {
return 0;
}
@ -262,24 +262,21 @@ rs_write(register tty_t *tp, int try)
rs_ostart(rs);
if ((rs->ohead += ocount) >= bufend(rs->obuf))
rs->ohead -= buflen(rs->obuf);
tp->tty_outoffset += count;
tp->tty_outcum += count;
if ((tp->tty_outleft -= count) == 0) {
/* Output is finished, reply to the writer. */
tty_reply(DEV_REVIVE, tp->tty_outcaller,
tp->tty_outproc, tp->tty_outgrant,
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
tp->tty_outcum);
tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
}
if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
/* Oops, the line has hung up. */
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, EIO);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
return 1;

View file

@ -18,16 +18,10 @@
#include <minix/drivers.h>
#include <termios.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/vm.h>
#include <sys/video.h>
#include <sys/mman.h>
#include <minix/tty.h>
#include <minix/callnr.h>
#include <minix/com.h>
#include <minix/sys_config.h>
#include <minix/vm.h>
#include "tty.h"
/* Set this to 1 if you want console output duplicated on the first
@ -142,6 +136,18 @@ static void vid_vid_copy(int src, int dst, int count);
static void get_6845(int reg, unsigned *val);
#endif
static int video_open(devminor_t minor, int access, endpoint_t user_endpt);
static int video_close(devminor_t minor);
static int video_ioctl(devminor_t minor, unsigned long request,
endpoint_t endpt, cp_grant_id_t grant, int flags,
endpoint_t user_endpt, cdev_id_t id);
static struct chardriver video_tab = {
.cdr_open = video_open,
.cdr_close = video_close,
.cdr_ioctl = video_ioctl
};
/*===========================================================================*
* cons_write *
*===========================================================================*/
@ -174,16 +180,14 @@ int try;
if (count > sizeof(buf)) count = sizeof(buf);
if (tp->tty_outcaller == KERNEL) {
/* We're trying to print on kernel's behalf */
memcpy(buf, (void *) tp->tty_outgrant + tp->tty_outoffset,
count);
memcpy(buf, (char *) tp->tty_outgrant + tp->tty_outcum, count);
} else {
if ((result = sys_safecopyfrom(tp->tty_outcaller,
tp->tty_outgrant, tp->tty_outoffset,
tp->tty_outgrant, tp->tty_outcum,
(vir_bytes) buf, count)) != OK) {
break;
}
}
tp->tty_outoffset += count;
tbuf = buf;
/* Update terminal data structure. */
@ -215,10 +219,10 @@ int try;
/* Reply to the writer if all output is finished or if an error occured. */
if (tp->tty_outleft == 0 || result != OK) {
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, result != OK ? result : tp->tty_outcum);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
result != OK ? result : tp->tty_outcum);
tp->tty_outcum = tp->tty_outleft = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
return 0;
@ -807,84 +811,69 @@ static void beep()
}
}
/*===========================================================================*
* video_open *
*===========================================================================*/
static int video_open(devminor_t minor, int UNUSED(access),
endpoint_t UNUSED(user_endpt))
{
/* Should grant IOPL */
disable_console();
return OK;
}
/*===========================================================================*
* video_close *
*===========================================================================*/
static int video_close(devminor_t minor)
{
reenable_console();
return OK;
}
/*===========================================================================*
* video_ioctl *
*===========================================================================*/
static int video_ioctl(devminor_t minor, unsigned long request,
endpoint_t endpt, cp_grant_id_t grant, int flags,
endpoint_t user_endpt, cdev_id_t id)
{
struct mapreqvm mapreqvm;
int r, do_map;
switch (request) {
case TIOCMAPMEM:
case TIOCUNMAPMEM:
do_map = (request == TIOCMAPMEM); /* else unmap */
if ((r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &mapreqvm,
sizeof(mapreqvm))) != OK)
return r;
if (do_map) {
mapreqvm.vaddr_ret = vm_map_phys(user_endpt,
(void *) mapreqvm.phys_offset, mapreqvm.size);
r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &mapreqvm,
sizeof(mapreqvm));
} else {
r = vm_unmap_phys(user_endpt, mapreqvm.vaddr, mapreqvm.size);
}
return r;
default:
return ENOTTY;
}
}
/*===========================================================================*
* do_video *
*===========================================================================*/
void do_video(message *m)
void do_video(message *m, int ipc_status)
{
int r;
/* Execute the requested device driver function. */
r= EINVAL; /* just in case */
switch (m->m_type) {
case DEV_OPEN:
/* Should grant IOPL */
disable_console();
tty_reply(DEV_OPEN_REPL, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, OK);
return;
case DEV_CLOSE:
reenable_console();
tty_reply(DEV_CLOSE_REPL, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, OK);
return;
case DEV_IOCTL_S:
switch(m->REQUEST) {
case TIOCMAPMEM:
case TIOCUNMAPMEM: {
int r, do_map;
struct mapreqvm mapreqvm;
do_map= (m->REQUEST == TIOCMAPMEM); /* else unmap */
r = sys_safecopyfrom(m->m_source,
(cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) &mapreqvm, sizeof(mapreqvm));
if (r != OK)
{
printf("tty: sys_safecopyfrom failed\n");
break;
}
/* In safe ioctl mode, the POSITION field contains
* the endpt number of the original requestor.
* USER_ENDPT is always FS.
*/
if(do_map) {
mapreqvm.vaddr_ret = vm_map_phys(m->POSITION,
(void *) mapreqvm.phys_offset, mapreqvm.size);
if((r = sys_safecopyto(m->m_source,
(cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) &mapreqvm,
sizeof(mapreqvm))) != OK) {
printf("tty: sys_safecopyto failed\n");
}
} else {
r = vm_unmap_phys(m->POSITION,
mapreqvm.vaddr, mapreqvm.size);
}
break;
}
default:
r= ENOTTY;
break;
}
break;
default:
printf(
"Warning, TTY(video) got unexpected request %d from %d\n",
m->m_type, m->m_source);
r= EINVAL;
}
tty_reply(DEV_REVIVE, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, r);
chardriver_process(&video_tab, m, ipc_status);
}
/*===========================================================================*
* beep_x *
*===========================================================================*/
@ -1137,12 +1126,11 @@ void select_console(int cons_line)
/*===========================================================================*
* con_loadfont *
*===========================================================================*/
int con_loadfont(m)
message *m;
int con_loadfont(endpoint_t endpt, cp_grant_id_t grant)
{
/* Load a font into the EGA or VGA adapter. */
int result;
int r, r2;
static struct sequence seq1[7] = {
{ GA_SEQUENCER_INDEX, 0x00, 0x01 },
{ GA_SEQUENCER_INDEX, 0x02, 0x04 },
@ -1164,17 +1152,14 @@ message *m;
seq2[6].value= color ? 0x0E : 0x0A;
result = ga_program(seq1); /* bring font memory into view */
r = ga_program(seq1); /* bring font memory into view */
if (r != OK) return r;
if(sys_safecopyfrom(m->m_source, (cp_grant_id_t) m->IO_GRANT, 0,
(vir_bytes) font_memory, GA_FONT_SIZE) != OK) {
printf("tty: copying from %d failed\n", m->m_source);
return EFAULT;
}
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) font_memory, GA_FONT_SIZE);
result = ga_program(seq2); /* restore */
r2 = ga_program(seq2); /* restore */
return(result);
return(r != OK ? r : r2);
}
/*===========================================================================*

View file

@ -123,19 +123,17 @@ static obs_t sfkey_obs[12]; /* observers for SHIFT F1-F12 */
static struct kbd
{
int minor;
devminor_t minor;
int nr_open;
char buf[KBD_BUFSZ];
int offset;
int avail;
int req_size;
int req_proc;
size_t req_size;
cdev_id_t req_id;
cp_grant_id_t req_grant;
vir_bytes req_addr_offset;
int incaller;
int select_ops;
int select_proc;
int select_minor; /* sanity check only, can be removed */
endpoint_t req_caller;
unsigned int select_ops;
endpoint_t select_proc;
} kbd, kbdaux;
/* Data that is to be sent to the keyboard. Each byte is ACKed by the
@ -155,7 +153,6 @@ static long sticky_alt_mode = 0;
static long debug_fkeys = 1;
static timer_t tmr_kbd_wd;
static void handle_req(struct kbd *kbdp, message *m);
static void kbc_cmd0(int cmd);
static void kbc_cmd1(int cmd, int data);
static int kbc_read(void);
@ -171,220 +168,284 @@ static int kb_read(struct tty *tp, int try);
static unsigned map_key(int scode);
static void kbd_watchdog(timer_t *tmrp);
int micro_delay(u32_t usecs)
static int kbd_open(devminor_t minor, int access, endpoint_t user_endpt);
static int kbd_close(devminor_t minor);
static ssize_t kbd_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 kbd_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 kbd_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
static int kbd_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
static int kbd_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
static struct chardriver kbd_tab = {
.cdr_open = kbd_open,
.cdr_close = kbd_close,
.cdr_read = kbd_read,
.cdr_write = kbd_write,
.cdr_ioctl = kbd_ioctl,
.cdr_cancel = kbd_cancel,
.cdr_select = kbd_select
};
/*===========================================================================*
* line2kbd *
*===========================================================================*/
static struct kbd *
line2kbd(devminor_t minor)
{
/* TTY can't use the library micro_delay() as that calls PM. */
tickdelay(micros_to_ticks(usecs));
switch (minor) {
case KBD_MINOR: return &kbd;
case KBDAUX_MINOR: return &kbdaux;
default: return NULL;
}
}
/*===========================================================================*
* kbd_open *
*===========================================================================*/
static int
kbd_open(devminor_t minor, int UNUSED(access), endpoint_t UNUSED(user_endpt))
{
struct kbd *kbdp;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
kbdp->nr_open++;
return OK;
}
/*===========================================================================*
* do_kbd *
* kbd_close *
*===========================================================================*/
void do_kbd(message *m)
static int
kbd_close(devminor_t minor)
{
handle_req(&kbd, m);
}
struct kbd *kbdp;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
/*===========================================================================*
* do_kbdaux *
*===========================================================================*/
void do_kbdaux(message *m)
{
handle_req(&kbdaux, m);
}
/*===========================================================================*
* handle_req *
*===========================================================================*/
static void handle_req(kbdp, m)
struct kbd *kbdp;
message *m;
{
int i, n, r, ops, watch;
unsigned char c;
/* Execute the requested device driver function. */
r= EINVAL; /* just in case */
switch (m->m_type) {
case DEV_OPEN:
kbdp->nr_open++;
tty_reply(DEV_OPEN_REPL, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, OK);
return;
case DEV_CLOSE:
kbdp->nr_open--;
if (kbdp->nr_open < 0)
{
if (kbdp->nr_open < 0) {
printf("TTY(kbd): open count is negative\n");
kbdp->nr_open= 0;
}
if (kbdp->nr_open == 0)
kbdp->avail= 0;
tty_reply(DEV_CLOSE_REPL, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, OK);
return;
case DEV_READ_S:
return OK;
}
/*===========================================================================*
* kbd_read *
*===========================================================================*/
static ssize_t
kbd_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)
{
ssize_t n, r;
struct kbd *kbdp;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
/* We handle only one request at a time. */
if (kbdp->req_size)
{
/* We handle only request at a time */
r= EIO;
break;
}
if (kbdp->avail == 0)
{
if (m->FLAGS & FLG_OP_NONBLOCK) {
r = EAGAIN;
break;
}
/* Should record proc */
kbdp->req_size= m->COUNT;
kbdp->req_proc= m->USER_ENDPT;
kbdp->req_grant= (cp_grant_id_t) m->IO_GRANT;
kbdp->req_addr_offset= 0;
kbdp->incaller= m->m_source;
return;
return EIO;
if ((ssize_t) size <= 0)
return EINVAL;
/* If no data is available, suspend the caller. */
if (kbdp->avail == 0) {
if (flags & FLG_OP_NONBLOCK)
return EAGAIN;
kbdp->req_size = size;
kbdp->req_id = id;
kbdp->req_grant = grant;
kbdp->req_caller = endpt;
return EDONTREPLY;
}
/* Handle read request */
n= kbdp->avail;
if (n > m->COUNT)
n= m->COUNT;
/* Handle read request. */
n = kbdp->avail;
if (n > (ssize_t) size)
n = size;
if (kbdp->offset + n > KBD_BUFSZ)
n= KBD_BUFSZ-kbdp->offset;
n = KBD_BUFSZ - kbdp->offset;
if (n <= 0)
panic("do_kbd(READ): bad n: %d", n);
r= sys_safecopyto(m->m_source, (cp_grant_id_t) m->IO_GRANT, 0,
r = sys_safecopyto(endpt, grant, 0,
(vir_bytes) &kbdp->buf[kbdp->offset], n);
if (r == OK)
{
kbdp->offset= (kbdp->offset+n) % KBD_BUFSZ;
if (r == OK) {
kbdp->offset = (kbdp->offset + n) % KBD_BUFSZ;
kbdp->avail -= n;
r= n;
} else {
printf("copy in read kbd failed: %d\n", r);
r = n;
} else
printf("TTY(kbd): copy in read kbd failed: %d\n", r);
return r;
}
/*===========================================================================*
* kbd_write *
*===========================================================================*/
static ssize_t
kbd_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)
{
struct kbd *kbdp;
unsigned char c;
size_t i;
int r;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
if (kbdp != &kbdaux) {
printf("TTY(kbd): write to keyboard not implemented\n");
return EINVAL;
}
break;
case DEV_WRITE_S:
if (kbdp != &kbdaux)
{
printf("write to keyboard not implemented\n");
r= EINVAL;
break;
}
/* Assume that output to AUX only happens during
* initialization and we can afford to lose input. This should
* be fixed at a later time.
/*
* Assume that output to AUX only happens during initialization and we
* can afford to lose input. This should be fixed at a later time.
*/
for (i= 0; i<m->COUNT; i++)
{
r= sys_safecopyfrom(m->m_source, (cp_grant_id_t)
m->IO_GRANT, i, (vir_bytes) &c, 1);
for (i = 0; i < size; i++) {
r = sys_safecopyfrom(endpt, grant, i, (vir_bytes) &c, 1);
if (r != OK)
break;
return i ? i : r;
kbc_cmd1(KBC_WRITE_AUX, c);
}
r= i;
break;
case CANCEL:
kbdp->req_size= 0;
r= EAGAIN;
break;
case DEV_SELECT:
ops = m->DEV_SEL_OPS & (SEL_RD|SEL_WR|SEL_ERR);
watch = (m->DEV_SEL_OPS & SEL_NOTIFY) ? 1 : 0;
return r;
}
r= 0;
if (kbdp->avail && (ops & SEL_RD))
{
r |= SEL_RD;
break;
}
if (ops && watch)
{
kbdp->select_ops |= ops;
kbdp->select_proc= m->m_source;
kbdp->select_minor= m->DEV_MINOR;
}
assert(kbdp->minor == m->DEV_MINOR);
select_reply(DEV_SEL_REPL1, m->m_source, m->DEV_MINOR, r);
return;
case DEV_IOCTL_S:
if (kbdp == &kbd && m->REQUEST == KIOCSLEDS)
{
/*===========================================================================*
* kbd_ioctl *
*===========================================================================*/
static int
kbd_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t UNUSED(user_endpt),
cdev_id_t id)
{
struct kbd *kbdp;
kio_leds_t leds;
kio_bell_t bell;
clock_t ticks;
unsigned char b;
int r;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
if (kbdp != &kbd)
return ENOTTY; /* we only support ioctls on keyboards now */
r= sys_safecopyfrom(m->m_source, (cp_grant_id_t)
m->IO_GRANT, 0, (vir_bytes) &leds,
switch (request) {
case KIOCSLEDS:
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &leds,
sizeof(leds));
if (r != OK)
break;
b= 0;
return r;
b = 0;
if (leds.kl_bits & KBD_LEDS_NUM) b |= NUM_LOCK;
if (leds.kl_bits & KBD_LEDS_CAPS) b |= CAPS_LOCK;
if (leds.kl_bits & KBD_LEDS_SCROLL) b |= SCROLL_LOCK;
if (kbdout.avail == 0)
kbdout.offset= 0;
if (kbdout.offset + kbdout.avail + 2 > KBD_OUT_BUFSZ)
{
kbdout.offset = 0;
if (kbdout.offset + kbdout.avail + 2 > KBD_OUT_BUFSZ) {
/* Output buffer is full. Ignore this command.
* Reset ACK flag.
*/
kbdout.expect_ack= 0;
}
else
{
kbdout.buf[kbdout.offset+kbdout.avail]=
LED_CODE;
kbdout.buf[kbdout.offset+kbdout.avail+1]= b;
kbdout.expect_ack = 0;
} else {
kbdout.buf[kbdout.offset+kbdout.avail] = LED_CODE;
kbdout.buf[kbdout.offset+kbdout.avail+1] = b;
kbdout.avail += 2;
}
if (!kbdout.expect_ack)
kbd_send();
r= OK;
break;
}
if (kbdp == &kbd && m->REQUEST == KIOCBELL)
{
kio_bell_t bell;
clock_t ticks;
r = sys_safecopyfrom(m->m_source, (cp_grant_id_t)
m->IO_GRANT, 0, (vir_bytes) &bell,
return OK;
case KIOCBELL:
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &bell,
sizeof(bell));
if (r != OK)
break;
return r;
ticks= bell.kb_duration.tv_usec * system_hz / 1000000;
ticks = bell.kb_duration.tv_usec * system_hz / 1000000;
ticks += bell.kb_duration.tv_sec * system_hz;
if (!ticks)
ticks++;
beep_x(bell.kb_pitch, ticks);
r= OK;
break;
}
r= ENOTTY;
break;
return OK;
default:
printf("Warning, TTY(kbd) got unexpected request %d from %d\n",
m->m_type, m->m_source);
r= EINVAL;
return ENOTTY;
}
tty_reply(DEV_REVIVE, m->m_source, m->USER_ENDPT,
(cp_grant_id_t) m->IO_GRANT, r);
}
/*===========================================================================*
* kbd_cancel *
*===========================================================================*/
static int
kbd_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
{
struct kbd *kbdp;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
if (kbdp->req_size > 0 && endpt == kbdp->req_caller &&
id == kbdp->req_id)
return EINTR;
return EDONTREPLY;
}
/*===========================================================================*
* kbd_select *
*===========================================================================*/
static int
kbd_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
{
struct kbd *kbdp;
int watch, ready_ops;
if ((kbdp = line2kbd(minor)) == NULL)
return ENXIO;
watch = (ops & SEL_NOTIFY);
ops &= (SEL_RD | SEL_WR | SEL_ERR);
ready_ops = 0;
if (kbdp->avail && (ops & SEL_RD))
ready_ops |= SEL_RD;
if (ops & SEL_WR)
ready_ops |= SEL_WR; /* writes never block */
ops &= ~ready_ops;
if (ops && watch) {
kbdp->select_ops |= ops;
kbdp->select_proc = endpt;
}
return ready_ops;
}
/*===========================================================================*
* do_kbd *
*===========================================================================*/
void
do_kbd(message *m, int ipc_status)
{
chardriver_process(&kbd_tab, m, ipc_status);
}
/*===========================================================================*
* map_key *
@ -469,7 +530,7 @@ void kbd_interrupt(message *UNUSED(m_ptr))
if (n <= 0)
panic("kbd_interrupt: bad n: %d", n);
kbdp->req_size= 0;
r= sys_safecopyto(kbdp->incaller, kbdp->req_grant, 0,
r= sys_safecopyto(kbdp->req_caller, kbdp->req_grant, 0,
(vir_bytes)&kbdp->buf[kbdp->offset], n);
if (r == OK)
{
@ -478,14 +539,12 @@ void kbd_interrupt(message *UNUSED(m_ptr))
r= n;
} else printf("copy in revive kbd failed: %d\n", r);
tty_reply(DEV_REVIVE, kbdp->incaller, kbdp->req_proc,
kbdp->req_grant, r);
kbdp->req_grant = GRANT_INVALID;
chardriver_reply_task(kbdp->req_caller, kbdp->req_id, r);
kbdp->req_caller = NONE;
}
/* Only satisfy pending select if characters weren't just read. */
if (kbdp->avail && (kbdp->select_ops & SEL_RD)) {
assert(kbdp->select_minor == kbdp->minor);
select_reply(DEV_SEL_REPL2, kbdp->select_proc, kbdp->minor,
chardriver_reply_select(kbdp->select_proc, kbdp->minor,
SEL_RD);
kbdp->select_ops &= ~SEL_RD;
}
@ -1024,12 +1083,10 @@ void kb_init_once(void)
/*===========================================================================*
* kbd_loadmap *
*===========================================================================*/
int kbd_loadmap(m)
message *m;
int kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant)
{
/* Load a new keymap. */
return sys_safecopyfrom(m->m_source, (cp_grant_id_t) m->IO_GRANT,
0, (vir_bytes) keymap, (vir_bytes) sizeof(keymap));
return sys_safecopyfrom(endpt, grant, 0, (vir_bytes) keymap, sizeof(keymap));
}
/*===========================================================================*

View file

@ -246,10 +246,11 @@ static int rs_write(register tty_t *tp, int try)
/* Copy from user space to the RS232 output buffer. */
if (tp->tty_outcaller == KERNEL) {
/* We're trying to print on kernel's behalf */
memcpy(rs->ohead, (void *) tp->tty_outgrant + tp->tty_outoffset, count);
memcpy(rs->ohead, (char *) tp->tty_outgrant + tp->tty_outcum,
count);
} else {
if ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
tp->tty_outoffset, (vir_bytes) rs->ohead, count)) != OK)
tp->tty_outcum, (vir_bytes) rs->ohead, count)) != OK)
printf("TTY: sys_safecopyfrom() failed: %d", r);
}
@ -265,22 +266,20 @@ static int rs_write(register tty_t *tp, int try)
rs_ostart(rs);
if ((rs->ohead += ocount) >= bufend(rs->obuf))
rs->ohead -= buflen(rs->obuf);
tp->tty_outoffset += count;
tp->tty_outcum += count;
if ((tp->tty_outleft -= count) == 0) {
/* Output is finished, reply to the writer. */
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, tp->tty_outcum);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
tp->tty_outcum);
tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
}
if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
/* Oops, the line has hung up. */
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, EIO);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
return 1;

View file

@ -9,20 +9,16 @@
* 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,
* etc.) using the pty_read() and pty_write() functions as the "keyboard" and
* "screen" functions of the ttypX devices.
* etc.) using the pty_slave_read() and pty_slave_write() functions as the
* "keyboard" and "screen" functions of the ttypX devices.
* Be careful when reading this code, the terms "reading" and "writing" are
* used both for the tty and the pty end of the pseudo tty. Writes to one
* end are to be read at the other end and vice-versa.
* 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.
*/
#include <minix/drivers.h>
#include <assert.h>
#include <termios.h>
#include <signal.h>
#include <minix/com.h>
#include <minix/callnr.h>
#include <sys/select.h>
#include "tty.h"
#if NR_PTYS > 0
@ -32,21 +28,19 @@ typedef struct pty {
tty_t *tty; /* associated TTY structure */
char state; /* flags: busy, closed, ... */
/* Read call on /dev/ptypX. */
int rdcaller; /* process making the call (usually FS) */
int rdproc; /* process that wants to read from the pty */
cp_grant_id_t rdgrant; /* grant for readers address space */
vir_bytes rdoffset; /* offset in above grant */
int rdleft; /* # bytes yet to be read */
int rdcum; /* # bytes written so far */
/* 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 /dev/ptypX. */
int wrcaller; /* process making the call (usually FS) */
int wrproc; /* process that wants to write to the pty */
cp_grant_id_t wrgrant; /* grant for writers address space */
vir_bytes wroffset; /* offset in above grant */
int wrleft; /* # bytes yet to be written */
int wrcum; /* # 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 */
/* Output buffer. */
int ocount; /* # characters in the buffer */
@ -54,9 +48,8 @@ typedef struct pty {
char obuf[2048]; /* buffer for bytes going to the pty reader */
/* select() data. */
int select_ops; /* Which operations do we want to know about? */
int select_proc; /* Who wants to know about it? */
dev_t select_minor; /* sanity check only, can be removed */
unsigned int select_ops; /* Which operations do we want to know about? */
endpoint_t select_proc; /* Who wants to know about it? */
} pty_t;
#define TTY_ACTIVE 0x01 /* tty is open/active */
@ -66,159 +59,288 @@ typedef struct pty {
static pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */
static int pty_write(tty_t *tp, int try);
static void pty_echo(tty_t *tp, int c);
static void pty_start(pty_t *pp);
static void pty_finish(pty_t *pp);
static int pty_read(tty_t *tp, int try);
static int pty_close(tty_t *tp, int try);
static int pty_icancel(tty_t *tp, int try);
static int pty_ocancel(tty_t *tp, int try);
static void pty_select(tty_t *tp, message *m);
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
};
/*===========================================================================*
* do_pty *
* pty_master_open *
*===========================================================================*/
void do_pty(tty_t *tp, message *m_ptr)
static int pty_master_open(devminor_t minor, int UNUSED(access),
endpoint_t UNUSED(user_endpt))
{
/* Perform an open/close/read/write call on a /dev/ptypX device. */
pty_t *pp = tp->tty_priv;
int r;
tty_t *tp;
pty_t *pp;
switch (m_ptr->m_type) {
case DEV_READ_S:
/* Check, store information on the reader, do I/O. */
if (pp->state & TTY_CLOSED) {
r = 0;
break;
}
if (pp->rdleft != 0 || pp->rdcum != 0) {
r = EIO;
break;
}
if (m_ptr->COUNT <= 0) {
r = EINVAL;
break;
}
if (pp->rdgrant != GRANT_INVALID) {
r = ENOBUFS;
break;
}
pp->rdcaller = m_ptr->m_source;
pp->rdproc = m_ptr->USER_ENDPT;
pp->rdgrant = (cp_grant_id_t) m_ptr->IO_GRANT;
pp->rdoffset = 0;
pp->rdleft = m_ptr->COUNT;
pty_start(pp);
handle_events(tp);
if (pp->rdleft == 0) {
pp->rdgrant = GRANT_INVALID;
return; /* already done */
}
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
pp = tp->tty_priv;
if (m_ptr->FLAGS & FLG_OP_NONBLOCK) {
r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
pp->rdleft = pp->rdcum = 0;
pp->rdgrant = GRANT_INVALID;
} else {
return; /* do suspend */
}
break;
if (pp->state & PTY_ACTIVE)
return EIO;
case DEV_WRITE_S:
/* Check, store information on the writer, do I/O. */
if (pp->state & TTY_CLOSED) {
r = EIO;
break;
}
if (pp->wrleft != 0 || pp->wrcum != 0) {
r = EIO;
break;
}
if (m_ptr->COUNT <= 0) {
r = EINVAL;
break;
}
if (pp->wrgrant != GRANT_INVALID) {
r = ENOBUFS;
break;
}
pp->wrcaller = m_ptr->m_source;
pp->wrproc = m_ptr->USER_ENDPT;
pp->wrgrant = (cp_grant_id_t) m_ptr->IO_GRANT;
pp->wroffset = 0;
pp->wrleft = m_ptr->COUNT;
handle_events(tp);
if (pp->wrleft == 0) {
pp->wrgrant = GRANT_INVALID;
return; /* already done */
}
if (m_ptr->FLAGS & FLG_OP_NONBLOCK) {
r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
pp->wrleft = pp->wrcum = 0;
pp->wrgrant = GRANT_INVALID;
r = EAGAIN;
} else {
return; /* do suspend */
}
break;
case DEV_OPEN:
if (!(pp->state & PTY_ACTIVE)) {
pp->state |= PTY_ACTIVE;
pp->rdcum = 0;
pp->wrcum = 0;
r = OK;
} else {
r = EIO;
}
tty_reply(DEV_OPEN_REPL, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
return;
case DEV_CLOSE:
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);
}
tty_reply(DEV_CLOSE_REPL, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, OK);
return;
case DEV_SELECT:
pty_select(tp, m_ptr);
return;
case CANCEL:
r = EINTR;
if (m_ptr->USER_ENDPT == pp->rdproc) {
/* Cancel a read from a PTY. */
r = pp->rdcum > 0 ? pp->rdcum : EAGAIN;
pp->rdleft = pp->rdcum = 0;
pp->rdgrant = GRANT_INVALID;
}
if (m_ptr->USER_ENDPT == pp->wrproc) {
/* Cancel a write to a PTY. */
r = pp->wrcum > 0 ? pp->wrcum : EAGAIN;
pp->wrleft = pp->wrcum = 0;
pp->wrgrant = GRANT_INVALID;
}
break;
default:
r = EINVAL;
}
tty_reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
return OK;
}
/*===========================================================================*
* pty_write *
* pty_master_read *
*===========================================================================*/
static int pty_write(tty_t *tp, int try)
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 */
}
if (flags & FLG_OP_NONBLOCK) {
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 */
}
if (flags & FLG_OP_NONBLOCK) {
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;
int r;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
pp = tp->tty_priv;
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;
}
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;
}
/* Request not found. */
return EDONTREPLY;
}
/*===========================================================================*
* select_try_pty *
*===========================================================================*/
static int select_try_pty(tty_t *tp, int ops)
{
pty_t *pp = tp->tty_priv;
int r = 0;
if (ops & SEL_WR) {
/* Write won't block on error. */
if (pp->state & TTY_CLOSED) r |= SEL_WR;
else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
else if (tp->tty_inleft > 0) r |= SEL_WR; /* There's a reader. */
}
if (ops & SEL_RD) {
/* Read won't block on error. */
if (pp->state & TTY_CLOSED) r |= SEL_RD;
else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
}
return r;
}
/*===========================================================================*
* select_retry_pty *
*===========================================================================*/
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;
watch = (ops & SEL_NOTIFY);
ops &= (SEL_RD | SEL_WR | SEL_ERR);
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)
{
/* (*dev_write)() routine for PTYs. Transfer bytes from the writer on
* /dev/ttypX to the output buffer.
@ -226,15 +348,13 @@ static int pty_write(tty_t *tp, int try)
pty_t *pp = tp->tty_priv;
int count, ocount, s;
/* PTY closed down? */
if (pp->state & PTY_CLOSED) {
if (try) return 1;
if (tp->tty_outleft > 0) {
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, EIO);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
return 0;
}
@ -252,11 +372,11 @@ static int pty_write(tty_t *tp, int try)
/* Copy from user space to the PTY output buffer. */
if (tp->tty_outcaller == KERNEL) {
/* We're trying to print on kernel's behalf */
memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outoffset,
memcpy(pp->ohead, (void *) tp->tty_outgrant + tp->tty_outcum,
count);
} else {
if ((s = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
tp->tty_outoffset, (vir_bytes) pp->ohead,
tp->tty_outcum, (vir_bytes) pp->ohead,
count)) != OK) {
break;
}
@ -275,15 +395,13 @@ static int pty_write(tty_t *tp, int try)
pp->ohead -= buflen(pp->obuf);
pty_start(pp);
tp->tty_outoffset += count;
tp->tty_outcum += count;
if ((tp->tty_outleft -= count) == 0) {
/* Output is finished, reply to the writer. */
tty_reply(DEV_REVIVE, tp->tty_outcaller, tp->tty_outproc,
tp->tty_outgrant, tp->tty_outcum);
chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
tp->tty_outcum);
tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
tp->tty_outcaller = NONE;
}
}
pty_finish(pp);
@ -291,9 +409,9 @@ static int pty_write(tty_t *tp, int try)
}
/*===========================================================================*
* pty_echo *
* pty_slave_echo *
*===========================================================================*/
static void pty_echo(tty_t *tp, int c)
static void pty_slave_echo(tty_t *tp, int c)
{
/* Echo one character. (Like pty_write, but only one character, optionally.) */
@ -330,11 +448,10 @@ static void pty_start(pty_t *pp)
if (count == 0) break;
/* Copy from the output buffer to the readers address space. */
if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant,
pp->rdoffset, (vir_bytes) pp->otail, count)) != OK) {
if((s = sys_safecopyto(pp->rdcaller, pp->rdgrant, pp->rdcum,
(vir_bytes) pp->otail, count)) != OK) {
break;
}
pp->rdoffset += count;
/* Bookkeeping. */
pp->ocount -= count;
@ -352,19 +469,18 @@ static void pty_finish(pty_t *pp)
/* Finish the read request of a PTY reader if there is at least one byte
* transferred.
*/
if (pp->rdcum > 0) {
tty_reply(DEV_REVIVE, pp->rdcaller, pp->rdproc, pp->rdgrant,
pp->rdcum);
pp->rdleft = pp->rdcum = 0;
pp->rdgrant = GRANT_INVALID;
}
if (pp->rdcum > 0) {
chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
pp->rdleft = pp->rdcum = 0;
pp->rdcaller = NONE;
}
}
/*===========================================================================*
* pty_read *
* pty_slave_read *
*===========================================================================*/
static int pty_read(tty_t *tp, int try)
static int pty_slave_read(tty_t *tp, int try)
{
/* 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.)
@ -375,10 +491,10 @@ static int pty_read(tty_t *tp, int try)
if (pp->state & PTY_CLOSED) {
if (try) return 1;
if (tp->tty_inleft > 0) {
tty_reply(DEV_REVIVE, tp->tty_incaller, tp->tty_inproc,
tp->tty_ingrant, tp->tty_incum);
chardriver_reply_task(tp->tty_incaller, tp->tty_inid,
tp->tty_incum);
tp->tty_inleft = tp->tty_incum = 0;
tp->tty_ingrant = GRANT_INVALID;
tp->tty_incaller = NONE;
}
return 1;
}
@ -393,12 +509,11 @@ static int pty_read(tty_t *tp, int try)
int s;
/* Transfer one character to 'c'. */
if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wroffset,
if ((s = sys_safecopyfrom(pp->wrcaller, pp->wrgrant, pp->wrcum,
(vir_bytes) &c, 1)) != OK) {
printf("pty: safecopy failed (error %d)\n", s);
break;
}
pp->wroffset++;
/* Input processing. */
if (in_process(tp, &c, 1, -1) == 0) break;
@ -406,10 +521,9 @@ static int pty_read(tty_t *tp, int try)
/* PTY writer bookkeeping. */
pp->wrcum++;
if (--pp->wrleft == 0) {
tty_reply(DEV_REVIVE, pp->wrcaller, pp->wrproc, pp->wrgrant,
pp->wrcum);
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
pp->wrcum = 0;
pp->wrgrant = GRANT_INVALID;
pp->wrcaller = NONE;
}
}
@ -417,9 +531,9 @@ static int pty_read(tty_t *tp, int try)
}
/*===========================================================================*
* pty_open *
* pty_slave_open *
*===========================================================================*/
static int pty_open(tty_t *tp, int UNUSED(try))
static int pty_slave_open(tty_t *tp, int UNUSED(try))
{
/* The tty side has been opened. */
pty_t *pp = tp->tty_priv;
@ -435,9 +549,9 @@ static int pty_open(tty_t *tp, int UNUSED(try))
}
/*===========================================================================*
* pty_close *
* pty_slave_close *
*===========================================================================*/
static int pty_close(tty_t *tp, int UNUSED(try))
static int pty_slave_close(tty_t *tp, int UNUSED(try))
{
/* The tty side has closed, so shut down the pty side. */
pty_t *pp = tp->tty_priv;
@ -445,17 +559,15 @@ static int pty_close(tty_t *tp, int UNUSED(try))
if (!(pp->state & PTY_ACTIVE)) return 0;
if (pp->rdleft > 0) {
tty_reply(DEV_REVIVE, pp->rdcaller, pp->rdproc, pp->rdgrant,
pp->rdcum);
chardriver_reply_task(pp->rdcaller, pp->rdid, pp->rdcum);
pp->rdleft = pp->rdcum = 0;
pp->rdgrant = GRANT_INVALID;
pp->rdcaller = NONE;
}
if (pp->wrleft > 0) {
tty_reply(DEV_REVIVE, pp->wrcaller, pp->wrproc, pp->wrgrant,
pp->wrcum);
pp->wrcum = 0;
pp->wrgrant = GRANT_INVALID;
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum);
pp->wrleft = pp->wrcum = 0;
pp->wrcaller = NONE;
}
if (pp->state & PTY_CLOSED) pp->state = 0;
@ -465,27 +577,26 @@ static int pty_close(tty_t *tp, int UNUSED(try))
}
/*===========================================================================*
* pty_icancel *
* pty_slave_icancel *
*===========================================================================*/
static int pty_icancel(tty_t *tp, int UNUSED(try))
static int pty_slave_icancel(tty_t *tp, int UNUSED(try))
{
/* Discard waiting input. */
pty_t *pp = tp->tty_priv;
if (pp->wrleft > 0) {
tty_reply(DEV_REVIVE, pp->wrcaller, pp->wrproc, pp->wrgrant,
pp->wrcum + pp->wrleft);
chardriver_reply_task(pp->wrcaller, pp->wrid, pp->wrcum + pp->wrleft);
pp->wrcum = pp->wrleft = 0;
pp->wrgrant = GRANT_INVALID;
pp->wrcaller = NONE;
}
return 0;
}
/*===========================================================================*
* pty_ocancel *
* pty_slave_ocancel *
*===========================================================================*/
static int pty_ocancel(tty_t *tp, int UNUSED(try))
static int pty_slave_ocancel(tty_t *tp, int UNUSED(try))
{
/* Drain the output buffer. */
pty_t *pp = tp->tty_priv;
@ -509,86 +620,21 @@ void pty_init(tty_t *tp)
pp = tp->tty_priv = &pty_table[line];
pp->tty = tp;
pp->select_ops = 0;
pp->rdgrant = GRANT_INVALID;
pp->wrgrant = GRANT_INVALID;
pp->rdcaller = NONE;
pp->wrcaller = NONE;
/* Set up output queue. */
pp->ohead = pp->otail = pp->obuf;
/* Fill in TTY function hooks. */
tp->tty_devread = pty_read;
tp->tty_devwrite = pty_write;
tp->tty_echo = pty_echo;
tp->tty_icancel = pty_icancel;
tp->tty_ocancel = pty_ocancel;
tp->tty_open = pty_open;
tp->tty_close = pty_close;
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;
tp->tty_select_ops = 0;
}
/*===========================================================================*
* select_try_pty *
*===========================================================================*/
static int select_try_pty(tty_t *tp, int ops)
{
pty_t *pp = tp->tty_priv;
int r = 0;
if (ops & SEL_WR) {
/* Write won't block on error. */
if (pp->state & TTY_CLOSED) r |= SEL_WR;
else if (pp->wrleft != 0 || pp->wrcum != 0) r |= SEL_WR;
else r |= SEL_WR;
}
if (ops & SEL_RD) {
/* Read won't block on error. */
if (pp->state & TTY_CLOSED) r |= SEL_RD;
else if (pp->rdleft != 0 || pp->rdcum != 0) r |= SEL_RD;
else if (pp->ocount > 0) r |= SEL_RD; /* Actual data. */
}
return r;
}
/*===========================================================================*
* select_retry_pty *
*===========================================================================*/
void select_retry_pty(tty_t *tp)
{
pty_t *pp = tp->tty_priv;
dev_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);
assert(minor == pp->select_minor);
select_reply(DEV_SEL_REPL2, pp->select_proc, minor, r);
pp->select_ops &= ~r;
}
}
/*===========================================================================*
* pty_select *
*===========================================================================*/
static void pty_select(tty_t *tp, message *m)
{
pty_t *pp = tp->tty_priv;
int ops, ready_ops = 0, watch;
ops = m->DEV_SEL_OPS & (SEL_RD|SEL_WR|SEL_ERR);
watch = (m->DEV_SEL_OPS & SEL_NOTIFY) ? 1 : 0;
ready_ops = select_try_pty(tp, ops);
if (!ready_ops && ops && watch) {
pp->select_ops |= ops;
pp->select_proc = m->m_source;
pp->select_minor = m->DEV_MINOR;
}
select_reply(DEV_SEL_REPL1, m->m_source, m->DEV_MINOR, ready_ops);
}
#endif /* NR_PTYS > 0 */

View file

@ -60,11 +60,7 @@
#include <termios.h>
#include <sys/ioc_tty.h>
#include <signal.h>
#include <minix/callnr.h>
#include <minix/sys_config.h>
#include <minix/tty.h>
#include <minix/keymap.h>
#include <minix/endpoint.h>
#include "tty.h"
#include <sys/time.h>
@ -101,13 +97,6 @@ struct kmessages kmess;
static void tty_timed_out(timer_t *tp);
static void settimer(tty_t *tty_ptr, int enable);
static void do_cancel(tty_t *tp, message *m_ptr);
static void do_ioctl(tty_t *tp, message *m_ptr);
static void do_open(tty_t *tp, message *m_ptr);
static void do_close(tty_t *tp, message *m_ptr);
static void do_read(tty_t *tp, message *m_ptr);
static void do_write(tty_t *tp, message *m_ptr);
static void do_select(tty_t *tp, message *m_ptr);
static void in_transfer(tty_t *tp);
static int tty_echo(tty_t *tp, int ch);
static void rawecho(tty_t *tp, int ch);
@ -118,12 +107,32 @@ static void setattr(tty_t *tp);
static void tty_icancel(tty_t *tp);
static void tty_init(void);
static void do_new_kmess(void);
static tty_t * line2tty(int line);
static void set_console_line(char term[CONS_ARG]);
static void set_kernel_color(char color[CONS_ARG]);
static void set_color(tty_t *tp, int color);
static void reset_color(tty_t *tp);
static int do_open(devminor_t minor, int access, endpoint_t user_endpt);
static int do_close(devminor_t minor);
static ssize_t do_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 do_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 do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id);
static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id);
static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt);
static struct chardriver tty_tab = {
.cdr_open = do_open,
.cdr_close = do_close,
.cdr_read = do_read,
.cdr_write = do_write,
.cdr_ioctl = do_ioctl,
.cdr_cancel = do_cancel,
.cdr_select = do_select
};
/* Default attributes. */
static struct termios termios_defaults = {
TINPUT_DEF, TOUTPUT_DEF, TCTRL_DEF, TLOCAL_DEF,
@ -160,7 +169,7 @@ int main(void)
message tty_mess; /* buffer for all incoming messages */
int ipc_status;
unsigned line;
int r, code;
int r;
register tty_t *tp;
/* SEF local startup. */
@ -217,7 +226,6 @@ int main(void)
switch (tty_mess.m_type) {
case TTY_FKEY_CONTROL: /* (un)register a fkey observer */
case OLD_FKEY_CONTROL: /* old number */
do_fkey_ctl(&tty_mess);
continue;
case INPUT_EVENT:
@ -227,76 +235,34 @@ int main(void)
; /* do nothing; end switch */
}
if (!IS_DEV_RQ(tty_mess.m_type)) {
chardriver_process(&tty_tab, &tty_mess, ipc_status);
continue;
}
/* Only device requests should get to this point.
* All requests have a minor device number.
*/
line = tty_mess.DEVICE;
if (line == CONS_MINOR || line == LOG_MINOR) {
/* /dev/log output goes to /dev/console */
if (consoleline != CONS_MINOR) {
/* Console output must redirected */
line = consoleline;
tty_mess.DEVICE = line;
}
}
if (line == KBD_MINOR) {
do_kbd(&tty_mess);
continue;
} else if (line == KBDAUX_MINOR) {
do_kbdaux(&tty_mess);
if (line == KBD_MINOR || line == KBDAUX_MINOR) {
do_kbd(&tty_mess, ipc_status);
continue;
} else if (line == VIDEO_MINOR) {
do_video(&tty_mess);
do_video(&tty_mess, ipc_status);
continue;
} else {
tp = line2tty(line);
} else if (line - PTYPX_MINOR < NR_PTYS &&
tty_mess.m_type != DEV_IOCTL_S) {
/* Terminals and pseudo terminals belong together. We can only
* make a distinction between the two based on position in the
* tty_table and not on minor number (i.e., use ispty macro).
* Hence this special case.
*/
if (line - PTYPX_MINOR < NR_PTYS &&
tty_mess.m_type != DEV_IOCTL_S){
do_pty(tp, &tty_mess);
continue;
}
}
/* If the device doesn't exist or is not configured return ENXIO. */
if (tp == NULL || ! tty_active(tp)) {
if (tty_mess.m_source == LOG_PROC_NR)
continue;
/* Can all of these occur? Probably not. We're by far most
* likely to see DEV_OPEN, but better safe than sorry..
*/
switch (tty_mess.m_type) {
case DEV_OPEN: code = DEV_OPEN_REPL; break;
case DEV_CLOSE: code = DEV_CLOSE_REPL; break;
default: code = DEV_REVIVE; break;
}
tty_reply(code, tty_mess.m_source, tty_mess.USER_ENDPT,
(cp_grant_id_t) tty_mess.IO_GRANT, ENXIO);
do_pty(&tty_mess, ipc_status);
continue;
}
/* Execute the requested device driver function. */
switch (tty_mess.m_type) {
case DEV_READ_S: do_read(tp, &tty_mess); break;
case DEV_WRITE_S: do_write(tp, &tty_mess); break;
case DEV_IOCTL_S: do_ioctl(tp, &tty_mess); break;
case DEV_OPEN: do_open(tp, &tty_mess); break;
case DEV_CLOSE: do_close(tp, &tty_mess); break;
case DEV_SELECT: do_select(tp, &tty_mess); break;
case CANCEL: do_cancel(tp, &tty_mess); break;
default:
printf("Warning, TTY got unexpected request %d from %d\n",
tty_mess.m_type, tty_mess.m_source);
tty_reply(DEV_REVIVE, tty_mess.m_source, tty_mess.USER_ENDPT,
(cp_grant_id_t) tty_mess.IO_GRANT, EINVAL);
}
chardriver_process(&tty_tab, &tty_mess, ipc_status);
}
return 0;
@ -305,47 +271,41 @@ int main(void)
static void
set_color(tty_t *tp, int color)
{
message msg;
char buf[8];
buf[0] = '\033';
snprintf(&buf[1], sizeof(buf) - 1, "[1;%dm", color);
memset(&msg, 0, sizeof(msg));
msg.m_source = KERNEL;
msg.IO_GRANT = buf;
msg.COUNT = sizeof(buf);
do_write(tp, &msg);
do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf),
FLG_OP_NONBLOCK, 0);
}
static void
reset_color(tty_t *tp)
{
message msg;
char buf[8];
#define SGR_COLOR_RESET 39
buf[0] = '\033';
snprintf(&buf[1], sizeof(buf) - 1, "[0;%dm", SGR_COLOR_RESET);
memset(&msg, 0, sizeof(msg));
msg.m_source = KERNEL;
msg.IO_GRANT = buf;
msg.COUNT = sizeof(buf);
do_write(tp, &msg);
do_write(tp->tty_minor, 0, KERNEL, (cp_grant_id_t) buf, sizeof(buf),
FLG_OP_NONBLOCK, 0);
}
static tty_t *
line2tty(int line)
tty_t *
line2tty(devminor_t line)
{
/* Convert a terminal line to tty_table pointer */
tty_t* tp;
/* /dev/log goes to /dev/console, and both may be redirected. */
if (line == CONS_MINOR || line == LOG_MINOR)
line = consoleline;
if (line == KBD_MINOR || line == KBDAUX_MINOR || line == VIDEO_MINOR) {
return(NULL);
} else if ((line - CONS_MINOR) < NR_CONS) {
tp = tty_addr(line - CONS_MINOR);
} else if (line == LOG_MINOR) {
tp = tty_addr(consoleline);
} else if ((line - RS232_MINOR) < NR_RS_LINES) {
tp = tty_addr(line - RS232_MINOR + NR_CONS);
} else if ((line - TTYPX_MINOR) < NR_PTYS) {
@ -356,6 +316,9 @@ line2tty(int line)
tp = NULL;
}
if (tp != NULL && !tty_active(tp))
tp = NULL;
return(tp);
}
@ -430,7 +393,7 @@ set_console_line(char term[CONS_ARG])
}
/* Serial lines */
assert(NR_RS_LINES <= 9);/* bellow assumes this is the case */
assert(NR_RS_LINES <= 9);/* below assumes this is the case */
for (i = 0; i < NR_RS_LINES; i++) {
char sercons[6];
strlcpy(sercons, "tty00", sizeof(sercons));
@ -465,7 +428,6 @@ do_new_kmess(void)
static int prev_next = 0;
int next, bytes, copy, restore = 0;
tty_t *tp, rtp;
message print_kmsg;
assert(_minix_kerninfo);
kmess_ptr = _minix_kerninfo->kmessages;
@ -491,7 +453,7 @@ do_new_kmess(void)
}
tp = line2tty(consoleline);
if (tp == NULL || !tty_active(tp))
if (tp == NULL)
panic("Don't know where to send kernel messages");
if (tp->tty_outleft > 0) {
/* Terminal is already printing */
@ -502,11 +464,9 @@ do_new_kmess(void)
if (kernel_msg_color != 0)
set_color(tp, kernel_msg_color);
memset(&print_kmsg, 0, sizeof(print_kmsg));
print_kmsg.m_source = KERNEL;
print_kmsg.IO_GRANT = kernel_buf_copy;
print_kmsg.COUNT = bytes;
do_write(tp, &print_kmsg);
do_write(tp->tty_minor, 0, KERNEL,
(cp_grant_id_t) kernel_buf_copy, bytes,
FLG_OP_NONBLOCK, 0);
if (kernel_msg_color != 0)
reset_color(tp);
if (restore) {
@ -541,37 +501,33 @@ static void sef_cb_signal_handler(int signo)
/*===========================================================================*
* do_read *
*===========================================================================*/
static void do_read(tp, m_ptr)
register tty_t *tp; /* pointer to tty struct */
register message *m_ptr; /* pointer to message sent to the task */
static ssize_t do_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)
{
/* A process wants to read from a terminal. */
tty_t *tp;
int r;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
/* Check if there is already a process hanging in a read, check if the
* parameters are correct, do I/O.
*/
if (tp->tty_inleft > 0) {
r = EIO;
} else
if (m_ptr->COUNT <= 0) {
r = EINVAL;
} else if (tp->tty_ingrant != GRANT_INVALID) {
/* This is actually a fundamental problem with TTY; it can handle
* only one reader per minor device. If we don't return an error,
* we'll overwrite the previous reader and that process will get
* stuck forever. */
r = ENOBUFS;
} else {
/* Copy information from the message to the tty struct. */
tp->tty_incaller = m_ptr->m_source;
tp->tty_inproc = m_ptr->USER_ENDPT;
tp->tty_ingrant = (cp_grant_id_t) m_ptr->IO_GRANT;
tp->tty_inoffset = 0;
tp->tty_inleft = m_ptr->COUNT;
if (tp->tty_incaller != NONE || tp->tty_inleft > 0)
return EIO;
if (size <= 0)
return EINVAL;
if (!(tp->tty_termios.c_lflag & ICANON)
&& tp->tty_termios.c_cc[VTIME] > 0) {
/* Copy information from the message to the tty struct. */
tp->tty_incaller = endpt;
tp->tty_inid = id;
tp->tty_ingrant = grant;
assert(tp->tty_incum == 0);
tp->tty_inleft = size;
if (!(tp->tty_termios.c_lflag & ICANON) && tp->tty_termios.c_cc[VTIME] > 0) {
if (tp->tty_termios.c_cc[VMIN] == 0) {
/* MIN & TIME specify a read timer that finishes the
* read in TIME/10 seconds if no bytes are available.
@ -593,89 +549,90 @@ register message *m_ptr; /* pointer to message sent to the task */
in_transfer(tp);
/* ...then go back for more. */
handle_events(tp);
if (tp->tty_inleft == 0) {
return; /* already done */
}
if (tp->tty_inleft == 0)
return EDONTREPLY; /* already done */
/* There were no bytes in the input queue available. */
if (m_ptr->FLAGS & FLG_OP_NONBLOCK) {
if (flags & FLG_OP_NONBLOCK) {
tty_icancel(tp);
r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
tp->tty_inleft = tp->tty_incum = 0;
tp->tty_ingrant = GRANT_INVALID;
} else {
return; /* suspend the caller */
tp->tty_incaller = NONE;
return r;
}
}
tty_reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
if (tp->tty_select_ops)
select_retry(tp);
return EDONTREPLY; /* suspend the caller */
}
/*===========================================================================*
* do_write *
*===========================================================================*/
static void do_write(tp, m_ptr)
register tty_t *tp;
register message *m_ptr; /* pointer to message sent to the task */
static ssize_t do_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)
{
/* A process wants to write on a terminal. */
tty_t *tp;
int r;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
/* Check if there is already a process hanging in a write, check if the
* parameters are correct, do I/O.
*/
if (tp->tty_outleft > 0) {
r = EIO;
} else
if (m_ptr->COUNT <= 0) {
r = EINVAL;
} else {
if (tp->tty_outcaller != NONE || tp->tty_outleft > 0)
return EIO;
if (size <= 0)
return EINVAL;
/* Copy message parameters to the tty structure. */
tp->tty_outcaller = m_ptr->m_source;
tp->tty_outproc = m_ptr->USER_ENDPT;
tp->tty_outgrant = (cp_grant_id_t) m_ptr->IO_GRANT;
tp->tty_outoffset = 0;
tp->tty_outleft = m_ptr->COUNT;
tp->tty_outcaller = endpt;
tp->tty_outid = id;
tp->tty_outgrant = grant;
assert(tp->tty_outcum == 0);
tp->tty_outleft = size;
/* Try to write. */
handle_events(tp);
if (tp->tty_outleft == 0)
return; /* already done */
return EDONTREPLY; /* already done */
/* None or not all the bytes could be written. */
if (m_ptr->FLAGS & FLG_OP_NONBLOCK) {
if (flags & FLG_OP_NONBLOCK) {
r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
} else {
return; /* suspend the caller */
tp->tty_outcaller = NONE;
return r;
}
}
tty_reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
if (tp->tty_select_ops)
select_retry(tp);
return EDONTREPLY; /* suspend the caller */
}
/*===========================================================================*
* do_ioctl *
*===========================================================================*/
static void do_ioctl(tp, m_ptr)
register tty_t *tp;
message *m_ptr; /* pointer to message sent to task */
static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id)
{
/* Perform an IOCTL on this terminal. Posix termios calls are handled
* by the IOCTL system call
/* Perform an IOCTL on this terminal. POSIX termios calls are handled
* by the IOCTL system call.
*/
int r;
union {
int i;
} param;
tty_t *tp;
int i, r;
size_t size;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
/* Size of the ioctl parameter. */
switch (m_ptr->REQUEST) {
switch (request) {
case TCGETS: /* Posix tcgetattr function */
case TCSETS: /* Posix tcsetattr function, TCSANOW option */
case TCSETSW: /* Posix tcsetattr function, TCSADRAIN option */
@ -709,45 +666,41 @@ message *m_ptr; /* pointer to message sent to task */
}
r = OK;
switch (m_ptr->REQUEST) {
switch (request) {
case TCGETS:
/* Get the termios attributes. */
r = sys_safecopyto(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT, 0,
(vir_bytes) &tp->tty_termios, (vir_bytes) size);
r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
size);
break;
case TCSETSW:
case TCSETSF:
case TCDRAIN:
if (tp->tty_outleft > 0) {
if (m_ptr->FLAGS & FLG_OP_NONBLOCK) {
r = EAGAIN;
} else {
if (flags & FLG_OP_NONBLOCK)
return EAGAIN;
/* Wait for all ongoing output processing to finish. */
tp->tty_iocaller = m_ptr->m_source;
tp->tty_ioproc = m_ptr->USER_ENDPT;
tp->tty_ioreq = m_ptr->REQUEST;
tp->tty_iogrant = (cp_grant_id_t) m_ptr->IO_GRANT;
return;
tp->tty_iocaller = endpt;
tp->tty_ioid = id;
tp->tty_ioreq = request;
tp->tty_iogrant = grant;
return EDONTREPLY; /* suspend the caller */
}
break;
}
if (m_ptr->REQUEST == TCDRAIN) break;
if (m_ptr->REQUEST == TCSETSF) tty_icancel(tp);
if (request == TCDRAIN) break;
if (request == TCSETSF) tty_icancel(tp);
/*FALL THROUGH*/
case TCSETS:
/* Set the termios attributes. */
r = sys_safecopyfrom(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT,
0, (vir_bytes) &tp->tty_termios, (vir_bytes) size);
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_termios,
size);
if (r != OK) break;
setattr(tp);
break;
case TCFLSH:
r = sys_safecopyfrom(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT,
0, (vir_bytes) &param.i, (vir_bytes) size);
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, size);
if (r != OK) break;
switch (param.i) {
switch (i) {
case TCIFLUSH: tty_icancel(tp); break;
case TCOFLUSH: (*tp->tty_ocancel)(tp, 0); break;
case TCIOFLUSH: tty_icancel(tp); (*tp->tty_ocancel)(tp, 0); break;
@ -756,13 +709,12 @@ message *m_ptr; /* pointer to message sent to task */
break;
case TCFLOW:
r = sys_safecopyfrom(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT,
0, (vir_bytes) &param.i, (vir_bytes) size);
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &i, size);
if (r != OK) break;
switch (param.i) {
switch (i) {
case TCOOFF:
case TCOON:
tp->tty_inhibited = (param.i == TCOOFF);
tp->tty_inhibited = (i == TCOOFF);
tp->tty_events = 1;
break;
case TCIOFF:
@ -781,27 +733,24 @@ message *m_ptr; /* pointer to message sent to task */
break;
case TIOCGWINSZ:
r = sys_safecopyto(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT, 0,
(vir_bytes) &tp->tty_winsize, (vir_bytes) size);
r = sys_safecopyto(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
size);
break;
case TIOCSWINSZ:
r = sys_safecopyfrom(m_ptr->m_source, (cp_grant_id_t) m_ptr->IO_GRANT,
0, (vir_bytes) &tp->tty_winsize, (vir_bytes) size);
r = sys_safecopyfrom(endpt, grant, 0, (vir_bytes) &tp->tty_winsize,
size);
sigchar(tp, SIGWINCH, 0);
break;
case KIOCSMAP:
/* Load a new keymap (only /dev/console). */
if (isconsole(tp)) r = kbd_loadmap(m_ptr);
if (isconsole(tp)) r = kbd_loadmap(endpt, grant);
break;
case TIOCSFON_OLD:
printf("TTY: old TIOCSFON ignored.\n");
break;
case TIOCSFON:
/* Load a font into an EGA or VGA card (hs@hck.hr) */
if (isconsole(tp)) r = con_loadfont(m_ptr);
if (isconsole(tp)) r = con_loadfont(endpt, grant);
break;
/* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is
@ -813,30 +762,30 @@ message *m_ptr; /* pointer to message sent to task */
r = ENOTTY;
}
/* Send the reply. */
tty_reply(DEV_REVIVE, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
return r;
}
/*===========================================================================*
* do_open *
*===========================================================================*/
static void do_open(tp, m_ptr)
register tty_t *tp;
message *m_ptr; /* pointer to message sent to task */
static int do_open(devminor_t minor, int access, endpoint_t user_endpt)
{
/* A tty line has been opened. Make it the callers controlling tty if
* O_NOCTTY is *not* set and it is not the log device. 1 is returned if
* the tty is made the controlling tty, otherwise OK or an error code.
*/
tty_t *tp;
int r = OK;
if (m_ptr->DEVICE == LOG_MINOR) {
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
if (minor == LOG_MINOR) {
/* The log device is a write-only diagnostics device. */
if (m_ptr->COUNT & R_BIT) r = EACCES;
if (access & R_BIT) return EACCES;
} else {
if (!(m_ptr->COUNT & O_NOCTTY)) {
tp->tty_pgrp = m_ptr->USER_ENDPT;
if (!(access & O_NOCTTY)) {
tp->tty_pgrp = user_endpt;
r = 1;
}
tp->tty_openct++;
@ -845,20 +794,22 @@ message *m_ptr; /* pointer to message sent to task */
(*tp->tty_open)(tp, 0);
}
}
tty_reply(DEV_OPEN_REPL, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, r);
return r;
}
/*===========================================================================*
* do_close *
*===========================================================================*/
static void do_close(tp, m_ptr)
register tty_t *tp;
message *m_ptr; /* pointer to message sent to task */
static int do_close(devminor_t minor)
{
/* A tty line has been closed. Clean up the line if it is the last close. */
tty_t *tp;
if (m_ptr->DEVICE != LOG_MINOR && --tp->tty_openct == 0) {
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
if (minor != LOG_MINOR && --tp->tty_openct == 0) {
tp->tty_pgrp = 0;
tty_icancel(tp);
(*tp->tty_ocancel)(tp, 0);
@ -867,53 +818,49 @@ message *m_ptr; /* pointer to message sent to task */
tp->tty_winsize = winsize_defaults;
setattr(tp);
}
tty_reply(DEV_CLOSE_REPL, m_ptr->m_source, m_ptr->USER_ENDPT,
(cp_grant_id_t) m_ptr->IO_GRANT, OK);
return OK;
}
/*===========================================================================*
* do_cancel *
*===========================================================================*/
static void do_cancel(tp, m_ptr)
register tty_t *tp;
message *m_ptr; /* pointer to message sent to task */
static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id)
{
/* A signal has been sent to a process that is hanging trying to read or write.
* The pending read or write must be finished off immediately.
*/
endpoint_t proc_nr;
cp_grant_id_t grant;
int mode;
int r = EDONTREPLY;
tty_t *tp;
int r;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
/* Check the parameters carefully, to avoid cancelling twice. */
proc_nr = m_ptr->USER_ENDPT;
grant = (cp_grant_id_t) m_ptr->IO_GRANT;
mode = m_ptr->COUNT;
if ((mode & R_BIT) && tp->tty_inleft != 0 && proc_nr == tp->tty_inproc &&
tp->tty_ingrant == grant) {
r = EDONTREPLY;
if (tp->tty_inleft != 0 && endpt == tp->tty_incaller && id == tp->tty_inid) {
/* Process was reading when killed. Clean up input. */
tty_icancel(tp);
r = tp->tty_incum > 0 ? tp->tty_incum : EAGAIN;
tp->tty_inleft = tp->tty_incum = 0;
tp->tty_ingrant = GRANT_INVALID;
}
if ((mode & W_BIT) && tp->tty_outleft != 0 && proc_nr == tp->tty_outproc &&
tp->tty_outgrant == grant) {
tp->tty_incaller = NONE;
} else if (tp->tty_outleft != 0 && endpt == tp->tty_outcaller &&
id == tp->tty_outid) {
/* Process was writing when killed. Clean up output. */
r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN;
tp->tty_outleft = tp->tty_outcum = 0;
tp->tty_outgrant = GRANT_INVALID;
}
if (tp->tty_ioreq != 0 && proc_nr == tp->tty_ioproc) {
tp->tty_outcaller = NONE;
} else if (tp->tty_ioreq != 0 && endpt == tp->tty_iocaller &&
id == tp->tty_ioid) {
/* Process was waiting for output to drain. */
tp->tty_ioreq = 0;
r = EINTR;
tp->tty_ioreq = 0;
tp->tty_iocaller = NONE;
}
if (r != EDONTREPLY)
tp->tty_events = 1;
/* Only reply if we found a matching request. */
if (r != EDONTREPLY)
tty_reply(DEV_REVIVE, m_ptr->m_source, proc_nr, grant, r);
return r;
}
int select_try(struct tty *tp, int ops)
@ -955,14 +902,43 @@ int select_retry(struct tty *tp)
int ops;
if (tp->tty_select_ops && (ops = select_try(tp, tp->tty_select_ops))) {
assert(tp->tty_select_minor == tp->tty_minor);
select_reply(DEV_SEL_REPL2, tp->tty_select_proc, tp->tty_minor,
chardriver_reply_select(tp->tty_select_proc, tp->tty_minor,
ops);
tp->tty_select_ops &= ~ops;
}
return OK;
}
/*===========================================================================*
* do_select *
*===========================================================================*/
static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt)
{
tty_t *tp;
int ready_ops, watch;
if ((tp = line2tty(minor)) == NULL)
return ENXIO;
/* Translated minor numbers are a problem when sending late replies. */
if (tp->tty_minor != minor)
return EBADF;
watch = (ops & SEL_NOTIFY);
ops &= (SEL_RD | SEL_WR | SEL_ERR);
ready_ops = select_try(tp, ops);
ops &= ~ready_ops;
if (ops && watch) {
tp->tty_select_ops |= ops;
tp->tty_select_proc = endpt;
}
assert(tp->tty_minor == minor);
return ready_ops;
}
/*===========================================================================*
* handle_events *
*===========================================================================*/
@ -979,8 +955,8 @@ tty_t *tp; /* TTY to check for events. */
* to avoid swamping the TTY task. Messages may be overwritten when the
* lines are fast or when there are races between different lines, input
* and output, because MINIX only provides single buffering for interrupt
* messages (in proc.c). This is handled by explicitly checking each line
* for fresh input and completed output on each interrupt.
* messages. This is handled by explicitly checking each line for fresh input
* and completed output on each interrupt.
*/
do {
@ -1001,10 +977,9 @@ tty_t *tp; /* TTY to check for events. */
/* Reply if enough bytes are available. */
if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) {
tty_reply(DEV_REVIVE, tp->tty_incaller, tp->tty_inproc,
tp->tty_ingrant, tp->tty_incum);
chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
tp->tty_inleft = tp->tty_incum = 0;
tp->tty_ingrant = GRANT_INVALID;
tp->tty_incaller = NONE;
}
if (tp->tty_select_ops)
{
@ -1045,10 +1020,8 @@ register tty_t *tp; /* pointer to terminal to read from */
if (++bp == bufend(buf)) {
/* Temp buffer full, copy to user space. */
sys_safecopyto(tp->tty_incaller,
tp->tty_ingrant, tp->tty_inoffset,
(vir_bytes) buf,
(vir_bytes) buflen(buf));
tp->tty_inoffset += buflen(buf);
tp->tty_ingrant, tp->tty_incum,
(vir_bytes) buf, (vir_bytes) buflen(buf));
tp->tty_incum += buflen(buf);
bp = buf;
}
@ -1068,19 +1041,16 @@ register tty_t *tp; /* pointer to terminal to read from */
if (bp > buf) {
/* Leftover characters in the buffer. */
count = bp - buf;
sys_safecopyto(tp->tty_incaller,
tp->tty_ingrant, tp->tty_inoffset,
sys_safecopyto(tp->tty_incaller, tp->tty_ingrant, tp->tty_incum,
(vir_bytes) buf, (vir_bytes) count);
tp->tty_inoffset += count;
tp->tty_incum += count;
}
/* Usually reply to the reader, possibly even if incum == 0 (EOF). */
if (tp->tty_inleft == 0) {
tty_reply(DEV_REVIVE, tp->tty_incaller, tp->tty_inproc,
tp->tty_ingrant, tp->tty_incum);
chardriver_reply_task(tp->tty_incaller, tp->tty_inid, tp->tty_incum);
tp->tty_inleft = tp->tty_incum = 0;
tp->tty_ingrant = GRANT_INVALID;
tp->tty_incaller = NONE;
}
}
@ -1510,9 +1480,8 @@ tty_t *tp;
if (result == OK) setattr(tp);
}
tp->tty_ioreq = 0;
tty_reply(DEV_REVIVE, tp->tty_iocaller, tp->tty_ioproc, tp->tty_iogrant,
result);
tp->tty_iogrant = GRANT_INVALID;
chardriver_reply_task(tp->tty_iocaller, tp->tty_ioid, result);
tp->tty_iocaller = NONE;
}
/*===========================================================================*
@ -1571,57 +1540,6 @@ tty_t *tp;
(*tp->tty_ioctl)(tp, 0);
}
/*===========================================================================*
* tty_reply *
*===========================================================================*/
void
tty_reply_f(
file, line, code, replyee, proc_nr, grant, status)
char *file;
int line;
int code; /* DEV_OPEN_REPL, DEV_CLOSE_REPL, DEV_REVIVE */
endpoint_t replyee; /* destination address for the reply */
endpoint_t proc_nr; /* to whom should the reply go? */
cp_grant_id_t grant; /* which grant was involved? */
int status; /* reply code */
{
message m;
assert(code == DEV_OPEN_REPL || code == DEV_CLOSE_REPL || code == DEV_REVIVE);
/* Don't reply to KERNEL (kernel messages) */
if (replyee == KERNEL) return;
memset(&m, 0, sizeof(m));
m.REP_ENDPT = proc_nr;
m.REP_IO_GRANT = grant;
m.REP_STATUS = status;
status = _sendcall(replyee, code, &m);
if (status != OK)
printf("tty`tty_reply: send to %d failed: %d\n", replyee, status);
}
/*===========================================================================*
* select_reply *
*===========================================================================*/
void select_reply(int code, endpoint_t replyee, dev_t minor, int ops)
{
message m;
int status;
memset(&m, 0, sizeof(m));
m.DEV_MINOR = minor;
m.DEV_SEL_OPS = ops;
status = _sendcall(replyee, code, &m);
if (status != OK)
printf("tty`select_reply: send to %d failed: %d\n", replyee, status);
}
/*===========================================================================*
* sigchar *
*===========================================================================*/
@ -1696,7 +1614,7 @@ static void tty_init()
tp->tty_intail = tp->tty_inhead = tp->tty_inbuf;
tp->tty_min = 1;
tp->tty_ingrant = tp->tty_outgrant = tp->tty_iogrant = GRANT_INVALID;
tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE;
tp->tty_termios = termios_defaults;
tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close =
tp->tty_open = tty_devnop;
@ -1750,27 +1668,3 @@ int enable; /* set timer if true, otherwise unset */
cancel_timer(&tty_ptr->tty_tmr);
}
}
/*===========================================================================*
* do_select *
*===========================================================================*/
static void do_select(tp, m_ptr)
register tty_t *tp; /* pointer to tty struct */
register message *m_ptr; /* pointer to message sent to the task */
{
int ops, ready_ops = 0, watch;
ops = m_ptr->DEV_SEL_OPS & (SEL_RD|SEL_WR|SEL_ERR);
watch = (m_ptr->DEV_SEL_OPS & SEL_NOTIFY) ? 1 : 0;
ready_ops = select_try(tp, ops);
if (!ready_ops && ops && watch) {
tp->tty_select_ops |= ops;
tp->tty_select_proc = m_ptr->m_source;
tp->tty_select_minor = m_ptr->DEV_MINOR;
}
assert(tp->tty_minor == m_ptr->DEV_MINOR);
select_reply(DEV_SEL_REPL1, m_ptr->m_source, tp->tty_minor, ready_ops);
}

View file

@ -1,5 +1,6 @@
/* tty.h - Terminals */
#include <minix/chardriver.h>
#include <timers.h>
#undef lock
@ -34,7 +35,7 @@ typedef void(*devfunarg_t) (struct tty *tp, int c);
typedef struct tty {
int tty_events; /* set when TTY should inspect this line */
int tty_index; /* index into TTY table */
int tty_minor; /* device minor number */
devminor_t tty_minor; /* device minor number */
/* Input queue. Typed characters are stored here until read by a program. */
u16_t *tty_inhead; /* pointer to place where next char goes */
@ -61,27 +62,24 @@ typedef struct tty {
char tty_openct; /* count of number of opens of this tty */
/* Information about incomplete I/O requests is stored here. */
endpoint_t tty_incaller; /* process that made the call (usually VFS) */
endpoint_t tty_inproc; /* process that wants to read from tty */
endpoint_t tty_incaller; /* process that made the call, or NONE */
cdev_id_t tty_inid; /* ID of suspended read request */
cp_grant_id_t tty_ingrant; /* grant where data is to go */
vir_bytes tty_inoffset; /* offset into grant */
int tty_inleft; /* how many chars are still needed */
int tty_incum; /* # chars input so far */
endpoint_t tty_outcaller; /* process that made the call (usually VFS) */
endpoint_t tty_outproc; /* process that wants to write to tty */
size_t tty_inleft; /* how many chars are still needed */
size_t tty_incum; /* # chars input so far */
endpoint_t tty_outcaller; /* process that made the call, or NONE */
cdev_id_t tty_outid; /* ID of suspended write request */
cp_grant_id_t tty_outgrant; /* grant where data comes from */
vir_bytes tty_outoffset; /* offset into grant */
int tty_outleft; /* # chars yet to be output */
int tty_outcum; /* # chars output so far */
endpoint_t tty_iocaller; /* process that made the call (usually VFS) */
endpoint_t tty_ioproc; /* process that wants to do an ioctl */
int tty_ioreq; /* ioctl request code */
size_t tty_outleft; /* # chars yet to be output */
size_t tty_outcum; /* # chars output so far */
endpoint_t tty_iocaller; /* process that made the call, or NONE */
cdev_id_t tty_ioid; /* ID of suspended ioctl request */
unsigned int tty_ioreq; /* ioctl request code */
cp_grant_id_t tty_iogrant; /* virtual address of ioctl buffer or grant */
/* select() data */
int tty_select_ops; /* which operations are interesting */
endpoint_t tty_select_proc; /* which process wants notification */
dev_t tty_select_minor; /* sanity check only, can be removed */
/* Miscellaneous. */
devfun_t tty_ioctl; /* set line speed, etc. at the device level */
@ -135,14 +133,11 @@ extern struct kmessages kmess;
void handle_events(struct tty *tp);
void sigchar(struct tty *tp, int sig, int mayflush);
void tty_task(void);
tty_t *line2tty(devminor_t minor);
int in_process(struct tty *tp, char *buf, int count, int scode);
void out_process(struct tty *tp, char *bstart, char *bpos, char *bend,
int *icount, int *ocount);
void tty_wakeup(clock_t now);
#define tty_reply(c, r, p, g, s) tty_reply_f(__FILE__, __LINE__, (c), (r), (p), (g), (s))
void tty_reply_f(char *f, int l, int code, endpoint_t replyee,
endpoint_t proc_nr, cp_grant_id_t grant, int status);
void select_reply(int code, endpoint_t replyee, dev_t minor, int ops);
int select_try(struct tty *tp, int ops);
int select_retry(struct tty *tp);
@ -155,22 +150,21 @@ void kputc(int c);
void cons_stop(void);
void scr_init(struct tty *tp);
void toggle_scroll(void);
int con_loadfont(message *m);
int con_loadfont(endpoint_t endpt, cp_grant_id_t grant);
void select_console(int cons_line);
void beep_x( unsigned freq, clock_t dur);
void do_video(message *m);
void do_video(message *m, int ipc_status);
/* keyboard.c */
void kb_init(struct tty *tp);
void kb_init_once(void);
int kbd_loadmap(message *m);
int kbd_loadmap(endpoint_t endpt, cp_grant_id_t grant);
void do_fkey_ctl(message *m);
void kbd_interrupt(message *m);
void do_kbd(message *m);
void do_kbd(message *m, int ipc_status);
void do_kb_inject(message *m);
void do_kbdaux(message *m);
/* pty.c */
void do_pty(struct tty *tp, message *m_ptr);
void do_pty(message *m_ptr, int ipc_status);
void pty_init(struct tty *tp);
void select_retry_pty(struct tty *tp);

View file

@ -18,7 +18,7 @@ INCS+= acpi.h audio_fw.h bitmap.h \
reboot.h rs.h safecopies.h sched.h sef.h sffs.h \
sound.h spin.h sys_config.h sysinfo.h \
syslib.h sysutil.h termios.h timers.h type.h \
tty.h u64.h usb.h usb_ch9.h vbox.h \
u64.h usb.h usb_ch9.h vbox.h \
vboxfs.h vboxif.h vboxtype.h vm.h \
vfsif.h vtreefs.h libminixfs.h netsock.h \
virtio.h

View file

@ -1275,7 +1275,6 @@
#define TTY_FKEY_CONTROL (TTY_RQ_BASE + 1) /* control an F-key at TTY */
#define OLD_FKEY_CONTROL 98 /* previously used for TTY_FKEY_CONTROL */
# define FKEY_REQUEST m2_i1 /* request to perform at TTY */
# define FKEY_MAP 10 /* observe function key */
# define FKEY_UNMAP 11 /* stop observing function key */

View file

@ -1,28 +0,0 @@
#ifndef _MINIX_TTY_H
#define _MINIX_TTY_H
#include <sys/types.h>
#define TTYMAGIC 0xb105
/* A struct that the tty driver can use to pass values to the boot monitor.
* Currently only the value of the origin of the first vty (console), so the
* boot monitor can properly display it when panicing (tty isn't scheduled
* to switch to the first vty). It's written at the end of video memory
* (video memory base + video memory size - sizeof(struct boot_tty_info).
*/
struct boot_tty_info {
u16_t reserved[30]; /* reserved, set to 0 */
u16_t consorigin; /* origin in video memory of console */
u16_t conscursor; /* position of cursor of console */
u16_t flags; /* flags indicating which fields are valid */
u16_t magic; /* magic number indicating struct is valid */
};
#define BTIF_CONSORIGIN 0x01 /* consorigin is set */
#define BTIF_CONSCURSOR 0x02 /* conscursor is set */
#endif

View file

@ -22,7 +22,6 @@
#define TIOCSWINSZ _IOW('T', 17, struct winsize)
#define TIOCGPGRP _IOW('T', 18, int)
#define TIOCSPGRP _IOW('T', 19, int)
#define TIOCSFON_OLD _IOW('T', 20, u8_t [8192])
#define TIOCSFON _IOW_BIG(1, u8_t [8192])
/* Keyboard ioctls. */