ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)

串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了。

那接下来uart的操作是如何进行的呢?

操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了那些工作。

s3c24xx_serial_modinit -->uart_register_driver -->tty_register_driver 中有如下语句:

    cdev_init(&driver->cdev, &tty_fops);

此处将 driver->cdev->ops=&tty_fops

而tty_fops如下:

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系统调用open(“/dev/s3c2410_serial0”,)一层一层调用到会调用到tty_open。

/**
* tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
* tty_open and tty_release keep up the tty count that contains the
* number of opens done on a tty. We cannot use the inode-count, as
* different inodes might point to the same tty.
*
* Open-counting is needed for pty masters, as well as for keeping
* track of serial lines: DTR is dropped when the last close happens.
* (This is not done solely through tty->count, now. - Ted 1/27/92)
*
* The termios state of a pty is reset on first open so that
* settings don't persist across reuse.
*
* Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
* tty->count should protect the rest.
* ->siglock protects ->signal/->sighand
*/ static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = NULL;
int noctty, retval;
struct tty_driver *driver;
int index;
dev_t device = inode->i_rdev;//获取主次设备号
unsigned saved_flags = filp->f_flags; nonseekable_open(inode, filp);//通知内核设备不支持 llseek retry_open:
noctty = filp->f_flags & O_NOCTTY;
index = -;
retval = ; mutex_lock(&tty_mutex);
lock_kernel(); if (device == MKDEV(TTYAUX_MAJOR, )) {//判断打开的设备是否是5 0(/dev/tty)
tty = get_current_tty();
if (!tty) {
unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
driver = tty_driver_kref_get(tty->driver);
index = tty->index;
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
/* FIXME: Should we take a driver reference ? */
tty_kref_put(tty);
goto got_driver;
}
#ifdef CONFIG_VT
if (device == MKDEV(TTY_MAJOR, )) {// 4 0(/dev/tty0)
extern struct tty_driver *console_driver;
driver = tty_driver_kref_get(console_driver);
index = fg_console;
noctty = ;
goto got_driver;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR, )) {//5 1(/dev/console)
struct tty_driver *console_driver = console_device(&index);
if (console_driver) {
driver = tty_driver_kref_get(console_driver);
if (driver) {
/* Don't let /dev/console block */
filp->f_flags |= O_NONBLOCK;
noctty = ;
goto got_driver;
}
}
unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
/********************若都没有,则执行下面这句******************************/
/*
此函数的作用就是通过设备号来找到设备对应的tty_driver,并且将索引号码保存在index中
因为一个tty_driver对应的是所有此种类型的tty设备,比如所有的串口设备,所以需要通过这个索引号
index来判断打开的是具体哪个设备。并且每个具体的设备对应着一个用来描述自己的tty_struct。
而系统后面的操作全部和这个tty_struct相关。
*/
driver = get_tty_driver(device, &index); if (!driver) {
unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
got_driver:
if (!tty) {
/* check whether we're reopening an existing tty */
tty = tty_driver_lookup_tty(driver, inode, index); if (IS_ERR(tty)) {
unlock_kernel();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
} if (tty) {
retval = tty_reopen(tty);//判断是否有tty_struct
if (retval)
tty = ERR_PTR(retval);
} else
tty = tty_init_dev(driver, index, );//不存在则创建并初始化一个tty_struct mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
unlock_kernel();
return PTR_ERR(tty);
} filp->private_data = tty;
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 = ;
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty->name);
#endif
if (!retval) {
if (tty->ops->open)
/********************************************************/
/*调用uart_open*/
retval = tty->ops->open(tty, filp); // ===============>>>>>>>>>
/********************************************************/
else
retval = -ENODEV;
}
filp->f_flags = saved_flags; if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
!capable(CAP_SYS_ADMIN))
retval = -EBUSY; if (retval) {
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
tty_release(inode, filp);
if (retval != -ERESTARTSYS) {
unlock_kernel();
return retval;
}
if (signal_pending(current)) {
unlock_kernel();
return retval;
}
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
unlock_kernel();
goto retry_open;
}
unlock_kernel(); mutex_lock(&tty_mutex);
lock_kernel();
spin_lock_irq(&current->sighand->siglock);
if (!noctty &&
current->signal->leader &&
!current->signal->tty &&
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(&current->sighand->siglock);
unlock_kernel();
mutex_unlock(&tty_mutex);
return ;
}
/*
* calls to uart_open are serialised by the BKL in
* fs/char_dev.c:chrdev_open()
* Note that if this fails, then uart_close() _will_ be called.
*
* In time, we want to scrap the "opening nonpresent ports"
* behaviour and implement an alternative way for setserial
* to set base addresses/ports/types. This will allow us to
* get rid of a certain amount of extra tests.
*/
static int uart_open(struct tty_struct *tty, struct file *filp)
{
struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
struct uart_state *state;
struct tty_port *port;
int retval, line = tty->index; BUG_ON(!kernel_locked());
pr_debug("uart_open(%d) called\n", line); /*
* tty->driver->num won't change, so we won't fail here with
* tty->driver_data set to something non-NULL (and therefore
* we won't get caught by uart_close()).
*/
retval = -ENODEV;
if (line >= tty->driver->num)
goto fail; /*
* We take the semaphore inside uart_get to guarantee that we won't
* be re-entered while allocating the state structure, or while we
* request any IRQs that the driver may need. This also has the nice
* side-effect that it delays the action of uart_hangup, so we can
* guarantee that state->port.tty will always contain something
* reasonable.
*/ /*
找到保存在tty_driver中的uart_state。
最后将其值赋值给tty_struct。此处特别注意一下,这个uart_state会被放到tty_struct的driver_data中的!
因为后面的write、read都是从driver_data中找到这个uart_state的!
*/
state = uart_get(drv, line);
if (IS_ERR(state)) {
retval = PTR_ERR(state);
goto fail;
}
port = &state->port; /*
* Once we set tty->driver_data here, we are guaranteed that
* uart_close() will decrement the driver module use count.
* Any failures from here onwards should not touch the count.
*/
tty->driver_data = state;
state->uart_port->state = state;
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? : ;
tty->alt_speed = ;
tty_port_tty_set(port, tty); /*
* If the port is in the middle of closing, bail out now.
*/
if (tty_hung_up_p(filp)) {
retval = -EAGAIN;
port->count--;
mutex_unlock(&port->mutex);
goto fail;
} /*
* Make sure the device is in D0 state.
*/
if (port->count == )
uart_change_pm(state, ); /*
* Start up the serial port.
*/
retval = uart_startup(state, );//初始化串口硬件 ==========>>>>>>>>>>>>>>> /*
* If we succeeded, wait until the port is ready.
*/
if (retval == )
retval = uart_block_til_ready(filp, state);
mutex_unlock(&port->mutex); /*
* If this is the first open to succeed, adjust things to suit.
*/
if (retval == && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); uart_update_termios(state);
} fail:
return retval;
}
/*
* Startup the port. This will be called once per open. All calls
* will be serialised by the per-port mutex.
*/
static int uart_startup(struct uart_state *state, int init_hw)
{
struct uart_port *uport = state->uart_port;
struct tty_port *port = &state->port;
unsigned long page;
int retval = ; if (port->flags & ASYNC_INITIALIZED)
return ; /*
* Set the TTY IO error marker - we will only clear this
* once we have successfully opened the port. Also set
* up the tty->alt_speed kludge
*/
set_bit(TTY_IO_ERROR, &port->tty->flags); if (uport->type == PORT_UNKNOWN)
return ; /*
* Initialise and allocate the transmit and temporary
* buffer.
*/
if (!state->xmit.buf) {
/* This is protected by the per port mutex */
page = get_zeroed_page(GFP_KERNEL);
if (!page)
return -ENOMEM; state->xmit.buf = (unsigned char *) page;
uart_circ_clear(&state->xmit);
} retval = uport->ops->startup(uport); ==========>>>>>>>>>调用s3c24xx_serial_startup()
if (retval == ) {
if (init_hw) {
/*
* 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 (port->tty->termios->c_cflag & CBAUD)
uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
} if (port->flags & ASYNC_CTS_FLOW) {
spin_lock_irq(&uport->lock);
if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
port->tty->hw_stopped = ;
spin_unlock_irq(&uport->lock);
} set_bit(ASYNCB_INITIALIZED, &port->flags); clear_bit(TTY_IO_ERROR, &port->tty->flags);
} if (retval && capable(CAP_SYS_ADMIN))
retval = ; return retval;
}

至此,通过open函数后s3c24xx的uart硬件部分也初始化好了,接着可以通过write、read函数收发数据了。

《《《《=============总结===============》》》》

open的主要作用是 在内核通过创建并初始化一个tty_struct来描述具体对应的一个硬件设备,比如这里就是用一个tty_struct来描述s3c24xx上的uart0的,然后找到uart_port

中ops的startup方法初始化uart的硬件。

具体的tty_struct初始化过程中最重要的几步如下

1.初始化tty-struct的ops,就是将tty_driver中的ops赋值给tty_struct

2.初始化tty线路规程操作集

3.初始化tty_struct中的uart_state,uart_state中包含uart_port信息,这一步通过步骤1中ops中的open方法来完成。

4.根据步骤3中找到的uart_state,找到里面的uart_port的ops中的startup方法来初始化uart硬件。

open的流程大致如下:

open

--tty_open

|

--get_tty_driver

--tty_init_dev

--tty->ops->open(uart_open)

|

--uart_startup

|

--uport->ops->startup(s3c24xx_serial_startup())

|

--request_irq(rx_irq)

--request_irq(tx_irq)

参考:http://blog.csdn.net/rockrockwu/article/details/7897283

上一篇:ARM-Linux S5PV210 UART驱动(2)---- 终端设备驱动


下一篇:ARM-Linux S5PV210 UART驱动(6)----platform device的添加