linux-tty_io.c

/*
 *  linux/kernel/tty_io.c
 *
 *  (C) 1991  Linus Torvalds
 */

/*
 * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
 * or rs-channels. It also implements echoing, cooked mode etc.
 *
 * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
 */

#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>

#define ALRMMASK (1<<(SIGALRM-1))                    //--定时警告信号在信号位图中对应的位屏蔽位

#include <linux/sched.h>
#include <linux/tty.h>
#include <asm/segment.h>
#include <asm/system.h>

int kill_pg(int pgrp, int sig, int priv);
int is_orphaned_pgrp(int pgrp);

#define _L_FLAG(tty,f)    ((tty)->termios.c_lflag & f)
#define _I_FLAG(tty,f)    ((tty)->termios.c_iflag & f)
#define _O_FLAG(tty,f)    ((tty)->termios.c_oflag & f)

#define L_CANON(tty)    _L_FLAG((tty),ICANON)
#define L_ISIG(tty)    _L_FLAG((tty),ISIG)
#define L_ECHO(tty)    _L_FLAG((tty),ECHO)
#define L_ECHOE(tty)    _L_FLAG((tty),ECHOE)
#define L_ECHOK(tty)    _L_FLAG((tty),ECHOK)
#define L_ECHOCTL(tty)    _L_FLAG((tty),ECHOCTL)
#define L_ECHOKE(tty)    _L_FLAG((tty),ECHOKE)
#define L_TOSTOP(tty)    _L_FLAG((tty),TOSTOP)

#define I_UCLC(tty)    _I_FLAG((tty),IUCLC)
#define I_NLCR(tty)    _I_FLAG((tty),INLCR)
#define I_CRNL(tty)    _I_FLAG((tty),ICRNL)
#define I_NOCR(tty)    _I_FLAG((tty),IGNCR)
#define I_IXON(tty)    _I_FLAG((tty),IXON)

#define O_POST(tty)    _O_FLAG((tty),OPOST)
#define O_NLCR(tty)    _O_FLAG((tty),ONLCR)
#define O_CRNL(tty)    _O_FLAG((tty),OCRNL)
#define O_NLRET(tty)    _O_FLAG((tty),ONLRET)
#define O_LCUC(tty)    _O_FLAG((tty),OLCUC)

#define C_SPEED(tty)    ((tty)->termios.c_cflag & CBAUD)
#define C_HUP(tty)    (C_SPEED((tty)) == B0)

#ifndef MIN                                        //--宏,取最小值
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#define QUEUES    (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
static struct tty_queue tty_queues[QUEUES];
struct tty_struct tty_table[256];

#define con_queues tty_queues
#define rs_queues ((3*MAX_CONSOLES) + tty_queues)
#define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
#define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)

#define con_table tty_table
#define rs_table (64+tty_table)
#define mpty_table (128+tty_table)
#define spty_table (192+tty_table)

int fg_console = 0;

/*
 * these are the tables used by the machine code handlers.
 * you can implement virtual consoles.
 */
struct tty_queue * table_list[]={            //--供rs_io.s程序使用
    con_queues + 0, con_queues + 1,
    rs_queues + 0, rs_queues + 1,
    rs_queues + 3, rs_queues + 4
    };

void change_console(unsigned int new_console)    //--改变前台控制台,将前台控制台设定为指定的虚拟控制台
{
    if (new_console == fg_console || new_console >= NR_CONSOLES)
        return;
    fg_console = new_console;
    table_list[0] = con_queues + 0 + fg_console*3;
    table_list[1] = con_queues + 1 + fg_console*3;
    update_screen();
}

static void sleep_if_empty(struct tty_queue * queue)    //--如果队列缓冲区空则让进程进入可中断睡眠状态
{                                        //--指定队列的指针
    cli();
    while (!(current->signal & ~current->blocked) && EMPTY(queue))
        interruptible_sleep_on(&queue->proc_list);
    sti();
}

static void sleep_if_full(struct tty_queue * queue)        //--如果队列缓冲区满则让进程进入可中断睡眠状态
{
    if (!FULL(queue))
        return;
    cli();
    while (!(current->signal & ~current->blocked) && LEFT(queue)<128)
        interruptible_sleep_on(&queue->proc_list);
    sti();
}

void wait_for_keypress(void)                            //--等待按键
{
    sleep_if_empty(tty_table[fg_console].secondary);
}

void copy_to_cooked(struct tty_struct * tty)            //--复制成规范式字符序列
{
    signed char c;

    if (!(tty->read_q || tty->write_q || tty->secondary)) {
        printk("copy_to_cooked: missing queues/n/r");
        return;
    }
    while (1) {            //--根据终端termios结构中的输入和本地标志,对从tty读队列缓冲区中取出的每个字符进行适当的处理
        if (EMPTY(tty->read_q))
            break;
        if (FULL(tty->secondary))
            break;
        GETCH(tty->read_q,c);
        if (c==13) {
            if (I_CRNL(tty))
                c=10;
            else if (I_NOCR(tty))
                continue;
        } else if (c==10 && I_NLCR(tty))
            c=13;
        if (I_UCLC(tty))
            c=tolower(c);
        if (L_CANON(tty)) {                            //--本地模式标志集中规范模式标志CANON
            if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==KILL_CHAR(tty))) {
                /* deal with killing the input line */
                while(!(EMPTY(tty->secondary) ||
                        (c=LAST(tty->secondary))==10 ||
                        ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
                     (c==EOF_CHAR(tty))))) {
                    if (L_ECHO(tty)) {
                        if (c<32)
                            PUTCH(127,tty->write_q);
                        PUTCH(127,tty->write_q);
                        tty->write(tty);
                    }
                    DEC(tty->secondary->head);
                }
                continue;
            }
            if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==ERASE_CHAR(tty))) {                //--该字符是删除控制字符ERASE?
                if (EMPTY(tty->secondary) ||
                   (c=LAST(tty->secondary))==10 ||
                   ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
                    (c==EOF_CHAR(tty))))
                    continue;
                if (L_ECHO(tty)) {
                    if (c<32)
                        PUTCH(127,tty->write_q);
                    PUTCH(127,tty->write_q);
                    tty->write(tty);
                }
                DEC(tty->secondary->head);
                continue;
            }
        }
        if (I_IXON(tty)) {                            //--如果设置了IXON标志,则....
            if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==STOP_CHAR(tty))) {
                tty->stopped=1;
                tty->write(tty);
                continue;
            }
            if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==START_CHAR(tty))) {
                tty->stopped=0;
                tty->write(tty);
                continue;
            }
        }
        if (L_ISIG(tty)) {                            //--若输入模式标志集中ISIG标志置位,则...
            if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==INTR_CHAR(tty))) {
                kill_pg(tty->pgrp, SIGINT, 1);
                continue;
            }
            if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==QUIT_CHAR(tty))) {
                kill_pg(tty->pgrp, SIGQUIT, 1);
                continue;
            }
            if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
                (c==SUSPEND_CHAR(tty))) {
                if (!is_orphaned_pgrp(tty->pgrp))
                    kill_pg(tty->pgrp, SIGTSTP, 1);
                continue;
            }
        }
        if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
                  c==EOF_CHAR(tty)))
            tty->secondary->data++;
        if (L_ECHO(tty)) {                            //--如果本地模式标志集中回显标志ECHO在置位状态,则..
            if (c==10) {
                PUTCH(10,tty->write_q);
                PUTCH(13,tty->write_q);
            } else if (c<32) {
                if (L_ECHOCTL(tty)) {
                    PUTCH('^',tty->write_q);
                    PUTCH(c+64,tty->write_q);
                }
            } else
                PUTCH(c,tty->write_q);
            tty->write(tty);
        }
        PUTCH(c,tty->secondary);
    }
    wake_up(&tty->secondary->proc_list);            //--最后唤醒等待该辅助缓冲队列的进程
}

