一、TTY数据发送调用关系
这个上一篇open有点相似:
从samsung.c>>uart_register_driver>>tty_register_driver>注册>tty_fops(file_operations结构)>>tty_write:
-
static ssize_t tty_write(struct file *file, const char __user *buf,
-
size_t count, loff_t *ppos)
-
{
-
struct tty_struct *tty;
-
struct inode *inode = file->f_path.dentry->d_inode;
-
ssize_t ret;
-
struct tty_ldisc *ld;
-
-
tty = (struct tty_struct *)file->private_data;
-
if (tty_paranoia_check(tty, inode, "tty_write"))
-
return -EIO;
-
if (!tty || !tty->ops->write ||
-
(test_bit(TTY_IO_ERROR, &tty->flags)))
-
return -EIO;
-
/* Short term debug to catch buggy drivers */
-
if (tty->ops->write_room == NULL)
-
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
-
tty->driver->name);
-
ld = tty_ldisc_ref_wait(tty);
-
if (!ld->ops->write)
-
ret = -EIO;
-
else
-
ret = do_tty_write(ld->ops->write, tty, file, buf, count); //ld->ops是线路规程的结构,并调用了write
-
tty_ldisc_deref(ld);
-
return ret;
-
}
tty_ldisc_N_tty结构:
-
struct tty_ldisc_ops tty_ldisc_N_TTY = {
-
.magic = TTY_LDISC_MAGIC,
-
.name = "n_tty",
-
.open = n_tty_open,
-
.close = n_tty_close,
-
.flush_buffer = n_tty_flush_buffer,
-
.chars_in_buffer = n_tty_chars_in_buffer,
-
.read = n_tty_read,
-
.write = n_tty_write, //write指向的是n_tty_write
-
.ioctl = n_tty_ioctl,
-
.set_termios = n_tty_set_termios,
-
.poll = n_tty_poll,
-
.receive_buf = n_tty_receive_buf,
-
.write_wakeup = n_tty_write_wakeup
-
};
n_tty_write:
-
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
-
const unsigned char *buf, size_t nr)
-
{
-
const unsigned char *b = buf;
-
DECLARE_WAITQUEUE(wait, current);
-
int c;
-
ssize_t retval = 0;
-
-
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
-
if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
-
retval = tty_check_change(tty);
-
if (retval)
-
return retval;
-
}
-
-
/* Write out any echoed characters that are still pending */
-
process_echoes(tty);
-
-
add_wait_queue(&tty->write_wait, &wait); //将当前进程放到等待队列中
-
while (1) {
-
set_current_state(TASK_INTERRUPTIBLE); //进入此处继续执行的原因可能是被信号打断,而不是条件得到了满足。
-
if (signal_pending(current)) {
-
retval = -ERESTARTSYS;
-
break;
-
} //只有条件得到了满足,我们才会继续,否则,直接返回!
-
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
-
retval = -EIO;
-
break;
-
}
-
if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
-
while (nr > 0) {
-
ssize_t num = process_output_block(tty, b, nr);
-
if (num < 0) {
-
if (num == -EAGAIN)
-
break;
-
retval = num;
-
goto break_out;
-
}
-
b += num;
-
nr -= num;
-
if (nr == 0)
-
break;
-
c = *b;
-
if (process_output(c, tty) < 0)
-
break;
-
b++; nr--;
-
}
-
if (tty->ops->flush_chars)
-
tty->ops->flush_chars(tty);
-
} else {
-
while (nr > 0) {
-
c = tty->ops->write(tty, b, nr); //调用到具体的驱动中的write函数
-
if (c < 0) {
-
retval = c;
-
goto break_out;
-
}
-
if (!c)
-
break;
-
b += c;
-
nr -= c;
-
}
-
}
-
if (!nr)
-
break;
-
if (file->f_flags & O_NONBLOCK) { //全部写入,返回
-
retval = -EAGAIN;
-
break;
-
}
-
schedule(); ///执行到这里,当前进程才会真正让出cpu!!!
-
}
-
break_out:
-
__set_current_state(TASK_RUNNING);
-
remove_wait_queue(&tty->write_wait, &wait);
-
if (b - buf != nr && tty->fasync)
-
set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
-
return (b - buf) ? b - buf : retval;
-
}
tty->ops->write是uart_ops,里面的uart_write:
-
static int
-
uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
-
{
-
......
-
uart_start(tty); //调用了uart_start函数
-
return ret;
-
}
uart_start函数:
-
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_ops ,调用了stat_tx指向的函数
-
}
这里就和上一篇的open类似,只不过调用的函数指针不一样,这次是start_tx(s3c24xx_serial_start_tx)函数:
-
static void s3c24xx_serial_start_tx(struct uart_port *port)
-
{
-
struct s3c24xx_uart_port *ourport = to_ourport(port);
-
-
//dump_stack();
-
-
if (!tx_enabled(port)) { //如果没有打开发送
-
if (port->flags & UPF_CONS_FLOW)
-
s3c24xx_serial_rx_disable(port);
-
-
enable_irq(ourport->tx_irq); //使能发送中断:接收发送中断有FIFO,FIFO里面数据够了,如果激活了中断,中断处理程序被调用。
-
tx_enabled(port) = 1;
-
}
-
}
总结:这里路线也一样file_operations->write,调用了线路规程n_tty_write,然后调用uart_ops中的uart_write函数,最后找到了s3c24xx_serial_start_tx的驱动。
二、串口发送函数
在write中其实只是判断了是否有发送端口打开,但是真正去发送数据的。是串口中断程序,所以我们需要把串口中断程序流程了解。
应用程序中的write对应于2440串口内核中,是uart_write:
-
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) {
-
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); //把buf中的数据复制到circ循环缓冲中去
-
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;
-
}
串口发送函数s3c24xx_serial_tx_chars:
-
static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
-
{
-
struct s3c24xx_uart_port *ourport = id;
-
struct uart_port *port = &ourport->port;
-
struct circ_buf *xmit = &port->info->xmit;
-
int count = 256;
-
-
if (port->x_char) { //如果port中有字符内容,如果有将x_char写入S3C2410_UTXH中
-
wr_regb(port, S3C2410_UTXH, port->x_char);
-
port->icount.tx++;
-
port->x_char = 0;
-
goto out;
-
}
-
-
/* if there isnt anything more to transmit, or the uart is now
-
* stopped, disable the uart and exit
-
*/
-
-
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { //判断循环缓冲是否为空,或者发送功能关闭
-
s3c24xx_serial_stop_tx(port); //关闭串口中断
-
goto out;
-
}
-
-
/* try and drain the buffer... */
-
-
while (!uart_circ_empty(xmit) && count-- > 0) { //如果循环缓冲非空
-
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull) //判断FIFO是否满了
-
break;
-
-
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]); //将数据写入UTXH中
-
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); //调整循环缓冲的位置
-
port->icount.tx++;
-
}
-
-
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) //WAKEUP_CHARS是256,循环缓冲的数据量低于256时。满了必须把想发送串口的pending
-
uart_write_wakeup(port); //唤醒
-
-
if (uart_circ_empty(xmit)) //当循环缓冲为空
-
s3c24xx_serial_stop_tx(port); //关闭发送功能,保证不会一直产生中断
-
-
out:
-
return IRQ_HANDLED;
-
}
write------->uart_write(将数据写入循环缓冲)------>start_tx(激活中断)
发送中断处理函数,从循环缓冲中读取数据,发送到串口。
阅读(538) | 评论(0) | 转发(0) |