Linux/Android 串口总结

前言

简单总结了下 Linux/Android 串口相关操作

参考资料:
《Linux 设备驱动开发详解》
《Linux 设备驱动程序》
《Linux 内核完全注释》
《Unix 环境高级编程》

协议相关

话不多说了,嵌入式常用串口为三根线: GND/RX/TX, 接线时需要交叉连接。
即:
GNC <> GND
RX <
> TX
TX <==> RX

串口配置如下:
在这里插入图片描述
抓取波形图如下:
在这里插入图片描述

解释如下:
在这里插入图片描述

软件相关

Kernel

终端概念

在这里插入图片描述
在这里插入图片描述

汇总:
在这里插入图片描述

tty 核心概览

在这里插入图片描述
在这里插入图片描述

上面说的有点绕,甚至还有点逻辑不清,线路规程吧,举个简单例子,以在终端输入命令来说,键盘硬件输出的都是按下的键码,
但是在终端上,最终会转化为回车,换行等各种操作,这个将键码转换为回车换行的就是一种线程规程。

结构体

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

串口核心结构体

在这里插入图片描述

串口驱动编写流程

1. uart_register_driver(): 注册串口驱动 uart_driver 
		///////////////////////////////////////////////////////////////////////////////
		// 封装接口: 不需要我们做什么
		1.1 alloc_tty_driver(): 分配 tty_driver  结构体
		1.2 设置 tty_driver 结构体
		1.3 初始化各个串口端口
		1.4 tty_register_driver(): 注册 tty_driver 驱动 
		///////////////////////////////////////////////////////////////////////////////
2.  硬件及相关结构体初始化
3. uart_add_one_port(): 注册  uart_port 结构体
4. uart_remove_one_port(): 注销 uart_port 结构体
5. uart_unregister_driver(): 注销 uart_driver 结构体

参考例子

代码:Samsung.c (drivers\tty\serial)
平台:S3C24xx

初始化流程:

// Samsung.c (drivers\tty\serial)
module_init(s3c24xx_serial_modinit);
static int __init s3c24xx_serial_modinit//(void)
{
    int ret;

    ret = uart_register_driver(&s3c24xx_uart_drv);
                                        //    // 结构体中有名字什么的,只起到领头作用
                                        //    static struct uart_driver s3c24xx_uart_drv = {
                                        //        .owner      = THIS_MODULE,
                                        //        .driver_name    = "s3c2410_serial",
                                        //        .nr     = CONFIG_SERIAL_SAMSUNG_UARTS,
                                        //        .cons       = S3C24XX_SERIAL_CONSOLE,
                                        //        .dev_name   = S3C24XX_SERIAL_NAME,
                                        //        .major      = S3C24XX_SERIAL_MAJOR,
                                        //        .minor      = S3C24XX_SERIAL_MINOR,
                                        //    };
                        int uart_register_driver//(struct uart_driver *drv)
                        {
                            struct tty_driver *normal;
                            int i, retval;

                            BUG_ON(drv->state);

                            /*
                             * Maybe we should be using a slab cache for this, especially if
                             * we have a large number of ports to handle.
                             */
                            // 0. 分配 uart_state 结构体的内存空间,后面串口操作全部会根据此结构快速中转
                            drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
                            if (!drv->state)
                                goto out;

                            // 1. 分配 tty_driver 
                            normal = alloc_tty_driver(drv->nr);
                            if (!normal)
                                goto out_kfree;

                            // 2. 设置 tty_driver 
                            drv->tty_driver = normal;

                            normal->driver_name = drv->driver_name;
                            normal->name        = drv->dev_name;
                            normal->major       = drv->major;
                            normal->minor_start = drv->minor;
                            normal->type        = TTY_DRIVER_TYPE_SERIAL;
                            normal->subtype     = SERIAL_TYPE_NORMAL;
                            normal->init_termios    = tty_std_termios;
                                                            // 默认的链路设置 
                                                            struct ktermios tty_std_termios = { /* for the benefit of tty drivers  */
                                                                .c_iflag = ICRNL | IXON,
                                                                .c_oflag = OPOST | ONLCR,
                                                                .c_cflag = B38400 | CS8 | CREAD | HUPCL,
                                                                .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
                                                                       ECHOCTL | ECHOKE | IEXTEN,
                                                                .c_cc = INIT_C_CC,
                                                                .c_ispeed = 38400,
                                                                .c_ospeed = 38400
                                                            };
                            normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
                            normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
                            normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
                            normal->driver_state    = drv;
                            tty_set_operations(normal, &uart_ops);
                                                    ////////////////////////////////////////////////////////////
                                                    //【二级操作函数】
                                                    static const struct tty_operations uart_ops = {
                                                        .open       = uart_open,
                                                        .close      = uart_close,
                                                        .write      = uart_write,
                                                        .put_char   = uart_put_char,
                                                        .flush_chars    = uart_flush_chars,
                                                        .write_room = uart_write_room,
                                                        .chars_in_buffer= uart_chars_in_buffer,
                                                        .flush_buffer   = uart_flush_buffer,
                                                        .ioctl      = uart_ioctl,
                                                        .throttle   = uart_throttle,
                                                        .unthrottle = uart_unthrottle,
                                                        .send_xchar = uart_send_xchar,
                                                        .set_termios    = uart_set_termios,
                                                        .set_ldisc  = uart_set_ldisc,
                                                        .stop       = uart_stop,
                                                        .start      = uart_start,
                                                        .hangup     = uart_hangup,
                                                        .break_ctl  = uart_break_ctl,
                                                        .wait_until_sent= uart_wait_until_sent,
                                                    #ifdef CONFIG_PROC_FS
                                                        .proc_fops  = &uart_proc_fops,
                                                    #endif
                                                        .tiocmget   = uart_tiocmget,
                                                        .tiocmset   = uart_tiocmset,
                                                        .get_icount = uart_get_icount,
                                                    #ifdef CONFIG_CONSOLE_POLL
                                                        .poll_init  = uart_poll_init,
                                                        .poll_get_char  = uart_poll_get_char,
                                                        .poll_put_char  = uart_poll_put_char,
                                                    #endif
                                                    };


                            /*
                             * Initialise the UART state(s).
                             */
                            // 3. 初始化各个串口端口
                            for (i = 0; i < drv->nr; i++) {
                                struct uart_state *state = drv->state + i;
                                struct tty_port *port = &state->port;

                                tty_port_init(port);
                                            void tty_port_init//(struct tty_port *port)
                                            {
                                                memset(port, 0, sizeof(*port));
                                                init_waitqueue_head(&port->open_wait);
                                                init_waitqueue_head(&port->close_wait);
                                                init_waitqueue_head(&port->delta_msr_wait);
                                                mutex_init(&port->mutex);
                                                mutex_init(&port->buf_mutex);
                                                spin_lock_init(&port->lock);
                                                port->close_delay = (50 * HZ) / 100;
                                                port->closing_wait = (3000 * HZ) / 100;
                                                kref_init(&port->kref);
                                            }

                                port->ops = &uart_port_ops;
                                                static const struct tty_port_operations uart_port_ops = {
                                                    .activate   = uart_port_activate,
                                                    .shutdown   = uart_port_shutdown,
                                                    .carrier_raised = uart_carrier_raised,
                                                    .dtr_rts    = uart_dtr_rts,
                                                };

                                port->close_delay     = HZ / 2; /* .5 seconds */
                                port->closing_wait    = 30 * HZ;/* 30 seconds */
                            }

                             // 4. 注册 tty_driver 驱动 
                            retval = tty_register_driver(normal);
                                            /////////////////////////////////////////////////////////////////////////////
                                            // 字符设备 
                                            alloc_chrdev_region(&dev, driver->minor_start, driver->num, driver->name);
                                            register_chrdev_region(dev, driver->num, driver->name);
                                            cdev_init(&driver->cdev, &tty_fops);
                                                        ////////////////////////////////////////////////////////////////
                                                        // 字符设备操作函数【一级操作函数】
                                                        static const struct file_operations tty_fops = {
                                                            .llseek     = no_llseek,
                                                            .read       = tty_read,
                                                            .write      = tty_write,
                                                            .poll       = tty_poll,
                                                            .unlocked_ioctl = tty_ioctl,
                                                            .compat_ioctl   = tty_compat_ioctl,
                                                            .open       = tty_open,
                                                            .release    = tty_release,
                                                            .fasync     = tty_fasync,
                                                        };
                                            cdev_add(&driver->cdev, dev, driver->num);
                                            tty_register_device(driver, i, NULL);
                                                device_create(tty_class, device, dev, NULL, name);

                            if (retval >= 0)
                                return retval;

                            put_tty_driver(normal);
                        out_kfree:
                            kfree(drv->state);
                        out:
                            return -ENOMEM;
                        }
    if (ret < 0) {
        printk(KERN_ERR "failed to register UART driver\n");
        return -1;
    }

    return platform_driver_register(&samsung_serial_driver);
                    // 调用对应的 probe 函数
                    s3c24xx_serial_probe()
                        // 1. 获得串口端口设置 
                        ourport = &s3c24xx_serial_ports[probe_index];
                                            static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
                                                [0] = {
                                                    .port = {
                                                        .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
                                                        .iotype     = UPIO_MEM,
                                                        .uartclk    = 0,
                                                        .fifosize   = 16,
                                                        .ops        = &s3c24xx_serial_ops,
                                                        .flags      = UPF_BOOT_AUTOCONF,
                                                        .line       = 0,
                                                    }
                                                },
                                                [1] = {
                                                    .port = {
                                                        .lock       = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
                                                        .iotype     = UPIO_MEM,
                                                        .uartclk    = 0,
                                                        .fifosize   = 16,
                                                        .ops        = &s3c24xx_serial_ops,//【三级操作函数】
                                                                            static struct uart_ops s3c24xx_serial_ops = {
                                                                                .pm     = s3c24xx_serial_pm,
                                                                                .tx_empty   = s3c24xx_serial_tx_empty,
                                                                                .get_mctrl  = s3c24xx_serial_get_mctrl,
                                                                                .set_mctrl  = s3c24xx_serial_set_mctrl,
                                                                                .stop_tx    = s3c24xx_serial_stop_tx,
                                                                                .start_tx   = s3c24xx_serial_start_tx,
                                                                                .stop_rx    = s3c24xx_serial_stop_rx,
                                                                                .enable_ms  = s3c24xx_serial_enable_ms,
                                                                                .break_ctl  = s3c24xx_serial_break_ctl,
                                                                                .startup    = s3c24xx_serial_startup,
                                                                                .shutdown   = s3c24xx_serial_shutdown,
                                                                                .set_termios    = s3c24xx_serial_set_termios,
                                                                                .type       = s3c24xx_serial_type,
                                                                                .release_port   = s3c24xx_serial_release_port,
                                                                                .request_port   = s3c24xx_serial_request_port,
                                                                                .config_port    = s3c24xx_serial_config_port,
                                                                                .verify_port    = s3c24xx_serial_verify_port,
                                                                            };
                                                        .flags      = UPF_BOOT_AUTOCONF,
                                                        .line       = 1,
                                                    }
                                                },
                                                。。。
                                            };
                        ourport->drv_data = s3c24xx_get_driver_data(pdev);
                        // 2. 初始化串口端口
                        s3c24xx_serial_init_port(ourport, pdev);
                                    // 获得资源,初始化 fifo、获得中断号、获得时钟等

                        // 3. 注册串口端口,建立 uart_driver 和 uart_port 之是的联系
                        uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
                            uart_configure_port(drv, state, uport);
                            // 注册串口设备
                            tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);

                        // 4. 属性文件与cpu设置
                        device_create_file(&pdev->dev, &dev_attr_clock_source);
                        s3c24xx_serial_cpufreq_register(ourport);

}

