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

串口的open操作(tty_open) .

2012-09-26 15:22 260 查看
根据前面的操作,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了。

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

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

应用层通过open系统调用open(“/dev/s3c2410_serial0”,)一层一层调用到会调用到tty_open。

因为串口在linux下是作为tty设备的,结合前面的注册过程可以分析这里首先调用的就是tty_open这个函数。

[cpp]
view plaincopyprint?

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

cdev_init(&driver->cdev, &tty_fops);
因为根据注册的时候将s3c2410_serial0注册为一个字符设备,字符设备对应的驱动为tty_fops

[cpp]
view plaincopyprint?

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,
};

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,
};


所以这里就调用的是tty_open函数。
下面具体分析tty_open

[cpp]
view plaincopyprint?

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);

retry_open:
noctty = filp->f_flags & O_NOCTTY;
index = -1;
retval = 0;

mutex_lock(&tty_mutex);
tty_lock();

if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
tty_unlock();
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, 0)) {
extern struct tty_driver *console_driver;
driver = tty_driver_kref_get(console_driver);
index = fg_console;
noctty = 1;
goto got_driver;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR, 1)) {
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 = 1;
goto got_driver;
}
}
tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}

<SPAN style="COLOR: #ff0000"> driver = get_tty_driver(device, &index);</SPAN>
if (!driver) {
tty_unlock();
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)) {
tty_unlock();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
}

if (tty) {
retval = tty_reopen(tty);
if (retval)
tty = ERR_PTR(retval);
} else
<SPAN style="COLOR: #ff0000"> tty = tty_init_dev(driver, index, 0);</SPAN>

mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
tty_unlock();
return PTR_ERR(tty);
}

retval = tty_add_file(tty, filp);
if (retval) {
tty_unlock();
return retval;
}

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)
<SPAN style="COLOR: #ff0000">retval = tty->ops->open(tty, filp);</SPAN>
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_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;

if (signal_pending(current))
return retval;

schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
tty_unlock();
goto retry_open;
}
tty_unlock();

mutex_lock(&tty_mutex);
tty_lock();
spin_lock_irq(¤t->sighand->siglock);
if (!noctty &&
current->signal->leader &&
!current->signal->tty &&
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(¤t->sighand->siglock);
tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
}

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);

retry_open:
noctty = filp->f_flags & O_NOCTTY;
index  = -1;
retval = 0;

mutex_lock(&tty_mutex);
tty_lock();

if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
tty_unlock();
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, 0)) {
extern struct tty_driver *console_driver;
driver = tty_driver_kref_get(console_driver);
index = fg_console;
noctty = 1;
goto got_driver;
}
#endif
if (device == MKDEV(TTYAUX_MAJOR, 1)) {
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 = 1;
goto got_driver;
}
}
tty_unlock();
mutex_unlock(&tty_mutex);
return -ENODEV;
}

driver = get_tty_driver(device, &index);
if (!driver) {
tty_unlock();
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)) {
tty_unlock();
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);

mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
tty_unlock();
return PTR_ERR(tty);
}

retval = tty_add_file(tty, filp);
if (retval) {
tty_unlock();
return retval;
}

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);
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_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;

if (signal_pending(current))
return retval;

schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
tty_unlock();
goto retry_open;
}
tty_unlock();

mutex_lock(&tty_mutex);
tty_lock();
spin_lock_irq(¤t->sighand->siglock);
if (!noctty &&
current->signal->leader &&
!current->signal->tty &&
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(¤t->sighand->siglock);
tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
}
函数首先判断打开的设备是否是
5 0(/dev/tty)

5 1(/dev/console)

4 0(/dev/tty0)

此处打开的是/dev/s3c2410_serial0设备号为204 64

所以前面的判断全部失败,直接执行标红的那条语句

[cpp]
view plaincopyprint?

driver = get_tty_driver(device, &index);

driver = get_tty_driver(device, &index);
get_tty_driver函数如下:

[cpp]
view plaincopyprint?

static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;

list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
if (device < base || device >= base + p->num)
continue;
*index = device - base;
return tty_driver_kref_get(p);
}
return NULL;
}

static struct tty_driver *get_tty_driver(dev_t device, int *index)
{
struct tty_driver *p;

list_for_each_entry(p, &tty_drivers, tty_drivers) {
dev_t base = MKDEV(p->major, p->minor_start);
if (device < base || device >= base + p->num)
continue;
*index = device - base;
return tty_driver_kref_get(p);
}
return NULL;
}
可见,此函数的作用就是通过设备号来找到设备对应的tty_driver,并且将索引号码保存在index中
因为一个tty_driver对应的是所有此种类型的tty设备,比如所有的串口设备,所以需要通过这个索引号

index来判断打开的是具体哪个设备。并且每个具体的设备对应着一个用来描述自己的tty_struct。

而系统后面的操作全部和这个tty_struct相关。

然后接着open函数会判断是否有tty_struct,假如不存在则创建并初始化一个tty_struct。

tty_struct是tty结构体中最重要的一个数据结构,内核通过tty-struct这个结构体来描述一个具体的tty设备在内核中

的活动状况的,后面对设备的write、read中都需要使用到这个tty_struct,并且严重依赖这个结构体。

由于此处是不存在tty_struct,所以直接调用函数初始化tty_struct

[cpp]
view plaincopyprint?

tty = tty_init_dev(driver, index, 0);

tty = tty_init_dev(driver, index, 0);


参数driver为tty_driver,是前面通过get_tty_driver获得的,index此处为0,也是通过前面get_tty_driver获得的。
tty_init_dev()函数具体如下

[cpp]
view plaincopyprint?

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
int first_ok)
{
struct tty_struct *tty;
int retval;

/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
return ERR_PTR(-EIO);
}

/*
* First time open is complex, especially for PTY devices.
* This code guarantees that either everything succeeds and the
* TTY is ready for operation, or else the table slots are vacated
* and the allocated memory released. (Except that the termios
* and locked termios may be retained.)
*/

if (!try_module_get(driver->owner))
return ERR_PTR(-ENODEV);

tty = alloc_tty_struct();
if (!tty)
goto fail_no_mem;
<SPAN style="COLOR: #ff0000">initialize_tty_struct(tty, driver, idx);</SPAN>

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);
if (retval)
goto release_mem_out;
return tty;

fail_no_mem:
module_put(driver->owner);
return ERR_PTR(-ENOMEM);

/* call the tty release_tty routine to clean out this slot */
release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
release_tty(tty, idx);
return ERR_PTR(retval);
}

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
int first_ok)
{
struct tty_struct *tty;
int retval;

/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
return ERR_PTR(-EIO);
}

/*
* First time open is complex, especially for PTY devices.
* This code guarantees that either everything succeeds and the
* TTY is ready for operation, or else the table slots are vacated
* and the allocated memory released.  (Except that the termios
* and locked termios may be retained.)
*/

if (!try_module_get(driver->owner))
return ERR_PTR(-ENODEV);

tty = alloc_tty_struct();
if (!tty)
goto fail_no_mem;
initialize_tty_struct(tty, driver, idx);

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);
if (retval)
goto release_mem_out;
return tty;

fail_no_mem:
module_put(driver->owner);
return ERR_PTR(-ENOMEM);

/* call the tty release_tty routine to clean out this slot */
release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
release_tty(tty, idx);
return ERR_PTR(retval);
}

这个函数首先是给tty_struct分配好内存,然后将其初始化,初始化的工作在函数

[cpp]
view plaincopyprint?

initialize_tty_struct(tty, driver, idx);

initialize_tty_struct(tty, driver, idx);
中完成。参数tty为待初始化的tty_struct,driver为前面通过get_tty_driver获得的tty_driver,index为具体的索引号,同样是通过get_tty_driver获得的,此处index值为0。

初始化的tty_struct部分信息来自于tty_driver,所以将tty_driver传递进此函数。

具体的initialize_tty_struct函数如下

[cpp]
view plaincopyprint?

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;
<SPAN style="COLOR: #ff0000">tty_ldisc_init(tty);</SPAN>
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty);
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;
<SPAN style="COLOR: #ff0000">tty->ops = driver->ops;</SPAN>
<SPAN style="COLOR: #ff0000">tty->index = idx;</SPAN>
tty_line_name(driver, idx, tty->name);
tty->dev = tty_get_device(tty);
}

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);
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty);
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;
tty->index = idx;
tty_line_name(driver, idx, tty->name);
tty->dev = tty_get_device(tty);
}
初始化中最重要的两步已标红,其中第一步和tty线路规程相关。之前分析过tty线路规程初始化部分的代码,

而这里的初始化就是根据数组的索引号,从前面初始化好的tty线路规程操作方法数组中获取对应的操作方法,然后将其填充到

tty_struct中对应的域中。具体如下

[cpp]
view plaincopyprint?

void tty_ldisc_init(struct tty_struct *tty) { struct tty_ldisc *ld = tty_ldisc_get(N_TTY); if (IS_ERR(ld)) panic("n_tty: init_tty"); tty_ldisc_assign(tty, ld); }

