You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
9.7 KiB
483 lines
9.7 KiB
/* $Id: termio.c,v 1.1.1.1 2006/09/14 01:59:06 root Exp $ */
|
|
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <queue.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <termio.h>
|
|
#include <unistd.h>
|
|
#include <setjmp.h>
|
|
#include <sys/ioctl.h>
|
|
#include <pmon/netio/netio.h>
|
|
#include <pmon.h>
|
|
|
|
int _read __P((int, char *, int));
|
|
int _write __P((int, char *, int));
|
|
int _open __P((char *, int, int));
|
|
int _close __P((int));
|
|
static void _chwrite __P((DevEntry *, char));
|
|
|
|
extern void gsignal __P((struct jmp_buf *, int sig));
|
|
extern ConfigEntry ConfigTable[];
|
|
|
|
DevEntry DevTable[DEV_MAX];
|
|
|
|
File _file[OPEN_MAX] =
|
|
{
|
|
#ifdef NETIO
|
|
{0, 1, -1},
|
|
{0, 1, -1},
|
|
{0, 1, -1}
|
|
#else
|
|
{0, 1},
|
|
{0, 1},
|
|
{0, 1}
|
|
#endif
|
|
};
|
|
|
|
typedef int tFunc __P((int, int, int, int));
|
|
|
|
struct TermEntry {
|
|
char *name;
|
|
tFunc *func;
|
|
};
|
|
|
|
struct TermEntry TermTable[] =
|
|
{
|
|
#if 0
|
|
{"tvi920", tvi920},
|
|
#endif
|
|
{"vt100", vt100},
|
|
{0}};
|
|
|
|
int *curlst; /* list of files open in the current context */
|
|
|
|
static void
|
|
setsane(DevEntry *p)
|
|
{
|
|
p->t.c_iflag |= (ISTRIP | ICRNL | IXON);
|
|
p->t.c_oflag = (ONLCR);
|
|
p->t.c_lflag = (ICANON | ISIG | ECHO | ECHOE);
|
|
p->t.c_cc[VINTR] = CNTRL ('c');
|
|
p->t.c_cc[VEOL] = '\n';
|
|
p->t.c_cc[VEOL2] = CNTRL ('c');
|
|
p->t.c_cc[VERASE] = CNTRL ('h');
|
|
p->t.c_cc[V_STOP] = CNTRL ('s');
|
|
p->t.c_cc[V_START] = CNTRL ('q');
|
|
}
|
|
|
|
void
|
|
reschedule (p)
|
|
char *p;
|
|
{
|
|
scandevs ();
|
|
}
|
|
|
|
#define reschedule(p) scandevs()
|
|
|
|
|
|
/** _write(fd,buf,n) write n bytes from buf to fd */
|
|
int
|
|
_write (fd, buf, n)
|
|
int fd, n;
|
|
char *buf;
|
|
{
|
|
int i;
|
|
DevEntry *p;
|
|
|
|
|
|
if (!_file[fd].valid)
|
|
return (-1);
|
|
|
|
#ifdef NETIO
|
|
if (_file[fd].netfd >= 0)
|
|
return netwrite (_file[fd].netfd, buf, n);
|
|
#endif
|
|
|
|
p = &DevTable[_file[fd].dev];
|
|
for (i = 0; i < n; i++) {
|
|
if (p->t.c_oflag & ONLCR && buf[i] == '\n') {
|
|
_chwrite (p, '\r');
|
|
}
|
|
_chwrite (p, buf[i]);
|
|
scandevs ();
|
|
}
|
|
return (i);
|
|
}
|
|
|
|
static void
|
|
_chwrite (p, ch)
|
|
DevEntry *p;
|
|
char ch;
|
|
{
|
|
|
|
while (p->txoff)
|
|
reschedule ("write txoff");
|
|
if (p->handler) {
|
|
while (!(*p->handler) (OP_TXRDY, p->sio, p->chan, NULL))
|
|
reschedule ("write txrdy");
|
|
(*p->handler) (OP_TX, p->sio, p->chan, ch);
|
|
#if 0
|
|
} else {
|
|
/* early error? use main console (and hope someone has programmed it!) */
|
|
ConfigEntry *q = &ConfigTable[0];
|
|
while (!(*q->handler) (OP_TXRDY, q->devinfo, q->chan))
|
|
continue;
|
|
(*q->handler) (OP_TX, q->devinfo, q->chan, ch);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
scandevs ()
|
|
{
|
|
int c, n;
|
|
DevEntry *p;
|
|
|
|
for (p = DevTable; p->rxq; p++) {
|
|
while ((*p->handler) (OP_RXRDY, p->sio, p->chan, NULL)) {
|
|
c = (*p->handler) (OP_RX, p->sio, p->chan, NULL);
|
|
if (p->t.c_iflag & ISTRIP)
|
|
c &= 0x7f;
|
|
if (p->t.c_lflag & ISIG) {
|
|
if (c == p->t.c_cc[VINTR]) {
|
|
gsignal (p->intr/*XXX*/, 2 /*SIGINT*/);
|
|
continue;
|
|
}
|
|
}
|
|
if (p->t.c_iflag & IXON) {
|
|
if (p->t.c_iflag & IXANY && p->txoff) {
|
|
p->txoff = 0;
|
|
continue;
|
|
}
|
|
if (c == p->t.c_cc[V_STOP]) {
|
|
p->txoff = 1;
|
|
continue;
|
|
}
|
|
if (c == p->t.c_cc[V_START]) {
|
|
p->txoff = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
n = Qspace (p->rxq);
|
|
if (n > 0) {
|
|
Qput (p->rxq, c);
|
|
if (n < 20 && !p->rxoff) {
|
|
(*p->handler) (OP_RXSTOP, p->sio, p->chan, p->rxoff = 1);
|
|
if (p->t.c_iflag & IXOFF)
|
|
_chwrite (p, CNTRL ('S'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
tgt_poll ();
|
|
#ifdef NETIO
|
|
tgt_netpoll ();
|
|
#endif
|
|
}
|
|
|
|
/** ioctl(fd,op,argp) perform control operation on fd */
|
|
|
|
int
|
|
ioctl (int fd, unsigned long op, ...)
|
|
{
|
|
DevEntry *p;
|
|
struct termio *at;
|
|
int i;
|
|
void *argp;
|
|
va_list ap;
|
|
|
|
va_start(ap, op);
|
|
argp = va_arg(ap, void *);
|
|
va_end(ap);
|
|
if (!_file[fd].valid)
|
|
return (-1);
|
|
|
|
#ifdef NETIO
|
|
if (_file[fd].netfd >= 0)
|
|
return netioctl (_file[fd].netfd, op, argp);
|
|
#endif
|
|
|
|
if (_file[fd].dev < 0)
|
|
return (-1);
|
|
p = &DevTable[_file[fd].dev];
|
|
|
|
switch (op) {
|
|
case TCGETA:
|
|
*(struct termio *)argp = p->t;
|
|
break;
|
|
case TCSETAF: /* after flush of input queue */
|
|
while (!Qempty (p->rxq))
|
|
Qget (p->rxq);
|
|
(*p->handler) (OP_FLUSH, p->sio, p->chan, 1);
|
|
if (p->rxoff) {
|
|
(*p->handler) (OP_RXSTOP, p->sio, p->chan, p->rxoff = 0);
|
|
if (p->t.c_iflag & IXOFF)
|
|
_chwrite (p, CNTRL ('Q'));
|
|
}
|
|
case TCSETAW: /* after write */
|
|
/* no txq, so no delay needed */
|
|
at = (struct termio *)argp;
|
|
if (p->t.c_ispeed != at->c_ispeed) { /* XXX FIX Split speed */
|
|
if ((*p->handler) (OP_BAUD, p->sio, p->freq, at->c_ispeed))
|
|
return (-1);
|
|
}
|
|
p->t = *at;
|
|
break;
|
|
case FIONREAD:
|
|
scandevs ();
|
|
*(int *)argp = Qused (p->rxq);
|
|
break;
|
|
case SETINTR:
|
|
p->intr = (struct jmp_buf *) argp;
|
|
break;
|
|
case GETINTR:
|
|
*(struct jmp_buf **) argp = p->intr;
|
|
break;
|
|
case SETSANE:
|
|
(*p->handler) (OP_RESET, p->sio, p->chan, 0);
|
|
setsane(p);
|
|
break;
|
|
case SETNCNE:
|
|
if (argp)
|
|
*(struct termio *)argp = p->t;
|
|
p->t.c_lflag &= ~(ICANON | ECHO | ECHOE);
|
|
p->t.c_cc[4] = 1;
|
|
break;
|
|
case CBREAK:
|
|
if (argp)
|
|
*(struct termio *)argp = p->t;
|
|
p->t.c_lflag &= ~(ICANON | ECHO);
|
|
p->t.c_cc[4] = 1;
|
|
break;
|
|
case GETTERM:
|
|
*(int *)argp = 0;
|
|
if (p->tfunc == 0)
|
|
return (-1);
|
|
strcpy ((char *)argp, p->tname);
|
|
break;
|
|
case SETTERM:
|
|
for (i = 0; TermTable[i].name; i++) {
|
|
if (!strcmp(argp, TermTable[i].name))
|
|
break;
|
|
}
|
|
if (TermTable[i].name == 0)
|
|
return (-1);
|
|
p->tname = TermTable[i].name;
|
|
p->tfunc = TermTable[i].func;
|
|
break;
|
|
case TERMTYPE:
|
|
if (TermTable[fd].name == 0)
|
|
return (-1);
|
|
strcpy ((char *)argp, TermTable[fd].name);
|
|
return (fd + 1);
|
|
default:
|
|
return (-1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
devinit ()
|
|
{
|
|
int i, brate;
|
|
ConfigEntry *q;
|
|
DevEntry *p;
|
|
char dname[5];
|
|
char *s;
|
|
|
|
strcpy (dname, "ttyx");
|
|
for (i = 0; ConfigTable[i].devinfo && i < DEV_MAX; i++) {
|
|
q = &ConfigTable[i];
|
|
p = &DevTable[i];
|
|
p->txoff = 0;
|
|
p->rxoff = 0;
|
|
if (q->chan == 0)
|
|
(*q->handler) (OP_INIT, q->devinfo, 0, q->rxqsize);
|
|
p->qsize = q->rxqsize;
|
|
p->rxq = Qcreate (p->qsize);
|
|
if (p->rxq == 0)
|
|
return (-1);
|
|
dname[3] = (i < 10) ? i + '0' : i - 10 + 'a';
|
|
|
|
if (!(s = getenv (dname)) || (brate = getbaudrate (s)) == 0)
|
|
brate = q->brate;
|
|
|
|
/* set default baudrate in case anything goes wrong */
|
|
(void) (*q->handler) (OP_BAUD, q->devinfo, q->freq, q->brate);
|
|
|
|
/*
|
|
* program requested baud rate, but fall back to default
|
|
* if there is a problem
|
|
*/
|
|
if (brate != q->brate) {
|
|
if ((*q->handler) (OP_BAUD, q->devinfo, q->freq, brate))
|
|
brate = q->brate;
|
|
}
|
|
|
|
p->sio = q->devinfo;
|
|
p->chan = q->chan;
|
|
p->handler = q->handler;
|
|
p->intr = 0;
|
|
p->tfunc = 0;
|
|
p->t.c_cflag = 0;
|
|
p->t.c_ispeed = brate;
|
|
p->t.c_ospeed = brate;
|
|
setsane(p);
|
|
_chwrite (p, CNTRL ('Q'));
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#if 0
|
|
/** ttctl(fd,op,a1,a2) perform terminal specific operation */
|
|
static int
|
|
ttctl (fd, op, a1, a2)
|
|
int fd, op, a1, a2;
|
|
{
|
|
DevEntry *p;
|
|
int r;
|
|
|
|
if (!_file[fd].valid || _file[fd].dev < 0)
|
|
return (-1);
|
|
|
|
p = &DevTable[_file[fd].dev];
|
|
if (p->tfunc == 0)
|
|
return (-1);
|
|
r = (*p->tfunc) (fd, op, a1, a2);
|
|
return (r);
|
|
}
|
|
#endif
|
|
|
|
/** _open(fname,mode,perms) return fd for fname */
|
|
int
|
|
_open (fname, mode, perms)
|
|
char *fname;
|
|
int mode, perms;
|
|
{
|
|
int i, c, dev;
|
|
char *dname;
|
|
|
|
for (i = 0; i < OPEN_MAX && _file[i].valid; i++);
|
|
if (i == OPEN_MAX)
|
|
return (-1);
|
|
|
|
dname = fname;
|
|
if (strncmp (dname, "/dev/", 5) == 0)
|
|
dname += 5;
|
|
|
|
if (strlen (dname) == 4 && strncmp (dname, "tty", 3) == 0) {
|
|
c = dname[3];
|
|
if (c >= 'a' && c <= 'z')
|
|
dev = c - 'a';
|
|
else if (c >= 'A' && c <= 'Z')
|
|
dev = c - 'A';
|
|
else if (c >= '0' && c <= '9')
|
|
dev = c - '0';
|
|
if (dev >= DEV_MAX || DevTable[dev].rxq == 0)
|
|
return (-1);
|
|
_file[i].dev = dev;
|
|
#ifdef NETIO
|
|
_file[i].netfd = -1;
|
|
#endif
|
|
_file[i].valid = 1;
|
|
{
|
|
DevEntry *p = &DevTable[dev];
|
|
(*p->handler) (OP_OPEN, p->sio, p->chan, 0);
|
|
}
|
|
ioctl (i, SETSANE);
|
|
} else {
|
|
#ifdef NETIO
|
|
if ((_file[i].netfd = netopen (fname, mode)) < 0)
|
|
return (-1);
|
|
_file[i].dev = -1;
|
|
_file[i].valid = 1;
|
|
#else
|
|
return (-1);
|
|
#endif
|
|
}
|
|
if (curlst)
|
|
*curlst |= (1 << i);
|
|
return (i);
|
|
}
|
|
|
|
/** _close(fd) close fd */
|
|
int
|
|
_close (fd)
|
|
int fd;
|
|
{
|
|
if (_file[fd].valid) {
|
|
#ifdef NETIO
|
|
if (_file[fd].netfd >= 0) {
|
|
netclose (_file[fd].netfd);
|
|
_file[fd].netfd = -1;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
DevEntry *p = &DevTable[_file[fd].dev];
|
|
(*p->handler) (OP_CLOSE, p->sio, p->chan, 0);
|
|
}
|
|
_file[fd].valid = 0;
|
|
if (curlst)
|
|
*curlst &= ~(1 << fd);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
/** _read(fd,buf,n) read n bytes into buf from fd */
|
|
int
|
|
_read (fd, buf, n)
|
|
int fd, n;
|
|
char *buf;
|
|
{
|
|
int i, used;
|
|
DevEntry *p;
|
|
char ch;
|
|
|
|
if (!_file[fd].valid)
|
|
return (-1);
|
|
|
|
#ifdef NETIO
|
|
if (_file[fd].netfd >= 0)
|
|
return netread (_file[fd].netfd, buf, n);
|
|
#endif
|
|
|
|
p = &DevTable[_file[fd].dev];
|
|
for (i = 0; i < n;) {
|
|
scandevs ();
|
|
while ((used = Qused (p->rxq)) == 0)
|
|
reschedule ("read Qempty");
|
|
if (used < 20 && p->rxoff) {
|
|
(*p->handler) (OP_RXSTOP, p->sio, p->chan, p->rxoff = 0);
|
|
if (p->t.c_iflag & IXOFF)
|
|
_chwrite (p, CNTRL ('Q'));
|
|
}
|
|
ch = Qget (p->rxq);
|
|
if (p->t.c_iflag & ICRNL && ch == '\r')
|
|
ch = '\n';
|
|
if (p->t.c_lflag & ICANON) {
|
|
if (ch == p->t.c_cc[VERASE]) {
|
|
if (i > 0) {
|
|
i--;
|
|
if (p->t.c_lflag & ECHOE)
|
|
write (fd, "\b \b", 3);
|
|
else if (p->t.c_lflag & ECHO)
|
|
write (fd, "\b", 1);
|
|
}
|
|
continue;
|
|
}
|
|
if (p->t.c_lflag & ECHO)
|
|
write (fd, &ch, 1);
|
|
buf[i++] = ch;
|
|
if (ch == p->t.c_cc[VEOL] || ch == p->t.c_cc[VEOL2])
|
|
break;
|
|
} else {
|
|
buf[i++] = ch;
|
|
}
|
|
}
|
|
return (i);
|
|
}
|
|
|