लिनक्स/linux-0.01.tar/kernel/tty io.c
< लिनक्स | linux-0.01.tar
/* * '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); }