------------------------------------------
本文系本站原创,欢迎转载!
------------------------------------------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;//在open里面有设置
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);//注意这里的传入的参数
tty_ldisc_deref(ld);
return ret;
}
主要工作变为do_tty_write(ld->ops->write, tty, file, buf, count),我们下面来看这个函数:
static inline ssize_t do_tty_write(
ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
struct tty_struct *tty,
struct file *file,
const char __user *buf,
size_t count)
{
ssize_t ret, written = 0;
unsigned int chunk;
ret = tty_write_lock(tty, file->f_flags & O_NDELAY);//上锁
if (ret < 0)
return ret;
/*
* We chunk up writes into a temporary buffer. This
* simplifies low-level drivers immensely, since they
* don't have locking issues and user mode accesses.
*
* But if TTY_NO_WRITE_SPLIT is set, we should use a
* big chunk-size..
*
* The default chunk-size is 2kB, because the NTTY
* layer has problems with bigger chunks. It will
* claim to be able to handle more characters than
* it actually does.
*
* FIXME: This can probably go away now except that 64K chunks
* are too likely to fail unless switched to vmalloc...
*/
chunk = 2048;
if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
chunk = 65536;
if (count < chunk)
chunk = count;
/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
if (tty->write_cnt < chunk) {
unsigned char *buf_chunk;
if (chunk < 1024)
chunk = 1024;
buf_chunk = kmalloc(chunk, GFP_KERNEL);
if (!buf_chunk) {
ret = -ENOMEM;
goto out;
}
kfree(tty->write_buf);
tty->write_cnt = chunk;
tty->write_buf = buf_chunk;
}
//上面准备好写的buf和大小长度
/* Do the write .. */
for (;;) {
size_t size = count;
if (size > chunk)
size = chunk;
ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size))//从用户空间的buf拷贝到write_buf
break;
ret = write(tty, file, tty->write_buf, size);//这个函数是传进来的write函数,即ld->ops->write,也就是线路规程的函数
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (signal_pending(current)) //检查当前进程是否有信号处理
break;
cond_resched();
}
if (written) {
struct inode *inode = file->f_path.dentry->d_inode;
inode->i_mtime = current_fs_time(inode->i_sb);
ret = written;
}
out:
tty_write_unlock(tty);
return ret;
}
我们看到最终还是执行到了线路规程的write函数,ld->ops->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的driver的ops,即对应serial_core中的uart_ops
tty->ops->flush_chars(tty);
} else {//没加工过的数据
while (nr > 0) {
c = tty->ops->write(tty, b, nr);//同样调用serial_core中的uart_ops->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();
}
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->flush_chars(tty)还是tty->ops->write(tty, b, nr),到serial_core中去找uart_ops:
static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
.put_char = uart_put_char,
.flush_chars = uart_flush_chars,
.write_room = uart_write_room,
.chars_in_buffer= uart_chars_in_buffer,
.flush_buffer = uart_flush_buffer,
.ioctl = uart_ioctl,
.throttle = uart_throttle,
.unthrottle = uart_unthrottle,
.send_xchar = uart_send_xchar,
.set_termios = uart_set_termios,
.set_ldisc = uart_set_ldisc,
.stop = uart_stop,
.start = uart_start,
.hangup = uart_hangup,
.break_ctl = uart_break_ctl,
.wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
.proc_fops = &uart_proc_fops,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
tty->ops->flush_chars对应了uart_flush_chars,tty->ops->write对应了uart_write。我们先看uart_flush_chars
static void uart_flush_chars(struct tty_struct *tty)
{
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->port;
unsigned long flags;
spin_lock_irqsave(&port->lock, flags);
__uart_start(tty);
spin_unlock_irqrestore(&port->lock, flags);
}
转到了__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);//对应到了port的ops的start_tx,在本系统中对应了mxc_ops
}
我们看下mxc_ops:
static struct uart_ops mxc_ops = {
.tx_empty = mxcuart_tx_empty,
.set_mctrl = mxcuart_set_mctrl,
.get_mctrl = mxcuart_get_mctrl,
.stop_tx = mxcuart_stop_tx,
.start_tx = mxcuart_start_tx,
.stop_rx = mxcuart_stop_rx,
.enable_ms = mxcuart_enable_ms,
.break_ctl = mxcuart_break_ctl,
.startup = mxcuart_startup,
.shutdown = mxcuart_shutdown,
.set_termios = mxcuart_set_termios,
.type = mxcuart_type,
.pm = mxcuart_pm,
.release_port = mxcuart_release_port,
.request_port = mxcuart_request_port,
.config_port = mxcuart_config_port,
.verify_port = mxcuart_verify_port,
.send_xchar = mxcuart_send_xchar,
};
我们看到port->ops->start_tx对应了mxcuart_start_tx:
static void mxcuart_start_tx(struct uart_port *port)
{
uart_mxc_port *umxc = (uart_mxc_port *) port;
struct circ_buf *xmit = &umxc->port.info->xmit;
volatile unsigned int cr1;
mxc_dma_requestbuf_t writechnl_request;
int tx_num;
cr1 = readl(port->membase + MXC_UARTUCR1);
/* Enable Transmitter rdy interrupt */
if (umxc->dma_enabled == 1) {//这个标记很重要,决定了是dma操作,还是普通操作
/*
* If the channel is in use then return immediately and use
* the dma_tx tasklet to transfer queued data when current DMA
* transfer is complete
*/
if (dma_list[umxc->port.line].dma_txchnl_inuse == 1) {
return;
}
tx_num = uart_circ_chars_pending(xmit);
if (tx_num > 0) {
dma_list[umxc->port.line].dma_txchnl_inuse = 1;
if (xmit->tail > xmit->head) {
memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
UART_XMIT_SIZE - xmit->tail);
memcpy(umxc->tx_buf +
(UART_XMIT_SIZE - xmit->tail), xmit->buf,
xmit->head);
} else {
memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
tx_num);
}
umxc->tx_handle =
dma_map_single(umxc->port.dev, umxc->tx_buf,
TXDMA_BUFF_SIZE, DMA_TO_DEVICE);
writechnl_request.dst_addr =
umxc->port.mapbase + MXC_UARTUTXD;
writechnl_request.src_addr = umxc->tx_handle;
writechnl_request.num_of_bytes = tx_num;
if ((mxc_dma_config
(dma_list[umxc->port.line].wr_channel,
&writechnl_request, 1,
MXC_DMA_MODE_WRITE)) == 0) {
mxc_dma_enable(dma_list[umxc->port.line].
wr_channel);
}
cr1 |= MXC_UARTUCR1_TXDMAEN;
}
} else {
cr1 |= MXC_UARTUCR1_TRDYEN;
}
writel(cr1, port->membase + MXC_UARTUCR1);
}
再看tty->ops->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);
circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
buf += c;
count -= c;
ret += c;
}
spin_unlock_irqrestore(&port->lock, flags);
uart_start(tty);//最终也调用了上面分析的uart_start
return ret;
}
我们看到tty_write操作层次是这样的: tty_core->tty_ldisc->serial_core->mxc_uart
好了,至此我们分析了tty的注册,open,read,write流程框架。还有相关的其它一些操作可以类似地去分析。