·LINUX设备驱动之tty及console驱动(一)

转载 2012年03月27日 14:06:00

Tty用于表示各种终端,这些终端包括串行端口终端(/dev/ttySn)、伪终端(/dev/pty/)、控制台终端(/dev/ttyn, /dev/console)等,tty驱动架构中,包括tty核心、tty线路规程、tty驱动层概念,用户空间通过文件操作与tty_core交互,tty_core根据文件操作的类型选择line discipline(如read和write操作)或直接跟tty_driver(如ioctl操作)交互,而line discipline对相应的终端进行一系列的输入与输出规范设置,然后再交给tty_driver处理,从层次上看,这个line discipline就是tty特有的对一些操作的统一设置。

 

一.Tty驱动的注册

上面提到的tty_driver,在内核中必然存在它对应的结构:

0333       struct tty_driver {

0334              int    magic;            /* magic number for this structure */

0335              struct kref kref;     /* Reference management */

0336              struct cdev cdev;

0337              struct module  *owner;

0338              const char      *driver_name;

0339              const char      *name;

0340              int    name_base;     /* offset of printed name */

0341              int    major;            /* major device number */

0342              int    minor_start;    /* start of minor device number */

0343              int    minor_num;    /* number of *possible* devices */

0344              int    num;              /* number of devices allocated */

0345              short       type;              /* type of tty driver */

0346              short       subtype;  /* subtype of tty driver */

0347              struct ktermios init_termios; /* Initial termios */

0348              int    flags;             /* tty driver flags */

0349              struct proc_dir_entry *proc_entry; /* /proc fs entry */

0350              struct tty_driver *other; /* only used for the PTY driver */

0351      

0352              /*

0353              * Pointer to the tty data structures

0354              */

0355              struct tty_struct **ttys;

0356              struct ktermios **termios;

0357              struct ktermios **termios_locked;

0358              void *driver_state;

0359      

0360              /*

0361              * Driver methods

0362              */

0363      

0364              const struct tty_operations *ops;

0365              struct list_head tty_drivers;

0366       };

暂先不理会这个数据结构各字段的含义,像其他驱动程序一样,内核提供了创建和注册tty_driver的函数,分别是alloc_tty_driver和tty_register_driver。

2838       struct tty_driver *alloc_tty_driver(int lines)

2839       {

2840              struct tty_driver *driver;

2841      

2842              driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);

2843              if (driver) {

2844                     kref_init(&driver->kref);

2845                     driver->magic = TTY_DRIVER_MAGIC;

2846                     driver->num = lines;

2847                     /* later we'll move allocation of tables here */

2848              }

2849              return driver;

2850       }

第2842行为tty_driver分配内存空间。

第2844行以原子性操作初始化driver的引用计数为1。

第2844行定义driver->magic为TTY_DRIVER_MAGIC,这个魔数是在定义ioctl 命令时使用,根据 Linux 内核惯例来为驱动选择 ioctl 号。

第2844行driver->num用来表示次设备号的个数。

接着看tty_register_driver函数:

2907       int tty_register_driver(struct tty_driver *driver)

2908       {

2909              int error;

2910              int i;

2911                     dev_t dev;

2912              void **p = NULL;

2913      

2914              if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {

2915                     p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);

2916                     if (!p)

2917                            return -ENOMEM;

2918              }

2919      

2920              if (!driver->major) {

2921                     error = alloc_chrdev_region(&dev, driver->minor_start,

2922                                                 driver->num, driver->name);

2923                     if (!error) {

2924                            driver->major = MAJOR(dev);

2925                            driver->minor_start = MINOR(dev);

2926                     }

2927              } else {

2928                     dev = MKDEV(driver->major, driver->minor_start);

2929                     error = register_chrdev_region(dev, driver->num, driver->name);

2930              }

2931              if (error < 0) {

2932                     kfree(p);

2933                     return error;

2934              }

2935      

