Hello,
I am currently coding a serial port driver (to be combined with drivers/serial/serial_core.c). I have noticed that most serial drivers have the tty_flip_buffer_push() inside the interrupt service routine (ISR). As far as I know, this function is the one the gets the data from the flip buffer and gives it to the user application that requested it. I am wondering why does it have to be inside an ISR? When you call read() in a user program, it does not interrupt.
In my UART testbench, interrupt occurs when something happens in the read FIFO, for example whenever a data is ready to be read. The interrupt service routine will acknowledge this interrupt by getting the data out of the FIFO then putting it in the flip buffer. If the tty_flip_buffer_push() is always inside the ISR, it will always flip the buffer and give the data to the user even though there is no one requesting it?
example of uart driver in the linux source. this is the isr for read.
Code:
static void
uart00_rx_chars(struct uart_port *port, struct pt_regs *regs)
{
struct uart_info *info = port->info;
struct tty_struct *tty = info->tty;
unsigned int status, ch, rds, flg, ignored = 0;
status = UART_GET_RSR(port);
while (UART_RX_DATA(status)) {
/*
* We need to read rds before reading the
* character from the fifo
*/
rds = UART_GET_RDS(port);
ch = UART_GET_CHAR(port);
port->icount.rx++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
flg = TTY_NORMAL;
/*
* Note that the error handling code is
* out of the main execution path
*/
if (rds & (UART_RDS_BI_MSK |UART_RDS_FE_MSK|UART_RDS_PE_MSK))
goto handle_error;
if (uart_handle_sysrq_char(port, ch, regs))
goto ignore_char;
error_return:
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
ignore_char:
status = UART_GET_RSR(port);
}
out:
tty_flip_buffer_push(tty);
return;
handle_error:
if (rds & UART_RDS_BI_MSK) {
status &= ~(UART_RDS_FE_MSK | UART_RDS_PE_MSK);
port->icount.brk++;
#ifdef SUPPORT_SYSRQ
if (uart_handle_break(port))
goto ignore_char;
#endif
} else if (rds & UART_RDS_PE_MSK)
port->icount.parity++;
else if (rds & UART_RDS_FE_MSK)
port->icount.frame++;
if (rds & UART_RDS_OE_MSK)
port->icount.overrun++;
if (rds & port->ignore_status_mask) {
if (++ignored > 100)
goto out;
goto ignore_char;
}
rds &= port->read_status_mask;
if (rds & UART_RDS_BI_MSK)
flg = TTY_BREAK;
else if (rds & UART_RDS_PE_MSK)
flg = TTY_PARITY;
else if (rds & UART_RDS_FE_MSK)
flg = TTY_FRAME;
if (rds & UART_RDS_OE_MSK) {
/*
* CHECK: does overrun affect the current character?
* ASSUMPTION: it does not.
*/
*tty->flip.flag_buf_ptr++ = flg;
*tty->flip.char_buf_ptr++ = ch;
tty->flip.count++;
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
goto ignore_char;
ch = 0;
flg = TTY_OVERRUN;
}
#ifdef SUPPORT_SYSRQ
port->sysrq = 0;
#endif
goto error_return;
}
...
static void uart00_int(int irq, void *dev_id, struct pt_regs *regs)
{
struct uart_port *port = dev_id;
unsigned int status, pass_counter = 0;
status = UART_GET_INT_STATUS(port);
do {
if (status & UART_ISR_RI_MSK)
uart00_rx_chars(port, regs);
if (status & (UART_ISR_TI_MSK | UART_ISR_TII_MSK))
uart00_tx_chars(port);
if (status & UART_ISR_MI_MSK)
uart00_modem_status(port);
if (pass_counter++ > UART00_ISR_PASS_LIMIT)
break;
status = UART_GET_INT_STATUS(port);
} while (status);
}
I hope anyone can help me by clarifying this design. Thanks
Best regards,
Archie