aboutsummaryrefslogtreecommitdiff
path: root/linux/kernel/tty_io.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/kernel/tty_io.c')
-rw-r--r--linux/kernel/tty_io.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/linux/kernel/tty_io.c b/linux/kernel/tty_io.c
new file mode 100644
index 0000000..68a390c
--- /dev/null
+++ b/linux/kernel/tty_io.c
@@ -0,0 +1,306 @@
+/*
+ * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
+ * or rs-channels. It also implements echoing, cooked mode etc (well,
+ * not currently, but ...)
+ */
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+
+#define ALRMMASK (1<<(SIGALRM-1))
+
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)
+#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)
+#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)
+
+#define L_CANON(tty) _L_FLAG((tty),ICANON)
+#define L_ISIG(tty) _L_FLAG((tty),ISIG)
+#define L_ECHO(tty) _L_FLAG((tty),ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
+#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
+#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
+
+#define I_UCLC(tty) _I_FLAG((tty),IUCLC)
+#define I_NLCR(tty) _I_FLAG((tty),INLCR)
+#define I_CRNL(tty) _I_FLAG((tty),ICRNL)
+#define I_NOCR(tty) _I_FLAG((tty),IGNCR)
+
+#define O_POST(tty) _O_FLAG((tty),OPOST)
+#define O_NLCR(tty) _O_FLAG((tty),ONLCR)
+#define O_CRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_NLRET(tty) _O_FLAG((tty),ONLRET)
+#define O_LCUC(tty) _O_FLAG((tty),OLCUC)
+
+struct tty_struct tty_table[] = {
+ {
+ {0,
+ OPOST|ONLCR, /* change outgoing NL to CRNL */
+ 0,
+ ICANON | ECHO | ECHOCTL | ECHOKE,
+ 0, /* console termio */
+ INIT_C_CC},
+ 0, /* initial pgrp */
+ 0, /* initial stopped */
+ con_write,
+ {0,0,0,0,""}, /* console read-queue */
+ {0,0,0,0,""}, /* console write-queue */
+ {0,0,0,0,""} /* console secondary queue */
+ },{
+ {0, /*IGNCR*/
+ OPOST | ONLRET, /* change outgoing NL to CR */
+ B2400 | CS8,
+ 0,
+ 0,
+ INIT_C_CC},
+ 0,
+ 0,
+ rs_write,
+ {0x3f8,0,0,0,""}, /* rs 1 */
+ {0x3f8,0,0,0,""},
+ {0,0,0,0,""}
+ },{
+ {0, /*IGNCR*/
+ OPOST | ONLRET, /* change outgoing NL to CR */
+ B2400 | CS8,
+ 0,
+ 0,
+ INIT_C_CC},
+ 0,
+ 0,
+ rs_write,
+ {0x2f8,0,0,0,""}, /* rs 2 */
+ {0x2f8,0,0,0,""},
+ {0,0,0,0,""}
+ }
+};
+
+/*
+ * these are the tables used by the machine code handlers.
+ * you can implement pseudo-tty's or something by changing
+ * them. Currently not done.
+ */
+struct tty_queue * table_list[]={
+ &tty_table[0].read_q, &tty_table[0].write_q,
+ &tty_table[1].read_q, &tty_table[1].write_q,
+ &tty_table[2].read_q, &tty_table[2].write_q
+ };
+
+void tty_init(void)
+{
+ rs_init();
+ con_init();
+}
+
+void tty_intr(struct tty_struct * tty, int signal)
+{
+ int i;
+
+ if (tty->pgrp <= 0)
+ return;
+ for (i=0;i<NR_TASKS;i++)
+ if (task[i] && task[i]->pgrp==tty->pgrp)
+ task[i]->signal |= 1<<(signal-1);
+}
+
+static void sleep_if_empty(struct tty_queue * queue)
+{
+ cli();
+ while (!current->signal && EMPTY(*queue))
+ interruptible_sleep_on(&queue->proc_list);
+ sti();
+}
+
+static void sleep_if_full(struct tty_queue * queue)
+{
+ if (!FULL(*queue))
+ return;
+ cli();
+ while (!current->signal && LEFT(*queue)<128)
+ interruptible_sleep_on(&queue->proc_list);
+ sti();
+}
+
+void copy_to_cooked(struct tty_struct * tty)
+{
+ signed char c;
+
+ while (!EMPTY(tty->read_q) && !FULL(tty->secondary)) {
+ GETCH(tty->read_q,c);
+ if (c==13)
+ if (I_CRNL(tty))
+ c=10;
+ else if (I_NOCR(tty))
+ continue;
+ else ;
+ else if (c==10 && I_NLCR(tty))
+ c=13;
+ if (I_UCLC(tty))
+ c=tolower(c);
+ if (L_CANON(tty)) {
+ if (c==ERASE_CHAR(tty)) {
+ if (EMPTY(tty->secondary) ||
+ (c=LAST(tty->secondary))==10 ||
+ c==EOF_CHAR(tty))
+ continue;
+ if (L_ECHO(tty)) {
+ if (c<32)
+ PUTCH(127,tty->write_q);
+ PUTCH(127,tty->write_q);
+ tty->write(tty);
+ }
+ DEC(tty->secondary.head);
+ continue;
+ }
+ if (c==STOP_CHAR(tty)) {
+ tty->stopped=1;
+ continue;
+ }
+ if (c==START_CHAR(tty)) {
+ tty->stopped=0;
+ continue;
+ }
+ }
+ if (!L_ISIG(tty)) {
+ if (c==INTR_CHAR(tty)) {
+ tty_intr(tty,SIGINT);
+ continue;
+ }
+ }
+ if (c==10 || c==EOF_CHAR(tty))
+ tty->secondary.data++;
+ if (L_ECHO(tty)) {
+ if (c==10) {
+ PUTCH(10,tty->write_q);
+ PUTCH(13,tty->write_q);
+ } else if (c<32) {
+ if (L_ECHOCTL(tty)) {
+ PUTCH('^',tty->write_q);
+ PUTCH(c+64,tty->write_q);
+ }
+ } else
+ PUTCH(c,tty->write_q);
+ tty->write(tty);
+ }
+ PUTCH(c,tty->secondary);
+ }
+ wake_up(&tty->secondary.proc_list);
+}
+
+int tty_read(unsigned channel, char * buf, int nr)
+{
+ struct tty_struct * tty;
+ char c, * b=buf;
+ int minimum,time,flag=0;
+ long oldalarm;
+
+ if (channel>2 || nr<0) return -1;
+ tty = &tty_table[channel];
+ oldalarm = current->alarm;
+ time = (unsigned) 10*tty->termios.c_cc[VTIME];
+ minimum = (unsigned) tty->termios.c_cc[VMIN];
+ if (time && !minimum) {
+ minimum=1;
+ if (flag=(!oldalarm || time+jiffies<oldalarm))
+ current->alarm = time+jiffies;
+ }
+ if (minimum>nr)
+ minimum=nr;
+ while (nr>0) {
+ if (flag && (current->signal & ALRMMASK)) {
+ current->signal &= ~ALRMMASK;
+ break;
+ }
+ if (current->signal)
+ break;
+ if (EMPTY(tty->secondary) || (L_CANON(tty) &&
+ !tty->secondary.data && LEFT(tty->secondary)>20)) {
+ sleep_if_empty(&tty->secondary);
+ continue;
+ }
+ do {
+ GETCH(tty->secondary,c);
+ if (c==EOF_CHAR(tty) || c==10)
+ tty->secondary.data--;
+ if (c==EOF_CHAR(tty) && L_CANON(tty))
+ return (b-buf);
+ else {
+ put_fs_byte(c,b++);
+ if (!--nr)
+ break;
+ }
+ } while (nr>0 && !EMPTY(tty->secondary));
+ if (time && !L_CANON(tty))
+ if (flag=(!oldalarm || time+jiffies<oldalarm))
+ current->alarm = time+jiffies;
+ else
+ current->alarm = oldalarm;
+ if (L_CANON(tty)) {
+ if (b-buf)
+ break;
+ } else if (b-buf >= minimum)
+ break;
+ }
+ current->alarm = oldalarm;
+ if (current->signal && !(b-buf))
+ return -EINTR;
+ return (b-buf);
+}
+
+int tty_write(unsigned channel, char * buf, int nr)
+{
+ static cr_flag=0;
+ struct tty_struct * tty;
+ char c, *b=buf;
+
+ if (channel>2 || nr<0) return -1;
+ tty = channel + tty_table;
+ while (nr>0) {
+ sleep_if_full(&tty->write_q);
+ if (current->signal)
+ break;
+ while (nr>0 && !FULL(tty->write_q)) {
+ c=get_fs_byte(b);
+ if (O_POST(tty)) {
+ if (c=='\r' && O_CRNL(tty))
+ c='\n';
+ else if (c=='\n' && O_NLRET(tty))
+ c='\r';
+ if (c=='\n' && !cr_flag && O_NLCR(tty)) {
+ cr_flag = 1;
+ PUTCH(13,tty->write_q);
+ continue;
+ }
+ if (O_LCUC(tty))
+ c=toupper(c);
+ }
+ b++; nr--;
+ cr_flag = 0;
+ PUTCH(c,tty->write_q);
+ }
+ tty->write(tty);
+ if (nr>0)
+ schedule();
+ }
+ return (b-buf);
+}
+
+/*
+ * Jeh, sometimes I really like the 386.
+ * This routine is called from an interrupt,
+ * and there should be absolutely no problem
+ * with sleeping even in an interrupt (I hope).
+ * Of course, if somebody proves me wrong, I'll
+ * hate intel for all time :-). We'll have to
+ * be careful and see to reinstating the interrupt
+ * chips before calling this, though.
+ */
+void do_tty_interrupt(int tty)
+{
+ copy_to_cooked(tty_table+tty);
+}