杂项流程汇总

框架

在这里插入图片描述
在这里插入图片描述

流程汇总

// 打开串口,会注册串口处理中断

APP:    open()
-----------------------------------------------------------------
tty_open
    // 初始化 tty 设备
    tty_init_dev()
        // 分析一个 tty 结构体 
        alloc_tty_struct();
        initialize_tty_struct()
            tty_ldisc_init(tty);
                // 获得下面终端设置的 N_TTY 默认的操作函数集,即 tty_ldisc_N_TTY
                struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
                tty_ldisc_assign(tty, ld);
                    tty->ldisc = ld;
                retval = tty_ldisc_setup(tty, tty->link);
                    retval = tty_ldisc_open(tty, ld);
                        // 调用线程规程的 open 函数,即 tty_ldisc_N_TTY.ops.open 
                        ret = ld->ops->open(tty);

            tty_buffer_init(tty);
                // 这个是在 tty_flip_buffer_push() 中调用的
                INIT_WORK(&tty->buf.work, flush_to_ldisc);

    tty->ops->open(tty, filp);
            // 在初始化阶段设置成了 tty_operations 的,即 uart_ops
            uart_open
                // 设置 uart_state 结构体,这是在 uart_register_driver() 中分配的空间 
                struct uart_state *state = drv->state + line;
                tty->driver_data = state;

                uart_startup(tty, state, 0);
                    uart_port_startup(tty, state, init_hw);
                        // 调用驱动里面设置的 uart_ops 操作函数集
                        uport->ops->startup(uport);
                                s3c24xx_serial_startup
                                        // 1. 使能串口接收功能 
                                        rx_enabled(port) = 1;
                                        // 2. 为数据接收注册中断处理程序
                                        request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
                                                      s3c24xx_serial_portname(port), ourport);
                                        // 3. 使能串口发送功能
                                        tx_enabled(port) = 1;
                                        // 4. 为数据发送注册中断处理程序
                                        request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
                                                      s3c24xx_serial_portname(port), ourport);


                        // 设置波特率
                        uart_change_speed(tty, state, NULL);
                            uport->ops->set_termios(uport, termios, old_termios);

