if (count < c)
c = count;
if (c <= 0) //缓存区太小则退出
break;
//将用户空间buf中大小为c的内容拷贝到缓存中
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端口操作的锁
uart_start(tty); //开始发送
return ret;
}
根据上面对uart_write的分析,我们知道tty_driver的write()函数接收三个参数tty_struct,发送数据指针和要发送的字节数。uart_state作为这个驱动tty的私有数据,其中circ_buf定义了缓冲区,我们向这个缓冲区拷贝待发送的内容后,执行uart_start(tty)进行发送数据。那我们继续看跟踪uart_start函数
static void uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags); //获取UART端口操作的锁
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags); //释放UART端口操作的锁
}
static void __uart_start(struct tty_struct *tty)
{
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
!tty->stopped && !tty->hw_stopped) //缓冲区有数据并开启发送状态
port->ops->start_tx(port); //调用uart_ops下的start_tx,即s3c24xx_serial_start_tx
}
注意__uart_start函数中的port->ops->start_tx(port)便实现了tty层和uart层的相连,由tty层的write()调用uart层的write()。
好了,上面讲的是发送数据,读者可能注意到struct tty_operations uart_ops中没有提到read()函数。因为发送是用户主动的,而接收拾用户调用read()从一片缓冲区读取已经放好的数据,这个缓冲区由struct tty_flip_buffer结构体实现。因为tty核提供了这样的缓冲逻辑,所以每个tty驱动并非一定要实现它自身的缓冲逻辑。Tty驱动不需要关注struct tty_flip_buffe的细节,从tty驱动接收到的来自硬件层的字符将被tty_insert_filp_char()函数插入filp缓冲区。如果传输的字节数count大于或等于TTY_FLIPBUF_SIEZE,这个flip缓冲区就需要被刷新到用户,刷新是通过调用tty_flip_buffer_push()实现的。
接着,我们继续看struct tty_operations uart_ops中对termios的设置函数set_termios,即uart_set_termios。这个set_termios需要根据用户对termios的设置完成实际的硬件设置。新的设置被保存在tty_struct中,旧的设置被保存在old参数中,若新旧参数相同,则什么都不需要做,对于被改的设置,需要完成硬件上的设置。好了,下面我们还是看看uart_set_termios的实现吧。
static void uart_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct uart_state *state = tty->driver_data; //获取私有数据
unsigned long flags;
unsigned int cflag = tty->termios->c_cflag; //获取当前线路设置
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
//如果新旧线路设置的控制状态,输入输出速度等信息一样,则退出
if ((cflag ^ old_termios->c_cflag) == 0 &&
tty->termios->c_ospeed == old_termios->c_ospeed &&
tty->termios->c_ispeed == old_termios->c_ispeed &&
RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {
return;
}
uart_change_speed(state, old_termios); //用新的线路规程的速度更新旧的线路规程
//处理波特率为B0情况
if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
uart_clear_mctrl(state->uart_port, TIOCM_RTS | TIOCM_DTR);
/处理波特率为非B0情况
if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
unsigned int mask = TIOCM_DTR;
if (!(cflag & CRTSCTS) ||
!test_bit(TTY_THROTTLED, &tty->flags))
mask |= TIOCM_RTS;
uart_set_mctrl(state->uart_port, mask); //设置modem控制
}
//处理无数据流控制情况
if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
spin_lock_irqsave(&state->uart_port->lock, flags);
tty->hw_stopped = 0;
__uart_start(tty);
spin_unlock_irqrestore(&state->uart_port->lock, flags);