/*
* 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.
*/
/*
* 'tty_io.c'tty һַصĸоǿ̨Ǵͨóͬ
* ʵ˻ԡ淶()ģʽȡ
*
* Kill-lineллJohn T Kahl
*/
#include <ctype.h>		// ַͷļһЩйַжϺתĺꡣ
#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <signal.h>		// źͷļźŷųźŽṹԼźŲԭ͡

// ӦźźλͼеĶӦλ
#define ALRMMASK (1<<(SIGALRM-1))	// (alarm)źλ
#define KILLMASK (1<<(SIGKILL-1))	// ֹ(kill)źλ
#define INTMASK (1<<(SIGINT-1))	// ж(int)źλ
#define QUITMASK (1<<(SIGQUIT-1))	// ˳(quit)źλ
#define TSTPMASK (1<<(SIGTSTP-1))	// tty ֹͣ(tty stop)źλ

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/tty.h>		// tty ͷļйtty_ioͨŷĲ
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ
#include <asm/system.h>		// ϵͳͷļû޸/жŵȵǶʽꡣ

#define _L_FLAG(tty,f) ((tty)->termios.c_lflag & f)	// ȡtermios ṹеıģʽ־
#define _I_FLAG(tty,f) ((tty)->termios.c_iflag & f)	// ȡtermios ṹеģʽ־
#define _O_FLAG(tty,f) ((tty)->termios.c_oflag & f)	// ȡtermios ṹеģʽ־

// ȡtermios ṹбģʽ־еһ־λ
#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)	// 淶ģʽʱȡKILL ǰб־λ
#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)	// ȡԿַ־λ
#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)	// 淶ģʽʱȡKILL вԱ־λ

// ȡtermios ṹģʽ־еһ־λ
#define I_UCLC(tty) _I_FLAG((tty),IUCLC)	// ȡģʽ־ддСдת־λ
#define I_NLCR(tty) _I_FLAG((tty),INLCR)	// ȡзNL תسCR ־λ
#define I_CRNL(tty) _I_FLAG((tty),ICRNL)	// ȡسCR תзNL ־λ
#define I_NOCR(tty) _I_FLAG((tty),IGNCR)	// ȡԻسCR ־λ

// ȡtermios ṹģʽ־еһ־λ
#define O_POST(tty) _O_FLAG((tty),OPOST)	// ȡģʽ־ִ־
#define O_NLCR(tty) _O_FLAG((tty),ONLCR)	// ȡзNL תسзCR-NL ־
#define O_CRNL(tty) _O_FLAG((tty),OCRNL)	// ȡسCR תзNL ־
#define O_NLRET(tty) _O_FLAG((tty),ONLRET)	// ȡзNL ִлسܵı־
#define O_LCUC(tty) _O_FLAG((tty),OLCUC)	// ȡСдתдַ־

// tty ݽṹtty_table 顣аʼݣֱӦ̨ն1 
// ն2 ĳʼݡ
struct tty_struct tty_table[] = {
  {
   {ICRNL,			/* change incoming CR to NL *//* CR תΪNL */
    OPOST | ONLCR,		/* change outgoing NL to CRNL *//* NL תCRNL */
    0,				// ģʽ־ʼΪ0
    ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,	// ģʽ־
    0,				/* console termio */// ̨termio
    INIT_C_CC},			// ַ顣
   0,				/* initial pgrp */// ʼ顣
   0,				/* initial stopped */// ʼֹͣ־
   con_write,			// tty дָ롣
   {0, 0, 0, 0, ""},		/* console read-queue */// tty ̨С
   {0, 0, 0, 0, ""},		/* console write-queue */// tty ̨дС
   {0, 0, 0, 0, ""}		/* console secondary queue */// tty ̨(ڶ)С
   }, {
       {0,			/* no translation */// ģʽ־0ת
	0,			/* no translation */// ģʽ־0ת
	B2400 | CS8,		// ģʽ־2400bps8 λλ
	0,			// ģʽ־0
	0,			// й0
	INIT_C_CC},		// ַ顣
       0,			// ʼ顣
       0,			// ʼֹͣ־
       rs_write,		// 1 tty дָ롣
       {0x3f8, 0, 0, 0, ""},	/* rs 1 */// ն1 С
       {0x3f8, 0, 0, 0, ""},	// ն1 дС
       {0, 0, 0, 0, ""}		// ն1 С
       }, {
	   {0,			/* no translation */// ģʽ־0ת
	    0,			/* no translation */// ģʽ־0ת
	    B2400 | CS8,	// ģʽ־2400bps8 λλ
	    0,			// ģʽ־0
	    0,			// й0
	    INIT_C_CC},		// ַ顣
	   0,			// ʼ顣
	   0,			// ʼֹͣ־
	   rs_write,		// 2 tty дָ롣
	   {0x2f8, 0, 0, 0, ""},	/* rs 2 */// ն2 С
	   {0x2f8, 0, 0, 0, ""},	// ն2 дС
	   {0, 0, 0, 0, ""}	// ն2 С
	   }
};

