//中断处理函数,在下面2种情况下被调用
//1.输入一个字符时;2.输出一个字符时,当serial8250_start_tx打开发生器空中断后,因为发送器为空,所以输出的时候利用该中断从输出缓冲区向发送寄存器拷贝字符
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);
}
//1.当输入一个字符时被调用,例如我们在控制台输入'l'
//2.输出的时候时候,当serial8250_start_tx打开发生器空中断后,导致它被调用
static void serial8250_handle_port(struct uart_8250_port *up)
{
unsigned int status;
unsigned long flags;
spin_lock_irqsave(&up->port.lock, flags);
//1.输入时读取中断状态寄存器,status 为0x61,表示UART_LSR_DR,UART_LSR_THRE,UART_LSR_TEMT位置位
//既有数据,且发送器和传输保存寄存器为空;2.输出时status 为0x60
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);//进入transmit_chars进行处理
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;
}
//1.当输入字符进入transmit_char时xmit为空,所以条件为真
//2.当输出字符进入transmit_char时xmit不为空,所以条件为假
if (uart_circ_empty(xmit)) {
__stop_tx(up);
return;
}
count = up->tx_loadsz;//对cns3xxx初始化tx_loadsz为0,所以下面的while都是直到xmit为空为此
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);
//如果输出缓冲区的字符少于WAKEUP_CHARS,则唤醒等待输出的进程
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(&up->port);
DEBUG_INTR("THRE...");
//输出字符,导致缓冲区为空,调用 __stop_tx关闭中断
if (uart_circ_empty(xmit))
__stop_tx(up);
}
static inline void __stop_tx(struct uart_8250_port *p)
{
//如果发生器空中断打开,则关闭,
//1.当输入字符导致__stop_tx时,此时中断已经关闭,所以条件为假
//2.当输出字符导致__stop_tx时,此时中断已经关闭,所以条件为假
if (p->ier & UART_IER_THRI) {
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
}
}
#define uart_circ_chars_pending(circ) \
(CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
/* Return count in buffer. */
#define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1))
static void
receive_chars(struct uart_8250_port *up, unsigned int *status)
{
struct tty_struct *tty = up->port.info->port.tty;
unsigned char ch, lsr = *status;
int max_count = 256;
char flag;
do {
if (likely(lsr & UART_LSR_DR))
ch = serial_inp(up, UART_RX);//读取字符
else
/*
* Intel 82571 has a Serial Over Lan device that will
* set UART_LSR_BI without setting UART_LSR_DR when
* it receives a break. To avoid reading from the
* receive buffer without UART_LSR_DR bit set, we
* just force the read character to be 0
*/
ch = 0;
flag = TTY_NORMAL;
up->port.icount.rx++;
lsr |= up->lsr_saved_flags;
up->lsr_saved_flags = 0;
if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {//输入错误
/*
* For statistics only
*/
if (lsr & UART_LSR_BI) {
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
up->port.icount.brk++;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask.
*/
if (uart_handle_break(&up->port))
goto ignore_char;
} else if (lsr & UART_LSR_PE)
up->port.icount.parity++;
else if (lsr & UART_LSR_FE)
up->port.icount.frame++;
if (lsr & UART_LSR_OE)
up->port.icount.overrun++;
/*
* Mask off conditions which should be ignored.
*/
lsr &= up->port.read_status_mask;
if (lsr & UART_LSR_BI) {
DEBUG_INTR("handling break....");
flag = TTY_BREAK;
} else if (lsr & UART_LSR_PE)
flag = TTY_PARITY;
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
if (uart_handle_sysrq_char(&up->port, ch))
goto ignore_char;
//把字符放入tty接收缓冲区
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
ignore_char:
lsr = serial_inp(up, UART_LSR);
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));//循环到无字符,或者达到最大输入字符数
spin_unlock(&up->port.lock);
tty_flip_buffer_push(tty);//通知上层读取字符
spin_lock(&up->port.lock);
*status = lsr;
}
static inline void
uart_insert_char(struct uart_port *port, unsigned int status,
unsigned int overrun, unsigned int ch, unsigned int flag)
{
struct tty_struct *tty = port->info->port.tty;
//把字符放入接收缓冲区
if ((status & port->ignore_status_mask & ~overrun) == 0)
tty_insert_flip_char(tty, ch, flag);
/*
* Overrun is special. Since it's reported immediately,
* it doesn't affect the current character.
*/
if (status & ~port->ignore_status_mask & overrun)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}
static inline int tty_insert_flip_char(struct tty_struct *tty,
unsigned char ch, char flag)
{
struct tty_buffer *tb = tty->buf.tail;
//系统启动后,第一次输入时候,tty->buf.tail为NULL(在TTY OPEN初始化);以后输入时则不为空
if (tb && tb->used < tb->size) {
tb->flag_buf_ptr[tb->used] = flag;
tb->char_buf_ptr[tb->used++] = ch;
return 1;
}
//系统启动后,第一次输入时候,会进入处理
return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}
int tty_insert_flip_string_flags(struct tty_struct *tty,
const unsigned char *chars, const char *flags, size_t size)
{
int copied = 0;
do {
//判断缓冲区是否够用,不够会增加,返回可用缓冲区的长度
int space = tty_buffer_request_room(tty, size - copied);
struct tty_buffer *tb = tty->buf.tail;
/* If there is no space then tb may be NULL */
if (unlikely(space == 0))
break;
memcpy(tb->char_buf_ptr + tb->used, chars, space);//把输入放入tty buff缓冲区
memcpy(tb->flag_buf_ptr + tb->used, flags, space);
tb->used += space;
copied += space;
chars += space;
flags += space;
/* There is a small chance that we need to split the data over
several buffers. If this is the case we must loop */
} while (unlikely(size > copied));//直到完全拷贝完为止
return copied;
}
int tty_buffer_request_room(struct tty_struct *tty, size_t size)
{
struct tty_buffer *b, *n;
int left;
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
/* OPTIMISATION: We could keep a per tty "zero" sized buffer to
remove this conditional if its worth it. This would be invisible
to the callers */
if ((b = tty->buf.tail) != NULL)//缓冲区尾不为空
left = b->size - b->used;//缓冲区尾部剩余空间
else
left = 0;
//剩余空间不够
if (left < size) {
/* This is the slow path - looking for new buffers to use */
if ((n = tty_buffer_find(tty, size)) != NULL) {//在自由缓冲区链查找>size可用缓冲区,找不到则重新分配
if (b != NULL) {
b->next = n;//如果b不为NULL,则把心的缓冲区链入其后
b->commit = b->used;
} else
tty->buf.head = n;
tty->buf.tail = n;
} else
size = left;
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
return size;
}
static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
{
struct tty_buffer **tbh = &tty->buf.free;
while ((*tbh) != NULL) {//在自由buf链寻找可用的缓冲区
struct tty_buffer *t = *tbh;
if (t->size >= size) {
*tbh = t->next;
t->next = NULL;
t->used = 0;
t->commit = 0;
t->read = 0;
tty->buf.memory_used += t->size;
return t;
}
tbh = &((*tbh)->next);
}
/* Round the buffer size out */
size = (size + 0xFF) & ~0xFF;
return tty_buffer_alloc(tty, size);//找不到则分配
/* Should possibly check if this fails for the largest buffer we
have queued and recycle that ? */
}
static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
{
struct tty_buffer *p;
if (tty->buf.memory_used + size > 65536)
return NULL;
p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
if (p == NULL)
return NULL;
p->used = 0;
p->size = size;
p->next = NULL;
p->commit = 0;
p->read = 0;
p->char_buf_ptr = (char *)(p->data);
p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
tty->buf.memory_used += size;
return p;
}
void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)//如果低延迟,则立即调用 flush_to_ldisc
flush_to_ldisc(&tty->buf.work.work);
else//否则通过工作队列,其实也是调用 flush_to_ldisc
schedule_delayed_work(&tty->buf.work, 1);
}
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
unsigned long flags;
struct tty_ldisc *disc;
struct tty_buffer *tbuf, *head;
char *char_buf;
unsigned char *flag_buf;
disc = tty_ldisc_ref(tty);
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
/* So we know a flush is running */
set_bit(TTY_FLUSHING, &tty->flags);
head = tty->buf.head;
if (head != NULL) {
tty->buf.head = NULL;
for (;;) {
int count = head->commit - head->read;
if (!count) {//当第2此循环时count为0
if (head->next == NULL)
break;
tbuf = head;
head = head->next;
tty_buffer_free(tty, tbuf);
continue;
}
/* Ldisc or user is trying to flush the buffers
we are feeding to the ldisc, stop feeding the
line discipline as we want to empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags))
break;
if (!tty->receive_room) {
schedule_delayed_work(&tty->buf.work, 1);
break;
}
if (count > tty->receive_room)//当我们输入'l'时,count为1,receive_room为4095
count = tty->receive_room;
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;//头部加count
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);//此时调用n_tty_receive_buf
spin_lock_irqsave(&tty->buf.lock, flags);
}
/* Restore the queue head */
tty->buf.head = head;
}
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
clear_bit(TTY_FLUSHING, &tty->flags);
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);
}
阅读(2785) | 评论(0) | 转发(0) |