分享

console?,tty?,和keyboard调用关系

 waston 2013-08-27

console ,tty ,和keyboard调用关系

1。console的过程描述
例如pmon下其内核命令 g console=ttyS0,115200 root=/dev/sda1 init=/bin/sh rw
对console的过程讨论主要是讨论console=ttyS0 如何影响选取哪种console?

1)在kernel/printk.c中的
__setup("console=", console_setup);
给出了用于解释console=ttyS0的函数console_setup
console_setup调用的__add_preferred_console确定了ttyS0在console_cmdline[]的索引号selected_console

2)其次,各种外围的驱动调用kernel/printk.c中的register_console来注册其console,register_console有如下语句:
1196         if (i == selected_console) {
1198             console->flags |= CON_CONSDEV;
1199             preferred_console = selected_console;
1200         }
在i的循环中当regist的console名字与console_cmdline[selected_console]的名字相同时console->flags |= CON_CONSDEV
如下语句:
1223     if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
1224         console->next = console_drivers;
1225         console_drivers = console;
1226         if (console->next)
1227             console->next->flags &= ~CON_CONSDEV;
1228     } else {
1229         console->next = console_drivers->next;
1230         console_drivers->next = console;
1231     }
可知,在console_drivers链表中,只有与console_cmdline[selected_console]的名字相同console放在链表首位。

由此可得console=ttyS0实际上是保证console_drivers链表的首个console是名称为ttyS的console

2。tty_driver的选择
对tty_driver的说明主要通过如何根据不同的tty选择相应的tty_driver表示

1)tty_driver的注册实例
通过一个具体的tty_driver的注册例子来表示
在drivers/char/vt.c的vty_init函数中
2965     console_driver->owner = THIS_MODULE;
2966     console_driver->name = "tty";
2967     console_driver->name_base = 1;
2968     console_driver->major = TTY_MAJOR;
2969     console_driver->minor_start = 1;
2970     console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
2971     console_driver->init_termios = tty_std_termios;
2972     if (default_utf8)
2973         console_driver->init_termios.c_iflag |= IUTF8;
2974     console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
2975     tty_set_operations(console_driver, &con_ops);
2976     if (tty_register_driver(console_driver))
2977         panic("Couldn't register console driver\n");
其中tty_set_operations(console_driver, &con_ops);是设置tty_driver使用的一系列操作,con_ops是tty_operations类型,tty_register_driver(console_driver)即是注册tty_driver

2)tty_driver注册的作用,drivers/char/tty_io.c tty_register_driver中如下语句:
3534     mutex_lock(&tty_mutex);
3535     list_add(&driver->tty_drivers, &tty_drivers);
3536     mutex_unlock(&tty_mutex);
即是将各种tty_driver串联成一个链表tty_drivers

3)如何根据文件类型选择相应的tty_driver,
对于tty设备文件操作,调用的是tty_fops操作结构体(在drivers/char/tty_io.c中)的操作,其声明如下:
 810 static const struct file_operations tty_fops = {
 811     .llseek     = no_llseek,
 812     .read       = tty_read,
 813     .write      = tty_write,
 814     .poll       = tty_poll,
 815     .unlocked_ioctl = tty_ioctl,
 816     .compat_ioctl   = tty_compat_ioctl,
 817     .open       = tty_open,
 818     .release    = tty_release,
 819     .fasync     = tty_fasync,
 820 };
其对tty_driver的选择是在tty_open中的
tty_open 调用_tty_open再调用__tty_open,__tty_open中有如下语句:
2190     if (device == MKDEV(TTYAUX_MAJOR, 0)) {
2192         tty = get_current_tty();
2193         if (!tty) {
2194             mutex_unlock(&tty_mutex);
2195             return -ENXIO;
2196         }
2197         driver = tty->driver;
2198         index = tty->index;
2199         filp->f_flags |= O_NONBLOCK;
2200        
2201         goto got_driver;
2202     }
2203 #ifdef CONFIG_VT
2204     if (device == MKDEV(TTY_MAJOR, 0)) {
2205         extern struct tty_driver *console_driver;
2207         driver = console_driver;
2208         index = fg_console;
2209         noctty = 1;
2210         goto got_driver;
2211     }
2212 #endif
2213     if (device == MKDEV(TTYAUX_MAJOR, 1)) {
2215         driver = console_device(&index);
2216         if (driver) {
2217            
2218             filp->f_flags |= O_NONBLOCK;
2219             noctty = 1;
2220             goto got_driver;
2221         }
2222         mutex_unlock(&tty_mutex);
2223         return -ENODEV;
2225
2226     driver = get_tty_driver(device, &index);
2227     if (!driver) {
2228         mutex_unlock(&tty_mutex);
2229         return -ENODEV;
2230     }
可见,通过device与MKDEV的对比选择相应的tty_driver,前两个分支显而易见,对于driver=console_device(&index)
在kernel/printk.c中如下,
1087 struct tty_driver *console_device(int *index)
1088 {
1089     struct console *c;
1090     struct tty_driver *driver = NULL;
1091
1092     acquire_console_sem();
1093     for (c = console_drivers; c != NULL; c = c->next) {
1094         if (!c->device)
1095             continue;
1096         driver = c->device(c, index);
1097         if (driver)
1098             break;
1099     }
1100     release_console_sem();
1101     return driver;
1102 }
即console_device选择console_drivers最先有效的console的相关tty_driver,c->device中的device是console的一个成员函数,用于设
备和tty_driver的映射。

由__tty_open中选择driver后,再通过
2234     retval = init_dev(driver, index, &tty);
的语句,使得tty_driver于tty相关联。

如上的整个过程 构成了tty_driver的选择

3.键盘的显示

1)键盘的中断处理
以i8042,型号为at的键盘为,在drivers/char/serio/i8042.c中于两句注册中断的语句
1129     error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
1130                 "i8042", i8042_platform_device);
注册ps2 鼠标中断,一般中断号是12
1155     error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED | IRQF_DISABLED,
1156                 "i8042", i8042_platform_device);
注册ps2键盘中断,一般中断号是1

