之前对struct termios结构体的几个字段一直困惑,主要不知道它的作用,内核对应的struct ktermios结构体如下
struct ktermios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_cc[NCCS]; /* control characters */
cc_t c_line; /* line discipline (== c_cc[19]) */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
其中的tcflag_t c_cflag字段是控制字段,包括奇偶校验、停止位...,应用层在调用tcsetattr()函数时最终调用串口驱动nuc970_serial.c的驱动函数,如下
static void
nuc970serial_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old)
{
struct uart_nuc970_port *up = (struct uart_nuc970_port *)port;
unsigned int lcr = 0;
unsigned long flags;
unsigned int baud, quot;
switch (termios->c_cflag & CSIZE) {
case CS5:
lcr = 0;
break;
case CS6:
lcr |= 1;
break;
case CS7:
lcr |= 2;
break;
default:
case CS8:
lcr |= 3;
break;
}
if (termios->c_cflag & CSTOPB)
lcr |= NSB;
if (termios->c_cflag & PARENB)
lcr |= PBE;
if (!(termios->c_cflag & PARODD))
lcr |= EPE;
if (termios->c_cflag & CMSPAR)
lcr |= SPE;
baud = uart_get_baud_rate(port, termios, old,
port->uartclk / 16 / 0xffff,
port->uartclk / 16);
quot = nuc970serial_get_divisor(port, baud);
/*
* Ok, we're now changing the port state. Do it with
* interrupts disabled.
*/
spin_lock_irqsave(&up->port.lock, flags);
up->port.read_status_mask = RX_OVER_IF /*| UART_LSR_THRE | UART_LSR_DR*/;
if (termios->c_iflag & INPCK)
up->port.read_status_mask |= FEF | PEF;
if (termios->c_iflag & (BRKINT | PARMRK))
up->port.read_status_mask |= BIF;
/*
* Characteres to ignore
*/
up->port.ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
up->port.ignore_status_mask |= FEF | PEF;
if (termios->c_iflag & IGNBRK) {
up->port.ignore_status_mask |= BIF;
/*
* If we're ignoring parity and break indicators,
* ignore overruns too (for real raw support).
*/
if (termios->c_iflag & IGNPAR)
up->port.ignore_status_mask |= RX_OVER_IF;
}
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
else
up->mcr &= ~UART_MCR_AFE;
nuc970serial_set_mctrl(&up->port, up->port.mctrl);
serial_out(up, UART_REG_BAUD, quot | 0x30000000);
serial_out(up, UART_REG_LCR, lcr);
spin_unlock_irqrestore(&up->port.lock, flags);
}
可以看到在上面有对termios->c_cflag字段进行判断,最终转换为up->port.ignore_status_mask屏蔽字,关于该屏蔽字的功能,主要解决串口驱动在接收到数据时,出现奇偶校验错误的判断,可以看下如下的串口驱动的接收函数
static void
receive_chars(struct uart_nuc970_port *up)
{
unsigned char ch;
unsigned int fsr;
int max_count = 256;
char flag;
do {
ch = (unsigned char)serial_in(up, UART_REG_RBR);
fsr = serial_in(up, UART_REG_FSR);
flag = TTY_NORMAL;
up->port.icount.rx++;
if (unlikely(fsr & (BIF | FEF | PEF | RX_OVER_IF))) {
if (fsr & BIF) {
serial_out(up, UART_REG_FSR, BIF);
up->port.icount.brk++;
if (uart_handle_break(&up->port))
continue;
}
if (fsr & FEF) {
serial_out(up, UART_REG_FSR, FEF);
up->port.icount.parity++;
}
if (fsr & PEF) {
serial_out(up, UART_REG_FSR, PEF);
up->port.icount.frame++;
}
if (fsr & RX_OVER_IF) {
serial_out(up, UART_REG_FSR, RX_OVER_IF);
up->port.icount.overrun++;
}
// FIXME: check port->read_status_mask to determin report flags
if (fsr & BIF)
flag = TTY_BREAK;
if (fsr & PEF)
flag = TTY_PARITY;
if (fsr & FEF)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch))
continue;
uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
} while (!(fsr & RX_EMPTY) && (max_count-- > 0));
spin_lock(&up->port.lock);
tty_flip_buffer_push(&up->port.state->port);
spin_unlock(&up->port.lock);
}
通过如下函数进行奇偶校验
uart_insert_char(&up->port, fsr, RX_OVER_IF, ch, flag);
void uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_port *tport = &port->state->port;
if ((status & port->ignore_status_mask & ~overrun) == 0)
if (tty_insert_flip_char(tport, ch, flag) == 0)
++port->icount.buf_overrun;
/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (status & ~port->ignore_status_mask & overrun)
if (tty_insert_flip_char(tport, 0, TTY_OVERRUN) == 0)
++port->icount.buf_overrun;
}
在该函数内部,可以看到前面初始化的成员ignore_status_mask。