linux serial构架分析及驱动开发(6)
这一节分析串口核心中对写操作的处理,从用户空间调用write系统调用开始,首先执行tty_write函数,在该函数中执行do_tty_write,将用户空间的数据复制到tty->write_buf中,然后调用线路规程中的写函数即write_chain,最后write_chain调用uart_write或uart_flush_chars,再调用专用port->ops中的数据将数据发射出
这一节分析串口核心中对写操作的处理,从用户空间调用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)