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):drivers/serial/serial_core.c
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): drivers/char/tty_io.c
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开始
待更新

猜你喜欢

转载自blog.csdn.net/qq_34090137/article/details/85199877