微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux下的串口总线驱动(二)

Linux下的串口总线驱动(二)

时间:11-22 来源:互联网 点击:

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);

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top