以下是在include/uapi/linux/tty.h中定义了现有的线规号,如果需要定义新的,则需要在后面添加新的
1 /* line disciplines */ 2 #define N_TTY 0 3 #define N_SLIP 1 4 ... ... 5 #define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */ 6 #define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
1.应用层发送数据
-->write()
-->file_operation.tty_write /* file_operation函数集在何时被赋值? */
-->do_tty_write(ld->ops->write, tty, file, buf, count) /* tty_ldisc->tty_ldisc_ops->write */
-->tty_ldisc_ops.ldisc_write /* tty_ldisc_ops函数集在何时被赋值? */ ///该write函数是在线路规程模块中定义
-->tty->driver->ops->write (tty, tbuf->buf, tbuf->count) /* tty_struct->tty_driver->tty_operations->write */
-->tty_operations.uart_write /* tty_operation函数集在何时被赋值? */
-->uart_start(tty);
-->__uart_start(tty);
-->port->ops->start_tx(port); /* uart_port->uart_ops->start_tx */
-->uart_ops.imx_start_tx /* uart_ops函数集在何时被赋值? */
至此消息也发送出去了,从消息流程可以看出来消息是经过ldisc线路规程层,然后tty层,然后到硬件驱动层。
上面的调用关系有个问题,为什么顺序是调到ldisc层,又调回tty层,再直接到hardware层?
- file_operation.do_tty_write(ld->ops->write, tty, file, buf, count)::: tty_ldisc->ops->(*write)(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr);
- tty_ldisc_ops.n_tty_write(struct tty_struct *tty, struct file *file,const unsigned char *buf, size_t nr):::tty->ops->write(tty, b, nr); tty_operations->ops->(*write)(struct tty_struct * tty,const unsigned char *buf, int count)
- tty_operations.uart_write(struct tty_struct * tty, const unsigned char *buf, int count )::: memcpy(circ->buf + circ->head, buf, c);
- uart_ops.imx_start_tx(struct uart_port *port)::: writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
2.uart和tty逐层调用关系
2.1.关于file_operation函数集的赋值
- uart_register_driver函数中完成了file_operations和tty_operations函数集的初始化
-->uart_register_driver(uart_driver*) //uart driver驱动文件中
-->tty_set_operations(normal, &uart_ops);//完成tty_operation赋值,serail_core.c中初始化结构体
-->tty_register_driver(normal)
-->tty_cdev_add(driver, dev, 0, driver->num);
-->cdev_init(&driver->cdevs[index], &tty_fops);//而tty_fops为file_operations 类型,tty_io.c中初始化结构体
1 /* drivers/tty/tty_io.c */ 2 static const struct file_operations tty_fops = { 3 .llseek = no_llseek, 4 .read = tty_read, 5 .write = tty_write, 6 .poll = tty_poll, 7 .unlocked_ioctl = tty_ioctl, 8 .compat_ioctl = tty_compat_ioctl, 9 .open = tty_open, 10 .release = tty_release, 11 .fasync = tty_fasync, 12 };
2.2.关于tty_operation函数集的赋值(同上)
-->uart_register_driver(uart_driver*) //uart driver驱动文件中
-->tty_set_operations(normal, &uart_ops); //完成tty_operation赋值,serial_core.c
-->tty_register_driver(normal)
1 /* drivers/tty/serial/serial_core.c */ 2 static const struct tty_operations uart_ops = { 3 .open = uart_open, 4 .close = uart_close, 5 .write = uart_write, 6 .put_char = uart_put_char, 7 .flush_chars = uart_flush_chars, 8 .write_room = uart_write_room, 9 .chars_in_buffer= uart_chars_in_buffer, 10 .flush_buffer = uart_flush_buffer, 11 .ioctl = uart_ioctl, 12 .throttle = uart_throttle, 13 .unthrottle = uart_unthrottle, 14 .send_xchar = uart_send_xchar, 15 .set_termios = uart_set_termios, 16 .set_ldisc = uart_set_ldisc, 17 .stop = uart_stop, 18 .start = uart_start, 19 .hangup = uart_hangup, 20 .break_ctl = uart_break_ctl, 21 .wait_until_sent= uart_wait_until_sent, 22 #ifdef CONFIG_PROC_FS 23 .proc_fops = &uart_proc_fops, 24 #endif 25 .tiocmget = uart_tiocmget, 26 .tiocmset = uart_tiocmset, 27 .get_icount = uart_get_icount, 28 #ifdef CONFIG_CONSOLE_POLL 29 .poll_init = uart_poll_init, 30 .poll_get_char = uart_poll_get_char, 31 .poll_put_char = uart_poll_put_char, 32 #endif 33 };
2.3.关于tty_ldisc_ops函数集的赋值
-->tty_register_ldisc(ldisc, tty_ldisc_ops); /* 此函数可以是在线路规程模块初始化时调用 */
-->tty_ldiscs[disc] = tty_ldisc_ops; /* 可见是tty_ldiscs中包含每个线路规程号对应的ops函数集, 假如是默认tty, 则在n_tty.c中,全局变量tty_ldisc_N_TTY */
下面只是默认N_tty.c中默认线路规程号N_tty的例子,用户可以编写自己的线路规程模块,进行数据封装,有自己的方法集。
1 /* drivers/tty/N_tty.c */ 2 struct tty_ldisc_ops tty_ldisc_N_TTY = { 3 .magic = TTY_LDISC_MAGIC, 4 .name = "n_tty", 5 .open = n_tty_open, 6 .close = n_tty_close, 7 .flush_buffer = n_tty_flush_buffer, 8 .chars_in_buffer = n_tty_chars_in_buffer, 9 .read = n_tty_read, 10 .write = n_tty_write, 11 .ioctl = n_tty_ioctl, 12 .set_termios = n_tty_set_termios, 13 .poll = n_tty_poll, 14 .receive_buf = n_tty_receive_buf, 15 .write_wakeup = n_tty_write_wakeup, 16 .fasync = n_tty_fasync, 17 .receive_buf2 = n_tty_receive_buf2, 18 };
2.4.关于uart_ops函数集赋值
-->platform_driver_register(platform_driver*)
-->serial_imx_probe() //设备驱动匹配
-->sport->port.ops = &imx_pops; //调用也是通过port口来调ops
这里举的例子是freescale的串口方法集
1 /* drivers/tty/serial/imx.c */ 2 static struct uart_ops imx_pops = { 3 .tx_empty = imx_tx_empty, 4 .set_mctrl = imx_set_mctrl, 5 .get_mctrl = imx_get_mctrl, 6 .stop_tx = imx_stop_tx, 7 .start_tx = imx_start_tx, 8 .stop_rx = imx_stop_rx, 9 .enable_ms = imx_enable_ms, 10 .break_ctl = imx_break_ctl, 11 .startup = imx_startup, 12 .shutdown = imx_shutdown, 13 .flush_buffer = imx_flush_buffer, 14 .set_termios = imx_set_termios, 15 .type = imx_type, 16 .release_port = imx_release_port, 17 .request_port = imx_request_port, 18 .config_port = imx_config_port, 19 .verify_port = imx_verify_port, 20 #if defined(CONFIG_CONSOLE_POLL) 21 .poll_get_char = imx_poll_get_char, 22 .poll_put_char = imx_poll_put_char, 23 #endif 24 #ifdef CONFIG_IB2_SUPPORT 25 .ioctl = imx_ioctl, 26 #endif 27 };
3.线路规程号配置
3.1.Console的线路规程是怎么回事?
-->__init console_init(void)
-->tty_ldisc_begin(); /* Setup the default TTY line discipline. */
-->(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); /* tty_ldisc_N_TTY中对应都是n_tty_**函数集 */
3.2.那么其他串口的的线路规程默认是怎样的?也是N_TTY吗?
答案:是的,这要从应用层open设备文件开始说起
亦可参考该链接:http://blog.csdn.net/rockrockwu/article/details/7897283
-->open("/dev/ttys0",O_RDWR|O_NOCTTY); /* app layer */
-->tty_operations.tty_open
-->tty_init_dev
-->initialize_tty_struct
-->tty_ldisc_init
-->struct tty_ldisc *ld = tty_ldisc_get(N_TTY)
-->struct tty_struct *tty->ldisc = ld; /* 至此tty_struct和N_TTY绑定,该串口默认线路规程 */
3.3.应用程序修改线路规程
如果不适用默认的线路规程,需要在串口实现一些协议,那该如何做呢?--新建线路规程号,实现线路规程代码
ioctl(fd, TIOCSETD, &ldisc); //application:ldisc=25
-->tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) /* tty_io.c*/
-->tiocsetd(tty, p)
-->tty_set_ldisc(tty, ldisc)
-->tty->ldisc = new_ldisc; /* Now set up the new line discipline. */
至此完成了新的ldisc设置。
4.推荐几个Linux uart的博客
http://www.uml.org.cn/embeded/201209071.asp
http://www.wowotech.net/linux_kenrel/183.html
http://blog.csdn.net/goodluckwhh/article/details/13368279