LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (http://www.linuxquestions.org/questions/programming-9/)
-   -   Serial driver problem on reading data from device (http://www.linuxquestions.org/questions/programming-9/serial-driver-problem-on-reading-data-from-device-656262/)

archieval 07-16-2008 10:53 PM

Serial driver problem on reading data from device
 
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

pinniped 07-16-2008 11:22 PM

The data is not being sent to the user; the line discipline (N_TTY by default) is being notified that data has been received. The line discipline may need to do special things such as write to the UART control registers, set a flag when an XON/XOFF character is received, and so on. The simplest line discipline I can think of is N_PPP, so have a look at that code if you want to see what happens to characters as they are received.

archieval 07-23-2008 12:42 AM

I see. If these data are not sent to the user immediately when a tty_flip_buffer_push, I presume it is stored in another buffer in the tty core. If another data was available, and push is again called, does the data fills up the next location in the tty core buffer? or is the buffer replaced with the new data (discards the previous content of the "I assumed" tty core buffer)?

Best regards,
Archie


All times are GMT -5. The time now is 04:09 PM.