linux serial构架分析及驱动开发(6)

    技术2024-07-12  69

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

    最新回复(0)