2936              if (p) {

2937                     driver->ttys = (struct tty_struct **)p;

2938                     driver->termios = (struct ktermios **)(p + driver->num);

2939              } else {

2940                     driver->ttys = NULL;

2941                     driver->termios = NULL;

2942              }

2943      

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

2945              driver->cdev.owner = driver->owner;

2946              error = cdev_add(&driver->cdev, dev, driver->num);

2947              if (error) {

2948                     unregister_chrdev_region(dev, driver->num);

2949                     driver->ttys = NULL;

2950                     driver->termios = NULL;

2951                     kfree(p);

2952                     return error;

2953              }

2954      

2955              mutex_lock(&tty_mutex);

2956              list_add(&driver->tty_drivers, &tty_drivers);

2957              mutex_unlock(&tty_mutex);

2958      

2959              if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {

2960                     for (i = 0; i < driver->num; i++)

2961                         tty_register_device(driver, i, NULL);

2962              }

2963              proc_tty_register_driver(driver);

2964              driver->flags |= TTY_DRIVER_INSTALLED;

2965              return 0;

2966       }

第2914~2917、2936~2942行,如果driver->flags 定义了TTY_DRIVER_DEVPTS_MEM,则使用devpts进行动态内存映射,分配num * 2个字空间,初始化driver->ttys和driver->termios。

函数的其他大部分代码为为tty_driver创建和注册字符设备,指定字符设备的操作集为tty_fops。

第2956行将tty_driver插入到tty_drivers链表中,这样就可以通过比较设备号来获得相应设备号的tty_driver。

第2963行调用proc_tty_register_driver注册tty_driver在/proc下的交互文件。

 

二.Tty设备文件的操作

tty_driver对应的字符设备的文件操作为tty_fops:

0410       static const struct file_operations tty_fops = {

0411                     .llseek            = no_llseek,

0412              .read              = tty_read,

0413              .write             = tty_write,

0414              .poll        = tty_poll,

0415              .unlocked_ioctl       = tty_ioctl,

0416              .compat_ioctl  = tty_compat_ioctl,

0417              .open             = tty_open,

0418              .release    = tty_release,

0419              .fasync           = tty_fasync,

0420       };

我们将分析这些具体的函数,其中将涉及到线路规程的内容。

首先看tty_open函数:

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

1844       {

1845              int ret;

1846      

1847              lock_kernel();

1848              ret = __tty_open(inode, filp);

1849              unlock_kernel();

1850              return ret;

1851       }

第1847行用于获得大内核锁。

大内核锁本质上是自旋锁,但又不同于自旋锁,自旋锁不可以递归获得锁的,因为那样会导致死锁,但大内核锁可以递归获得锁,大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。

另外如果没有定义CONFIG_LOCK_KERNEL的话,lock_kernel什么也不做。

第1849行,释放大内核锁。

第1848行,转入__tty_open(inode, filp)函数:

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