// 关于 N_TTY 的来历

start_kernel(void)
    console_init();
        /* Setup the default TTY line discipline. */
        tty_ldisc_begin();
            /* Setup the default TTY line discipline. */
            (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
                                            //    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,
                                            //        .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
                                            //    };

                        tty_ldiscs[disc] = new_ldisc;
                        new_ldisc->num = disc;
                        new_ldisc->refcount = 0;

// 串口发送

APP:    write()
-----------------------------------------------------------------                            
tty_write
    do_tty_write(ld->ops->write, tty, file, buf, count);
            ret = write(tty, file, tty->write_buf, size);
                /////////////////////////////////////////////////////////////////////////////////////////////////
                // 即调用对应的 ld->ops->write  函数,这个函数在 open() 中设置为 tty_ldisc_N_TTY
                n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)
                        while (1) {
                            process_output_block(tty, b, nr);    
                                // 查看可用缓冲空间
                                tty_write_room(tty);
                                    if (tty->ops->write_room)
                                            return tty->ops->write_room(tty);
                                /////////////////////////////////////////////////////////////////////////////////
                                // 调用串口注册函数中设置的 uart_ops->uart_write,他是 tty_operations 类型的
                                i = tty->ops->write(tty, buf, i);
                                        uart_write
                                            // 获得 uart_state 结构体,这是在 open 时设置的 
                                            struct uart_state *state = tty->driver_data;
                                            uart_start(tty);
                                                __uart_start(tty);
                                                    port->ops->start_tx(port);
                                                        ///////////////////////////////////////////////////////////////////
                                                        // 调用对应串口驱动程序中写的操作函数
                                                        s3c24xx_serial_start_tx
                                                                if (!tx_enabled(port)) {
                                                                    if (port->flags & UPF_CONS_FLOW)
                                                                        s3c24xx_serial_rx_disable(port);

                                                                    if (s3c24xx_serial_has_interrupt_mask(port))
                                                                        __clear_bit(S3C64XX_UINTM_TXD,
                                                                            portaddrl(port, S3C64XX_UINTM));
                                                                    else
                                                                        // 仅仅是使能中断来激活发送处理函数
                                                                        enable_irq(ourport->tx_irq);
                                                                    // 使能串口发送功能
                                                                    tx_enabled(port) = 1;
                                                                }
                        }

// 串口发送中断

static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
    struct s3c24xx_uart_port *ourport = id;
    struct uart_port *port = &ourport->port;
    struct circ_buf *xmit = &port->state->xmit;
    int count = 256;

    // 1. 判断  x_char 是否为 0 ,不为 0 ,则发判断 0
    if (port->x_char) {
        wr_regb(port, S3C2410_UTXH, port->x_char);
        port->icount.tx++;
        port->x_char = 0;
        goto out;
    }

    /* if there isn't anything more to transmit, or the uart is now
     * stopped, disable the uart and exit
    */
    // 2. 如果发送缓冲空或者驱动被设置为停止发送的状态,则取消发送
    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
        s3c24xx_serial_stop_tx(port);
        goto out;
    }

    /* try and drain the buffer... */
    // 3. 循环发送,循环条件:发送缓冲区不为空
    while (!uart_circ_empty(xmit) && count-- > 0) {
        // 3.1 发送 fifo 如果满,退出发送
        if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
            break;
        // 3.2 将要发送的字符写入发送寄存器
        wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
        // 3.3 修改循环缓冲的尾部位置
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        port->icount.tx++;//更新发送的统计量  
    }

    // 4. 如果发送缓冲中的剩余数据量 uart_circ_chars_pending < 256 则唤醒之前阻塞的发送进程 uart_write_wakeup
    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
        uart_write_wakeup(port);

    // 5. 如果发送缓冲空,则关闭发送使能
    if (uart_circ_empty(xmit))
        s3c24xx_serial_stop_tx(port);

 out:
    return IRQ_HANDLED;//函数出口,表示中断已经处理  
}

