一文了解TTY驱动程序架构
扫描二维码
随时随地手机看文章
TTY驱动程序架构
1. TTY感念解析
在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。
串口终端(/dev/ttyS*)
串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是 /dev/ttySAC0;
/dev/ttySAC1……
控制台终端(/dev/console)
在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。
/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数” console=ttySAC0”就把console映射到了串口0。
虚拟终端(/dev/tty*)
当用户登录时,使用的是虚拟终端。使用Ctcl+Alt+[F1—F6]组合键时,我们就可以切换到tty1、tty2、tty3等上面去。tty1–tty6等称为虚拟终端,而tty0则是当前所使用虚拟终端的一个别名.
串口终端和控制台终端是从内核启动时就关联的,内核通过printk来调用控制台终端,从而在串口中显示。
串口终端又有多种虚拟终端tty1,tty2,tty3...等用来个应用程序使用。
2.TTY架构分析
Linux tty子系统包含:tty核心,tty线路规程和tty驱动。
① tty核心是对整个tty设备的抽象,对用户提供统一的接口。
② tty线路规程是对传输数据的格式化。
③ tty驱动则是面向tty设备的硬件驱动。
3.查看tty调用流程(回溯dump_stack())
linux-tq2440/drivers/serial/samsung.c:
在s3c24xx_serial_start_tx()中添加一行:
dump_stack();
目的是调用回溯函数。然后编译内核make uImage ARCH=arm CROSS_COMPILE=arm-linux-,启动开发板调试:
Backtrace:
[] (dump_backtrace+0x0/0x10c) from [] (dump_stack+0x18/0x1c)
r7:c394c802 r6:00000000 r5:c38bda00 r4:c04caf20
[] (dump_stack+0x0/0x1c) from [] (s3c24xx_serial_start_tx+0x14/0x64)
[] (s3c24xx_serial_start_tx+0x0/0x64) from [] (uart_start+0x68/0x6c)
r5:c38bda00 r4:60000013
[] (uart_start+0x0/0x6c) from [] (uart_write+0xc0/0xe4)
r5:c38bda00 r4:00000000
[] (uart_write+0x0/0xe4) from [] (n_tty_write+0x1d8/0x448)
[] (n_tty_write+0x0/0x448) from [] (tty_write+0x14c/0x244)
[] (tty_write+0x0/0x244) from [] (redirected_tty_write+0x88/0x98)
[] (redirected_tty_write+0x0/0x98) from [] (vfs_write+0xb4/0xe8)
r9:c39ca000 r8:c0045008 r7:00000004 r6:c39cbf78 r5:40000000
r4:c3953680
[] (vfs_write+0x0/0xe8) from [] (sys_write+0x4c/0x84)
r7:00000004 r6:c3953680 r5:00000000 r4:00000000
[] (sys_write+0x0/0x84) from [] (ret_fast_syscall+0x0/0x2c)
r6:001d27f8 r5:40000000 r4:00000002
redirected_tty_write:
ssize_t redirected_tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct file *p = NULL;
spin_lock(&redirect_lock);
if (redirect) {
get_file(redirect);
p = redirect;
}
spin_unlock(&redirect_lock);
if (p) {
ssize_t res;
res = vfs_write(p, buf, count, &p->f_pos);
fput(p);
return res;
}
return tty_write(file, buf, count, ppos); //这里调用了tty_write
}
tty_write:
static ssize_t tty_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct tty_struct *tty;
struct inode *inode = file->f_path.dentry->d_inode;
ssize_t ret;
struct tty_ldisc *ld;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode, "tty_write"))
return -EIO;
if (!tty || !tty->ops->write ||
(test_bit(TTY_IO_ERROR, &tty->flags)))
return -EIO;
/* Short term debug to catch buggy drivers */
if (tty->ops->write_room == NULL)
printk(KERN_ERR "tty driver %s lacks a write_room method.n",
tty->driver->name);
ld = tty_ldisc_ref_wait(tty);
if (!ld->ops->write)
ret = -EIO;
else
ret = do_tty_write(ld->ops->write, tty, file, buf, count); //这里调用了do_tty_write,ld就是结构体tty_ldisc(tty线路规程)
tty_ldisc_deref(ld);
return ret;
}
tty_write>>struct tty_ldisc:
struct tty_ldisc {
struct tty_ldisc_ops *ops; //此处就是它调用的tty_ldisc_ops结构
int refcount;
};
然后tty_ldisc中的tty_ldisc_ops在n_tty.c中被定义好了:
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read = n_tty_read,
.write = n_tty_write, //在这,所以调用do_tty_write其实就是调用了n_tty_write
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
n_tty_write:
/**
* n_tty_write - write function for tty
* @tty: tty device
* @file: file object
* @buf: userspace buffer pointer
* @nr: size of I/O
*
* Write function of the terminal device. This is serialized with
* respect to other write callers but not to termios changes, reads
* and other such events. Since the receive code will echo characters,
* thus calling driver write methods, the output_lock is used in
* the output processing functions called here as well as in the
* echo processing function to protect the column state and space
* left in the buffer.
*
* This code must be sure never to sleep through a hangup.
*
* Locking: output_lock to protect column state and space left
* (note that the process_output*() functions take this
* lock themselves)
*/
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
const unsigned char *buf, size_t nr)
{
const unsigned char *b = buf;
DECLARE_WAITQUEUE(wait, current);
int c;
ssize_t retval = 0;
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
retval = tty_check_change(tty);
if (retval)
return retval;
}
/* Write out any echoed characters that are still pending */
process_echoes(tty);
add_wait_queue(&tty->write_wait, &wait);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
while (nr > 0) {
ssize_t num = process_output_block(tty, b, nr);
if (num < 0) {
if (num == -EAGAIN)
break;
retval = num;
goto break_out;
}
b += num;
nr -= num;
if (nr == 0)
break;
c = *b;
if (process_output(c, tty) < 0)
break;
b++; nr--;
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
} else {
while (nr > 0) {
c = tty->ops->write(tty, b, nr); //这里的tty->ops->write和上面的类似,tty_struct结构,调用了uart_write。
if (c < 0) {
retval = c;
goto break_out;
}
if (!c)
break;
b += c;
nr -= c;
}
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
break_out:
__set_current_state(TASK_RUNNING);
remove_wait_queue(&tty->write_wait, &wait);
if (b - buf != nr && tty->fasync)
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
return (b - buf) ? b - buf : retval;
}
serial_core.c:
static int
uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port;
struct circ_buf *circ;
unsigned long flags;
int c, ret = 0;
/*
* This means you called this function _after_ the port was
* closed. No cookie for you.
*/
if (!state) {
WARN_ON(1);
return -EL3HLT;
}
port = state->port;
circ = &state->info.xmit;
if (!circ->buf)
return 0;
spin_lock_irqsave(&port->lock, flags);
while (1) {
c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
if (count < c)
c = count;
if (c <= 0)
break;
memcpy(circ->buf + circ->head, buf, c);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
spin_unlock_irqrestore(&port->lock, flags);
uart_start(tty); //这里调用了uart_start
return ret;
}
同一个文件:
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port); //这里的port->ops->start_tx,估计就是指向s3c24xx_serial_start_tx
}
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty); //这里调用了__uart_start
spin_unlock_irqrestore(&port->lock, flags);
}