可见键盘的中断处理程序是i8042_interrupt --------------------------drivers/input/serio/i8042.c中
i8042继续调用函数       serio_interrupt --------------------------drivers/input/serio/serio.c
serio_interrupt 继续调用ret = serio->drv->interrupt(serio, data, dfl);
其中serio->drv是struct serio_driver* 类型。根据键盘型号知道其指向的是
1142 static struct serio_driver atkbd_drv = {   ------------------drivers/input/keyboard/atkbd.c
1143     .driver     = {
1144         .name   = "atkbd",
1145     },
1146     .description    = DRIVER_DESC,
1147     .id_table   = atkbd_serio_ids,
1148     .interrupt  = atkbd_interrupt,
1149     .connect    = atkbd_connect,
1150     .reconnect  = atkbd_reconnect,
1151     .disconnect = atkbd_disconnect,
1152     .cleanup    = atkbd_cleanup,
1153 };
因此其最终调用的是  atkbd_interrupt  ----------------------------drivers/input/keyboard/atkbd.c
atkbd_interrupt字符处理调用的是 input_event----------------------drivers/input/input.c
input_event调用的是 input_handle_event---------------------------drivers/input/input.c
input_handle_event 调用的是 input_pass_event (对于字符处理)-----drivers/input/input.c
input_pass_event 最终调用handle->handler->event
其中handle->handler 是struct input_handler*类型
键盘调用的struct input_handler* 在drivers/char/keyboard.c中定义如下:
1408 static struct input_handler kbd_handler = {
1409     .event      = kbd_event,
1410     .connect    = kbd_connect,
1411     .disconnect = kbd_disconnect,
1412     .start      = kbd_start,
1413     .name       = "kbd",
1414     .id_table   = kbd_ids,
1415 };
因此最终调用的是  kbd_event    ----------------------------------drivers/char/keyboard.c
kbd_event字符处理调用    kbd_keycode-----------------------------drivers/char/keyboard.c
kbd_keycode字符处理的语句是(*k_handler[type])(vc, keysym & 0xff, !down);
其中k_handler定义如下--------------------------------------------drivers/char/keyboard.c
  78 #define K_HANDLERS\
  79     k_self,     k_fn,       k_spec,     k_pad,\
  80     k_dead,     k_cons,     k_cur,      k_shift,\
  81     k_meta,     k_ascii,    k_lock,     k_lowercase,\
  82     k_slock,    k_dead2,    k_brl,      k_ignore
  83
  84 typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value,
  85                 char up_flag);
  86 static k_handler_fn K_HANDLERS;
  87 k_handler_fn *k_handler[16] = { K_HANDLERS };
  88 EXPORT_SYMBOL_GPL(k_handler);
如上对于字符处理一共有16中不同的函数,对于一般的字符显示调用的是k_self,
最终字符显示调用的是
 299 static void put_queue(struct vc_data *vc, int ch)-----------drivers/char/keyboard.c

2)字符显示put_queue的参数解释
字符显示put_queue函数如下:
 299 static void put_queue(struct vc_data *vc, int ch)
 300 {
 301     struct tty_struct *tty = vc->vc_tty;
 302
 303     printk("put_queue %c,%d, tty:%x\n" ,(char)ch,ch,tty);
 304     if (tty) {
 305         tty_insert_flip_char(tty, ch, 0);
 306         con_schedule_flip(tty);
 307     }
 308 }
除了键盘传入的ch字符,如何获得vc?
kbd_keycode传给put_queue是如下获得的:
struct vc_data *vc = vc_cons[fg_console].d;
vc_cons 是struct vc类型的数组.在drivers/char/vt.c中定义,con_init初始化



对于遇到的通过串口启动系统,板上键盘无法在串口上显示字符问题的原因是
console=ttyS0,时,串口调用的tty_driver 是

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多