1707       {

1708              struct tty_struct *tty = NULL;

1709              int noctty, retval;

1710              struct tty_driver *driver;

1711              int index;

1712              dev_t device = inode->i_rdev;

1713              unsigned saved_flags = filp->f_flags;

1714      

1715              nonseekable_open(inode, filp);

1716      

1717       retry_open:

1718              noctty = filp->f_flags & O_NOCTTY;

1719              index  = -1;

1720              retval = 0;

1721      

1722              mutex_lock(&tty_mutex);

1723      

1724              if (device == MKDEV(TTYAUX_MAJOR, 0)) {

1725                     tty = get_current_tty();

1726                     if (!tty) {

1727                            mutex_unlock(&tty_mutex);

1728                            return -ENXIO;

1729                     }

1730                     driver = tty_driver_kref_get(tty->driver);

1731                     index = tty->index;

1732                     filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */

1733                     /* noctty = 1; */

1734                     /* FIXME: Should we take a driver reference ? */

1735                     tty_kref_put(tty);

1736                     goto got_driver;

1737              }

1738       #ifdef CONFIG_VT

1739              if (device == MKDEV(TTY_MAJOR, 0)) {

1740                     extern struct tty_driver *console_driver;

1741                     driver = tty_driver_kref_get(console_driver);

1742                     index = fg_console;

1743                     noctty = 1;

1744                     goto got_driver;

1745              }

1746       #endif

1747              if (device == MKDEV(TTYAUX_MAJOR, 1)) {

1748                     struct tty_driver *console_driver = console_device(&index);

1749                     if (console_driver) {

1750                            driver = tty_driver_kref_get(console_driver);

1751                            if (driver) {

1752                                   /* Don't let /dev/console block */

1753                                   filp->f_flags |= O_NONBLOCK;

1754                                   noctty = 1;

1755                                   goto got_driver;

1756                            }

1757                     }

1758                     mutex_unlock(&tty_mutex);

1759                     return -ENODEV;

1760              }

1761      

1762              driver = get_tty_driver(device, &index);

1763              if (!driver) {

1764                     mutex_unlock(&tty_mutex);

1765                     return -ENODEV;

1766              }

1767       got_driver:

1768              if (!tty) {

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

1770                     tty = tty_driver_lookup_tty(driver, inode, index);

1771      

1772                     if (IS_ERR(tty)) {

1773                            mutex_unlock(&tty_mutex);

1774                            return PTR_ERR(tty);

1775                     }

1776              }

1777      

1778              if (tty) {

1779                     retval = tty_reopen(tty);

1780                     if (retval)

1781                            tty = ERR_PTR(retval);

1782              } else

1783                     tty = tty_init_dev(driver, index, 0);

1784      

1785              mutex_unlock(&tty_mutex);

1786              tty_driver_kref_put(driver);

1787              if (IS_ERR(tty))

1788                     return PTR_ERR(tty);

1789      

1790              filp->private_data = tty;

1791              file_move(filp, &tty->tty_files);

1792              check_tty_count(tty, "tty_open");

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

1794                  tty->driver->subtype == PTY_TYPE_MASTER)

1795                     noctty = 1;

1796       #ifdef TTY_DEBUG_HANGUP

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

1798       #endif

1799              if (!retval) {

1800                     if (tty->ops->open)

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

1802                     else

1803                            retval = -ENODEV;

1804              }

1805              filp->f_flags = saved_flags;

1806      

1807              if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&

1808                                                 !capable(CAP_SYS_ADMIN))

1809                     retval = -EBUSY;

1810      

1811              if (retval) {

1812       #ifdef TTY_DEBUG_HANGUP

1813                     printk(KERN_DEBUG "error %d in opening %s...", retval,

1814                            tty->name);

1815       #endif

1816                     tty_release_dev(filp);

1817                     if (retval != -ERESTARTSYS)

1818                            return retval;

1819                     if (signal_pending(current))

1820                            return retval;

1821                     schedule();

1822                     /*

1823                     * Need to reset f_op in case a hangup happened.

1824                     */

1825                     if (filp->f_op == &hung_up_tty_fops)

1826                            filp->f_op = &tty_fops;

1827                     goto retry_open;

1828              }

1829      

1830              mutex_lock(&tty_mutex);

1831              spin_lock_irq(&current->sighand->siglock);

1832              if (!noctty &&

1833                  current->signal->leader &&

1834                  !current->signal->tty &&

1835                  tty->session == NULL)

1836                     __proc_set_tty(current, tty);

1837              spin_unlock_irq(&current->sighand->siglock);

1838              mutex_unlock(&tty_mutex);

1839              return 0;

1840       }

第1712行获得对应节点的设备号。

第1724~1737行判断设备号是不是 (5,0) ,即当前进程的控制终端/dev/tty,如果当前进程的控制终端存在,则获得它并获得其tty_driver、index,跳到got_driver;如果当前进程的控制终端不存在则退出。