/*
 * Called when we need to send a SIGTTIN or SIGTTOU to our process
 * group
 *
 * We only request that a system call be restarted if there was if the
 * default signal handler is being used.  The reason for this is that if
 * a job is catching SIGTTIN or SIGTTOU, the signal handler may not want
 * the system call to be restarted blindly.  If there is no way to reset the
 * terminal pgrp back to the current pgrp (perhaps because the controlling
 * tty has been released on logout), we don't want to be in an infinite loop
 * while restarting the system call, and have it always generate a SIGTTIN
 * or SIGTTOU.  The default signal handler will cause the process to stop
 * thus avoiding the infinite loop problem.  Presumably the job-control
 * cognizant parent will fix things up before continuging its child process.
 */
int tty_signal(int sig, struct tty_struct *tty)        //--向使用终端的进程组中所有进程发送信号
{
    if (is_orphaned_pgrp(current->pgrp))
        return -EIO;        /* don't stop an orphaned pgrp */
    (void) kill_pg(current->pgrp,sig,1);
    if ((current->blocked & (1<<(sig-1))) ||
        ((int) current->sigaction[sig-1].sa_handler == 1))
        return -EIO;        /* Our signal will be ignored */
    else if (current->sigaction[sig-1].sa_handler)
        return -EINTR;        /* We _will_ be interrupted :-) */
    else
        return -ERESTARTSYS;    /* We _will_ be interrupted :-) */
                    /* (but restart after we continue) */
}

int tty_read(unsigned channel, char * buf, int nr)        //--tty读函数
{                                        //--从终端辅助缓冲队列中读取指定数量的字符,放到用户指定的缓冲区中
    struct tty_struct * tty;
    struct tty_struct * other_tty = NULL;
    char c, * b=buf;
    int minimum,time;

    if (channel > 255)
        return -EIO;
    tty = TTY_TABLE(channel);
    if (!(tty->write_q || tty->read_q || tty->secondary))
        return -EIO;
    if ((current->tty == channel) && (tty->pgrp != current->pgrp))
        return(tty_signal(SIGTTIN, tty));
    if (channel & 0x80)
        other_tty = tty_table + (channel ^ 0x40);
    time = 10L*tty->termios.c_cc[VTIME];
    minimum = tty->termios.c_cc[VMIN];
    if (L_CANON(tty)) {
        minimum = nr;
        current->timeout = 0xffffffff;
        time = 0;
    } else if (minimum)
        current->timeout = 0xffffffff;
    else {
        minimum = nr;
        if (time)
            current->timeout = time + jiffies;
        time = 0;
    }
    if (minimum>nr)
        minimum = nr;
    while (nr>0) {                    //--从辅助队列中循环取出字符并放到用户缓冲区buf中
        if (other_tty)
            other_tty->write(other_tty);
        cli();
        if (EMPTY(tty->secondary) || (L_CANON(tty) &&
            !FULL(tty->read_q) && !tty->secondary->data)) {
            if (!current->timeout ||
              (current->signal & ~current->blocked)) {
                  sti();
                break;
            }
            if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
                break;
            interruptible_sleep_on(&tty->secondary->proc_list);
            sti();
            continue;
        }
        sti();
        do {
            GETCH(tty->secondary,c);
            if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
                 c==EOF_CHAR(tty)) || c==10)
                tty->secondary->data--;
            if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
                 c==EOF_CHAR(tty)) && L_CANON(tty))
                break;
            else {
                put_fs_byte(c,b++);
                if (!--nr)
                    break;
            }
            if (c==10 && L_CANON(tty))
                break;
        } while (nr>0 && !EMPTY(tty->secondary));
        wake_up(&tty->read_q->proc_list);
        if (time)
            current->timeout = time+jiffies;
        if (L_CANON(tty) || b-buf >= minimum)
            break;
    }
    current->timeout = 0;
    if ((current->signal & ~current->blocked) && !(b-buf))
        return -ERESTARTSYS;
    return (b-buf);
}