/*
* these are the tables used by the machine code handlers.
* you can implement pseudo-tty's or something by changing
* them. Currently not done.
*/
/*
* ǻʹõĻеַͨ޸ʵ
* αtty ն˻ն͡Ŀǰû
*/
// tty еַrs_io.s ʹãȡöдеַ
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,	// п1 ն˶деַ
  &tty_table[2].read_q, &tty_table[2].write_q	// п2 ն˶деַ
};

//// tty ն˳ʼ
// ʼն˺Ϳ̨նˡ
void
tty_init (void)
{
  rs_init ();			// ʼжϳʹнӿ1 2(serial.c, 37)
  con_init ();			// ʼ̨նˡ(console.c, 617)
}

//// tty նַ
// tty - Ӧtty ն˽ṹָ룻mask - źλ
void
tty_intr (struct tty_struct *tty, int mask)
{
  int i;

// tty Сڵ0˳
  if (tty->pgrp <= 0)
    return;
// ɨ飬tty Ӧָźš
  for (i = 0; i < NR_TASKS; i++)
// ָ벻Ϊգŵtty ţøָźmask
    if (task[i] && task[i]->pgrp == tty->pgrp)
      task[i]->signal |= mask;
}

//// лý̽жϵ˯״̬
// queue - ָеָ롣
// ȡлַʱô˺
static void
sleep_if_empty (struct tty_queue *queue)
{
  cli ();			// жϡ
// ǰûźҪָĶлգý̽ж˯״̬
// еĽ̵ȴָָý̡
  while (!current->signal && EMPTY (*queue))
    interruptible_sleep_on (&queue->proc_list);
  sti ();			// жϡ
}

//// лý̽жϵ˯״̬
// queue - ָеָ롣
// лдʱô˺
static void
sleep_if_full (struct tty_queue *queue)
{
// л򷵻˳
  if (!FULL (*queue))
    return;
  cli ();			// жϡ
// ûźҪҶлпʣ<128ý̽ж˯״̬
// øöеĽ̵ȴָָý̡
  while (!current->signal && LEFT (*queue) < 128)
    interruptible_sleep_on (&queue->proc_list);
  sti ();			// жϡ
}

//// ȴ
// ̨Ķлý̽жϵ˯״̬
void
wait_for_keypress (void)
{
  sleep_if_empty (&tty_table[0].secondary);
}

