linux在tty3创建用户,我对linux理解之tty三

------------------------------------------

本文系本站原创,欢迎转载!------------------------------------------我们现在congtty core层的file operations开始分析。

static const struct file_operations tty_fops = {

.llseek        = no_llseek,

.read        = tty_read,

.write        = tty_write,

.poll        = tty_poll,

.unlocked_ioctl    = tty_ioctl,

.compat_ioctl    = tty_compat_ioctl,

.open        = tty_open,

.release    = tty_release,

.fasync        = tty_fasync,

};

先看open函数:

static int tty_open(struct inode *inode, struct file *filp)

{

int ret;

lock_kernel(); //哇哈哈,第一次看到传说中的大内核锁

ret = __tty_open(inode, filp);

unlock_kernel();

return ret;

}

转而调用__tty_open(inode, filp):

static int __tty_open(struct inode *inode, struct file *filp)

{

......

got_driver:

if (!tty) {

/* check whether we're reopening an existing tty */

tty = tty_driver_lookup_tty(driver, inode, index);

if (IS_ERR(tty)) {

mutex_unlock(&tty_mutex);

return PTR_ERR(tty);

}

}

if (tty) {

retval = tty_reopen(tty);

if (retval)

tty = ERR_PTR(retval);

} else

tty = tty_init_dev(driver, index, 0);//初始化一个tty设备,初始化线路规程和打开线路规程, 见1

mutex_unlock(&tty_mutex);

tty_driver_kref_put(driver);

if (IS_ERR(tty))

return PTR_ERR(tty);

filp->private_data = tty;//私有数据设置,read/write函数都将用到这个变量

file_move(filp, &tty->tty_files);

check_tty_count(tty, "tty_open");

if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&

tty->driver->subtype == PTY_TYPE_MASTER)

noctty = 1;

#ifdef TTY_DEBUG_HANGUP

printk(KERN_DEBUG "opening %s...", tty->name);

#endif

if (!retval) {

if (tty->ops->open)

retval = tty->ops->open(tty, filp);//ops对应driver的ops,即uart_ops,也就是调用serial_core中的uart_open,见2

else

retval = -ENODEV;

}

filp->f_flags = saved_flags;

......

}

1,tty_init_dev(driver, index, 0):

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,

int first_ok)

{

......

tty = alloc_tty_struct();

if (!tty)

goto fail_no_mem;

initialize_tty_struct(tty, driver, idx);//初始化tty结构,见1-1

retval = tty_driver_install_tty(driver, tty);

if (retval < 0) {

free_tty_struct(tty);

module_put(driver->owner);

return ERR_PTR(retval);

}

/*

* Structures all installed ... call the ldisc open routines.

* If we fail here just call release_tty to clean up.  No need

* to decrement the use counts, as release_tty doesn't care.

*/

retval = tty_ldisc_setup(tty, tty->link);//打开线路规程,在initialize_tty_struct中有对线路规程初始化,见1-2

if (retval)

goto release_mem_out;

return tty;

......

}

1-1,initialize_tty_struct(tty, driver, idx):

void initialize_tty_struct(struct tty_struct *tty,

struct tty_driver *driver, int idx)

{

memset(tty, 0, sizeof(struct tty_struct));

kref_init(&tty->kref);

tty->magic = TTY_MAGIC;

tty_ldisc_init(tty);//线路规程初始化,见1-1-1

tty->session = NULL;

tty->pgrp = NULL;

tty->overrun_time = jiffies;

tty->buf.head = tty->buf.tail = NULL;

tty_buffer_init(tty); //buffer初始化,见1-1-2

mutex_init(&tty->termios_mutex);

mutex_init(&tty->ldisc_mutex);

init_waitqueue_head(&tty->write_wait);

init_waitqueue_head(&tty->read_wait);

INIT_WORK(&tty->hangup_work, do_tty_hangup);

mutex_init(&tty->atomic_read_lock);

mutex_init(&tty->atomic_write_lock);

mutex_init(&tty->output_lock);

mutex_init(&tty->echo_lock);

spin_lock_init(&tty->read_lock);

spin_lock_init(&tty->ctrl_lock);

INIT_LIST_HEAD(&tty->tty_files);

INIT_WORK(&tty->SAK_work, do_SAK_work);

tty->driver = driver;

tty->ops = driver->ops;/*请注意这里的赋值,它将driver的ops赋值给了tty->ops,后面有很多用到该ops的使用,那我们

到时候要想起来它实际是对driver的ops的使用,这里driver就对应serial_core中的注册时的使用的那个driver*/

tty->index = idx;

tty_line_name(driver, idx, tty->name);

}

这个函数主要对tty_struct各个部分进行初始化。我们着重分析1-1-1和1-1-2。

1-1-1,tty_ldisc_init(tty):

void tty_ldisc_init(struct tty_struct *tty)

{

struct tty_ldisc *ld = tty_ldisc_get(N_TTY); //得到tty的线路规程,N_TTY是tty的线路规程号,内核启动时,将会注册这个N_TTY对应的ops:tty_ldisc_N_TTY

if (IS_ERR(ld))

panic("n_tty: init_tty");

tty_ldisc_assign(tty, ld); //设置线路规程给tty

}

所以经过这个初始化函数后,tty->ldisc将会被赋值为N_TTY对应的线路规程

1-1-2,tty_buffer_init(tty)

void tty_buffer_init(struct tty_struct *tty)

{//初始化buffer结构

spin_lock_init(&tty->buf.lock);

tty->buf.head = NULL;

tty->buf.tail = NULL;

tty->buf.free = NULL;

tty->buf.memory_used = 0;

INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);//这个工作队列非常重要!主要功能是将buf数据刷到线路规程,见1-1-2-1

}

这个函数主要初始化buf结构,我们主要分析一下tty->buf.work的具体作用。

1-1-2-1,INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)

static void flush_to_ldisc(struct work_struct *work)

{

struct tty_struct *tty =

container_of(work, struct tty_struct, buf.work.work);

unsigned long     flags;

struct tty_ldisc *disc;

disc = tty_ldisc_ref(tty);//得到tty的线路规程引用

if (disc == NULL)    /*  !TTY_LDISC */

return;

spin_lock_irqsave(&tty->buf.lock, flags);

if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {//设置flush标记

struct tty_buffer *head;

while ((head = tty->buf.head) != NULL) {

int count;

char *char_buf;

unsigned char *flag_buf;

count = head->commit - head->read;

if (!count) {

if (head->next == NULL)

break;

tty->buf.head = head->next;

tty_buffer_free(tty, head);

continue;

}

/* Ldisc or user is trying to flush the buffers

we are feeding to the ldisc, stop feeding the

line discipline as we want to empty the queue */

if (test_bit(TTY_FLUSHPENDING, &tty->flags))//如果想停止buffer转移到线路规程

break;

if (!tty->receive_room) { //当tty没接收空间的时候,延迟1个jiffies调用tty->buf.work,即flush_to_ldisc,把buf数据移到线路规程

schedule_delayed_work(&tty->buf.work, 1);

break;

}

if (count > tty->receive_room)

count = tty->receive_room;//不能大于接收空间的大小

char_buf = head->char_buf_ptr + head->read;

flag_buf = head->flag_buf_ptr + head->read;

head->read += count;

spin_unlock_irqrestore(&tty->buf.lock, flags);

disc->ops->receive_buf(tty, char_buf,

flag_buf, count);//执行线路规程的receive_buf,把buffer拷贝过来,这里的ops就是上面的tty_ldisc_N_TTY,见1-1-2-1-1

spin_lock_irqsave(&tty->buf.lock, flags);

}

clear_bit(TTY_FLUSHING, &tty->flags);//结束flush,设置结束标记

}

/* We may have a deferred request to flush the input buffer,

if so pull the chain under the lock and empty the queue */

if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {//如果设置停止flush标记,则清空buffer

__tty_buffer_flush(tty);

clear_bit(TTY_FLUSHPENDING, &tty->flags);

wake_up(&tty->read_wait);

}

spin_unlock_irqrestore(&tty->buf.lock, flags);

tty_ldisc_deref(disc);//释放一个线路规程的引用

}

可以看出其主要作用是将tty core的buffer刷到线路规程。

1-1-2-1-1,disc->ops->receive_buf(tty, char_buf, flag_buf, count):

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,

.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

};

可以看出tty_ldisc_N_TTY->receive_buf对应于n_tty_receive_buf:

static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,

char *fp, int count)

{

const unsigned char *p;

char *f, flags = TTY_NORMAL;

int    i;

char    buf[64];

unsigned long cpuflags;

if (!tty->read_buf)

return;

if (tty->real_raw) {

spin_lock_irqsave(&tty->read_lock, cpuflags);

i = min(N_TTY_BUF_SIZE - tty->read_cnt,

N_TTY_BUF_SIZE - tty->read_head);

i = min(count, i);//还是先确定要拷贝数据的长度小

memcpy(tty->read_buf + tty->read_head, cp, i);//拷贝到read buf

tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);

tty->read_cnt += i;

cp += i;

count -= i;

i = min(N_TTY_BUF_SIZE - tty->read_cnt,

N_TTY_BUF_SIZE - tty->read_head);

i = min(count, i);

memcpy(tty->read_buf + tty->read_head, cp, i);

tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);

