console ,tty ,和keyboard调用关系

源地址:http://blog.sina.com.cn/s/blog_633f46290100k4yt.html

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 是uart_driver,uart_open函数没有对vc_cons[fg_console].d->vc_tty赋值,因此put_queue无法打印字符
而console=tty时,串口调用的是console_driver , 其中 cons_open 函数有对vc_cons[fg_console].d->vc_tty赋值,因此put_queue>可以显示

你可能感兴趣的:(keyboard)