根据前面的操作,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了。
那接下来uart的操作是如何进行的呢?
操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了那些工作(做了大量工作 ,真的!)。
应用层通过open系统调用open(“/dev/s3c2410_serial0”,)一层一层调用到会调用到tty_open。
因为串口在linux下是作为tty设备的,结合前面的注册过程可以分析这里首先调用的就是tty_open这个函数。
- cdev_init(&driver->cdev, &tty_fops);
cdev_init(&driver->cdev, &tty_fops);
因为根据注册的时候将s3c2410_serial0注册为一个字符设备,字符设备对应的驱动为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,
- };
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
- 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
所以前面的判断全部失败,直接执行标红的那条语句
- driver = get_tty_driver(device, &index);
driver = get_tty_driver(device, &index);
get_tty_driver函数如下:
- 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
- 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()函数具体如下
- 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分配好内存,然后将其初始化,初始化的工作在函数
- 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函数如下
- 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中对应的域中。具体如下
- 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]对应的具体操作集如下
- 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。
最后通过函数
- tty_ldisc_assign(tty, ld);
tty_ldisc_assign(tty, ld);
将其填充到tty_struct中。
在这个tty_struct的初始化函数还需要注意的是
- 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中被设置成如下
- 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函数中继续往下执行。
调用函数
- 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函数中执行
- 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中会被执行到的代码是
- 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函数如下
- 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; }
函数通过
- state = uart_get(drv, line);
state = uart_get(drv, line);
来找到保存在tty_driver中的uart_state。
最后将其值赋值给tty_struct。此处特别注意一下,这个uart_state会被放到tty_struct的driver_data中的!因为后面的write、read都是