您的位置:首页 > 运维架构 > Linux

我对linux理解之tty五

2012-08-10 14:35 148 查看
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流程框架。还有相关的其它一些操作可以类似地去分析。
http://qrsdev.com/forum.php?mod=viewthread&tid=400&extra=page%3D1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: