一文了解TTY驅(qū)動(dòng)程序架構(gòu)
TTY驅(qū)動(dòng)程序架構(gòu)
1. TTY感念解析
在Linux系統(tǒng)中,終端是一類字符型設(shè)備,它包括多種類型,通常使用tty來簡稱各種類型的終端設(shè)備。
? 串口終端(/dev/ttyS*)
串口終端是使用計(jì)算機(jī)串口連接的終端設(shè)備。Linux把每個(gè)串行端口都看作是一個(gè)字符設(shè)備。這些串行端口所對(duì)應(yīng)的設(shè)備名稱是 /dev/ttySAC0;
/dev/ttySAC1……
? 控制臺(tái)終端(/dev/console)
在Linux系統(tǒng)中,計(jì)算機(jī)的輸出設(shè)備通常被稱為控制臺(tái)終端(Console),這里特指printk信息輸出到的設(shè)備。
/dev/console是一個(gè)虛擬的設(shè)備,它需要映射到真正的tty上,比如通過內(nèi)核啟動(dòng)參數(shù)” console=ttySAC0”就把console映射到了串口0。
? 虛擬終端(/dev/tty*)
當(dāng)用戶登錄時(shí),使用的是虛擬終端。使用Ctcl+Alt+[F1—F6]組合鍵時(shí),我們就可以切換到tty1、tty2、tty3等上面去。tty1–tty6等稱為虛擬終端,而tty0則是當(dāng)前所使用虛擬終端的一個(gè)別名.
串口終端和控制臺(tái)終端是從內(nèi)核啟動(dòng)時(shí)就關(guān)聯(lián)的,內(nèi)核通過printk來調(diào)用控制臺(tái)終端,從而在串口中顯示。
串口終端又有多種虛擬終端tty1,tty2,tty3...等用來個(gè)應(yīng)用程序使用。
2.TTY架構(gòu)分析?
Linux tty子系統(tǒng)包含:tty核心,tty線路規(guī)程和tty驅(qū)動(dòng)。
①?tty核心是對(duì)整個(gè)tty設(shè)備的抽象,對(duì)用戶提供統(tǒng)一的接口。
②?tty線路規(guī)程是對(duì)傳輸數(shù)據(jù)的格式化。
③?tty驅(qū)動(dòng)則是面向tty設(shè)備的硬件驅(qū)動(dòng)。
3.查看tty調(diào)用流程(回溯dump_stack())
linux-tq2440/drivers/serial/samsung.c:
在s3c24xx_serial_start_tx()中添加一行:
dump_stack();
目的是調(diào)用回溯函數(shù)。然后編譯內(nèi)核make uImage ARCH=arm CROSS_COMPILE=arm-linux-,啟動(dòng)開發(fā)板調(diào)試:
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); ? ? ? ? ? ? ? ? ? ?//這里調(diào)用了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); ? ? ?//這里調(diào)用了do_tty_write,ld就是結(jié)構(gòu)體tty_ldisc(tty線路規(guī)程)
????tty_ldisc_deref(ld);
????return ret;
}
tty_write>>struct tty_ldisc:
struct tty_ldisc {
????struct tty_ldisc_ops *ops; ? ? ? ? ? ? ? ? ? //此處就是它調(diào)用的tty_ldisc_ops結(jié)構(gòu)
????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, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//在這,所以調(diào)用do_tty_write其實(shí)就是調(diào)用了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結(jié)構(gòu),調(diào)用了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); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//這里調(diào)用了uart_start
????return ret;
}
同一個(gè)文件:
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,估計(jì)就是指向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); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//這里調(diào)用了__uart_start
????spin_unlock_irqrestore(&port->lock, flags);
}
? ? ? ? ??