8250串口控制台的回显处理

来源:互联网 发布:红旗歌舞团空难 知乎 编辑:程序博客网 时间:2024/05/18 06:32
static void echo_char(unsigned char c, struct tty_struct *tty)
{
    mutex_lock(&tty->echo_lock);

    if (c == ECHO_OP_START) {
        add_echo_byte(ECHO_OP_START, tty);
        add_echo_byte(ECHO_OP_START, tty);
    } else {
        if (iscntrl(c) && c != '\t')
            add_echo_byte(ECHO_OP_START, tty);
        add_echo_byte(c, tty);//把字符放入回显处理BUF
    }

    mutex_unlock(&tty->echo_lock);
}

static void add_echo_byte(unsigned char c, struct tty_struct *tty)
{
    int    new_byte_pos;

    if (tty->echo_cnt == N_TTY_BUF_SIZE) {
        /* Circular buffer is already at capacity */
        new_byte_pos = tty->echo_pos;

        /*
         * Since the buffer start position needs to be advanced,
         * be sure to step by a whole operation byte group.
         */
        if (tty->echo_buf[tty->echo_pos] == ECHO_OP_START) {
            if (tty->echo_buf[(tty->echo_pos + 1) &
                      (N_TTY_BUF_SIZE - 1)] ==
                        ECHO_OP_ERASE_TAB) {
                tty->echo_pos += 3;
                tty->echo_cnt -= 2;
            } else {
                tty->echo_pos += 2;
                tty->echo_cnt -= 1;
            }
        } else {
            tty->echo_pos++;
        }
        tty->echo_pos &= N_TTY_BUF_SIZE - 1;

        tty->echo_overrun = 1;
    } else {
        new_byte_pos = tty->echo_pos + tty->echo_cnt;
        new_byte_pos &= N_TTY_BUF_SIZE - 1;
        tty->echo_cnt++;
    }

    tty->echo_buf[new_byte_pos] = c;//放入回显BUF
}

static void process_echoes(struct tty_struct *tty)
{
    int    space, nr;
    unsigned char c;
    unsigned char *cp, *buf_end;

    if (!tty->echo_cnt)
        return;

    mutex_lock(&tty->output_lock);
    mutex_lock(&tty->echo_lock);

    space = tty_write_room(tty);

    buf_end = tty->echo_buf + N_TTY_BUF_SIZE;
    cp = tty->echo_buf + tty->echo_pos;
    nr = tty->echo_cnt;
    while (nr > 0) {
        c = *cp;
        if (c == ECHO_OP_START) {
            unsigned char op;
            unsigned char *opp;
            int no_space_left = 0;

            /*
             * If the buffer byte is the start of a multi-byte
             * operation, get the next byte, which is either the
             * op code or a control character value.
             */
            opp = cp + 1;
            if (opp == buf_end)
                opp -= N_TTY_BUF_SIZE;
            op = *opp;

            switch (op) {
                unsigned int num_chars, num_bs;

            case ECHO_OP_ERASE_TAB:
                if (++opp == buf_end)
                    opp -= N_TTY_BUF_SIZE;
                num_chars = *opp;

                /*
                 * Determine how many columns to go back
                 * in order to erase the tab.
                 * This depends on the number of columns
                 * used by other characters within the tab
                 * area.  If this (modulo 8) count is from
                 * the start of input rather than from a
                 * previous tab, we offset by canon column.
                 * Otherwise, tab spacing is normal.
                 */
                if (!(num_chars & 0x80))
                    num_chars += tty->canon_column;
                num_bs = 8 - (num_chars & 7);

                if (num_bs > space) {
                    no_space_left = 1;
                    break;
                }
                space -= num_bs;
                while (num_bs--) {
                    tty_put_char(tty, '\b');
                    if (tty->column > 0)
                        tty->column--;
                }
                cp += 3;
                nr -= 3;
                break;

            case ECHO_OP_SET_CANON_COL:
                tty->canon_column = tty->column;
                cp += 2;
                nr -= 2;
                break;

            case ECHO_OP_MOVE_BACK_COL:
                if (tty->column > 0)
                    tty->column--;
                cp += 2;
                nr -= 2;
                break;

            case ECHO_OP_START:
                /* This is an escaped echo op start code */
                if (!space) {
                    no_space_left = 1;
                    break;
                }
                tty_put_char(tty, ECHO_OP_START);
                tty->column++;
                space--;
                cp += 2;
                nr -= 2;
                break;

            default:
                if (iscntrl(op)) {
                    if (L_ECHOCTL(tty)) {
                        /*
                         * Ensure there is enough space
                         * for the whole ctrl pair.
                         */
                        if (space < 2) {
                            no_space_left = 1;
                            break;
                        }
                        tty_put_char(tty, '^');
                        tty_put_char(tty, op ^ 0100);//除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制信号被回显为 ^X, 这里 X 是比控制信号大 0x40 的 ASCII 码
                        tty->column += 2;
                        space -= 2;
                    } else {
                        if (!space) {
                            no_space_left = 1;
                            break;
                        }
                        tty_put_char(tty, op);
                        space--;
                    }
                }
                /*
                 * If above falls through, this was an
                 * undefined op.
                 */
                cp += 2;
                nr -= 2;
            }

            if (no_space_left)
                break;
        } else {
            int retval;

            retval = do_output_char(c, tty, space);//对于普通字符
            if (retval < 0)
                break;
            space -= retval;
            cp += 1;
            nr -= 1;
        }

        /* When end of circular buffer reached, wrap around */
        if (cp >= buf_end)
            cp -= N_TTY_BUF_SIZE;
    }

    if (nr == 0) {
        tty->echo_pos = 0;
        tty->echo_cnt = 0;
        tty->echo_overrun = 0;
    } else {
        int num_processed = tty->echo_cnt - nr;
        tty->echo_pos += num_processed;
        tty->echo_pos &= N_TTY_BUF_SIZE - 1;
        tty->echo_cnt = nr;
        if (num_processed > 0)
            tty->echo_overrun = 0;
    }

    mutex_unlock(&tty->echo_lock);
    mutex_unlock(&tty->output_lock);

    if (tty->ops->flush_chars)
        tty->ops->flush_chars(tty);//刷新输出缓存
}

