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);
-
}
阅读(791) | 评论(0) | 转发(0) |