linux终端设备:tty子系统相关的初始化
? linux類系統(tǒng)相較于windos類系統(tǒng)用“魔幻”也不為過,神奇的控制臺終端如ctrl+alt+F1至F6、UI桌面打開終端、遠(yuǎn)程ssh登錄等等,在神奇的命令行協(xié)助下總能隨時(shí)隨地完成手中的工作。
? 當(dāng)然,這些功能雖不算復(fù)雜,但龐大的架構(gòu)及繁多的代碼讓人有一種深不測的感覺。經(jīng)過一段時(shí)間對終端相關(guān)代碼的分析對它們有了初步的了解。它們采用較為一致的思路方式實(shí)現(xiàn),代碼難度也不算太高,具備字符驅(qū)動(dòng)相關(guān)的知識加上足夠的耐心便可以解開這個(gè)“龐然大物”。
? linux終端子系統(tǒng)主要圍繞著外設(shè)(輸入設(shè)備)到內(nèi)核的輸入事件處理及轉(zhuǎn)發(fā),到達(dá)我們目前看到的效果。
? 大致思路為:外設(shè) -> input事件子系統(tǒng) -> tty子系統(tǒng) -> 輸出/輸入對應(yīng)的終端種類,如console、pty等。
? 終端類型可以分為:tty子系統(tǒng)、tty設(shè)備、console設(shè)備、pty(偽終端)設(shè)備等等,在pty這一層又做了不同的處理對應(yīng)/dev/pts/ptmx->/dev/ptmx->/dev/pts/*等等,由于這部分內(nèi)容過于繁多,這里先簡單描述一小部分執(zhí)行邏輯,之后應(yīng)該還會有相關(guān)文章繼續(xù)向后擴(kuò)展內(nèi)容。
目錄
1. tty
1.1 tty_class_init
1.2 tty子系統(tǒng)注冊
2. console
2.1 console注冊
2.2 con_init
3. 源碼結(jié)構(gòu)
4. 部分結(jié)構(gòu)定義
5. 擴(kuò)展函數(shù)/變量
1. tty
1.1 tty_class_init
? tty子系統(tǒng)首先創(chuàng)建tty class,綁定設(shè)備號(動(dòng)態(tài)生成),由class對象tty_class關(guān)聯(lián)/dev/目錄下的tty相關(guān)訪問文件,如/dev/tty0、/dev/tty1
? 通常情況下,驅(qū)動(dòng)通過class名稱與其建立關(guān)系,如pci_driver的.name字段指定class名稱
static int __init tty_class_init(void) {tty_class = class_create(THIS_MODULE, "tty"); // 創(chuàng)建tty class指向__this_module(模塊所有者) || \/ —————————————————————————————————————————————————————————————————————— define class_create(owner, name) \ ({ \static struct lock_class_key __key; \__class_create(owner, name, &__key); \ })#ifdef MODULE extern struct module __this_module; #define THIS_MODULE (&__this_module) #else #define THIS_MODULE ((struct module *)0) #endif ——————————————————————————————————————————————————————————————————————if (IS_ERR(tty_class))return PTR_ERR(tty_class);tty_class->devnode = tty_devnode; // 設(shè)備號return 0; }postcore_initcall(tty_class_init);|| \/ ic char *tty_devnode(struct device *dev, umode_t *mode) {if (!mode)return NULL;if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||dev->devt == MKDEV(TTYAUX_MAJOR, 2))*mode = 0666;return NULL; }class
1.2 tty子系統(tǒng)注冊
? tty子系統(tǒng)通過tty_init函數(shù)完成注冊過程,它在字符設(shè)備初始化函數(shù)chr_dev_init中調(diào)用,tty子系統(tǒng)實(shí)際上也屬于字符設(shè)備
? tty子系統(tǒng)通過tty_fops結(jié)構(gòu)中的函數(shù)完成應(yīng)用層的訪問,如應(yīng)用程序調(diào)用open(“/dev/tty”…)函數(shù)
tty_sysctl_init(); // 注冊tty子系統(tǒng)標(biāo)簽(配置參數(shù)) || \/ register_sysctl_table(tty_root_table);tty_root_table
cdev_init(&tty_cdev, &tty_fops); // 初始化tty_cdev設(shè)備,關(guān)聯(lián)tty_fops結(jié)構(gòu)if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) // 注冊tty_cdev設(shè)備panic("Couldn't register /dev/tty driver\n");device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); // 創(chuàng)建tty_cdev設(shè)備,指向tty_class,通過open等函數(shù)訪問/dev/tty ...return 0; } fs_initcall(chr_dev_init);tty_fops
2. console
2.1 console注冊
? console緊跟著 tty子系統(tǒng)之后注冊,它具有自己的操作結(jié)構(gòu)console_fops,它在內(nèi)核中的形式與tty子系統(tǒng)相似,基于tty_class創(chuàng)建consdev設(shè)備
? vty_init函數(shù)注冊vc0_cdev設(shè)備(/dev/tty0),通過創(chuàng)建vcs、vcsu、vcsa相關(guān)設(shè)備,完成向虛擬圖形界面的數(shù)據(jù)輸出等工作。vty_init函數(shù)末尾可以看到鍵盤驅(qū)動(dòng)注冊函數(shù)(通常情況下,不同的按鍵廠商還是會做自己的驅(qū)動(dòng),至少在設(shè)備ID、廠商ID這部分是這樣)。
console_fops
consdev = device_create_with_groups(tty_class, NULL,MKDEV(TTYAUX_MAJOR, 1), NULL,cons_dev_groups, "console"); // 創(chuàng)建consdev設(shè)備,指向tty_class,通過open等函數(shù)訪問/dev/console if (IS_ERR(consdev))consdev = NULL;cons_dev_groups
#ifdef CONFIG_VTvty_init(&console_fops); #endifreturn 0; } || \/ int __init vty_init(const struct file_operations *console_fops) {cdev_init(&vc0_cdev, console_fops); // 初始化vc0_cdev設(shè)備,關(guān)聯(lián)console_fops結(jié)構(gòu)if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0) // 注冊vc0_cdev設(shè)備panic("Couldn't register /dev/tty0 driver\n");tty0dev = device_create_with_groups(tty_class, NULL,MKDEV(TTY_MAJOR, 0), NULL,vt_dev_groups, "tty0");//注冊tty0dev設(shè)備,指向tty_class,通過open等函數(shù)訪問/dev/tty0 if (IS_ERR(tty0dev))tty0dev = NULL;vcs_init(); // 注冊、創(chuàng)建vcs、vcsu、vcsa設(shè)備vcs_init
console_driver = tty_alloc_driver(MAX_NR_CONSOLES, TTY_DRIVER_REAL_RAW |TTY_DRIVER_RESET_TERMIOS); // 分配cosole驅(qū)動(dòng)if (IS_ERR(console_driver))panic("Couldn't allocate console driver\n");console_driver->name = "tty"; console_driver->name_base = 1;console_driver->major = TTY_MAJOR;console_driver->minor_start = 1;console_driver->type = TTY_DRIVER_TYPE_CONSOLE;console_driver->init_termios = tty_std_termios;tty_alloc_driver
if (default_utf8)console_driver->init_termios.c_iflag |= IUTF8;tty_set_operations(console_driver, &con_ops); // console_driver關(guān)聯(lián)con_ops結(jié)構(gòu)if (tty_register_driver(console_driver)) // 注冊tty設(shè)備,指定tty_class,這里包括tty的多個(gè)種類,如console、ptypanic("Couldn't register console driver\n");kbd_init(); // 注冊鍵盤驅(qū)動(dòng)console_map_init(); // 設(shè)置默認(rèn)unicode映射 #ifdef CONFIG_MDA_CONSOLEmda_console_init(); #endifreturn 0; }con_ops
tty_register_driver
kbd_init
2.2 con_init
? 虛擬控制臺相關(guān)的初始化,如控制臺的回調(diào)、控制臺的屏幕設(shè)置、vc_SAK組合按鍵監(jiān)控觸發(fā)的事件、虛擬屏幕緩存分配等等,最后注冊到通知鏈,表示控制臺已分配完成
static int __init con_init(void) {const char *display_desc = NULL;struct vc_data *vc;unsigned int currcons = 0, i;console_lock();if (!conswitchp)conswitchp = &dummy_con; // 虛擬控制臺的控制臺“開關(guān)”結(jié)構(gòu) (consw-控制臺的回調(diào))display_desc = conswitchp->con_startup(); // "dummy device"if (!display_desc) { fg_console = 0;console_unlock();return 0;}dummy_con
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {struct con_driver *con_driver = ®istered_con_driver[i];if (con_driver->con == NULL) { // 虛擬控制臺賦值con_driver->con = conswitchp;con_driver->desc = display_desc; // "dummy device"con_driver->flag = CON_DRIVER_FLAG_INIT;con_driver->first = 0;con_driver->last = MAX_NR_CONSOLES - 1;break;}}for (i = 0; i < MAX_NR_CONSOLES; i++)con_driver_map[i] = conswitchp;if (blankinterval) {blank_state = blank_normal_wait;mod_timer(&console_timer, jiffies + (blankinterval * HZ)); // 虛擬控制臺(屏幕,ctl+alt+F2...F6這種屏幕,而ctl+alt+F1屬于控制臺啟動(dòng)的類Xwindow這種模擬畫面)刷新時(shí)間}for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); // 虛擬控制臺分配INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); // 初始化工作隊(duì)列,vc_SAK屬于組合按鍵監(jiān)控觸發(fā)的事件,如alt+k+Prtscreentty_port_init(&vc->port); // 初始化tty端口及tty_bufhead等結(jié)構(gòu),用于翻轉(zhuǎn)緩沖區(qū)刷新到線路規(guī)程visual_init(vc, currcons, 1); // 初始化虛擬控制臺的一些參數(shù)/* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); // 虛擬屏幕緩存分配vc_init(vc, vc->vc_rows, vc->vc_cols,currcons || !vc->vc_sw->con_save_screen); // 虛擬控制臺屏幕的模式、范圍、顏色等設(shè)置}tty_port_init
visual_init
vc_init
3. 源碼結(jié)構(gòu)
? tty子系統(tǒng)標(biāo)簽
tatic struct ctl_table tty_root_table[] = {{.procname = "dev",.mode = 0555,.child = tty_dir_table, // 子級標(biāo)簽}, { } };? tty子系統(tǒng)子級標(biāo)簽
static struct ctl_table tty_dir_table[] = {{.procname = "tty",.mode = 0555,.child = tty_table, // 子級標(biāo)簽}, { } };? tty子系統(tǒng)子級標(biāo)簽 ,通過/proc/sys/dev/tty/ldisc_autoload來傳遞數(shù)字
static struct ctl_table tty_table[] = {{.procname = "ldisc_autoload",.data = &tty_ldisc_autoload,.maxlen = sizeof(tty_ldisc_autoload),.mode = 0644,.proc_handler = proc_dointvec,.extra1 = SYSCTL_ZERO,.extra2 = SYSCTL_ONE,},{ } };? tty_fops 文件操作接口
static const struct file_operations tty_fops = {.llseek = no_llseek,.read_iter = tty_read,.write_iter = tty_write,.splice_read = generic_file_splice_read,.splice_write = iter_file_splice_write,.poll = tty_poll,.unlocked_ioctl = tty_ioctl,.compat_ioctl = tty_compat_ioctl,.open = tty_open,.release = tty_release,.fasync = tty_fasync,.show_fdinfo = tty_show_fdinfo, };? console_fops 文件操作接口
static const struct file_operations console_fops = {.llseek = no_llseek,.read_iter = tty_read,.write_iter = redirected_tty_write,.splice_read = generic_file_splice_read,.splice_write = iter_file_splice_write,.poll = tty_poll,.unlocked_ioctl = tty_ioctl,.compat_ioctl = tty_compat_ioctl,.open = tty_open,.release = tty_release,.fasync = tty_fasync, };? vcs_fops 文件操作接口
static const struct file_operations vcs_fops = {.llseek = vcs_lseek,.read = vcs_read,.write = vcs_write,.poll = vcs_poll,.fasync = vcs_fasync,.open = vcs_open,.release = vcs_release, };? con_ops 文件操作接口
static const struct tty_operations con_ops = {.install = con_install,.open = con_open,.close = con_close,.write = con_write,.write_room = con_write_room,.put_char = con_put_char,.flush_chars = con_flush_chars,.ioctl = vt_ioctl, #ifdef CONFIG_COMPAT.compat_ioctl = vt_compat_ioctl, #endif.stop = con_stop,.start = con_start,.throttle = con_throttle,.unthrottle = con_unthrottle,.resize = vt_resize,.shutdown = con_shutdown,.cleanup = con_cleanup, };? dummy_con 虛擬控制臺的控制臺“開關(guān)”結(jié)構(gòu)
const struct consw dummy_con = {.owner = THIS_MODULE,.con_startup = dummycon_startup,.con_init = dummycon_init,.con_deinit = dummycon_deinit,.con_clear = dummycon_clear,.con_putc = dummycon_putc,.con_putcs = dummycon_putcs,.con_cursor = dummycon_cursor,.con_scroll = dummycon_scroll,.con_switch = dummycon_switch,.con_blank = dummycon_blank, }; EXPORT_SYMBOL_GPL(dummy_con);? tty_port_default_client_ops
const struct tty_port_client_operations tty_port_default_client_ops = {.receive_buf = tty_port_default_receive_buf,.lookahead_buf = tty_port_default_lookahead_buf,.write_wakeup = tty_port_default_wakeup, }; EXPORT_SYMBOL_GPL(tty_port_default_client_ops);4. 部分結(jié)構(gòu)定義
? struct class
* struct class——設(shè)備類* @name:類名。* @owner:模塊所有者。* @class_groups:該類的默認(rèn)屬性。* @dev_groups:屬于該類的設(shè)備的默認(rèn)屬性。* @dev_kobj:表示這個(gè)類并將其鏈接到層次結(jié)構(gòu)中的kobject。* @dev_uevent:當(dāng)設(shè)備被添加或從該類中移除時(shí)調(diào)用*很少其他東西生成uevent來添加環(huán)境*變量。* @devnode:回調(diào)提供devtmpfs。* @class_release:調(diào)用來釋放這個(gè)類。* @dev_release:被調(diào)用釋放設(shè)備。* @shutdown_pre:在驅(qū)動(dòng)程序關(guān)閉前的關(guān)閉時(shí)間調(diào)用。* @ns_type:回調(diào)使sysfs可以確定名稱空間。* @namespace:設(shè)備的命名空間屬于該類。* @get_ownership:允許類指定sysfs目錄的uid/gid*表示屬于該類的設(shè)備。通常與*設(shè)備的名稱空間。* @pm:該類的默認(rèn)設(shè)備電源管理操作。* @p:驅(qū)動(dòng)核心的私有數(shù)據(jù),不是別人*驅(qū)動(dòng)核心可以觸摸這個(gè)。**類是設(shè)備的高級視圖,它抽象出了底層*實(shí)現(xiàn)細(xì)節(jié)。驅(qū)動(dòng)程序可能看到的是SCSI磁盤或ATA磁盤,但是,*在類級別,它們都是簡單的磁盤。類允許用戶空間*根據(jù)設(shè)備的功能,而不是它們的性能來使用它們*連接或他們?nèi)绾喂ぷ鳌truct class {const char *name;struct module *owner;const struct attribute_group **class_groups;const struct attribute_group **dev_groups;struct kobject *dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*shutdown_pre)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);const struct dev_pm_ops *pm;struct subsys_private *p; };5. 擴(kuò)展函數(shù)/變量
? cons_dev_groups 經(jīng)過轉(zhuǎn)換為 attribute_group結(jié)構(gòu)的靜態(tài)對象
ATTRIBUTE_GROUPS(cons_dev); || \/ #define ATTRIBUTE_GROUPS(_name) \ static const struct attribute_group _name##_group = { \.attrs = _name##_attrs, \ }; \ __ATTRIBUTE_GROUPS(_name) || \/ define __ATTRIBUTE_GROUPS(_name) \ static const struct attribute_group *_name##_groups[] = { \&_name##_group, \NULL, \ }轉(zhuǎn)換成結(jié)構(gòu)定義為: static const struct attribute_group cons_dev_group = { .attrs = cons_dev_attrs, }; static const struct attribute_group *cons_dev_groups[] = { &cons_dev_group, NULL, }? vcs_init 注冊、創(chuàng)建vcs、vcsu、vcsa設(shè)備
int __init vcs_init(void) { unsigned int i;if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))panic("unable to get major %d for vcs device", VCS_MAJOR); // 注冊vcs設(shè)備,關(guān)聯(lián)vcs_fopsvc_class = class_create(THIS_MODULE, "vc"); // 創(chuàng)建vc class,指向__this_module(模塊所有者)device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); // 創(chuàng)建vcs設(shè)備,指向vc_class...device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu"); // 創(chuàng)建vcsu設(shè)備...device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); // 創(chuàng)建vcsa設(shè)備...for (i = 0; i < MIN_NR_CONSOLES; i++)// define MIN_NR_CONSOLES 1vcs_make_sysfs(i); // 創(chuàng)建多個(gè)vcs*、vcsu*、vcsa*設(shè)備...return 0; } || \/ void vcs_make_sysfs(int index) {device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,"vcs%u", index + 1);device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,"vcsu%u", index + 1);device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,"vcsa%u", index + 1); }vcs_fops
? tty_alloc_driver 分配tty驅(qū)動(dòng)
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,unsigned long flags) {struct tty_driver *driver;unsigned int cdevs = 1;int err;if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))return ERR_PTR(-EINVAL);driver = kzalloc(sizeof(*driver), GFP_KERNEL);if (!driver)return ERR_PTR(-ENOMEM);kref_init(&driver->kref);driver->num = lines;driver->owner = owner;driver->flags = flags;/* TTY_DRIVER_DEVPTS_MEM 0x0010** 不要使用標(biāo)準(zhǔn)的數(shù)組(&tty_driver.ttys和&tty_driver.termios)* 而是使用通過devpts文件系統(tǒng)輸入的動(dòng)態(tài)內(nèi)存* 這只適用于PTY驅(qū)動(dòng)程序**/if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {driver->ttys = kcalloc(lines, sizeof(*driver->ttys),GFP_KERNEL);driver->termios = kcalloc(lines, sizeof(*driver->termios),GFP_KERNEL);if (!driver->ttys || !driver->termios) {err = -ENOMEM;goto err_free_all;}}/* TTY_DRIVER_DYNAMIC_ALLOC 0x0040** 不要為驅(qū)動(dòng)程序(&tty_driver.ports)分配每行需要的結(jié)構(gòu)* 因?yàn)檫@會浪費(fèi)內(nèi)存* 這只適用于PTY驅(qū)動(dòng)程序**/if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {driver->ports = kcalloc(lines, sizeof(*driver->ports),GFP_KERNEL);if (!driver->ports) {err = -ENOMEM;goto err_free_all;}cdevs = lines;}driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);if (!driver->cdevs) {err = -ENOMEM;goto err_free_all;}return driver;... }EXPORT_SYMBOL(__tty_alloc_driver);? tty_register_driver
int tty_register_driver(struct tty_driver *driver) {int error;int i;dev_t dev;struct device *d;if (!driver->major) {error = alloc_chrdev_region(&dev, driver->minor_start,driver->num, driver->name);// 分配設(shè)備號if (!error) {driver->major = MAJOR(dev);driver->minor_start = MINOR(dev);}} else {dev = MKDEV(driver->major, driver->minor_start);error = register_chrdev_region(dev, driver->num, driver->name);// 注冊設(shè)備號}if (error < 0)goto err;if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {error = tty_cdev_add(driver, dev, 0, driver->num); // 分配、注冊驅(qū)動(dòng)并且關(guān)聯(lián)tty_fopsif (error)goto err_unreg_char;}tty_cdev_add
mutex_lock(&tty_mutex);list_add(&driver->tty_drivers, &tty_drivers); // 驅(qū)動(dòng)加入tty鏈表mutex_unlock(&tty_mutex);if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {for (i = 0; i < driver->num; i++) {d = tty_register_device(driver, i, NULL);if (IS_ERR(d)) {error = PTR_ERR(d);goto err_unreg_devs;}}}tty_register_device
proc_tty_register_driver(driver);driver->flags |= TTY_DRIVER_INSTALLED;return 0;? tty_cdev_add
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,unsigned int index, unsigned int count) {int err;/* init here, since reused cdevs cause crashes */driver->cdevs[index] = cdev_alloc(); // 分配字符設(shè)備結(jié)構(gòu)if (!driver->cdevs[index])return -ENOMEM;driver->cdevs[index]->ops = &tty_fops; // 為驅(qū)動(dòng)關(guān)聯(lián)tty_fops結(jié)構(gòu)driver->cdevs[index]->owner = driver->owner;err = cdev_add(driver->cdevs[index], dev, count); // 注冊字符設(shè)備if (err)kobject_put(&driver->cdevs[index]->kobj);return err; }? tty_register_device 注冊tty設(shè)備,指定tty_class,這里包括tty的多個(gè)種類,如console、pty
vice *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device) {return tty_register_device_attr(driver, index, device, NULL, NULL); } || \/ struct device *tty_register_device_attr(struct tty_driver *driver,unsigned index, struct device *device,void *drvdata,const struct attribute_group **attr_grp) {char name[64];dev_t devt = MKDEV(driver->major, driver->minor_start) + index;struct ktermios *tp;struct device *dev;int retval;if (index >= driver->num) {pr_err("%s: Attempt to register invalid tty line number (%d)\n",driver->name, index);return ERR_PTR(-EINVAL);}/* tty設(shè)備類型* #define TTY_DRIVER_TYPE_SYSTEM 0x0001 內(nèi)部使用* #define TTY_DRIVER_TYPE_CONSOLE 0x0002 控制臺* #define TTY_DRIVER_TYPE_SERIAL 0x0003 串口* #define TTY_DRIVER_TYPE_PTY 0x0004 PTY(偽終端)* #define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */* #define TTY_DRIVER_TYPE_SYSCONS 0x0006 內(nèi)部使用*/if (driver->type == TTY_DRIVER_TYPE_PTY)pty_line_name(driver, index, name); // pty通用名稱,PTY_TYPE_SLAVE類型指定為"tty",其他類型為nameelsetty_line_name(driver, index, name); // 包含TTY_DRIVER_UNNUMBERED_NODE類型創(chuàng)建名稱如"/dev/ttyprintk",否則如"/dev/ttyprintk0..."dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return ERR_PTR(-ENOMEM);dev->devt = devt;dev->class = tty_class; // 設(shè)置指定tty_classdev->parent = device;dev->release = tty_device_create_release;dev_set_name(dev, "%s", name);dev->groups = attr_grp;dev_set_drvdata(dev, drvdata);dev_set_uevent_suppress(dev, 1); // dev->kobj.uevent_suppress=1retval = device_register(dev); // 注冊設(shè)備... }? kbd_init 注冊鍵盤驅(qū)動(dòng)
int __init kbd_init(void) {int i;int error;for (i = 0; i < MAX_NR_CONSOLES; i++) {kbd_table[i].ledflagstate = kbd_defleds(); // &0x20 ? 數(shù)字鎖定模式kbd_table[i].default_ledflagstate = kbd_defleds();kbd_table[i].ledmode = LED_SHOW_FLAGS; // 傳統(tǒng)模式kbd_table[i].lockstate = KBD_DEFLOCK;kbd_table[i].slockstate = 0;kbd_table[i].modeflags = KBD_DEFMODE; // 鍵盤重復(fù) | 0-meta,1-meta=帶ESC的前綴kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; // Unicode模式}kbd_init_leds(); // LED觸發(fā)接口error = input_register_handler(&kbd_handler); // 注冊新的輸入處理程序if (error)return error;tasklet_enable(&keyboard_tasklet); // 用于減小結(jié)構(gòu)體tasklet_struct中字段count的值,等于0時(shí)重新相應(yīng)軟中斷重新使能tasklet_schedule(&keyboard_tasklet); // 等待獲得CPU資源并被調(diào)度執(zhí)行return 0; }? tty_port_init 初始化tty端口及tty_bufhead等結(jié)構(gòu),用于翻轉(zhuǎn)緩沖區(qū)刷新到線路規(guī)程
void tty_port_init(struct tty_port *port) { memset(port, 0, sizeof(*port));tty_buffer_init(port); // 初始化tty_bufhead結(jié)構(gòu),初始化工作隊(duì)列,用于flush_to_ldisc函數(shù) (翻轉(zhuǎn)緩沖區(qū)刷新到線路規(guī)程)init_waitqueue_head(&port->open_wait);init_waitqueue_head(&port->delta_msr_wait);mutex_init(&port->mutex);mutex_init(&port->buf_mutex);spin_lock_init(&port->lock);port->close_delay = (50 * HZ) / 100;port->closing_wait = (3000 * HZ) / 100;port->client_ops = &tty_port_default_client_ops;kref_init(&port->kref); } EXPORT_SYMBOL(tty_port_init);tty_port_default_client_ops
? visual_init 初始化虛擬控制臺的一些參數(shù)
static void visual_init(struct vc_data *vc, int num, int init) {/* ++Geert: vc->vc_sw->con_init determines console size */if (vc->vc_sw)module_put(vc->vc_sw->owner);vc->vc_sw = conswitchp; // 虛擬控制臺的控制臺“開關(guān)”結(jié)構(gòu) #ifndef VT_SINGLE_DRIVERif (con_driver_map[num])vc->vc_sw = con_driver_map[num]; // consw-控制臺的回調(diào) #endif__module_get(vc->vc_sw->owner);vc->vc_num = num; // 控制臺編號/* 對于每個(gè)現(xiàn)有的顯示器,* 我們都有一個(gè)指向該顯示器上當(dāng)前可見的控制臺的指針,* 允許適當(dāng)刷新fg_console以外的控制臺。* 除非低級驅(qū)動(dòng)程序提供自己的display_fg變量,* 否則我們將此變量用于“主顯示”。* /vc->vc_display_fg = &master_display_fg;if (vc->uni_pagedict_loc)con_free_unimap(vc);vc->uni_pagedict_loc = &vc->uni_pagedict;vc->uni_pagedict = NULL;vc->vc_hi_font_mask = 0;vc->vc_complement_mask = 0;vc->vc_can_do_color = 0;/** #define DEFAULT_BELL_PITCH 750* #define DEFAULT_BELL_DURATION (HZ/8)* #define DEFAULT_CURSOR_BLINK_MS 200*/vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;vc->vc_sw->con_init(vc, init); // dummycon_init函數(shù)if (!vc->vc_complement_mask)vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;vc->vc_s_complement_mask = vc->vc_complement_mask;vc->vc_size_row = vc->vc_cols << 1;vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row; }? vc_init 虛擬控制臺屏幕的模式、范圍、顏色等設(shè)置
static void vc_init(struct vc_data *vc, unsigned int rows,unsigned int cols, int do_clear) {int j, k ;vc->vc_cols = cols; vc->vc_rows = rows;vc->vc_size_row = cols << 1;vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;set_origin(vc);vc->vc_pos = vc->vc_origin;reset_vc(vc);for (j=k=0; j<16; j++) {vc->vc_palette[k++] = default_red[j] ;vc->vc_palette[k++] = default_grn[j] ;vc->vc_palette[k++] = default_blu[j] ;}vc->vc_def_color = default_color;vc->vc_ulcolor = default_underline_color;vc->vc_itcolor = default_italic_color;vc->vc_halfcolor = 0x08; /* grey */init_waitqueue_head(&vc->paste_wait);reset_terminal(vc, do_clear); }總結(jié)
以上是生活随笔為你收集整理的linux终端设备:tty子系统相关的初始化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java图书查询系统实例
- 下一篇: 07-15 shell命令 man ps