static int do_output_char(unsigned char c, struct tty_struct *tty, int space)
{
    int    spaces;

    if (!space)
        return -1;

    switch (c) {
    case '\n':
        if (O_ONLRET(tty))
            tty->column = 0;
        if (O_ONLCR(tty)) {
            if (space < 2)
                return -1;
            tty->canon_column = tty->column = 0;
            tty->ops->write(tty, "\r\n", 2);
            return 2;
        }
        tty->canon_column = tty->column;
        break;
    case '\r':
        if (O_ONOCR(tty) && tty->column == 0)
            return 0;
        if (O_OCRNL(tty)) {
            c = '\n';
            if (O_ONLRET(tty))
                tty->canon_column = tty->column = 0;
            break;
        }
        tty->canon_column = tty->column = 0;
        break;
    case '\t':
        spaces = 8 - (tty->column & 7);
        if (O_TABDLY(tty) == XTABS) {
            if (space < spaces)
                return -1;
            tty->column += spaces;
            tty->ops->write(tty, "        ", spaces);
            return spaces;
        }
        tty->column += spaces;
        break;
    case '\b':
        if (tty->column > 0)
            tty->column--;
        break;
    default:
        if (!iscntrl(c)) {
            if (O_OLCUC(tty))
                c = toupper(c);
            if (!is_continuation(c, tty))
                tty->column++;
        }
        break;
    }

    tty_put_char(tty, c);//输出字符
    return 1;
}

int tty_put_char(struct tty_struct *tty, unsigned char ch)
{
    if (tty->ops->put_char)
        return tty->ops->put_char(tty, ch);
    return tty->ops->write(tty, &ch, 1);
}

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,
#ifdef CONFIG_CONSOLE_POLL
    .poll_init    = uart_poll_init,
    .poll_get_char    = uart_poll_get_char,
    .poll_put_char    = uart_poll_put_char,
#endif
};

static int uart_put_char(struct tty_struct *tty, unsigned char ch)
{
    struct uart_state *state = tty->driver_data;

    return __uart_put_char(state->port, &state->info.xmit, ch);
}

static inline int
__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
{
    unsigned long flags;
    int ret = 0;

    if (!circ->buf)
        return 0;

    spin_lock_irqsave(&port->lock, flags);
    if (uart_circ_chars_free(circ) != 0) {
        circ->buf[circ->head] = c;//字符放入circ的buf,等待串口中断中输出
        circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
        ret = 1;
    }
    spin_unlock_irqrestore(&port->lock, flags);
    return ret;
}

static void uart_flush_chars(struct tty_struct *tty)
{
    uart_start(tty);
}

static void uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->port;
    unsigned long flags;

    spin_lock_irqsave(&port->lock, flags);
    __uart_start(tty);
    spin_unlock_irqrestore(&port->lock, flags);
}

static void __uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->port;

    if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf &&
        !tty->stopped && !tty->hw_stopped)
        port->ops->start_tx(port);//在circ buf中有数据调用串口的start_tx
}