int tty_write(unsigned channel, char * buf, int nr)        //--tty写函数
{                                        //--把用户缓冲区中的字符放入tty写队列缓冲区中
    static cr_flag=0;
    struct tty_struct * tty;
    char c, *b=buf;

    if (channel > 255)
        return -EIO;
    tty = TTY_TABLE(channel);
    if (!(tty->write_q || tty->read_q || tty->secondary))
        return -EIO;
    if (L_TOSTOP(tty) &&
        (current->tty == channel) && (tty->pgrp != current->pgrp))
        return(tty_signal(SIGTTOU, tty));
    while (nr>0) {
        sleep_if_full(tty->write_q);
        if (current->signal & ~current->blocked)
            break;
        while (nr>0 && !FULL(tty->write_q)) {
            c=get_fs_byte(b);
            if (O_POST(tty)) {
                if (c=='/r' && O_CRNL(tty))
                    c='/n';
                else if (c=='/n' && O_NLRET(tty))
                    c='/r';
                if (c=='/n' && !cr_flag && O_NLCR(tty)) {
                    cr_flag = 1;
                    PUTCH(13,tty->write_q);
                    continue;
                }
                if (O_LCUC(tty))
                    c=toupper(c);
            }
            b++; nr--;
            cr_flag = 0;
            PUTCH(c,tty->write_q);
        }
        tty->write(tty);
        if (nr>0)
            schedule();
    }
    return (b-buf);
}

/*
 * Jeh, sometimes I really like the 386.
 * This routine is called from an interrupt,
 * and there should be absolutely no problem
 * with sleeping even in an interrupt (I hope).
 * Of course, if somebody proves me wrong, I'll
 * hate intel for all time :-). We'll have to
 * be careful and see to reinstating the interrupt
 * chips before calling this, though.
 *
 * I don't think we sleep here under normal circumstances
 * anyway, which is good, as the task sleeping might be
 * totally innocent.
 */
void do_tty_interrupt(int tty)            //--tty中断处理调用函数
{
    copy_to_cooked(TTY_TABLE(tty));
}

void chr_dev_init(void)                    //--字符设备初始化函数。为扩展作准备
{
}

void tty_init(void)                        //--tty终端初始化函数
{                                        //--初始化所有终端缓冲队列,初始化串口终端和控制台终端
    int i;

    for (i=0 ; i < QUEUES ; i++)        //--首先初始化所有终端的缓冲队列结构
        tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
    rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
    rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
    rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
    rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
    for (i=0 ; i<256 ; i++) {
        tty_table[i] =  (struct tty_struct) {
             {0, 0, 0, 0, 0, INIT_C_CC},
            0, 0, 0, NULL, NULL, NULL, NULL
        };
    }
    con_init();                                //--console.c--con_init()
    for (i = 0 ; i<NR_CONSOLES ; i++) {        //--接着初始化控制台终端
        con_table[i] = (struct tty_struct) {
             {ICRNL,        /* change incoming CR to NL */
            OPOST|ONLCR,    /* change outgoing NL to CRNL */
            0,
            IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
            0,        /* console termio */
            INIT_C_CC},
            0,            /* initial pgrp */
            0,            /* initial session */
            0,            /* initial stopped */
            con_write,
            con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
        };
    }
    for (i = 0 ; i<NR_SERIALS ; i++) {        //--初始化串行终端的tty结构各字段
        rs_table[i] = (struct tty_struct) {
            {0, /* no translation */
            0,  /* no translation */
            B2400 | CS8,
            0,
            0,
            INIT_C_CC},
            0,
            0,
            0,
            rs_write,
            rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
        };
    }
    for (i = 0 ; i<NR_PTYS ; i++) {            //--初始化伪终端使用的tty结构
        mpty_table[i] = (struct tty_struct) {
            {0, /* no translation */
            0,  /* no translation */
            B9600 | CS8,
            0,
            0,
            INIT_C_CC},
            0,
            0,
            0,
            mpty_write,
            mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
        };
        spty_table[i] = (struct tty_struct) {
            {0, /* no translation */
            0,  /* no translation */
            B9600 | CS8,
            IXON | ISIG | ICANON,
            0,
            INIT_C_CC},
            0,
            0,
            0,
            spty_write,
            spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
        };
    }
    rs_init();
    printk("%d virtual consoles/n/r",NR_CONSOLES);
    printk("%d pty's/n/r",NR_PTYS);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
serial_core.c是Linux内核中的一个串口驱动核心文件,主要负责串口设备的初始化、读写和中断处理等操作。以下是该文件的代码解读: 1. 头文件包含 ``` #include <linux/kernel.h> #include <linux/module.h> #include <linux/device.h> #include <linux/init.h> #include <linux/serial.h> #include <linux/tty.h> #include <linux/tty_flip.h> #include <linux/console.h> #include <linux/console_struct.h> #include <linux/serial_core.h> #include <linux/serial_reg.h> #include <linux/io.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/wait.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> ``` 该文件包含了一些常用的头文件,如内核头文件、串口头文件、中断头文件等。 2. 宏定义 ``` #define serial_outp(port, value) writeb((value), (port)) #define serial_inp(port) readb(port) #define serial_outw(port, value) writew((value), (port)) #define serial_inw(port) readw(port) #define serial_outl(port, value) writel((value), (port)) #define serial_inl(port) readl(port) ``` 这些宏定义了读写串口寄存器的操作,通过调用内核提供的读写函数实现。 3. 结构体定义 ``` struct uart_port { spinlock_t lock; /* 锁 */ void __iomem *membase; /* MMIO基地址 */ unsigned char *mapbase; /* 端口映射基地址 */ unsigned char *membase_addr; /* MMIO地址 */ unsigned char *mapbase_addr; /* 端口映射地址 */ unsigned int iotype:2; /* 端口类型 */ unsigned int irq; /* 中断号 */ unsigned int uartclk; /* 时钟 */ unsigned int fifosize; /* FIFO大小 */ unsigned int flags; /* 标志 */ unsigned int regshift; /* 寄存器位移 */ unsigned int iobase; /* 端口基地址 */ unsigned int iolen; /* 端口长度 */ unsigned int regtype:2; /* 寄存器类型 */ unsigned int uartclk_high; /* 高位时钟 */ struct uart_state *state; /* 串口状态 */ struct uart_ops *ops; /* 串口操作 */ struct uart_driver *uartclk_reg; /* 时钟寄存器 */ struct console *cons; /* 控制台 */ struct device *dev; /* 设备 */ struct dma_chan *dma; /* DMA通道 */ struct dma_async_tx_descriptor *tx_dma; /* DMA传输描述符 */ struct dma_async_tx_descriptor *rx_dma; /* DMA传输描述符 */ unsigned int capabilities; /* 串口功能 */ unsigned int type; /* 串口类型 */ unsigned int line; /* 串口线路 */ unsigned int uartclk_rate; /* 时钟频率 */ struct ktermios *termios; /* 终端参数 */ struct ktermios *gpios; /* GPIO配置 */ struct delayed_work work; /* 延迟工作队列 */ }; ``` 该结构体定义了串口端口的各种信息,如锁、基地址、中断号、时钟、标志等。 4. 函数定义 该文件包含了众多函数定义,具体解读如下: (1) uart_get_baud_rate()函数 ``` unsigned int uart_get_baud_rate(struct uart_port *port, struct ktermios *termios, struct ktermios *old, unsigned int min, unsigned int max) ``` 该函数用于获取波特率,根据终端参数计算波特率并返回。 (2) uart_update_timeout()函数 ``` void uart_update_timeout(struct uart_port *port, unsigned int cflag) ``` 该函数用于更新串口超时时间,根据终端参数计算超时时间并更新。 (3) uart_register_driver()函数 ``` int uart_register_driver(struct uart_driver *uart_drv) ``` 该函数用于注册串口驱动,将驱动加入到内核串口驱动链表中。 (4) uart_unregister_driver()函数 ``` void uart_unregister_driver(struct uart_driver *uart_drv) ``` 该函数用于注销串口驱动,从内核串口驱动链表中移除。 (5) uart_add_one_port()函数 ``` int uart_add_one_port(struct uart_driver *drv, struct uart_port *port) ``` 该函数用于添加一个串口端口,将其加入到驱动的端口列表中。 (6) uart_remove_one_port()函数 ``` void uart_remove_one_port(struct uart_driver *drv, struct uart_port *port) ``` 该函数用于移除一个串口端口,从驱动的端口列表中删除。 (7) uart_suspend_port()函数 ``` int uart_suspend_port(struct uart_driver *drv, struct uart_port *port) ``` 该函数用于挂起一个串口端口,暂停其读写操作。 (8) uart_resume_port()函数 ``` int uart_resume_port(struct uart_driver *drv, struct uart_port *port) ``` 该函数用于恢复一个串口端口,重新开始读写操作。 (9) uart_change_speed()函数 ``` void uart_change_speed(struct uart_port *port, unsigned int new_speed) ``` 该函数用于改变串口的波特率,重新计算超时时间。 (10) uart_handle_sysrq_char()函数 ``` int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) ``` 该函数用于处理系统请求字符,将其发送到串口设备中。 (11) uart_insert_char()函数 ``` void uart_insert_char(struct uart_port *port, unsigned int status, unsigned int overrun, unsigned int ch, unsigned int flag) ``` 该函数用于向串口设备中插入一个字符,处理溢出和错误等情况。 (12) uart_write_wakeup()函数 ``` void uart_write_wakeup(struct uart_port *port) ``` 该函数用于唤醒串口设备的写操作,将等待的进程唤醒。 (13) uart_flush_buffer()函数 ``` void uart_flush_buffer(struct uart_port *port) ``` 该函数用于刷新串口设备的缓冲区,清空缓冲区中的数据。 (14) uart_start()函数 ``` void uart_start(struct uart_port *port) ``` 该函数用于启动串口设备的读操作,开始接收数据。 (15) uart_stop()函数 ``` void uart_stop(struct uart_port *port) ``` 该函数用于停止串口设备的读操作,停止接收数据。 (16) uart_shutdown()函数 ``` void uart_shutdown(struct uart_port *port) ``` 该函数用于关闭串口设备,释放资源。 (17) uart_handle_cts_change()函数 ``` void uart_handle_cts_change(struct uart_port *port, unsigned int status) ``` 该函数用于处理CTS(清除发送)信号的变化,控制发送操作。 (18) uart_handle_dcd_change()函数 ``` void uart_handle_dcd_change(struct uart_port *port, unsigned int status) ``` 该函数用于处理DCD(数据载波检测)信号的变化,控制读操作。 (19) uart_handle_dsr_change()函数 ``` void uart_handle_dsr_change(struct uart_port *port, unsigned int status) ``` 该函数用于处理DSR(数据终端就绪)信号的变化,控制读操作。 (20) uart_get_stats()函数 ``` void uart_get_stats(struct uart_port *port, struct uart_icount *icount) ``` 该函数用于获取串口设备的统计信息,包括接收、发送、错误等信息。 5. 总结 serial_core.c是Linux内核中的一个串口驱动核心文件,包含了众多的函数和结构体定义,实现了串口设备的初始化、读写、中断处理等操作。对于Linux内核开发人员来说,了解该文件的代码实现,对于理解串口驱动的原理和实现具有重要意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值