// 串口接收

APP:    read()
-----------------------------------------------------------------                            
tty_read
    if (ld->ops->read)
        i = (ld->ops->read)(tty, file, buf, count);
                /////////////////////////////////////////////////////////////////////////////////////////
                // 即调用对应的 ld->ops->read  函数,这个函数在 open() 中设置为 tty_ldisc_N_TTY
                n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)
                    add_wait_queue(&tty->read_wait, &wait);
                    while (nr) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        // 如果没有数据可读,调度其他程序
                        if (!input_available_p(tty, 0)) {
                        timeout = schedule_timeout(timeout);
                        }

                        // 如果有数据,则读从 tty_struct->read_buf 中读数据, 驱动有数据时,就会往这里写
                        uncopied = copy_from_read_buf(tty, &b, &nr);
                        uncopied += copy_from_read_buf(tty, &b, &nr);
                        
                    }
                    remove_wait_queue(&tty->read_wait, &wait);
                    __set_current_state(TASK_RUNNING);
                    // 设置剩余空间
                    n_tty_set_room(tty);

// 串口接收中断

static irqreturn_t s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
    struct s3c24xx_uart_port *ourport = dev_id;
    struct uart_port *port = &ourport->port;
    struct tty_struct *tty = port->state->port.tty;
    unsigned int ufcon, ch, flag, ufstat, uerstat;
    int max_count = 64;

    while (max_count-- > 0) {
        // 1. 读取 UFCON 寄存器 
        ufcon = rd_regl(port, S3C2410_UFCON);
        // 2. 读取 UFSTAT 寄存器 
        ufstat = rd_regl(port, S3C2410_UFSTAT);
        // 3. 如果接收 fifo 里的数据量为 0,则退出处理
        if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
            break;
        // 4. 读取 UERSTAT 寄存器 
        uerstat = rd_regl(port, S3C2410_UERSTAT);
        // 5. 从 URXH 寄存器中取出接收到的字符
        ch = rd_regb(port, S3C2410_URXH);
        // 6. 进行流控处理
        if (port->flags & UPF_CONS_FLOW) {
            int txe = s3c24xx_serial_txempty_nofifo(port);

            if (rx_enabled(port)) {
                if (!txe) {
                    rx_enabled(port) = 0;
                    continue;
                }
            } else {
                if (txe) {
                    ufcon |= S3C2410_UFCON_RESETRX;
                    wr_regl(port, S3C2410_UFCON, ufcon);
                    rx_enabled(port) = 1;
                    goto out;
                }
                continue;
            }
        }

        /* insert the character into the buffer */

        flag = TTY_NORMAL;
        port->icount.rx++;
        // 7. 根据 UERSTAT 寄存器的值,记录具体的错误类型
        if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
            dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
                ch, uerstat);

            /* check for break */
            if (uerstat & S3C2410_UERSTAT_BREAK) {
                dbg("break!\n");
                port->icount.brk++;
                if (uart_handle_break(port))
                    goto ignore_char;
            }

            if (uerstat & S3C2410_UERSTAT_FRAME)
                port->icount.frame++;
            if (uerstat & S3C2410_UERSTAT_OVERRUN)
                port->icount.overrun++;

            uerstat &= port->read_status_mask;

            if (uerstat & S3C2410_UERSTAT_BREAK)
                flag = TTY_BREAK;
            else if (uerstat & S3C2410_UERSTAT_PARITY)
                flag = TTY_PARITY;
            else if (uerstat & (S3C2410_UERSTAT_FRAME |
                        S3C2410_UERSTAT_OVERRUN))
                flag = TTY_FRAME;
        }
        // 8. 如果收到的是 sysrq 字符,进行特殊处理: uart_handle_sysrq_char()
        if (uart_handle_sysrq_char(port, ch))
            goto ignore_char;
        // 9. 把接收到的字符送进串口驱动的 read_buf: uart_insert_char()
        uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN, ch, flag);

 ignore_char:
        continue;
    }
    // 10. 把串口驱动接收到的数据送进线路规程的 read_buf , tty_flip_buffer_push()
    tty_flip_buffer_push(tty);

 out:
    return IRQ_HANDLED;
}

// 控制台初始化