static struct uart_ops serial8250_pops = {
    .tx_empty    = serial8250_tx_empty,
    .set_mctrl    = serial8250_set_mctrl,
    .get_mctrl    = serial8250_get_mctrl,
    .stop_tx    = serial8250_stop_tx,
    .start_tx    = serial8250_start_tx,
    .stop_rx    = serial8250_stop_rx,
    .enable_ms    = serial8250_enable_ms,
    .break_ctl    = serial8250_break_ctl,
    .startup    = serial8250_startup,
    .shutdown    = serial8250_shutdown,
    .set_termios    = serial8250_set_termios,
    .pm        = serial8250_pm,
    .type        = serial8250_type,
    .release_port    = serial8250_release_port,
    .request_port    = serial8250_request_port,
    .config_port    = serial8250_config_port,
    .verify_port    = serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
    .poll_get_char = serial8250_get_poll_char,
    .poll_put_char = serial8250_put_poll_char,
#endif
};

static void serial8250_start_tx(struct uart_port *port)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;

    if (!(up->ier & UART_IER_THRI)) {
        up->ier |= UART_IER_THRI;
        serial_out(up, UART_IER, up->ier);//平时中断关闭,要输出的时候打开中断,于是进入中断处理函数

        if (up->bugs & UART_BUG_TXEN) {
            unsigned char lsr, iir;
            lsr = serial_in(up, UART_LSR);
            up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
            iir = serial_in(up, UART_IIR) & 0x0f;
            if ((up->port.type == PORT_RM9000) ?
                (lsr & UART_LSR_THRE &&
                (iir == UART_IIR_NO_INT || iir == UART_IIR_THRI)) :
                (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT))
                transmit_chars(up);
        }
    }

    /*
     * Re-enable the transmitter if we disabled it.
     */
    if (up->port.type == PORT_16C950 && up->acr & UART_ACR_TXDIS) {
        up->acr &= ~UART_ACR_TXDIS;
        serial_icr_write(up, UART_ACR, up->acr);
    }
}

static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
    struct irq_info *i = dev_id;
    struct list_head *l, *end = NULL;
    int pass_counter = 0, handled = 0;

    DEBUG_INTR("serial8250_interrupt(%d)...", irq);

    spin_lock(&i->lock);

    l = i->head;
    do {
        struct uart_8250_port *up;
        unsigned int iir;

        up = list_entry(l, struct uart_8250_port, list);

        iir = serial_in(up, UART_IIR);
        if (!(iir & UART_IIR_NO_INT)) {
            serial8250_handle_port(up);

            handled = 1;

            end = NULL;
        } else if (up->port.iotype == UPIO_DWAPB &&
              (iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
            /* The DesignWare APB UART has an Busy Detect (0x07)
             * interrupt meaning an LCR write attempt occured while the
             * UART was busy. The interrupt must be cleared by reading
             * the UART status register (USR) and the LCR re-written. */
            unsigned int status;
            status = *(volatile u32 *)up->port.private_data;
            serial_out(up, UART_LCR, up->lcr);

            handled = 1;

            end = NULL;
        } else if (end == NULL)
            end = l;

        l = l->next;

        if (l == i->head && pass_counter++ > PASS_LIMIT) {
            /* If we hit this, we're dead. */
            printk(KERN_ERR "serial8250: too much work for "
                "irq%d\n", irq);
            break;
        }
    } while (l != end);

    spin_unlock(&i->lock);

    DEBUG_INTR("end.\n");

    return IRQ_RETVAL(handled);
}

static void serial8250_handle_port(struct uart_8250_port *up)
{
    unsigned int status;
    unsigned long flags;

    spin_lock_irqsave(&up->port.lock, flags);

    status = serial_inp(up, UART_LSR);

    DEBUG_INTR("status = %x...", status);

    if (status & (UART_LSR_DR | UART_LSR_BI))
        receive_chars(up, &status);
    check_modem_status(up);
    if (status & UART_LSR_THRE)
        transmit_chars(up);

    spin_unlock_irqrestore(&up->port.lock, flags);
}

static void transmit_chars(struct uart_8250_port *up)
{
    struct circ_buf *xmit = &up->port.info->xmit;
    int count;

    if (up->port.x_char) {
        serial_outp(up, UART_TX, up->port.x_char);
        up->port.icount.tx++;
        up->port.x_char = 0;
        return;
    }
    if (uart_tx_stopped(&up->port)) {
        serial8250_stop_tx(&up->port);
        return;
    }
    if (uart_circ_empty(xmit)) {//如果buf空
        __stop_tx(up);//停止发送,实在上是关闭发生器空中断
        return;
    }

    count = up->tx_loadsz;
    do {
        serial_out(up, UART_TX, xmit->buf[xmit->tail]);//输出字符
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        up->port.icount.tx++;
        if (uart_circ_empty(xmit))
            break;
    } while (--count > 0);

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
        uart_write_wakeup(&up->port);

    DEBUG_INTR("THRE...");

    if (uart_circ_empty(xmit))//输出完后为空
        __stop_tx(up);//关闭中断
}


0 0
原创粉丝点击