Linux 驅動 tty終端 呼叫過程分析
核心版本: 2.6.32.2
仍在修改中 2018.12.21
需進一步瞭解核心啟動過程中start_kernel以及module_init呼叫
module_init(serial8250_init);
module_init(tty_init);
console_init在start_kernel中呼叫
Register
serial8250_init(void): drivers/serial/8250.c
- 呼叫uart_register_driver(&serial8250_reg),serial8250_reg為struct
uart_driver,
其沒有fops結構,uart port才有具體的uart_ops結構
- serial8250_isa_devs=platform_device_alloc(“serial8250”,-1);
platform_device_add(serial8250_isa_devs - serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
- platform_driver_register(&serial8250_isa_driver);
uart_register_driver(struct uart_driver *drv)
drv傳入引數&serial8250_reg
- struct tty_driver normal = alloc_tty_driver(drv->nr);
drv->tty_driver = normal; 將serial8250_reg的引數傳給normal
- tty_set_operations(normal, &uart_ops);
- 呼叫tty_port_init, UART_NR個port, 由CONFIG決定
- tty_register_driver(normal)
tty_register_driver(struct tty_driver *driver)
driver傳入引數normal
- register_chrdev_region(dev, driver->num, driver->name); dev_t dev為空
- cdev_init(&driver->cdev, &tty_fops): cdev原為空,
初始化cdev,將fops和cdev聯合,為cdev_add作準備。 - cdev_add(&driver->cdev, dev, driver->num): 呼叫kobj_map()函式向系統中新增裝置
tty_init在這個過程中沒被呼叫? 什麼時候呼叫?
- 核心編譯時就會呼叫tty_init等module_init函式對tty終端進行cdev_init和cdev_add,以及生成/dev/tty檔案
- 若作為模組編譯時, 什麼時候呼叫?
busybox的insmod會呼叫新編譯的.ko中所有的module_init函式?比如8250其會呼叫8250和tty的init函式
write呼叫過程:
系統呼叫write(), 作業系統sys_write()呼叫, 進一步tty_write(), 下從此開始分析呼叫, 系統呼叫部分未了解
tty還有line discpline, 呼叫write, read, ioctl等會呼叫line discpline.
tty_wirte(), 呼叫do_tty_write()
do_tty_write(ld->ops->write, tty, file, buf, count): drivers/char/tty_io.c
- ld = tty_ldisc_ref_wait(tty);
- 呼叫ld = tty_ldisc_try(tty)
- 呼叫ld = get_ldisc(tty->ldisc);
- ld為返回的struct tty_ldisc, 見ld->ops和tty_ldisc_N_TTY部分分析,
得知ld->ops應該為tty_ldisc_N_TTY
- ld->ops->write對應n_tty_write(drivers/char/n_tty.c), 舊版核心為write_chan
- 呼叫write(tty, file, tty->write_buf, size), 即呼叫n_tty_write(tty, file,
tty->write_buf, size)
n_tty_write(tty, file, tty->write_buf, size)
- 呼叫tty->ops->flush_chars(tty)
- 呼叫uart_flush_chars(tty)
uart_flush_chars(tty)
- 呼叫uart_start(tty)
uart_start(tty)
- 先後呼叫__uart_start和port->ops->start_tx(port)
- port=tty->driver_data->uart_port
- 即執行tty->driver_data->uart_port->start_tx(port) port和uart關係見下
- 實際執行具體晶片如serial8250_start_tx
- serial8250_start_tx呼叫serial_icr_write(up, UART_ACR, up->acr)和serial_out
- Up->port.serial_out在early_serial_setup(struct uart_port *port)中設定p->serial_out = port->serial_out;
*int __init early_serial_setup(struct uart_port port)
初始化時引數port怎麼傳入??
ld->ops和tty_ldisc_N_TTY
console_init(void): drivers/char/tty_io.c, console_init什麼時候呼叫? Start_kernel呼叫很多初始化函式
- 開機初始化控制檯, 呼叫tty_ldisc_begin, line discpline在控制檯初始化同時初始化
- 呼叫tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); N_TTY=0
*tty_register_ldisc(int disc, struct tty_ldisc_ops new_ldisc) - tty_ldiscs[disc] = new_ldisc; 即繫結tty_ldisc_N_TTY為line discpline的operations
*struct tty_ldisc_ops get_ldops(int disc) - 獲得operation
*tty_ldisc tty_ldisc_get(int disc) - 呼叫ldops = get_ldops(disc);
- ld->ops = ldops;
- return ld
回到tty_write()函式中 - tty = (struct tty_struct *)file->private_data;
- file->private_data在tty_open時設立file->private_data=tty, 呼叫tty_init_dev後呼叫tty_ldisc_setup後呼叫initialize_tty_struct後呼叫tty_ldisc_init進一步呼叫tty_ldisc_get(N_TTY)實現N_TTY和operation的繫結
- 結論: tty->ldisc->ops為tty_ldisc_N_TTY
uart和port之間的關係
serial8250_console_init開始
待更新