start_kernel
    console_init();
        /* Setup the default TTY line discipline. */
        tty_ldisc_begin();
                /* Setup the default TTY line discipline. 
                设置 N_TTY 的默认操作函数为 tty_ldisc_N_TTY*/
                (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
                            // 设置 tty_ldiscs[] 数组对应项,这个数组保存各个 ldisc 类型的操作函数集
                            tty_ldiscs[disc] = new_ldisc;

        /*
         * set up the console device so that later boot sequences can
         * inform about problems etc..
         */
        call = __con_initcall_start;
        while (call < __con_initcall_end) {
            (*call)();
            call++;
        }

Linux

用户空间终端编程,整体框架如下图:
在这里插入图片描述
工作模式:
1. 规范模式:以行为处理单位
2. 非规范模式:输入字符不组成行

相关结构体

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

终端 I/O 函数摘要

在这里插入图片描述

特殊字符

在这里插入图片描述

操作示例

// 打开串口

/*	O_RDWR 读、写打开。
     * 	O_NOCTTY 如果p a t h n a m e指的是终端设备,则不将此设备分配作为此进程的控制终端。
     * 	O_NONBLOCK 如果p a t h n a m e指的是一个F I F O、一个块特殊文件或一个字符特殊文件,则此选择项为此文件的本次打开操作和后续的I / O操作设置非阻塞方式。
     *
     * 	O_RDONLY 只读打开。
		O_WRONLY 只写打开。
		O_APPEND 每次写时都加到文件的尾端。
		O_CREAT 若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。
		O_EXCL 如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。
		O_TRUNC 如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。
		O_SYNC 使每次w r i t e都等到物理I / O操作完成。
		O_NOCTTY:告诉Unix这个程序不想成为“控制终端”控制的程序,不说明这个标志的话,任何输入都会影响你的程序。
		O_NDELAY:告诉Unix这个程序不关心DCD信号线状态,即其他端口是否运行,不说明这个标志的话,该程序就会在DCD信号线为低电平时停止。
     * */
    m_nFd = open( "/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK); //support poll behavior

	// disable echo on serial lines
    if ( isatty( m_nFd ) )
	{
        struct termios  ios;
        tcgetattr( m_nFd, &ios );

        DEBUG__("iDasSerialPortService:ComManager:ComManager","开始设置波特率等参数 set to 115200bps, 8-N-1");// Set 8bit data, No parity, stop 1 bit (8N1):

        ios.c_cflag &= ~PARENB;//no parity
        ios.c_cflag &= ~CSTOPB;//stop 1 bit
        ios.c_cflag &= ~CSIZE;//Bit mask for data bits
        ios.c_cflag = B115200 | CS8 | CLOCAL | CREAD;//baud rate |8 bit data | Local line ,don't change ower of port| enable receiver
        ios.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF | IXANY);
        ios.c_oflag &= ~OPOST;  /*raw output*/
        ios.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  /*raw input*/

    	 tcsetattr( m_nFd, TCSANOW, &ios );
    }

读/写/关闭串口用标准 Linux 接口:

ret = read(m_nFd, buff, sizeof(buff) );
ret = write(m_nFd, start_buffer, len);
关闭串口:

Android

基于安卓 4.0 平台,通过 jni 往下使用标准 Linux 接口调用串口,相关文件下附
整体流程是这样的:

在 SerialHelper.java 类中
	调用 SerialPort.java 类初始化时
		调用 Jni 文件通过 open() 打开串口,
			然后返回文件描述符 fd
	然后就可以通过 SerialHelper.java 类中标准的读写接口往文件描述符里写数据了

注意需要添加相关权限。

Jni 文件

/*
 * Copyright 2009-2011 Cedric Priscal
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "SerialPort.h"

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

static speed_t getBaudrate(jint baudrate)
{
	switch(baudrate) {
	case 0: return B0;
	case 50: return B50;
	case 75: return B75;
	case 110: return B110;
	case 134: return B134;
	case 150: return B150;
	case 200: return B200;
	case 300: return B300;
	case 600: return B600;
	case 1200: return B1200;
	case 1800: return B1800;
	case 2400: return B2400;
	case 4800: return B4800;
	case 9600: return B9600;
	case 19200: return B19200;
	case 38400: return B38400;
	case 57600: return B57600;
	case 115200: return B115200;
	case 230400: return B230400;
	case 460800: return B460800;
	case 500000: return B500000;
	case 576000: return B576000;
	case 921600: return B921600;
	case 1000000: return B1000000;
	case 1152000: return B1152000;
	case 1500000: return B1500000;
	case 2000000: return B2000000;
	case 2500000: return B2500000;
	case 3000000: return B3000000;
	case 3500000: return B3500000;
	case 4000000: return B4000000;
	default: return -1;
	}
}

/*
 * Class:     android_serialport_SerialPort
 * Method:    open
 * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
 */
JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
  (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
{
	int fd;
	speed_t speed;
	jobject mFileDescriptor;

	/* Check arguments */
	{
		speed = getBaudrate(baudrate);
		if (speed == -1) {
			/* TODO: throw an exception */
			LOGE("Invalid baudrate");
			return NULL;
		}
	}

	/* Opening device */
	{
		jboolean iscopy;
		const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
		LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
		fd = open(path_utf, O_RDWR | flags);
		LOGD("open() fd = %d", fd);
		(*env)->ReleaseStringUTFChars(env, path, path_utf);
		if (fd == -1)
		{
			/* Throw an exception */
			LOGE("Cannot open port");
			/* TODO: throw an exception */
			return NULL;
		}
	}

	/* Configure device */
	{
		struct termios cfg;
		LOGD("Configuring serial port");
		if (tcgetattr(fd, &cfg))
		{
			LOGE("tcgetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}

		cfmakeraw(&cfg);
		cfsetispeed(&cfg, speed);
		cfsetospeed(&cfg, speed);

		if (tcsetattr(fd, TCSANOW, &cfg))
		{
			LOGE("tcsetattr() failed");
			close(fd);
			/* TODO: throw an exception */
			return NULL;
		}
	}

	/* Create a corresponding file descriptor */
	{
		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
	}

	return mFileDescriptor;
}

/*
 * Class:     cedric_serial_SerialPort
 * Method:    close
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
  (JNIEnv *env, jobject thiz)
{
	jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
	jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

	jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
	jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

	jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
	jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

	LOGD("close(fd = %d)", descriptor);
	close(descriptor);
}

Java 串口类

/*
 * Copyright 2009 Cedric Priscal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package android_serialport_api;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPort {

	private static final String TAG = "SerialPort";

	/*
	 * Do not remove or rename the field mFd: it is used by native method close();
	 */
	private FileDescriptor mFd;
	private FileInputStream mFileInputStream;
	private FileOutputStream mFileOutputStream;

	public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

		/* Check access permission */
		if (!device.canRead() || !device.canWrite()) {
			try {
				/* Missing read/write permission, trying to chmod the file */
				Process su;
				su = Runtime.getRuntime().exec("/system/bin/su");
				String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
						+ "exit\n";
				su.getOutputStream().write(cmd.getBytes());
				if ((su.waitFor() != 0) || !device.canRead()
						|| !device.canWrite()) {
					throw new SecurityException();
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new SecurityException();
			}
		}

		mFd = open(device.getAbsolutePath(), baudrate, flags);
		if (mFd == null) {
			Log.e(TAG, "native open returns null");
			throw new IOException();
		}
		mFileInputStream = new FileInputStream(mFd);
		mFileOutputStream = new FileOutputStream(mFd);
	}

	// Getters and setters
	public InputStream getInputStream() {
		return mFileInputStream;
	}

	public OutputStream getOutputStream() {
		return mFileOutputStream;
	}

	// JNI
	private native static FileDescriptor open(String path, int baudrate, int flags);
	public native void close();
	static {
		System.loadLibrary("serial_port");
	}
}

Java 辅助类

package com.bjw.ComAssistant;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

import com.bjw.bean.ComBean;
import android_serialport_api.SerialPort;

/**
 * @author benjaminwan
 *串口辅助工具类
 */
public abstract class SerialHelper{
	private SerialPort mSerialPort;
	private OutputStream mOutputStream;
	private InputStream mInputStream;
	private ReadThread mReadThread;
	private SendThread mSendThread;
	private String sPort="/dev/s3c2410_serial0";
	private int iBaudRate=9600;
	private boolean _isOpen=false;
	private byte[] _bLoopData=new byte[]{0x30};
	private int iDelay=500;
	//----------------------------------------------------
	public SerialHelper(String sPort,int iBaudRate){
		this.sPort = sPort;
		this.iBaudRate=iBaudRate;
	}
	public SerialHelper(){
		this("/dev/s3c2410_serial0",9600);
	}
	public SerialHelper(String sPort){
		this(sPort,9600);
	}
	public SerialHelper(String sPort,String sBaudRate){
		this(sPort,Integer.parseInt(sBaudRate));
	}
	//----------------------------------------------------
	public void open() throws SecurityException, IOException,InvalidParameterException{
		mSerialPort =  new SerialPort(new File(sPort), iBaudRate, 0);
		mOutputStream = mSerialPort.getOutputStream();
		mInputStream = mSerialPort.getInputStream();
		mReadThread = new ReadThread();
		mReadThread.start();
		mSendThread = new SendThread();
		mSendThread.setSuspendFlag();
		mSendThread.start();
		_isOpen=true;
	}
	//----------------------------------------------------
	public void close(){
		if (mReadThread != null)
			mReadThread.interrupt();
		if (mSerialPort != null) {
			mSerialPort.close();
			mSerialPort = null;
		}
		_isOpen=false;
	}
	//----------------------------------------------------
	public void send(byte[] bOutArray){
		try
		{
			mOutputStream.write(bOutArray);
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	//----------------------------------------------------
	public void sendHex(String sHex){
		byte[] bOutArray = MyFunc.HexToByteArr(sHex);
		send(bOutArray);		
	}
	//----------------------------------------------------
	public void sendTxt(String sTxt){
		byte[] bOutArray =sTxt.getBytes();
		send(bOutArray);		
	}
	//----------------------------------------------------
	private class ReadThread extends Thread {
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				try
				{
					if (mInputStream == null) return;
					byte[] buffer=new byte[512];
					int size = mInputStream.read(buffer);
					if (size > 0){
						ComBean ComRecData = new ComBean(sPort,buffer,size);
						onDataReceived(ComRecData);
					}
					try
					{
						Thread.sleep(50);//延时50ms
					} catch (InterruptedException e)
					{
						e.printStackTrace();
					}
				} catch (Throwable e)
				{
					e.printStackTrace();
					return;
				}
			}
		}
	}
	//----------------------------------------------------
	private class SendThread extends Thread{
		public boolean suspendFlag = true;// 控制线程的执行
		@Override
		public void run() {
			super.run();
			while(!isInterrupted()) {
				synchronized (this)
				{
					while (suspendFlag)
					{
						try
						{
							wait();
						} catch (InterruptedException e)
						{
							e.printStackTrace();
						}
					}
				}
				send(getbLoopData());
				try
				{
					Thread.sleep(iDelay);
				} catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
		}

		//线程暂停
		public void setSuspendFlag() {
		this.suspendFlag = true;
		}
		
		//唤醒线程
		public synchronized void setResume() {
		this.suspendFlag = false;
		notify();
		}
	}
	//----------------------------------------------------
	public int getBaudRate()
	{
		return iBaudRate;
	}
	public boolean setBaudRate(int iBaud)
	{
		if (_isOpen)
		{
			return false;
		} else
		{
			iBaudRate = iBaud;
			return true;
		}
	}
	public boolean setBaudRate(String sBaud)
	{
		int iBaud = Integer.parseInt(sBaud);
		return setBaudRate(iBaud);
	}
	//----------------------------------------------------
	public String getPort()
	{
		return sPort;
	}
	public boolean setPort(String sPort)
	{
		if (_isOpen)
		{
			return false;
		} else
		{
			this.sPort = sPort;
			return true;
		}
	}
	//----------------------------------------------------
	public boolean isOpen()
	{
		return _isOpen;
	}
	//----------------------------------------------------
	public byte[] getbLoopData()
	{
		return _bLoopData;
	}
	//----------------------------------------------------
	public void setbLoopData(byte[] bLoopData)
	{
		this._bLoopData = bLoopData;
	}
	//----------------------------------------------------
	public void setTxtLoopData(String sTxt){
		this._bLoopData = sTxt.getBytes();
	}
	//----------------------------------------------------
	public void setHexLoopData(String sHex){
		this._bLoopData = MyFunc.HexToByteArr(sHex);
	}
	//----------------------------------------------------
	public int getiDelay()
	{
		return iDelay;
	}
	//----------------------------------------------------
	public void setiDelay(int iDelay)
	{
		this.iDelay = iDelay;
	}
	//----------------------------------------------------
	public void startSend()
	{
		if (mSendThread != null)
		{
			mSendThread.setResume();
		}
	}
	//----------------------------------------------------
	public void stopSend()
	{
		if (mSendThread != null)
		{
			mSendThread.setSuspendFlag();
		}
	}
	//----------------------------------------------------
	protected abstract void onDataReceived(ComBean ComRecData);
}

相关修改

有一个项目上使用串口,收发数据量比较大,通过跳过平台自带的串口数据缓冲解决,核心就是驱动中置位如下标志
在这里插入图片描述

对应具体项目代码为:

项目串口使用比较频繁,而内核使用 kworker 来上报数据,会有延迟产生,故而可以通过添加标志 UPF_LOW_LATENCY
    uart->port.flags    = UPF_BOOT_AUTOCONF;
在这一行解决。