一、源码框架回顾
shell读数据,一开始的时候没有就休眠。数据从串口发送到驱动,驱动接收到中断,驱动读取串口数据,这个数据会传给行规程。
行规程获取到数据后,会回显。按下删除就删除一个字符,按下回车,就返回一个命令。如果找不到就提示找不到命令。
drivers/tty/tty_ldisc.c
int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) //行规程注册函数
搜索它的使用情况就可以看到有多少的行规程
# line filename / context / line
1 701 include/linux/tty.h <<GLOBAL>>
extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
2 77 drivers/tty/tty_ldisc.c <<GLOBAL>>
EXPORT_SYMBOL(tty_register_ldisc);
3 701 include/linux/tty.h <<GLOBAL>>
extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
4 837 drivers/bluetooth/hci_ldisc.c <<hci_uart_init>>
err = tty_register_ldisc(N_HCI, &hci_uart_ldisc);
5 298 input/serio/serport.c <<serport_init>>
retval = tty_register_ldisc(N_MOUSE, &serport_ldisc);
6 763 isdn/gigaset/ser-gigaset.c <<ser_gigaset_init>>
rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc);
7 858 misc/ti-st/st_core.c <<st_core_init>>
err = tty_register_ldisc(N_TI_WL, &st_ldisc_ops);
8 405 net/caif/caif_serial.c <<register_ldisc>>
result = tty_register_ldisc(N_CAIF, &caif_ldisc);
//...
16 137 pps/clients/pps-ldisc.c <<pps_tty_init>>
err = tty_register_ldisc(N_PPS, &pps_ldisc_ops);
17 189 staging/speakup/spk_ttyio.c <<spk_ttyio_register_ldisc>>
if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops))
//...
26 473 nfc/nci/uart.c <<nci_uart_init>>
return tty_register_ldisc(N_NCI, &nci_uart_ldisc);
27 503 soc/omap/ams-delta.c <<ams_delta_cx20442_init>>
ret = tty_register_ldisc(N_V253, &cx81801_ops);
有各种行规程,声卡的、网络的、蓝牙的。找一个最通用的驱动来分析drivers\tty\n_tty.c
二、读之前通过open确定了行规层
2.1 行规程注册
在tty设备中最常用的就是它,n_tty
文件:drivers\tty\n_tty.c
void __init n_tty_init(void)
{
tty_register_ldisc(N_TTY, &n_tty_ops); //使用N_TTY来找到行规程
}
以后可以通过标号N_TTY找到这个行规程。搜索使用的地方就是:
Cscope tag: N_TTY
# line filename / context / line
//...
7 2501 drivers/tty/n_tty.c <<n_tty_init>>
tty_register_ldisc(N_TTY, &n_tty_ops);
8 66 drivers/tty/tty_ldisc.c <<tty_register_ldisc>>
if (disc < N_TTY || disc >= NR_LDISCS)
9 96 drivers/tty/tty_ldisc.c <<tty_unregister_ldisc>>
if (disc < N_TTY || disc >= NR_LDISCS)
10 171 drivers/tty/tty_ldisc.c <<tty_ldisc_get>>
if (disc < N_TTY || disc >= NR_LDISCS)
11 532 drivers/tty/tty_ldisc.c <<tty_ldisc_restore>>
if (tty_ldisc_failto(tty, N_TTY) < 0 &&
12 676 drivers/tty/tty_ldisc.c <<tty_ldisc_reinit>>
BUG_ON(disc == N_TTY);
13 747 drivers/tty/tty_ldisc.c <<tty_ldisc_hangup>>
tty_ldisc_reinit(tty, N_TTY) < 0)
14 821 drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); //这里就获得了行规程的结构
继续反向查找
# line filename / context / line
1 706 include/linux/tty.h <<GLOBAL>>
extern int __must_check tty_ldisc_init(struct tty_struct *tty);
2 706 include/linux/tty.h <<GLOBAL>>
extern int __must_check tty_ldisc_init(struct tty_struct *tty);
3 2842 drivers/tty/tty_io.c <<alloc_tty_struct>>
if (tty_ldisc_init(tty)) { //在这里调用了tty_ldisc_init
4 819 drivers/tty/tty_ldisc.c <<tty_ldisc_init>>
int tty_ldisc_init(struct tty_struct *tty)
2.2 open设备时确定行规程
tty_open:打开串口时,会调用tty_open
tty_open_by_driver:调用driver来打开一个tty结构体
tty_init_dev:分配和设置tty结构体
tty = alloc_tty_struct(driver, idx); //分配一个行规程
tty_ldisc_init(tty); //初始化行规程
struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); //根据N_TTY整数找到之前的行规程
tty->ldisc = ld; //行规程会放在tty结构体里面,以后使用它来处理数据
三、read过程分析
流程为:
- APP读
- 使用行规程来读
- 无数据则休眠
- UART接收到数据,产生中断
- 中断程序从硬件上读入数据
- 发给行规程
- 行规程处理后存入buffer
- 行规程唤醒APP
- APP被唤醒后,从行规程buffer中读入数据,返回
3.1 tty_read
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
int i;
struct inode *inode = file_inode(file);
struct tty_struct *tty = file_tty(file);
struct tty_ldisc *ld;
//...
if (ld->ops->read)
i = ld->ops->read(tty, file, buf, count); //用到了行规程里的ops的read函数
else
i = -EIO;
tty_ldisc_deref(ld);
if (i > 0)
tty_update_time(&inode->i_atime);
return i;
}
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr)
{
struct n_tty_data *ldata = tty->disc_data;
unsigned char __user *b = buf;
DEFINE_WAIT_FUNC(wait, woken_wake_function); //这里申明了一个等待队列,没有数据就休眠
int c;
int minimum, time;
ssize_t retval = 0;
long timeout;
int packet;
size_t tail;
c = job_control(tty, file);
if (c < 0)
return c;
/*
* Internal serialization of reads.
*/
if (file->f_flags & O_NONBLOCK) {
if (!mutex_trylock(&ldata->atomic_read_lock))
return -EAGAIN;
} else {
if (mutex_lock_interruptible(&ldata->atomic_read_lock))
return -ERESTARTSYS;
}
down_read(&tty->termios_rwsem);
minimum = time = 0;
timeout = MAX_SCHEDULE_TIMEOUT;
//...
packet = tty->packet;
tail = ldata->read_tail;
add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (packet && tty->link->ctrl_status) {
//...
}
if (!input_available_p(tty, 0)) {
up_read(&tty->termios_rwsem);
tty_buffer_flush_work(tty->port);
down_read(&tty->termios_rwsem);
if (!input_available_p(tty, 0)) { //如果没有数据就等待
//...
timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
timeout); //没有数据就休眠
down_read(&tty->termios_rwsem);
continue;
}
}
if (ldata->icanon && !L_EXTPROC(tty)) {
retval = canon_copy_from_read_buf(tty, &b, &nr); //有数据后读取数据、返回
if (retval)
break;
} else {
int uncopied;
/* Deal with packet mode. */
if (packet && b == buf) {
if (put_user(TIOCPKT_DATA, b)) {
retval = -EFAULT;
break;
}
b++;
nr--;
}
uncopied = copy_from_read_buf(tty, &b, &nr); //行规程读函数
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
retval = -EFAULT;
break;
}
}
n_tty_check_unthrottle(tty);
//...
}
//...
return retval;
}
copy_from_read_buf函数解析
static int copy_from_read_buf(struct tty_struct *tty,
unsigned char __user **b,
size_t *nr)
{
//...
if (n) {
unsigned char *from = read_buf_addr(ldata, tail); //从行规程读取数据
retval = copy_to_user(*b, from, n); //返回给App层
n -= retval;
is_eof = n == 1 && *from == EOF_CHAR(tty);
tty_audit_add_data(tty, from, n);
zero_buffer(tty, from, n);
smp_store_release(&ldata->read_tail, ldata->read_tail + n);
//...
}
return retval;
}
四、数据的源头
drivers\tty\serial\imx.c
函数:imx_rxint,数据存入对应的tty_port,也就是每个tty_port对应一个串口
static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
unsigned long flags;
spin_lock_irqsave(&sport->port.lock, flags);
while (imx_uart_readl(sport, USR2) & USR2_RDR) { //读取硬件状态
u32 usr2;
flg = TTY_NORMAL;
sport->port.icount.rx++; //在对应的uart_port中更新统计信息
rx = imx_uart_readl(sport, URXD0); //得到数据
usr2 = imx_uart_readl(sport, USR2);
if (usr2 & USR2_BRCD) {
imx_uart_writel(sport, USR2_BRCD, USR2);
if (uart_handle_break(&sport->port))
continue;
}
//各种记录数据
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
goto out;
if (tty_insert_flip_char(port, rx, flg) == 0) //把数据存入tty_port里的tty_buffer
sport->port.icount.buf_overrun++;
}
out:
spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port); //通知行规程来处理
return IRQ_HANDLED;
}
void tty_flip_buffer_push(struct tty_port *port)
{
tty_schedule_flip(port);
}
void tty_schedule_flip(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
/* paired w/ acquire in flush_to_ldisc(); ensures
* flush_to_ldisc() sees buffer data.
*/
smp_store_release(&buf->tail->commit, buf->tail->used);
queue_work(system_unbound_wq, &buf->work); //使用工作队列来处理,对应flush_to_ldisc函数
}
在imx.c的porbe函数里有注册中断函数的过程:
static int imx_uart_probe(struct platform_device *pdev)
{
//...
rxirq = platform_get_irq(pdev, 0); //从设备树里获取的rxirq的中断号
txirq = platform_get_irq(pdev, 1);
rtsirq = platform_get_irq(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
sport->port.membase = base;
sport->port.type = PORT_IMX,
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
sport->port.ops = &imx_uart_pops;
sport->port.rs485_config = imx_uart_rs485_config;
sport->port.flags = UPF_BOOT_AUTOCONF;
timer_setup(&sport->timer, imx_uart_timeout, 0);
//...
if (txirq > 0) {
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_rxint, 0,
dev_name(&pdev->dev), sport); //在这里使用rxirq和imx_uart_rxint注册中断函数
//...
} else {
ret = devm_request_irq(&pdev->dev, rxirq, imx_uart_int, 0,
dev_name(&pdev->dev), sport);
if (ret) {
dev_err(&pdev->dev, "failed to request irq: %d\n", ret);
return ret;
}
}
imx_uart_ports[sport->port.line] = sport;
platform_set_drvdata(pdev, sport);
return uart_add_one_port(&imx_uart_uart_driver, &sport->port);
}