initial version
This commit is contained in:
484
Book-Lite/linux-0.12/kernel/chr_drv/tty_io.c
Normal file
484
Book-Lite/linux-0.12/kernel/chr_drv/tty_io.c
Normal file
@@ -0,0 +1,484 @@
|
||||
/*
|
||||
* linux/kernel/tty_io.c
|
||||
*
|
||||
* (C) 1991 Linus Torvalds
|
||||
*/
|
||||
|
||||
/*
|
||||
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
|
||||
* or rs-channels. It also implements echoing, cooked mode etc.
|
||||
*
|
||||
* Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define ALRMMASK (1<<(SIGALRM-1))
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tty.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
int kill_pg(int pgrp, int sig, int priv);
|
||||
int is_orphaned_pgrp(int pgrp);
|
||||
|
||||
#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 L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
|
||||
|
||||
#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 I_IXON(tty) _I_FLAG((tty),IXON)
|
||||
|
||||
#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)
|
||||
|
||||
#define C_SPEED(tty) ((tty)->termios.c_cflag & CBAUD)
|
||||
#define C_HUP(tty) (C_SPEED((tty)) == B0)
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define QUEUES (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
|
||||
static struct tty_queue tty_queues[QUEUES];
|
||||
struct tty_struct tty_table[256];
|
||||
|
||||
#define con_queues tty_queues
|
||||
#define rs_queues ((3*MAX_CONSOLES) + tty_queues)
|
||||
#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
|
||||
#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)
|
||||
|
||||
#define con_table tty_table
|
||||
#define rs_table (64+tty_table)
|
||||
#define mpty_table (128+tty_table)
|
||||
#define spty_table (192+tty_table)
|
||||
|
||||
int fg_console = 0;
|
||||
|
||||
/*
|
||||
* these are the tables used by the machine code handlers.
|
||||
* you can implement virtual consoles.
|
||||
*/
|
||||
struct tty_queue * table_list[]={
|
||||
con_queues + 0, con_queues + 1,
|
||||
rs_queues + 0, rs_queues + 1,
|
||||
rs_queues + 3, rs_queues + 4
|
||||
};
|
||||
|
||||
void change_console(unsigned int new_console)
|
||||
{
|
||||
if (new_console == fg_console || new_console >= NR_CONSOLES)
|
||||
return;
|
||||
fg_console = new_console;
|
||||
table_list[0] = con_queues + 0 + fg_console*3;
|
||||
table_list[1] = con_queues + 1 + fg_console*3;
|
||||
update_screen();
|
||||
}
|
||||
|
||||
static void sleep_if_empty(struct tty_queue * queue)
|
||||
{
|
||||
cli();
|
||||
while (!(current->signal & ~current->blocked) && 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 & ~current->blocked) && LEFT(queue)<128)
|
||||
interruptible_sleep_on(&queue->proc_list);
|
||||
sti();
|
||||
}
|
||||
|
||||
void wait_for_keypress(void)
|
||||
{
|
||||
sleep_if_empty(tty_table[fg_console].secondary);
|
||||
}
|
||||
|
||||
void copy_to_cooked(struct tty_struct * tty)
|
||||
{
|
||||
signed char c;
|
||||
|
||||
if (!(tty->read_q || tty->write_q || tty->secondary)) {
|
||||
printk("copy_to_cooked: missing queues\n\r");
|
||||
return;
|
||||
}
|
||||
while (1) {
|
||||
if (EMPTY(tty->read_q))
|
||||
break;
|
||||
if (FULL(tty->secondary))
|
||||
break;
|
||||
GETCH(tty->read_q,c);
|
||||
if (c==13) {
|
||||
if (I_CRNL(tty))
|
||||
c=10;
|
||||
else if (I_NOCR(tty))
|
||||
continue;
|
||||
} else if (c==10 && I_NLCR(tty))
|
||||
c=13;
|
||||
if (I_UCLC(tty))
|
||||
c=tolower(c);
|
||||
if (L_CANON(tty)) {
|
||||
if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==KILL_CHAR(tty))) {
|
||||
/* deal with killing the input line */
|
||||
while(!(EMPTY(tty->secondary) ||
|
||||
(c=LAST(tty->secondary))==10 ||
|
||||
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==EOF_CHAR(tty))))) {
|
||||
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 ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==ERASE_CHAR(tty))) {
|
||||
if (EMPTY(tty->secondary) ||
|
||||
(c=LAST(tty->secondary))==10 ||
|
||||
((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(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 (I_IXON(tty)) {
|
||||
if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==STOP_CHAR(tty))) {
|
||||
tty->stopped=1;
|
||||
tty->write(tty);
|
||||
continue;
|
||||
}
|
||||
if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==START_CHAR(tty))) {
|
||||
tty->stopped=0;
|
||||
tty->write(tty);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (L_ISIG(tty)) {
|
||||
if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==INTR_CHAR(tty))) {
|
||||
kill_pg(tty->pgrp, SIGINT, 1);
|
||||
continue;
|
||||
}
|
||||
if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==QUIT_CHAR(tty))) {
|
||||
kill_pg(tty->pgrp, SIGQUIT, 1);
|
||||
continue;
|
||||
}
|
||||
if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
|
||||
(c==SUSPEND_CHAR(tty))) {
|
||||
if (!is_orphaned_pgrp(tty->pgrp))
|
||||
kill_pg(tty->pgrp, SIGTSTP, 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when we need to send a SIGTTIN or SIGTTOU to our process
|
||||
* group
|
||||
*
|
||||
* We only request that a system call be restarted if there was if the
|
||||
* default signal handler is being used. The reason for this is that if
|
||||
* a job is catching SIGTTIN or SIGTTOU, the signal handler may not want
|
||||
* the system call to be restarted blindly. If there is no way to reset the
|
||||
* terminal pgrp back to the current pgrp (perhaps because the controlling
|
||||
* tty has been released on logout), we don't want to be in an infinite loop
|
||||
* while restarting the system call, and have it always generate a SIGTTIN
|
||||
* or SIGTTOU. The default signal handler will cause the process to stop
|
||||
* thus avoiding the infinite loop problem. Presumably the job-control
|
||||
* cognizant parent will fix things up before continuging its child process.
|
||||
*/
|
||||
int tty_signal(int sig, struct tty_struct *tty)
|
||||
{
|
||||
if (is_orphaned_pgrp(current->pgrp))
|
||||
return -EIO; /* don't stop an orphaned pgrp */
|
||||
(void) kill_pg(current->pgrp,sig,1);
|
||||
if ((current->blocked & (1<<(sig-1))) ||
|
||||
((int) current->sigaction[sig-1].sa_handler == 1))
|
||||
return -EIO; /* Our signal will be ignored */
|
||||
else if (current->sigaction[sig-1].sa_handler)
|
||||
return -EINTR; /* We _will_ be interrupted :-) */
|
||||
else
|
||||
return -ERESTARTSYS; /* We _will_ be interrupted :-) */
|
||||
/* (but restart after we continue) */
|
||||
}
|
||||
|
||||
int tty_read(unsigned channel, char * buf, int nr)
|
||||
{
|
||||
struct tty_struct * tty;
|
||||
struct tty_struct * other_tty = NULL;
|
||||
char c, * b=buf;
|
||||
int minimum,time;
|
||||
|
||||
if (channel > 255)
|
||||
return -EIO;
|
||||
tty = TTY_TABLE(channel);
|
||||
if (!(tty->write_q || tty->read_q || tty->secondary))
|
||||
return -EIO;
|
||||
if ((current->tty == channel) && (tty->pgrp != current->pgrp))
|
||||
return(tty_signal(SIGTTIN, tty));
|
||||
if (channel & 0x80)
|
||||
other_tty = tty_table + (channel ^ 0x40);
|
||||
time = 10L*tty->termios.c_cc[VTIME];
|
||||
minimum = tty->termios.c_cc[VMIN];
|
||||
if (L_CANON(tty)) {
|
||||
minimum = nr;
|
||||
current->timeout = 0xffffffff;
|
||||
time = 0;
|
||||
} else if (minimum)
|
||||
current->timeout = 0xffffffff;
|
||||
else {
|
||||
minimum = nr;
|
||||
if (time)
|
||||
current->timeout = time + jiffies;
|
||||
time = 0;
|
||||
}
|
||||
if (minimum>nr)
|
||||
minimum = nr;
|
||||
while (nr>0) {
|
||||
if (other_tty)
|
||||
other_tty->write(other_tty);
|
||||
cli();
|
||||
if (EMPTY(tty->secondary) || (L_CANON(tty) &&
|
||||
!FULL(tty->read_q) && !tty->secondary->data)) {
|
||||
if (!current->timeout ||
|
||||
(current->signal & ~current->blocked)) {
|
||||
sti();
|
||||
break;
|
||||
}
|
||||
if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
|
||||
break;
|
||||
interruptible_sleep_on(&tty->secondary->proc_list);
|
||||
sti();
|
||||
continue;
|
||||
}
|
||||
sti();
|
||||
do {
|
||||
GETCH(tty->secondary,c);
|
||||
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
|
||||
c==EOF_CHAR(tty)) || c==10)
|
||||
tty->secondary->data--;
|
||||
if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
|
||||
c==EOF_CHAR(tty)) && L_CANON(tty))
|
||||
break;
|
||||
else {
|
||||
put_fs_byte(c,b++);
|
||||
if (!--nr)
|
||||
break;
|
||||
}
|
||||
if (c==10 && L_CANON(tty))
|
||||
break;
|
||||
} while (nr>0 && !EMPTY(tty->secondary));
|
||||
wake_up(&tty->read_q->proc_list);
|
||||
if (time)
|
||||
current->timeout = time+jiffies;
|
||||
if (L_CANON(tty) || b-buf >= minimum)
|
||||
break;
|
||||
}
|
||||
current->timeout = 0;
|
||||
if ((current->signal & ~current->blocked) && !(b-buf))
|
||||
return -ERESTARTSYS;
|
||||
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 > 255)
|
||||
return -EIO;
|
||||
tty = TTY_TABLE(channel);
|
||||
if (!(tty->write_q || tty->read_q || tty->secondary))
|
||||
return -EIO;
|
||||
if (L_TOSTOP(tty) &&
|
||||
(current->tty == channel) && (tty->pgrp != current->pgrp))
|
||||
return(tty_signal(SIGTTOU, tty));
|
||||
while (nr>0) {
|
||||
sleep_if_full(tty->write_q);
|
||||
if (current->signal & ~current->blocked)
|
||||
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.
|
||||
*
|
||||
* I don't think we sleep here under normal circumstances
|
||||
* anyway, which is good, as the task sleeping might be
|
||||
* totally innocent.
|
||||
*/
|
||||
void do_tty_interrupt(int tty)
|
||||
{
|
||||
copy_to_cooked(TTY_TABLE(tty));
|
||||
}
|
||||
|
||||
void chr_dev_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tty_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0 ; i < QUEUES ; i++)
|
||||
tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
|
||||
rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
|
||||
rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
|
||||
rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
|
||||
rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
|
||||
for (i=0 ; i<256 ; i++) {
|
||||
tty_table[i] = (struct tty_struct) {
|
||||
{0, 0, 0, 0, 0, INIT_C_CC},
|
||||
0, 0, 0, NULL, NULL, NULL, NULL
|
||||
};
|
||||
}
|
||||
con_init();
|
||||
for (i = 0 ; i<NR_CONSOLES ; i++) {
|
||||
con_table[i] = (struct tty_struct) {
|
||||
{ICRNL, /* change incoming CR to NL */
|
||||
OPOST|ONLCR, /* change outgoing NL to CRNL */
|
||||
0,
|
||||
IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
|
||||
0, /* console termio */
|
||||
INIT_C_CC},
|
||||
0, /* initial pgrp */
|
||||
0, /* initial session */
|
||||
0, /* initial stopped */
|
||||
con_write,
|
||||
con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
|
||||
};
|
||||
}
|
||||
for (i = 0 ; i<NR_SERIALS ; i++) {
|
||||
rs_table[i] = (struct tty_struct) {
|
||||
{0, /* no translation */
|
||||
0, /* no translation */
|
||||
B2400 | CS8,
|
||||
0,
|
||||
0,
|
||||
INIT_C_CC},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
rs_write,
|
||||
rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
|
||||
};
|
||||
}
|
||||
for (i = 0 ; i<NR_PTYS ; i++) {
|
||||
mpty_table[i] = (struct tty_struct) {
|
||||
{0, /* no translation */
|
||||
0, /* no translation */
|
||||
B9600 | CS8,
|
||||
0,
|
||||
0,
|
||||
INIT_C_CC},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
mpty_write,
|
||||
mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
|
||||
};
|
||||
spty_table[i] = (struct tty_struct) {
|
||||
{0, /* no translation */
|
||||
0, /* no translation */
|
||||
B9600 | CS8,
|
||||
IXON | ISIG | ICANON,
|
||||
0,
|
||||
INIT_C_CC},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
spty_write,
|
||||
spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
|
||||
};
|
||||
}
|
||||
rs_init();
|
||||
printk("%d virtual consoles\n\r",NR_CONSOLES);
|
||||
printk("%d pty's\n\r",NR_PTYS);
|
||||
}
|
||||
Reference in New Issue
Block a user