void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty_ldisc_assign(tty, ld);
}
可见索引号是N_TTY,也就是tty_ldiscs[0]中的tty线路规程操纵方法集,tty_ldiscs[0]对应的具体操作集如下

[cpp]
view plaincopyprint?

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
};

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
};
在write过程中会调用其中的方法。tty_write->n_tty_write。

最后通过函数

[cpp]
view plaincopyprint?

tty_ldisc_assign(tty, ld);

tty_ldisc_assign(tty, ld);
将其填充到tty_struct中。

在这个tty_struct的初始化函数还需要注意的是

[cpp]
view plaincopyprint?

tty->ops = driver->ops;

tty->ops = driver->ops;
也就是将tty_struct中的ops设置成tty_driver中的ops。后面write、read的操作调用的是tty_struct中的ops而非tty_driver

中的ops

此处的tty_driver的ops在uart_register_driver中被设置成如下

[cpp]
view plaincopyprint?

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,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL

.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};

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,
.get_icount	= uart_get_icount,
#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_init_dev函数中继续往下执行。

调用函数

[cpp]
view plaincopyprint?

retval = tty_driver_install_tty(driver, tty);

retval = tty_driver_install_tty(driver, tty);
这个函数的主要作用就是将这个初始化好的

tty_struct存放到tty_driver的tty_structs[]数组中,存放的位置依据tty-struct->index

这样可以tty_driver中每个设备都可以根据index找到对应的tty_struct了。

最后再次返回tty_init_dev函数中执行

[cpp]
view plaincopyprint?

retval = tty_ldisc_setup(tty, tty->link);

retval = tty_ldisc_setup(tty, tty->link);


这个函数会调用tty线路规程中的open方法n_tty_open函数

n_tty_open中主要完成buff缓冲区大小设置的工作,具体不展开了(其实我也没仔细分析)。

最后这个tty_struct的初始化函数彻底执行完毕,退到tty_open函数中继续执行后续代码。

在tty_open中会被执行到的代码是

[cpp]
view plaincopyprint?

retval = tty->ops->open(tty, filp);

retval = tty->ops->open(tty, filp);


这里就是tty-struct中的ops,在tty_struct初始化的时候被赋值成tty_driver的ops,所以这里调用到的就是

uart_open函数。uart_open函数的重要作用是找到之前初始化的时候保存在tty_driver中的uart_state,因为这里面还有uart_port的重要信息。

找到这个uart_state后将其赋值给tty_struct。具体的uart_open函数如下

[cpp]
view plaincopyprint?

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(!tty_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.
*/
<SPAN style="COLOR: #ff0000">state = uart_get(drv, line);</SPAN>
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.
*/
<SPAN style="COLOR: #ff0000">tty->driver_data = state;</SPAN>
state->uart_port->state = state;
tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
tty->alt_speed = 0;
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 == 1)
uart_change_pm(state, 0);

/*
* Start up the serial port.
*/
<SPAN style="COLOR: #ff0000">retval = uart_startup(tty, state, 0);</SPAN>

/*
* If we succeeded, wait until the port is ready.
*/
mutex_unlock(&port->mutex);
if (retval == 0)
retval = tty_port_block_til_ready(port, tty, filp);

fail:
return retval;
}

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(!tty_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.
*/
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) ? 1 : 0;
tty->alt_speed = 0;
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 == 1)
uart_change_pm(state, 0);

/*
* Start up the serial port.
*/
retval = uart_startup(tty, state, 0);

/*
* If we succeeded, wait until the port is ready.
*/
mutex_unlock(&port->mutex);
if (retval == 0)
retval = tty_port_block_til_ready(port, tty, filp);

fail:
return retval;
}
函数通过

[cpp]
view plaincopyprint?

state = uart_get(drv, line);

state = uart_get(drv, line);


来找到保存在tty_driver中的uart_state。

最后将其值赋值给tty_struct。此处特别注意一下,这个uart_state会被放到tty_struct的driver_data中的!因为后面的write、read都是从driver_data中找到这个uar_state的!

最后函数条用uart_startup函数来初始化串口硬件

[cpp]
view plaincopyprint?

retval = uart_startup(tty, state, 0);

retval = uart_startup(tty, state, 0);
函数参数tty对应的就是tty_struct,state就是从tty_driver中找到的uart_state

具体的uart_startup函数如下

[cpp]
view plaincopyprint?

static int uart_startup(struct tty_struct *tty, 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 = 0;

if (port->flags & ASYNC_INITIALIZED)
return 0;

/*
* 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, &tty->flags);

if (uport->type == PORT_UNKNOWN)
return 0;

/*
* 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);
}

<SPAN style="COLOR: #ff0000">retval = uport->ops->startup(uport);</SPAN>
if (retval == 0) {
if (init_hw) {
/*
* Initialise the hardware port settings.
*/
uart_change_speed(tty, state, NULL);

/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
if (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))
tty->hw_stopped = 1;
spin_unlock_irq(&uport->lock);
}

set_bit(ASYNCB_INITIALIZED, &port->flags);

clear_bit(TTY_IO_ERROR, &tty->flags);
}

if (retval && capable(CAP_SYS_ADMIN))
retval = 0;

return retval;
}

static int uart_startup(struct tty_struct *tty, 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 = 0;

if (port->flags & ASYNC_INITIALIZED)
return 0;

/*
* 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, &tty->flags);

if (uport->type == PORT_UNKNOWN)
return 0;

/*
* 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);
if (retval == 0) {
if (init_hw) {
/*
* Initialise the hardware port settings.
*/
uart_change_speed(tty, state, NULL);

/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
if (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))
tty->hw_stopped = 1;
spin_unlock_irq(&uport->lock);
}

set_bit(ASYNCB_INITIALIZED, &port->flags);

clear_bit(TTY_IO_ERROR, &tty->flags);
}

if (retval && capable(CAP_SYS_ADMIN))
retval = 0;

return retval;
}
此函数的作用就是初始化s3c2440的uart,其中最重要的是调用了s3c2440的硬件操作函数集中的startup方法,

即uart_port的ops,此ops在初始化的时候被初始化为

[cpp]
view plaincopyprint?

static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
.enable_ms = s3c24xx_serial_enable_ms,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_serial_startup,
.shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
};

static struct uart_ops s3c24xx_serial_ops = {
.pm		= s3c24xx_serial_pm,
.tx_empty	= s3c24xx_serial_tx_empty,
.get_mctrl	= s3c24xx_serial_get_mctrl,
.set_mctrl	= s3c24xx_serial_set_mctrl,
.stop_tx	= s3c24xx_serial_stop_tx,
.start_tx	= s3c24xx_serial_start_tx,
.stop_rx	= s3c24xx_serial_stop_rx,
.enable_ms	= s3c24xx_serial_enable_ms,
.break_ctl	= s3c24xx_serial_break_ctl,
.startup	= s3c24xx_serial_startup,
.shutdown	= s3c24xx_serial_shutdown,
.set_termios	= s3c24xx_serial_set_termios,
.type		= s3c24xx_serial_type,
.release_port	= s3c24xx_serial_release_port,
.request_port	= s3c24xx_serial_request_port,
.config_port	= s3c24xx_serial_config_port,
.verify_port	= s3c24xx_serial_verify_port,
};
所以此处直接调用的是s3c24xx_serial_startup函数,此函数如下。

[cpp]
view plaincopyprint?

static int s3c24xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;

dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
port->mapbase, port->membase);

rx_enabled(port) = 1;

<SPAN style="COLOR: #ff0000"> ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);</SPAN>

if (ret != 0) {
printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}

ourport->rx_claimed = 1;

dbg("requesting tx irq...\n");

tx_enabled(port) = 1;

<SPAN style="COLOR: #ff0000"> ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);</SPAN>

if (ret) {
printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}

ourport->tx_claimed = 1;

dbg("s3c24xx_serial_startup ok\n");

/* the port reset code should have done the correct
* register setup for the port controls */

return ret;

err:
s3c24xx_serial_shutdown(port);
return ret;
}

static int s3c24xx_serial_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;

dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
port->mapbase, port->membase);

rx_enabled(port) = 1;

ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);

if (ret != 0) {
printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}

ourport->rx_claimed = 1;

dbg("requesting tx irq...\n");

tx_enabled(port) = 1;

ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);

if (ret) {
printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}

ourport->tx_claimed = 1;

dbg("s3c24xx_serial_startup ok\n");

/* the port reset code should have done the correct
* register setup for the port controls */

return ret;

err:
s3c24xx_serial_shutdown(port);
return ret;
}


其作用还是初始化,主要初始化的是s3c24xx的uart的中断。

设置uart的发送中断,接收中断处理函数。

至此,通过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)

中断处理函数如下:

s3c24xx_serial_rx_chars

s3c24xx_serial_tx_chars

====
/article/1803346.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: