这一节分析串口核心中对写操作的处理,从用户空间调用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的操作方式。

Logo

更多推荐