//// Ƴɹ淶ģʽַС
// ָtty ն˶леַƳɹ淶()ģʽַڸ(淶ģʽ)С
// tty - ָն˵tty ṹ
void
copy_to_cooked (struct tty_struct *tty)
{
  signed char c;

// tty ĶлղҸлΪգѭִд롣
  while (!EMPTY (tty->read_q) && !FULL (tty->secondary))
    {
// Ӷβȡһַcǰβָ롣
      GETCH (tty->read_q, c);
// ַģʽ־д
// ַǻسCR(13)سתб־CRNL λ򽫸ַתΪзNL(10)
// Իس־NOCR λԸַַ
      if (c == 13)
	if (I_CRNL (tty))
	  c = 10;
	else if (I_NOCR (tty))
	  continue;
	else;
// ַǻзNL(10)һתس־NLCR λתΪسCR(13)
      else if (c == 10 && I_NLCR (tty))
	c = 13;
// дתСд־UCLC λ򽫸ַתΪСдַ
      if (I_UCLC (tty))
	c = tolower (c);
// ģʽ־й淶죩ģʽ־CANON λ´
      if (L_CANON (tty))
	{
// ַǼַֹKILL(^U)ɾд
	  if (c == KILL_CHAR (tty))
	    {
/* deal with killing the input line *//* ɾд */
// tty вգ߸һַǻNL(10)߸ַļַ
// (^D)ѭִд롣
	      while (!(EMPTY (tty->secondary) ||
		       (c = LAST (tty->secondary)) == 10 ||
		       c == EOF_CHAR (tty)))
		{
// ػԱ־ECHO λôַǿַ(ֵ<32)tty дз
// ַERASEٷһַERASEҵøtty д
		  if (L_ECHO (tty))
		    {
		      if (c < 32)
			PUTCH (127, tty->write_q);
		      PUTCH (127, tty->write_q);
		      tty->write (tty);
		    }
// tty ͷָ1 ֽڡ
		  DEC (tty->secondary.head);
		}
	      continue;		// ȡַ
	    }
// ַɾַERASE(^H)ô
	  if (c == ERASE_CHAR (tty))
	    {
// tty ĸΪգһַǻзNL(10)ļ
// ַ
	      if (EMPTY (tty->secondary) ||
		  (c = LAST (tty->secondary)) == 10 || c == EOF_CHAR (tty))
		continue;
// ػԱ־ECHO λôַǿַ(ֵ<32)tty дз
// ַERASEٷһַERASEҵøtty д
	      if (L_ECHO (tty))
		{
		  if (c < 32)
		    PUTCH (127, tty->write_q);
		  PUTCH (127, tty->write_q);
		  tty->write (tty);
		}
// tty ͷָ1 ֽڣַ
	      DEC (tty->secondary.head);
	      continue;
	    }
//ַַֹͣ(^S)tty ֹͣ־ַ
	  if (c == STOP_CHAR (tty))
	    {
	      tty->stopped = 1;
	      continue;
	    }
// ַַֹͣ(^Q)λtty ֹͣ־ַ
	  if (c == START_CHAR (tty))
	    {
	      tty->stopped = 0;
	      continue;
	    }
	}
// ģʽ־ISIG ־λյINTRQUITSUSP DSUSP ַʱҪΪ
// Ӧźš
      if (L_ISIG (tty))
	{
// ַǼжϷ(^C)ǰ̷ͼжźţһַ
	  if (c == INTR_CHAR (tty))
	    {
	      tty_intr (tty, INTMASK);
	      continue;
	    }
// ַǼжϷ(^\)ǰ̷ͼ˳źţһַ
	  if (c == QUIT_CHAR (tty))
	    {
	      tty_intr (tty, QUITMASK);
	      continue;
	    }
	}
// ַǻзNL(10)ļEOF(^D)ַ1[??]
      if (c == 10 || c == EOF_CHAR (tty))
	tty->secondary.data++;
// ģʽ־лԱ־ECHO λôַǻзNL(10)򽫻зNL(10)
// ͻسCR(13)tty длУַǿַ(ֵַ<32)һԿַ־
// ECHOCTL λַ'^'ַc+64 tty д(Ҳʾ^C^H )򽫸ַ
// ֱӷtty дСøtty д
      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);
}

//// tty 
// channel - 豸ţbuf - ָ룻nr - ֽ
// Ѷֽ
int
tty_read (unsigned channel, char *buf, int nr)
{
  struct tty_struct *tty;
  char c, *b = buf;
  int minimum, time, flag = 0;
  long oldalarm;

// 汾linux ں˵նֻ3 豸ֱǿ̨(0)ն1(1)ʹն2(2)
// κδ2 豸ŶǷǷġдֽȻҲС0 ġ
  if (channel > 2 || nr < 0)
    return -1;
// tty ָָ豸ŶӦttb_table еtty ṹ
  tty = &tty_table[channel];
// ȱԭʱֵȻݿַVTIME VMIN öַĳʱʱֵ
// ڷǹ淶ģʽ£ֵǳʱʱֵMIN ʾΪҪȡַ
// TIME һʮ֮һļʱֵ
// ȡе()ʱֵ(δ)
  oldalarm = current->alarm;
// öʱʱֵtime Ҫٶȡַminimum
  time = 10L * tty->termios.c_cc[VTIME];
  minimum = tty->termios.c_cc[VMIN];
// ˶ʱʱֵtime ûٶȡminimumôڶһַ
// ʱʱ̷ءminimum=1
  if (time && !minimum)
    {
      minimum = 1;
// ԭʱֵ0 time+ǰϵͳʱֵСڽԭʱֵĻý̶ʱ
// ֵΪtime+ǰϵͳʱ䣬flag ־
      if (flag = (!oldalarm || time + jiffies < oldalarm))
	current->alarm = time + jiffies;
    }
// õٶȡַ>ַڴ˴ȡַ
  if (minimum > nr)
    minimum = nr;
// ֽ>0ѭִ²
  while (nr > 0)
    {
// flag Ϊ0(ԭʱֵ0 time+ǰϵͳʱֵСڽԭʱֵ)ҽж
// ʱźSIGALRMλ̵ĶʱźŲжѭ
      if (flag && (current->signal & ALRMMASK))
	{
	  current->signal &= ~ALRMMASK;
	  break;
	}
// ǰźҪ˳0
      if (current->signal)
	break;
// (淶ģʽ)Ϊգ˹淶ģʽ־ҸַΪ0 Լ
// ģʽппռ>20ж˯״̬غ
      if (EMPTY (tty->secondary) || (L_CANON (tty) &&
				     !tty->secondary.data
				     && LEFT (tty->secondary) > 20))
	{
	  sleep_if_empty (&tty->secondary);
	  continue;
	}
// ִ²ֱnr=0 ߸Ϊա
      do
	{
// ȡַc
	  GETCH (tty->secondary, c);
// ַļ(^D)ǻзNL(10)ַ1
	  if (c == EOF_CHAR (tty) || c == 10)
	    tty->secondary.data--;
// ַļ(^D)ҹ淶ģʽ־λ򷵻Ѷַ˳
	  if (c == EOF_CHAR (tty) && L_CANON (tty))
	    return (b - buf);
// 򽫸ַûݶλbuf Уַ1ַΪ0жѭ
	  else
	    {
	      put_fs_byte (c, b++);
	      if (!--nr)
		break;
	    }
	}
      while (nr > 0 && !EMPTY (tty->secondary));
// ʱʱֵtime Ϊ0 ҹ淶ģʽ־ûλ(ǹ淶ģʽ)ô
      if (time && !L_CANON (tty))
// ԭʱֵ0 time+ǰϵͳʱֵСڽԭʱֵĻý̶ʱֵ
// Ϊtime+ǰϵͳʱ䣬flag ־ý̵Ķʱֵڽԭʱֵ
	if (flag = (!oldalarm || time + jiffies < oldalarm))
	  current->alarm = time + jiffies;
	else
	  current->alarm = oldalarm;
// 淶ģʽ־λôûж1 ַжѭѶȡڻҪ
// ȡַҲжѭ
      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);		// Ѷȡַ
}