第1738~1746行判断设备号是不是 (4,0) ,即当前控制台console终端/dev/tty0,如果是则获得它并获得其tty_driver、index,跳到got_driver。

第1747~1760行判断设备号是不是 (5,1) ,即外接的控制台终端/dev/console,如果是外接的控制台则调用console_device获得其tty_driver跳到got_driver,console_device函数是在\kernel\printk.c中定义:

1116 struct tty_driver *console_device(int *index)

1117 {

1118        struct console *c;

1119        struct tty_driver *driver = NULL;

1120      

1121              acquire_console_sem();

1122              for_each_console(c) {

1123                     if (!c->device)

1124                            continue;

1125                     driver = c->device(c, index);

1126                     if (driver)

1127                            break;

1128              }

1129              release_console_sem();

1130              return driver;

1131       }

0042       #define for_each_console(con) \

0043              for (con = console_drivers; con != NULL; con = con->next)

0085       struct console *console_drivers;

也即在console_drivers找到该console,并调用其device函数获得其tty_driver。

如果不是上面三种情况,1762行调用get_tty_driver 函数:

0268       static struct tty_driver *get_tty_driver(dev_t device, int *index)

0269       {

0270              struct tty_driver *p;

0271      

0272              list_for_each_entry(p, &tty_drivers, tty_drivers) {

0273                     dev_t base = MKDEV(p->major, p->minor_start);

0274                     if (device < base || device >= base + p->num)

0275                            continue;

0276                     *index = device - base;

0277                     return tty_driver_kref_get(p);

0278              }

0279              return NULL;

0280       }

以文件设备号为关键字到tty_drivers链表中搜索所注册的driver,还记不记得在前面分析tty_register_driver函数时把相应的tty_driver插入到tty_drivers链表。

第1768~1776行tty检查是否存在,不存在则需要重新打开它,tty_driver_lookup_tty函数如下:

1145       static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,

1146                     struct inode *inode, int idx)

1147       {

1148              struct tty_struct *tty;

1149      

1150              if (driver->ops->lookup)

1151                     return driver->ops->lookup(driver, inode, idx);

1152      

1153              tty = driver->ttys[idx];

1154              return tty;

1155       }

如果定义了driver->ops->lookup,就会调用它重新赋值tty,否则为driver->ttys[idx],我们在前面分析中已经这段ttys数组存放了相应的tty_struct的地址或者为空。

第1778~ 1783行,如果tty不为空则调用tty_reopen函数快速打开它,否则调用tty_init_dev函数重新创建一个tty_struct结构并进行相应的设置,来看看这里两个函数。

1246       static int tty_reopen(struct tty_struct *tty)

1247       {

1248              struct tty_driver *driver = tty->driver;

1249      

1250              if (test_bit(TTY_CLOSING, &tty->flags))

1251                     return -EIO;

1252      

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

1254                  driver->subtype == PTY_TYPE_MASTER) {

1255                     /*

1256                     * special case for PTY masters: only one open permitted,

1257                     * and the slave side open count is incremented as well.

1258                     */

1259                     if (tty->count)

1260                            return -EIO;

1261      

1262                     tty->link->count++;

1263              }

1264              tty->count++;

1265              tty->driver = driver; /* N.B. why do this every time?? */

1266      

1267              mutex_lock(&tty->ldisc_mutex);

1268              WARN_ON(!test_bit(TTY_LDISC, &tty->flags));

1269              mutex_unlock(&tty->ldisc_mutex);

1270      

1271              return 0;

1272       }

这个函数比较简单,递增一些计数器后返回。第1265行,就如其注释,貌似可以去掉。

接着看tty_init_dev函数:

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

1300                                                               int first_ok)

1301       {

1302              struct tty_struct *tty;

1303              int retval;

1304      

1305              /* Check if pty master is being opened multiple times */

1306              if (driver->subtype == PTY_TYPE_MASTER &&

1307                     (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)

1308                     return ERR_PTR(-EIO);

1309      

1310              /*

1311              * First time open is complex, especially for PTY devices.

1312              * This code guarantees that either everything succeeds and the

1313              * TTY is ready for operation, or else the table slots are vacated

1314              * and the allocated memory released.  (Except that the termios

1315              * and locked termios may be retained.)

1316              */

1317      

1318              if (!try_module_get(driver->owner))

1319                     return ERR_PTR(-ENODEV);

1320      

1321              tty = alloc_tty_struct();

1322              if (!tty)

1323                     goto fail_no_mem;

1324              initialize_tty_struct(tty, driver, idx);

1325      

1326              retval = tty_driver_install_tty(driver, tty);

1327              if (retval < 0) {

1328                     free_tty_struct(tty);

1329                     module_put(driver->owner);

1330                     return ERR_PTR(retval);

1331              }

1332      

1333              /*

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

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

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

1337              */

1338      

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

1340              if (retval)

1341                     goto release_mem_out;

1342              return tty;

1343      

1344       fail_no_mem:

1345              module_put(driver->owner);

1346              return ERR_PTR(-ENOMEM);

1347      

1348              /* call the tty release_tty routine to clean out this slot */

1349       release_mem_out:

1350              if (printk_ratelimit())

1351                     printk(KERN_INFO "tty_init_dev: ldisc open failed, "

1352                                   "clearing slot %d\n", idx);

1353              release_tty(tty, idx);

1354              return ERR_PTR(retval);

1355       }

第1305~1331行重新创建和初始化一个tty_struct,这里调用的函数都比较简单,我们重点分析线路规程部分代码。

0167       struct tty_struct *alloc_tty_struct(void)

0168       {

0169              return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);

0170       }

2726       void initialize_tty_struct(struct tty_struct *tty,

2727                     struct tty_driver *driver, int idx)

2728       {

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

2730              kref_init(&tty->kref);

2731              tty->magic = TTY_MAGIC;

2732              tty_ldisc_init(tty);

2733              tty->session = NULL;

2734              tty->pgrp = NULL;

2735              tty->overrun_time = jiffies;

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

2737              tty_buffer_init(tty);

2738              mutex_init(&tty->termios_mutex);

2739              mutex_init(&tty->ldisc_mutex);

2740              init_waitqueue_head(&tty->write_wait);

2741              init_waitqueue_head(&tty->read_wait);

2742              INIT_WORK(&tty->hangup_work, do_tty_hangup);

2743              mutex_init(&tty->atomic_read_lock);

2744              mutex_init(&tty->atomic_write_lock);

2745              mutex_init(&tty->output_lock);

2746              mutex_init(&tty->echo_lock);

2747              spin_lock_init(&tty->read_lock);

2748              spin_lock_init(&tty->ctrl_lock);

2749              INIT_LIST_HEAD(&tty->tty_files);

2750              INIT_WORK(&tty->SAK_work, do_SAK_work);

2751      

2752              tty->driver = driver;

2753              tty->ops = driver->ops;

2754              tty->index = idx;

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

2756       }

第2732行tty_ldisc_init函数如下:

0857       void tty_ldisc_init(struct tty_struct *tty)

0858       {

0859              struct tty_ldisc *ld = tty_ldisc_get(N_TTY);

0860              if (IS_ERR(ld))

0861                     panic("n_tty: init_tty");

0862              tty_ldisc_assign(tty, ld);

0863       }

0190       static struct tty_ldisc *tty_ldisc_get(int disc)

0191       {

0192              struct tty_ldisc *ld;

0193              struct tty_ldisc_ops *ldops;

0194      

0195              if (disc < N_TTY || disc >= NR_LDISCS)

0196                     return ERR_PTR(-EINVAL);

0197      

0198              /*

0199              * Get the ldisc ops - we may need to request them to be loaded

0200              * dynamically and try again.

0201              */

0202              ldops = get_ldops(disc);

0203              if (IS_ERR(ldops)) {

0204                     request_module("tty-ldisc-%d", disc);

0205                     ldops = get_ldops(disc);

0206                     if (IS_ERR(ldops))

0207                            return ERR_CAST(ldops);

0208              }

0209      

0210              ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);

0211              if (ld == NULL) {

0212                     put_ldops(ldops);

0213                     return ERR_PTR(-ENOMEM);

0214              }

0215      

0216              ld->ops = ldops;

0217              atomic_set(&ld->users, 1);

0218              return ld;

0219       }

第0202~0208行获得相应的规程操作,这里处于保险,如果不成功则再重复一次获取规程操作,还是不成功就返回错误。

然后分配一个tty_ldisc结构,并把刚才获得的规程操作赋给它的ops字段,然后返回该tty_ldisc。

get_ldops函数如下:

0148       static struct tty_ldisc_ops *get_ldops(int disc)

0149       {

0150              unsigned long flags;

0151              struct tty_ldisc_ops *ldops, *ret;

0152      

0153              spin_lock_irqsave(&tty_ldisc_lock, flags);

0154              ret = ERR_PTR(-EINVAL);

0155              ldops = tty_ldiscs[disc];

0156              if (ldops) {

0157                     ret = ERR_PTR(-EAGAIN);

0158                     if (try_module_get(ldops->owner)) {

0159                            ldops->refcount++;

0160                            ret = ldops;

0161                     }

0162              }

0163              spin_unlock_irqrestore(&tty_ldisc_lock, flags);

0164              return ret;

0165       }

从0155行看出规程操作的结构变量存放在tty_ldiscs数组中,其声明如下:

0049       static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];

该数组什么时候初始化后面再做分析。

接着继续看tty_init_dev中调用的其他函数:

1201       static int tty_driver_install_tty(struct tty_driver *driver,

1202                                                 struct tty_struct *tty)

1203       {

1204              int idx = tty->index;

1205      

1206              if (driver->ops->install)

1207                     return driver->ops->install(driver, tty);

1208      

1209              if (tty_init_termios(tty) == 0) {

1210                     tty_driver_kref_get(driver);

1211                     tty->count++;

1212                     driver->ttys[idx] = tty;

1213                     return 0;

1214              }

1215              return -ENOMEM;

1216       }

1165       int tty_init_termios(struct tty_struct *tty)

1166       {

1167              struct ktermios *tp;

1168              int idx = tty->index;

1169      

1170              tp = tty->driver->termios[idx];

1171              if (tp == NULL) {

1172                     tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);

1173                     if (tp == NULL)

1174                            return -ENOMEM;

1175                     memcpy(tp, &tty->driver->init_termios,

1176                                                 sizeof(struct ktermios));

1177                     tty->driver->termios[idx] = tp;

1178              }

1179              tty->termios = tp;

1180              tty->termios_locked = tp + 1;

1181      

1182              /* Compatibility until drivers always set this */

1183              tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);

1184              tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);

1185              return 0;

1186       }

这些都是设置初始化一些结构字段,不做详细分析。

接着重点分析第1339 行tty_ldisc_setup函数,它用于打开线路规程,代码如下:

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

0788       {

0789              struct tty_ldisc *ld = tty->ldisc;

0790              int retval;

0791      

0792              retval = tty_ldisc_open(tty, ld);

0793              if (retval)

0794                     return retval;

0795      

0796              if (o_tty) {

0797                     retval = tty_ldisc_open(o_tty, o_tty->ldisc);

0798                     if (retval) {

0799                            tty_ldisc_close(tty, ld);

0800                            return retval;

0801                     }

0802                     tty_ldisc_enable(o_tty);

0803              }

0804              tty_ldisc_enable(tty);

0805              return 0;

0806       }

tty_ldisc_open调用相应规程操作的open函数。代码如下:

0443       static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)

0444       {

0445              WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));

