Linux TTY函数跟踪
1. 介绍
本文介绍了TTY打开、TTY读和TTY写操作的函数跟踪过程
2. 示例
下面是一个简单的Linux TTY打开和读写过程
#include <termios.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <stdlib.h> int main() { struct termios toptions; int fd, falgs; flags = O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY; fd = open("/dev/ttyS0", flags); tcflush(fd, TCIOFLUSH); memset(&toptions, 0, sizeof(toptions)); tcgetattr(fd, &toptions); cfmakeraw(&toptions);
tcgetattr(fd, &toptions); cfsetospeed(&toptions, B115200); cfsetispeed(&toptions, B115200); tcgetattr(fd, &toptions); toptions.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD | INPCK; toptions.c_cc[VMIN] = 1; toptions.c_cc[VTIME] = 0; tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &toptions); uint8_t data[64] = { 0x01, 0x09, 0x10, 0x00 }; write(fd, data, 4); memset(read_data, 0, 64); read(fd, data, 64); return 0; }
3. 打开
open会调用tty_open, 其分析如下:
tty_open tty_alloc_file /* 分配tty_file_private并赋值给file::private_data */ tty_open_current_tty /* 如果当前设备是/dev/tty则尝试重新打开, 通常返回NULL */ tty_open_by_driver /* 如果上面的函数返回NULL通过查找tty驱动打开tty设备 */ tty_lookup_driver /* 根据设备号查找对应的tty驱动 */ tty_driver_lookup_tty /* 根据tty_driver::tty_operations::lookup/tty_driver::tty_struct查找tty_struct是否存在 */ tty_reopen /* 如果tty_struct已分配, 则打开该tty */ tty_ldisc_reinit /* 初始化tty设备的线路规程 */ tty_ldisc_get /* 根据类别(默认为N_TTY)获取注册的对应线路规程, 并分配对应tty_ldisc实例 */ tty_set_termios_ldisc /* 设置该tty的线路规程 */ tty_ldisc_open /* 调用tty_ldisc::tty_ldisc_ops::open, 对于N_TTY为n_tty_open */ tty_init_dev /* 如果tty_struct没有分配则进行分配(!!!uart就是如此!!!) */ alloc_tty_struct /* 分配和初始化一个tty_struct, 包括driver, ops, index等 */ tty_ldisc_init /* 初始化tty_struct的线路规程 */ tty_ldisc_get /* 获取N_TTY对应线路规程, 并分配对应tty_ldisc实例 */ tty_struct::tty_operations = tty_driver::tty_operations /* 设置tty_struct的函数操作集 */ tty_driver_install_tty /* 通过tty_driver::tty_operations::install或者tty_standard_install为tty设备安装入口 */ tty_ldisc_setup /* 打开线路规程 */ tty_ldisc_open /* 调用tty_ldisc::tty_ldisc_ops::open, 对于N_TTY为n_tty_open */ tty_add_file /* 关联文件到tty_struce */ tty_struct::tty_operations::open /* 同tty_driver::tty_operations::open, 对于串口即uart_open */
uar_open分析如下
uart_open tty_port_open uart_port_activate /* 即tty_port::tty_port_operations::activate */ uart_startup uart_port_startup uart_change_pm /* 调用uart_port::uart_ops::pm */ get_zeroed_page /* 分配一页并分配给uart_state.xmit.buf */ uart_circ_clear /* 初始化TX环形缓冲区头和尾指针 */ uart_port::uart_ops::startup uart_change_speed
4. 写入
write会调用tty_write, 其分析如下:
tty_write file_tty /* 通过file::tty_file_private::tty_struct获取对应的tty_struct */ do_tty_write /* 调用tty_struct::tty_ldisc::tty_ldisc_ops::write */ n_tty_write /* 对于类型N_TTY的线路规程, write为n_tty_write */ process_echoes /* 处理所有待处理的echoed字符 */ __process_echoes /* 写入待处理的echo字符 */ tty_write_room /* 调用tty_struct::tty_operations::write_room */ echo_buf tty_put_char /* * 调用tty_struct::tty_operations::put_char * 或调用tty_struct::tty_operations::write */ do_output_char /* 调用tty_struct::tty_operations::write */ tty_struct::tty_operations::flush_chars /* 对于uart为uart_flush_buffer */ process_output_block process_output tty_struct::tty_operations::flush_chars tty_struct::tty_operations::write
/* 对于uart为uart_write */
uart_write分析如下
uart_write uart_state::circ_buf /* 获取环形缓冲区 */ memcpy /* 将待写数据拷贝至环形缓冲区 */ __uart_start uart_port::uart_ops::wake_peer uart_port::uart_ops::start_tx
5. 读取
read会调用tty_read, 其分析如下:
tty_read tty_struct::tty_ldisc::tty_ldisc_ops::read /* 对于类型N_TTY的线路规程, 为n_tty_read */ n_tty_read tty_buffer_flush_work flush_work flush_to_ldisc /* 将数据从tty缓冲区刷新到线路规程 */ receive_buf tty_ldisc_receive_buf /* 调用tty_struct::tty_ldisc::tty_ldisc_ops::receive_buf2 */ n_tty_receive_buf2 n_tty_receive_buf_common __receive_buf n_tty_receive_buf_real_raw canon_copy_from_read_buf /* ICANON ON */ copy_from_read_buf /* ICANON OFF */ copy_to_user /* 将数据拷贝至用户空间 */