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);//关闭中断
}
{
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);//关闭中断
}