一、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(激活中断)
发送中断处理函数,从循环缓冲中读取数据,发送到串口。
<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(34) | 评论(0) | 转发(0) |
0
上一篇:串口驱动分析-打开设备
下一篇:串口驱动分析-接收
相关热门文章
SHTML是什么_SSI有什么用...
查看linux中某个端口(port)...
卡尔曼滤波的原理说明...
shell中字符串操作
关于java中的“错误:找不到或...
给主人留下些什么吧!~~
评论热议