/*
* linux/kernel/chr_drv/tty_ioctl.c
*
* (C) 1991 Linus Torvalds
*/

#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <termios.h>		// նͷļҪ첽ͨſڵն˽ӿڡ

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <linux/tty.h>		// tty ͷļйtty_ioͨŷĲ

#include <asm/io.h>		// io ͷļӲ˿/䡣
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ
#include <asm/system.h>		// ϵͳͷļû޸/жŵȵǶʽꡣ

// ǲ飨Ϊ飩벨ӵĶӦϵμб˵
static unsigned short quotient[] = {
  0, 2304, 1536, 1047, 857,
  768, 576, 384, 192, 96,
  64, 48, 24, 12, 6, 3
};

//// ޸Ĵʡ
// tty - ն˶Ӧtty ݽṹ
// ڳ־DLAB(·ƼĴλ7)λ£ͨ˿0x3f8 0x3f9 UART ֱд
// ӵֽں͸ֽڡ
static void
change_speed (struct tty_struct *tty)
{
  unsigned short port, quot;

// ڴնˣtty ṹĶdata ֶδŵǴж˿ں(0x3f8 0x2f8)
  if (!(port = tty->read_q.data))
    return;
// tty termios ṹģʽ־ȡõĲţݴ˴Ӳȡ
// ӦĲֵCBAUD ǿģʽ־вλ롣
  quot = quotient[tty->termios.c_cflag & CBAUD];
  cli ();			// жϡ
  outb_p (0x80, port + 3);	/* set DLAB */// ó־DLAB
  outb_p (quot & 0xff, port);	/* LS of divisor */// ӵֽڡ
  outb_p (quot >> 8, port + 1);	/* MS of divisor */// Ӹֽڡ
  outb (0x03, port + 3);	/* reset DLAB */// λDLAB
  sti ();			// жϡ
}

//// ˢtty С
// gueue - ָĻָ롣
// еͷָβָ룬Ӷﵽջ(ַ)Ŀġ
static void
flush (struct tty_queue *queue)
{
  cli ();
  queue->head = queue->tail;
  sti ();
}

//// ȴַͳȥ
static void
wait_until_sent (struct tty_struct *tty)
{
/* do nothing - not implemented *//* ʲôû - δʵ */
}

//// BREAK Ʒ
static void
send_break (struct tty_struct *tty)
{
/* do nothing - not implemented *//* ʲôû - δʵ */
}

//// ȡնtermios ṹϢ
// tty - ָն˵tty ṹָ룻termios - ûtermios ṹָ롣
// 0 
static int
get_termios (struct tty_struct *tty, struct termios *termios)
{
  int i;

// ֤һûĻָָڴǷ㹻粻ڴ档
  verify_area (termios, sizeof (*termios));
// ָtty ṹеtermios ṹϢû termios ṹ
  for (i = 0; i < (sizeof (*termios)); i++)
    put_fs_byte (((char *) &tty->termios)[i], i + (char *) termios);
  return 0;
}

//// նtermios ṹϢ
// tty - ָն˵tty ṹָ룻termios - ûtermios ṹָ롣
// 0 
static int
set_termios (struct tty_struct *tty, struct termios *termios)
{
  int i;

// ȸûtermios ṹϢָtty ṹС
  for (i = 0; i < (sizeof (*termios)); i++)
    ((char *) &tty->termios)[i] = get_fs_byte (i + (char *) termios);
// ûп޸tty Ĵпڴ䲨ʣԸtermios ṹеĿģʽ־c_cflag
// ޸ĴоƬUART Ĵ䲨ʡ
  change_speed (tty);
  return 0;
}

//// ȡtermio ṹеϢ
// tty - ָն˵tty ṹָ룻termio - ûtermio ṹָ롣
// 0
static int
get_termio (struct tty_struct *tty, struct termio *termio)
{
  int i;
  struct termio tmp_termio;

// ֤һûĻָָڴǷ㹻粻ڴ档
  verify_area (termio, sizeof (*termio));
// termios ṹϢƵtermio ṹСĿΪģʽ־ͽתҲ
// termios ĳתΪtermio Ķ͡
  tmp_termio.c_iflag = tty->termios.c_iflag;
  tmp_termio.c_oflag = tty->termios.c_oflag;
  tmp_termio.c_cflag = tty->termios.c_cflag;
  tmp_termio.c_lflag = tty->termios.c_lflag;
// ֽṹc_line c_cc[]ֶȫͬġ
  tmp_termio.c_line = tty->termios.c_line;
  for (i = 0; i < NCC; i++)
    tmp_termio.c_cc[i] = tty->termios.c_cc[i];
// ָtty ṹеtermio ṹϢû termio ṹ
  for (i = 0; i < (sizeof (*termio)); i++)
    put_fs_byte (((char *) &tmp_termio)[i], i + (char *) termio);
  return 0;
}

/*
* This only works as the 386 is low-byt-first
*/
/*
* termio ú386 ֽǰķʽ¿á
*/
//// նtermio ṹϢ
// tty - ָն˵tty ṹָ룻termio - ûtermio ṹָ롣
// ûtermio ϢƵն˵termios ṹС0 
static int
set_termio (struct tty_struct *tty, struct termio *termio)
{
  int i;
  struct termio tmp_termio;

// ȸûtermio ṹϢʱtermio ṹС
  for (i = 0; i < (sizeof (*termio)); i++)
    ((char *) &tmp_termio)[i] = get_fs_byte (i + (char *) termio);
// ٽtermio ṹϢƵtty termios ṹСĿΪģʽ־ͽת
// Ҳtermio Ķתtermios ĳ͡
  *(unsigned short *) &tty->termios.c_iflag = tmp_termio.c_iflag;
  *(unsigned short *) &tty->termios.c_oflag = tmp_termio.c_oflag;
  *(unsigned short *) &tty->termios.c_cflag = tmp_termio.c_cflag;
  *(unsigned short *) &tty->termios.c_lflag = tmp_termio.c_lflag;
// ֽṹc_line c_cc[]ֶȫͬġ
  tty->termios.c_line = tmp_termio.c_line;
  for (i = 0; i < NCC; i++)
    tty->termios.c_cc[i] = tmp_termio.c_cc[i];
// û޸tty Ĵпڴ䲨ʣԸtermios ṹеĿģʽ־c_cflag
// ޸ĴоƬUART Ĵ䲨ʡ
  change_speed (tty);
  return 0;
}

