コンソール関連メモ
[関連ファイル]
char/tty_io.c TTYドライバ
char/n_tty.c
char/vt.c 仮想コンソール(/dev/ttyN)ドライバ
char/vc_screen.c 仮想コンソールメモリ(/dev/vcsN)ドライバ
video/console/fbcon.c 実際のグラフィックデバイス(FrameBuffer)
video/console/vgacon.c 実際のグラフィックデバイス(VGAテキストコンソール)
[参照]
man console
man console_ioctl
man pts
struct tty_struct <-- ttyをopenすると作成される
+----------------+
| | struct tty_driver
| driver |----> +------------------+
| | | |
+----------------+ | open,close,write | <--tty_set_operations()で設定
| ldisc | | putchar... | tty_register_driver()でtty_driversに登録
| open,close |
| read,write... |
+----------------+
| |
| |
+----------------+
struct tty_ldisc tty_ldiscs[NR_LDISCS]
+----------------+
| | tty_register_ldisc()で登録する
| |
| |
+----------------+
| |
| |
| |
+----------------+
:
:
struct file_operations tty_fops,console_fops,ptmx_fops
<--デバイスの種類によりハンドラが若干異なる。
ldiscとdriverの違いは?
tty_init()
TTYの設定
cdev_init(&tty_cdev, &tty_fops);
/dev/tty登録
Consoleの設定
cdev_init(&console_cdev, &console_fops);
/dev/console登録
PTYの設定
cdev_init(&ptmx_cdev, &ptmx_fops);
/dev/ptmx登録
VT100の設定
cdev_init(&vc0_cdev, &console_fops);
Major 4,Minor 0に/dev/vc/0登録
vty_init()
vcs_init() - 仮想コンソール初期化
vcsデバイス登録
tty_set_operations() - console用tty_driverに tty_operations登録
static struct tty_operations con_ops = {
.open = con_open,
.close = con_close,
.write = con_write,
.write_room = con_write_room,
.put_char = con_put_char,
.flush_chars = con_flush_chars,
.chars_in_buffer = con_chars_in_buffer,
.ioctl = vt_ioctl,
.stop = con_stop,
.start = con_start,
.throttle = con_throttle,
.unthrottle = con_unthrottle,
};
tty_register_driver() - consoleドライバ登録(Major 4, Minor 1〜)
tty_driversにチェーンされる
kbd_init() - KeyBoard初期化
console_map_init()
console_init()
tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY)
tty_ldiscs[]に登録
struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC, /* magic */
"n_tty", /* name */
0, /* num */
0, /* flags */
n_tty_open, /* open */
n_tty_close, /* close */
n_tty_flush_buffer, /* flush_buffer */
n_tty_chars_in_buffer, /* chars_in_buffer */
read_chan, /* read */
write_chan, /* write */
n_tty_ioctl, /* ioctl */
n_tty_set_termios, /* set_termios */
normal_poll, /* poll */
NULL, /* hangup */
n_tty_receive_buf, /* receive_buf */
n_tty_receive_room, /* receive_room */
n_tty_write_wakeup /* write_wakeup */
};
tty_io.c:: console,ttyの場合
tty_open(struct inode * inode, struct file * filp)
device = inode->i_rdev
get_tty_driver()
tty_driversからdeviceに該当するMajor,Minor#を持つdriverを返す。
<-- tty_register_driver()で登録されている
init_dev() - ttyの作成
tty_io.c:: PTYのopenルーチン
ptmx_open(struct inode * inode, struct file * filp)
init_dev() - ttyの作成
struct tty_structを確保(tty)
initialize_tty_struct() - tty_structを初期化
:
N_TTYのldiscをtty->ldiscに設定
:
get_tty_driver()で取得したdriverをtty->driverに設定
if (driver->type == TTY_DRIVER_TYPE_PTY) { - PTYデバイスなら...
対向用にもうひとつ struct tty_structを確保(o_tty)
Master<->Slave逆(driver->other)のものを作成する
tty->link,o_tty->linkで相互リンクする
}
/proc/tty/drivers - tty_register_driver()で登録されたドライバ(struct tty_driver)一覧
/dev/tty /dev/tty 5 0 system:/dev/tty
/dev/console /dev/console 5 1 system:console
/dev/ptmx /dev/ptmx 5 2 system
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
*** ここまでは決め打ちで表示している。***
driver_name name Major Minor type,subtype
serial /dev/ttyS 4 64-111 serial
pty_slave /dev/pts 136 0-1048575 pty:slave <--unix98_pty_init()で登録
pty_master /dev/ptm 128 0-1048575 pty:master <--unix98_pty_init()で登録
pty_slave /dev/ttyp 3 0-255 pty:slave <--legacy_pty_init()で登録
pty_master /dev/pty 2 0-255 pty:master <--legacy_pty_init()で登録
unknown /dev/tty 4 1-63 console <-- vt.c::vty_init()で登録
tty_io.c::
tty_write()
do_tty_write()
(*write)() - (tty->ldisc.write() - write_chan())
n_tty.c::
write_chan()
tty->driver->write() - 仮想コンソール(/dev/ttyN)だとcon_write()
擬似端末(/dev/pts,ptm,ttyp,pty)だとpty_write()
tty_io.c::
tty_read()
(ld->read)() - read_chan()
n_tty.c::
read_chan()
@@@@@@@@@@@@@@@@@@
---------------------------------
仮想コンソール
console_struct.h
struct vc vc_cons[currcons] - 仮想コンソール分存在する
+--------------+
| |
| vc_cols |コンソールサイズ
| vc_rows |
| | struct consw
| vc_sw |-----> +-------------+
| | | | <--実際のコンソールデバイスへのハンドラ
+--------------+ | con_init, |
: | con_putc... |
:
vt.c::
con_write()
do_con_write()
:
scr_writew(val, addr) - 指定アドレスにデータ書き出し
書き出しアドレスはpos(vc_cons[currcons].d->vc_pos)
ポインタ更新(pos+2)
全文字列を書き込み
FLUSH
sw->con_putcs() - 実際にH/Wに書き込み
VGAコンソールはNULL(scr_writew()のみでOK)
FrameBufferコンソールならfbcon_putcs()
仮想コンソールの切り替え
complete_change_console()
switch_screen()
redraw_screen(is_switch=1)
sw->con_switch()
------------------------------------------------
仮想コンソールメモリ(VCS)
vcs_init()
static struct file_operations vcs_fops = {
.llseek = vcs_lseek,
.read = vcs_read,
.write = vcs_write,
.open = vcs_open,
};
---------------------------------
PTY(擬似端末)
telnet <-> ネットワーク <-> telnetd <-> 擬似端末 <-> loginやshell
(Master) (Slave)
xterm <-> 擬似端末 <-> loginやshell
(Master) (Slave)
擬似端末のマスタとスレーブの両方がオープンされた後は、スレーブは、プ ロ
セスに対して、実端末 (real terminal) と全く同じインタフェースを提供する。
スレーブに書かれたデータはマスタ・ディスクリプタに対する入力として扱 わ
れ、マスタに書かれたデータはスレーブに対する入力として扱われる。
/dev/pts/1に書き込むと対応するWindowに表示される
<--対応するMasterから読みだされxtermで表示される?
実端末(/dev/tty)に書き込むと実際のデバイス(ビデオカード等)に出力される。
擬似端末(/dev/pts)に書き込んでも実際にはデバイスが存在しないので
Master(ptm)側に送信されるだけ。
書き込まれたデータの処理はptmを読みこむアプリケーションに依存する。
xtermならWindowに表示。telnetdならクライアント側へデータを送る。
legacy_pty_init()
unix98_pty_init()
tty_struct name tty_operations other
Unix98 PTY
ptm_driver /dev/ptm pty_ops pts_driver
pts_driver /dev/pts pty_ops ptm_driver
Legacy PTY
pty_driver /dev/pty pty_ops pty_slave_driver
pty_slave_driver /dev/ttyp pty_ops pty_driver
static struct tty_operations pty_ops = {
.open = pty_open,
.close = pty_close,
.write = pty_write,
.write_room = pty_write_room,
.flush_buffer = pty_flush_buffer,
.chars_in_buffer = pty_chars_in_buffer,
.unthrottle = pty_unthrottle,
.set_termios = pty_set_termios,
};
pty_write()
to = tty->link 対向(Master<->Slave)のtty
to->ldisc.receive_buf() - n_tty_receive_buf()????
Readバッファにデータ書き込み
Read待ちのプロセス(tty->read_wait)があればwakeup
対向(Master/Slave)のttyをReadすればデータを取得出来る
N_TTY_BUF_SIZE バッファサイズ
tty->read_buf Readバッファ
tty->read_head Readバッファオフセット
tty->read_cnt Readバッファ内のデータサイズ
--------------------
キーボード
kbd_init()
input_register_handler(&kbd_handler) - ハンドラの登録
static struct input_handler kbd_handler = {
.event = kbd_event,
.connect = kbd_connect,
.disconnect = kbd_disconnect,
.name = "kbd",
.id_table = kbd_ids,
};
登録時、.connect(kbd_connect())が呼ばれる
kbd_event() <--呼出し元は?
kbd_keycode()
keycode->keysym->typeに変換(文字なのかカーソルキーなのか...他)
(*k_handler[type])() - 種別に合わせたハンドラを呼出し
k_fn()
k_spec()
fn_handler[value](vc, regs)
fn_enter()
fn_boot_it()
ctrl_alt_del()
リブート
k_ascii()
kbd_bh()