当前位置:首页 > 芯闻号 > 充电吧
[导读]TTY驱动程序架构 1. TTY感念解析 在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。   串口终端(/dev/ttyS*) 串口终

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);
}

          

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