//// tty ն豸ioctl 
// dev - 豸ţcmd - ioctl arg - ָ롣
int
tty_ioctl (int dev, int cmd, int arg)
{
  struct tty_struct *tty;
// ȡtty 豸š豸5(tty ն)̵tty ֶμ豸ţ
// tty 豸ǸýûпնˣҲܷioctl ã
  if (MAJOR (dev) == 5)
    {
      dev = current->tty;
      if (dev < 0)
	panic ("tty_ioctl: dev<0");
// ֱӴ豸ȡ豸š
    }
  else
    dev = MINOR (dev);
// 豸ſ0(̨ն)1(1 ն)2(2 ն)
// tty ָӦ豸ŵtty ṹ
  tty = dev + tty_table;
// tty ioctl зֱ
  switch (cmd)
    {
    case TCGETS:
//ȡӦնtermios ṹеϢ
      return get_termios (tty, (struct termios *) arg);
    case TCSETSF:
// termios Ϣ֮ǰҪȵȴݴ꣬ˢ()С
// á
      flush (&tty->read_q);	/* fallthrough */
    case TCSETSW:
// նtermios Ϣ֮ǰҪȵȴݴ(ľ)޸Ĳ
// ӰҪʹʽ
      wait_until_sent (tty);	/* fallthrough */
    case TCSETS:
// Ӧնtermios ṹеϢ
      return set_termios (tty, (struct termios *) arg);
    case TCGETA:
// ȡӦնtermio ṹеϢ
      return get_termio (tty, (struct termio *) arg);
    case TCSETAF:
// termio Ϣ֮ǰҪȵȴݴ꣬ˢ()С
// á
      flush (&tty->read_q);	/* fallthrough */
    case TCSETAW:
// նtermio Ϣ֮ǰҪȵȴݴ(ľ)޸Ĳ
// ӰҪʹʽ
      wait_until_sent (tty);	/* fallthrough *//* ִ */
    case TCSETA:
// Ӧնtermio ṹеϢ
      return set_termio (tty, (struct termio *) arg);
    case TCSBRK:
// ȴд()ֵ0һbreak
      if (!arg)
	{
	  wait_until_sent (tty);
	  send_break (tty);
	}
      return 0;
    case TCXONC:
// ʼ/ֹͣơֵ01¿2
// 룻3¿롣
      return -EINVAL;		/* not implemented *//* δʵ */
    case TCFLSH:
//ˢдûͻյûжݡ0ˢ()У1
// ˢУ2ˢС
      if (arg == 0)
	flush (&tty->read_q);
      else if (arg == 1)
	flush (&tty->write_q);
      else if (arg == 2)
	{
	  flush (&tty->read_q);
	  flush (&tty->write_q);
	}
      else
	return -EINVAL;
      return 0;
    case TIOCEXCL:
// ն˴·רģʽ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCNXCL:
// λն˴·רģʽ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCSCTTY:
// tty Ϊնˡ(TIOCNOTTY - ֹtty Ϊն)
      return -EINVAL;		/* set controlling term NI *//* ÿնNI */
    case TIOCGPGRP:		// NI - Not Implemented
// ȡָն豸̵id֤ûȣȻtty pgrp ֶεû
      verify_area ((void *) arg, 4);
      put_fs_long (tty->pgrp, (unsigned long *) arg);
      return 0;
    case TIOCSPGRP:
// ָն豸̵id
      tty->pgrp = get_fs_long ((unsigned long *) arg);
      return 0;
    case TIOCOUTQ:
// лδͳַ֤ûȣȻƶַû
      verify_area ((void *) arg, 4);
      put_fs_long (CHARS (tty->write_q), (unsigned long *) arg);
      return 0;
    case TIOCINQ:
// лδȡַ֤ûȣȻƶַû
      verify_area ((void *) arg, 4);
      put_fs_long (CHARS (tty->secondary), (unsigned long *) arg);
      return 0;
    case TIOCSTI:
// ģն롣һַָָΪװַնϼġû
// ڸÿնϾгûȨ޻жȨޡ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCGWINSZ:
// ȡն豸ڴСϢμtermios.h еwinsize ṹ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCSWINSZ:
// ն豸ڴСϢμwinsize ṹ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCMGET:
// modem ״̬ߵĵǰ״̬λ־μtermios.h 185-196 У
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCMBIS:
// õmodem ״̬ߵ״̬(true false)
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCMBIC:
// λmodem ״̬ߵ״̬
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCMSET:
// modem ״̬ߵ״̬ĳһλλmodem Ӧ״̬߽ΪЧ
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCGSOFTCAR:
// ȡز־(1 - 0 - ر)
      return -EINVAL;		/* not implemented *//* δʵ */
    case TIOCSSOFTCAR:
// ز־(1 - 0 - ر)
      return -EINVAL;		/* not implemented *//* δʵ */
    default:
      return -EINVAL;
    }
}