tty->read_cnt += i;

spin_unlock_irqrestore(&tty->read_lock, cpuflags);

/*这里你可能奇怪为什么要拷贝两次呢?是因为数据读取缓存read_buf(0~N_TTY_BUF_SIZE)为一环形缓冲区。tty->read_tail, tty->read_tail指向第一个未被读取的数据,

tty->read_cnt缓存中的数据,tty->read_head指向 第一个未被占用的空间。由于是环形缓存tty->read_cnt不一定等于tty->read_head - tty->read_tail。

tty->read_head可能小于tty->read_tail所以可能有以下关系:

tty->read_cnt = N_TTY_BUF_SIZE -  tty->read_tail + tty->read_head。

所以将read_buf中的值考到用户空间需要考两次,*nr的值可能大于N_TTY_BUF_SIZE -  tty->read_tail

而小于tty->read_cnt。拷数据时是从tty->read_tail开始,第一次考取N_TTY_BUF_SIZE -  tty->read_tail, 第二次在read_buf的开始位置到tty->read_head之间获取还需的数据。*/

} else {

for (i = count, p = cp, f = fp; i; i--, p++) {

if (f)

flags = *f++;

switch (flags) {

case TTY_NORMAL:

n_tty_receive_char(tty, *p);

break;

case TTY_BREAK:

n_tty_receive_break(tty);

break;

case TTY_PARITY:

case TTY_FRAME:

n_tty_receive_parity_error(tty, *p);

break;

case TTY_OVERRUN:

n_tty_receive_overrun(tty);

break;

default:

printk(KERN_ERR "%s: unknown flag %d\n",

tty_name(tty, buf), flags);

break;

}

}

if (tty->ops->flush_chars)

tty->ops->flush_chars(tty);

}

n_tty_set_room(tty);//将剩下的空间赋值给tty->receive_room,以备查询

if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {

kill_fasync(&tty->fasync, SIGIO, POLL_IN);//向用户空间发一个异步信号

if (waitqueue_active(&tty->read_wait))

wake_up_interruptible(&tty->read_wait);//唤醒读进程

}

/*

* Check the remaining room for the input canonicalization

* mode.  We don't want to throttle the driver if we're in

* canonical mode and don't have a newline yet!

*/

if (tty->receive_room < TTY_THRESHOLD_THROTTLE)

tty_throttle(tty);//指示空间低于预定阀值

}

这个函数主要是将tty core层的数据刷到read_buf环形缓冲区中,下面我们回到tty_init_dev中的1-2部分。

1-2,tty_ldisc_setup(tty, tty->link)

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)

{

struct tty_ldisc *ld = tty->ldisc;//tty结构初始化的时候已经对它赋值,故这里可以使用

int retval;

retval = tty_ldisc_open(tty, ld);//如果是N_TTY,则对应的ops是tty_ldisc_N_TTY

if (retval)

return retval;

if (o_tty) {

retval = tty_ldisc_open(o_tty, o_tty->ldisc);//打开配对的tty

if (retval) {

tty_ldisc_close(tty, ld);

return retval;

}

tty_ldisc_enable(o_tty); //使能线路规程

}

tty_ldisc_enable(tty);

return 0;

}

这里通过线路规程的ops的open函数打开该tty。

2,tty->ops->open(tty, filp)

从initialize_tty_struct分析中知道,这里的ops就是driver对应的ops,也就是serial中的uart_ops,那我们看下uart_ops的open的函数:

static int uart_open(struct tty_struct *tty, struct file *filp)

{

......

/*

* Start up the serial port.

*/

retval = uart_startup(state, 0);//这是open的核心操作,见2-1

......

}

2-1,uart_startup(state, 0)

static int uart_startup(struct uart_state *state, int init_hw)

{

......

retval = port->ops->startup(port);//调用port口的ops,即mxc_ops,主要是对uart初始化,见2-1-1

if (retval == 0) {

if (init_hw) {//从open函数传下来的是0,这里不执行,但其它情况不一定

/*

* Initialise the hardware port settings.

*/

uart_change_speed(state, NULL);

/*

* Setup the RTS and DTR signals once the

* port is open and ready to respond.

*/

if (info->port.tty->termios->c_cflag & CBAUD)

uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);

}

if (info->flags & UIF_CTS_FLOW) {

spin_lock_irq(&port->lock);

if (!(port->ops->get_mctrl(port) & TIOCM_CTS))

info->port.tty->hw_stopped = 1;

spin_unlock_irq(&port->lock);

}

info->flags |= UIF_INITIALIZED;

clear_bit(TTY_IO_ERROR, &info->port.tty->flags);

}

if (retval && capable(CAP_SYS_ADMIN))

retval = 0;

return retval;

}

2-1-1,port->ops->startup(port)

port口子的ops在mxc_uart中,

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,

};

我们看startup对应到了mxcuart_startup:

static int mxcuart_startup(struct uart_port *port)

{

uart_mxc_port *umxc = (uart_mxc_port *) port;

int retval;

volatile unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;

/*

* Some UARTs need separate registrations for the interrupts as

* they do not take the muxed interrupt output to the ARM core

*/

if (umxc->ints_muxed == 1) {//从设备定义看,这个值为1

retval = request_irq(umxc->port.irq, mxcuart_int, 0,//定义中断函数

"mxcintuart", umxc);

if (retval != 0) {

return retval;

}

} else {

retval = request_irq(umxc->port.irq, mxcuart_tx_int,

0, "mxcintuart", umxc);

if (retval != 0) {

return retval;

} else {

retval = request_irq(umxc->irqs[0], mxcuart_rx_int,

0, "mxcintuart", umxc);

if (retval != 0) {

free_irq(umxc->port.irq, umxc);

return retval;

} else {

retval =

request_irq(umxc->irqs[1], mxcuart_mint_int,

0, "mxcintuart", umxc);

if (retval != 0) {

free_irq(umxc->port.irq, umxc);

free_irq(umxc->irqs[0], umxc);

return retval;

}

}

}

}

/* Initialize the DMA if we need SDMA data transfer */

if (umxc->dma_enabled == 1) {//是否需要dma传输

retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);

if (retval != 0) {

printk

(KERN_ERR

"MXC UART: Failed to initialize DMA for UART %d\n",

umxc->port.line);

mxcuart_free_interrupts(umxc);

return retval;

}

/* Configure the GPR register to receive SDMA events */

config_uartdma_event(umxc->port.line);

}

/*

* Clear Status Registers 1 and 2

*/

writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);

writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);

/* Configure the IOMUX for the UART */

gpio_uart_active(umxc->port.line, umxc->ir_mode);

/*

* Set the transceiver invert bits if required

*/

if (umxc->ir_mode == IRDA) {

echo_cancel = 1;

writel(umxc->ir_rx_inv | MXC_UARTUCR4_IRSC, umxc->port.membase

+ MXC_UARTUCR4);

writel(umxc->rxd_mux | umxc->ir_tx_inv,

umxc->port.membase + MXC_UARTUCR3);

} else {

writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);

}

/*

* Initialize UCR1,2 and UFCR registers

*/

if (umxc->dma_enabled == 1) {

cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);

} else {

cr2 =

(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);

}

writel(cr2, umxc->port.membase + MXC_UARTUCR2);

/* Wait till we are out of software reset */

do {

cr = readl(umxc->port.membase + MXC_UARTUCR2);

} while (!(cr & MXC_UARTUCR2_SRST));

if (umxc->mode == MODE_DTE) {

ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |

MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->

rx_threshold);

} else {

ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |

MXC_UARTUFCR_RFDIV | umxc->rx_threshold);

}

writel(ufcr, umxc->port.membase + MXC_UARTUFCR);

/*

* Finally enable the UART and the Receive interrupts

*/

if (umxc->ir_mode == IRDA) {

cr1 |= MXC_UARTUCR1_IREN;

}

if (umxc->dma_enabled == 1) {

cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |

MXC_UARTUCR1_UARTEN);

} else {

cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);

}

writel(cr1, umxc->port.membase + MXC_UARTUCR1);

return 0;

}

我们看下这个mxcuart_startup,主要是根据device定义对uart的初始化。

综上,tty_open的流程是tty_core->tty_ldisc和tty_core->serial_core->mxc_uart。它主要执行了tty_init_dev(driver, index, 0),这个函数主要是初始化一个tty设备,初始化线路规程和打开线路规程。还有执行了tty->ops->open(tty, filp),主要初始化了要使用的uart口子,有了这个基础,我们就可以对tty进行读写了。

阅读(491) | 评论(0) | 转发(0) |

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值