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;
在這一行解決。