------------------------------------------
本文系本站原创,欢迎转载!
------------------------------------------
我们现在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进行读写了。