0446              if (ld->ops->open)

0447                     return ld->ops->open(tty);

0448              return 0;

0449       }

这个open函数以后再分析。

tty_ldisc_enable函数如下:

0388       void tty_ldisc_enable(struct tty_struct *tty)

0389       {

0390              set_bit(TTY_LDISC, &tty->flags);

0391              clear_bit(TTY_LDISC_CHANGING, &tty->flags);

0392              wake_up(&tty_ldisc_wait);

0393       }

设置tty->flag的TTY_LDISC标志位,清tty->flag的TTY_LDISC_CHANGING标志位,然后唤醒tty_ldisc_wait等待队列中的进程。

做完这些后tty_init_dev函数第1342行返回该新创建的tty。

回到__tty_open函数中,第1790行把该tty赋给filp文件指针的private_data字段。

第1801行如果一切顺利的话,则调用tty->ops->open函数,在前面的代码中已经知道tty->ops为相应tty_driver的ops。

__tty_open剩下的代码都比较简单,不再分析。

至此,tty_open函数分析完了,该函数涉及到线路规程的部分内容,相对比较复杂,如果你觉得台混乱,可以先看完后面的分析,整理思路,然后再回来看一遍。

接着将分析tty_read和tty_write函数。

相关文章推荐

Linux设备模型之tty驱动架构分析

Linux设备模型之tty驱动架构分析         一:前言 Tty这个名称源于电传打字节的简称。在linux表示各种...
  • pan0755
  • pan0755
  • 2016年06月16日 17:01
  • 686

Linux终端tty设备驱动

在Linux系统中,终端设备非常重要,没有终端设备,系统将无法向用户反馈信息,Linux中包含控制台、串口和伪终端3类终端设备。 14.1节阐述了终端设备的概念及分类,14.2节给 出了Linux终...

Linux设备模型之tty驱动架构分析

Linux设备模型之tty驱动架构分析  一:前言 Tty这个名称源于电传打字节的简称。在linux表示各种终端。终端通常都跟硬件相对应。比如对应于输入设备键盘鼠标。输...

TTY设备驱动结构

《TTY设备驱动结构》来自:刘建文 | 学术半·IT歌·文作者:刘建文关键字:Linux 驱动程序永久链接地址:http://arttech.us/y-2011/tty-device-driver.h...

tty初探—uart驱动框架分析

本文参考了大量牛人的博客,对大神的分享表示由衷的感谢。 主要参考:     tty驱动分析 :http://www.wowotech.net/linux_kenrel/183.html      L...

linux下TTY驱动(serial)

之前因为是刚入门所以看了串口有关的东西,一开始看了stm32f407上的串口编程(Keil MDK),那算是裸的驱动了。   linux下的串口如果要正常工作的话,就必须通过TTY这个子系统,TTY子...

Linux TTY驱动--Uart_driver底层

Linux 中将串口驱动进行了分层,如图: 本节讲解与底层硬件密切相关的层,以S3C2440为例剖析:     实现文件有:/drivers/serial/samsung.c    /driv...

Linux tty驱动学习 - UART驱动的read操作流程

在用户空间对tty设备进行读操作,经过系统调用进入到tty核心层执行的第一个函数是tty_read()。在tty_read()函数中,从文件描述符file的私有数据结构中获得tty_struct,然后...
  • sbctsp
  • sbctsp
  • 2016年01月12日 11:23
  • 1599

linux kernel下输入输出console如何实现

kernel和user空间下都有一个console,关系到kernel下printk的方向和user下printf的方向,实现差别还是很大的。 kernel下的console是输入输出设备driver...

linux /dev/console设备解析

tty是一类char设备的通称,它们有相同的特性,比如对^C的处理,驱动使用tty_register_driver注册一个tty。 /dev/console是一个虚拟的tty,它映射到真正的...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:·LINUX设备驱动之tty及console驱动(一)
举报原因:
原因补充:

(最多只允许输入30个字)