这一节分析串口核心中对写操作的处理,从用户空间调用write系统调用开始,首先执行tty_write函数,在该函数中执行do_tty_write,将用户空间的数据复制到tty->write_buf中,然后调用线路规程中的写函数即write_chain,最后write_chain调用uart_write或uart_flush_chars,再调用专用port->ops中的数据将数据发射出去,具体的操作过程看下面的源码。
//uart_write现将待写入的数据写入到环形缓存state->info->xmit中,然后调用uart_start发射数据
static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct uart_state *state = tty->driver_data; struct uart_port *port; struct circ_buf *circ; unsigned long flags; int c, ret = 0;
/* * This means you called this function _after_ the port was * closed. No cookie for you. */ if (!state || !state->info) { WARN_ON(1); return -EL3HLT; }
port = state->port; circ = &state->info->xmit;
if (!circ->buf) return 0;
spin_lock_irqsave(&port->lock, flags); while (1) {
//对环形缓存中空间的判断 c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; if (c <= 0) //数据写完或者没有空间时退出循环 break; memcpy(circ->buf + circ->head, buf, c); circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } spin_unlock_irqrestore(&port->lock, flags);
uart_start(tty); //完成数据发射 return ret;}
//环形缓存中有数据而没有停止串口时,调用串口port->ops->start_tx发射数据,而uart_flush_chars直接调用uart_start
//把环形缓存中的数据发射出去
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);}
/
//uart_put_char 是将一个字符复制到用户空间,和uart_write的作用相同但是只针对一个字符且没有发射处理
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->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); ret = 1; } spin_unlock_irqrestore(&port->lock, flags); return ret;}
//uart_write_room检测环形缓存中是否存在空间,并返回可用的空间数量
static int uart_write_room(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; unsigned long flags; int ret;
spin_lock_irqsave(&state->port->lock, flags); ret = uart_circ_chars_free(&state->info->xmit); spin_unlock_irqrestore(&state->port->lock, flags); return ret;}
在前面的tty核心分析中我们就介绍过tty_read函数是从tty->read_buf中将数据读到用户空间的,而tty->read_buf中的数据来源于struct tty_bufhead管理的数据缓存。struct tty_bufhead管理的各个缓存中的数据来源于在中断中调用 tty_insert_flip_char 类函数将接受到的数据保存其中,所以对串口驱动而言没有什么特别的完全继承tty的操作方式。