//// tty д
// channel - 豸ţbuf - ָ룻nr - дֽ
// дֽ
int
tty_write (unsigned channel, char *buf, int nr)
{
  static cr_flag = 0;
  struct tty_struct *tty;
  char c, *b = buf;

// 汾linux ں˵նֻ3 豸ֱǿ̨(0)ն1(1)ʹն2(2)
// κδ2 豸ŶǷǷġдֽȻҲС0 ġ
  if (channel > 2 || nr < 0)
    return -1;
// tty ָָ豸ŶӦttb_table еtty ṹ
  tty = channel + tty_table;
// ַ豸һһַдģnr 0 ʱÿַѭ
  while (nr > 0)
    {
// ʱtty дǰ̽жϵ˯״̬
      sleep_if_full (&tty->write_q);
// ǰźҪ˳0
      if (current->signal)
	break;
// Ҫдֽ>0 tty двʱѭִ²
      while (nr > 0 && !FULL (tty->write_q))
	{
// ûݶڴȡһֽc
	  c = get_fs_byte (b);
// նģʽ־еִ־OPOST λִʱ̡
	  if (O_POST (tty))
	    {
// ַǻس'\r'(CR13)һسתз־OCRNL λ򽫸ַɻз
// '\n'(NL10)ַǻз'\n'(NL10)һתسܱ־ONLRET λĻ
// 򽫸ַɻس'\r'(CR13)
	      if (c == '\r' && O_CRNL (tty))
		c = '\n';
	      else if (c == '\n' && O_NLRET (tty))
		c = '\r';
// ַǻз'\n'һس־cr_flag ûλתس-б־ONLCR λĻ
// cr_flag λһسдСȻһַ
	      if (c == '\n' && !cr_flag && O_NLCR (tty))
		{
		  cr_flag = 1;
		  PUTCH (13, tty->write_q);
		  continue;
		}
// Сдתд־OLCUC λĻͽַתɴдַ
	      if (O_LCUC (tty))
		c = toupper (c);
	    }
// ûݻָb ǰ1 ֽڣдֽ1 ֽڣλcr_flag ־ֽڷtty
// дС
	  b++;
	  nr--;
	  cr_flag = 0;
	  PUTCH (c, tty->write_q);
	}
// ֽȫд꣬дִеöӦtty дֽҪд
// ȴдвԵõȳȥִ
      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.
*/
/*
* ǣʱúϲ386ӳǴһжϴеõģʹ
* жϴ˯ҲӦþû(ϣ)Ȼ֤
* ģôҽintel һ?ǱСģڵøӳ֮ǰ
* Ҫָжϡ
*
* ҲΪͨ»ᴦ˯ߣܺãΪ˯ȫġ
*/
//// tty жϴú - ִtty жϴ
// tty - ָtty ն˺ţ01 2
// ָtty ն˶леַƳɹ淶()ģʽַڸ(淶ģʽ)С
// ڴڶַж(rs_io.s, 109)ͼж(kerboard.S, 69)еá
void
do_tty_interrupt (int tty)
{
  copy_to_cooked (tty_table + tty);
}

//// ַ豸ʼգΪԺչ׼
void
chr_dev_init (void)
{
}
