Skip to the content.

i8042 : 键盘

i8042

TODO

整理

应该是,首先发送一个中断,然后驱动读去吧。

这个注册只能说是相当的直白了:

static void i8042_realizefn(DeviceState *dev, Error **errp)
{
    ISADevice *isadev = ISA_DEVICE(dev);
    ISAKBDState *isa_s = I8042(dev);
    KBDState *s = &isa_s->kbd;

    isa_init_irq(isadev, &s->irq_kbd, 1);
    isa_init_irq(isadev, &s->irq_mouse, 12);

    isa_register_ioport(isadev, isa_s->io + 0, 0x60);
    isa_register_ioport(isadev, isa_s->io + 1, 0x64);

    s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
    s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
    qemu_register_reset(kbd_reset, s);
}

info mtree -f

  0000000000000060-0000000000000060 (prio 0, i/o): i8042-data
  0000000000000061-0000000000000061 (prio 0, i/o): pcspk
  0000000000000062-0000000000000063 (prio 0, i/o): io @0000000000000062
  0000000000000064-0000000000000064 (prio 0, i/o): i8042-cmd

info qtree

dev: i8042, id ""
  gpio-out "a20" 1
  isa irqs 1,12

观测了一下 guest 的 /proc/interrupts, isa 注册的 1 和 12 就是 linux irq

isa_get_irq [4]
isa_get_irq [7]
isa_get_irq [6]
isa_get_irq [1]
isa_get_irq [12]
isa_get_irq [14]
isa_get_irq [15]

isa 的中断就是直接和 X86MachineState 的 gsi 相同的:

pc_init1 ==> isa_bus_irqs(isa_bus, x86ms->gsi);

从 host 如何捕获数据 : gnome 或者 sdl 之类的用户态的库

#0  ps2_queue_noirq (s=0x555556a59700, b=17) at ../hw/input/ps2.c:195
#1  0x0000555555a487cd in ps2_queue (s=0x555556a59700, b=<optimized out>) at ../hw/input/ps2.c:217
#2  0x0000555555a488ab in ps2_put_keycode (opaque=0x555556a59700, keycode=<optimized out>) at ../hw/input/ps2.c:275
#3  0x0000555555a48b05 in ps2_keyboard_event (dev=0x555556a59700, src=<optimized out>, evt=<optimized out>) at ../hw/input/ps2.c:475
#4  0x0000555555900af8 in qemu_input_event_send_impl (src=0x55555653a140, evt=0x555557bab8b0) at ../ui/input.c:349
#5  0x000055555590145b in qemu_input_event_send_key (src=0x55555653a140, key=0x555556b70910, down=<optimized out>) at ../ui/input.c:422
#6  0x00005555559014b6 in qemu_input_event_send_key_qcode (src=<optimized out>, q=q@entry=Q_KEY_CODE_ALT, down=down@entry=true) at ../ui/input.c:444
#7  0x0000555555a2176a in qkbd_state_key_event (down=<optimized out>, qcode=Q_KEY_CODE_ALT, kbd=0x555556e18d90) at ../ui/kbd-state.c:102
#8  qkbd_state_key_event (kbd=0x555556e18d90, qcode=qcode@entry=Q_KEY_CODE_ALT, down=<optimized out>) at ../ui/kbd-state.c:40
#9  0x0000555555893f73 in gd_key_event (widget=<optimized out>, key=0x55555682fc00, opaque=0x5555569c09b0) at ../ui/gtk.c:1179
#10 0x00007ffff78334fb in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#11 0x00007ffff6fcd802 in g_closure_invoke () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#12 0x00007ffff6fe1814 in  () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#13 0x00007ffff6fec47d in g_signal_emit_valist () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#14 0x00007ffff6fed0f3 in g_signal_emit () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#15 0x00007ffff77ddc23 in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#16 0x00007ffff77ff5db in gtk_window_propagate_key_event () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#17 0x00007ffff7803873 in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#18 0x00007ffff78335ef in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#19 0x00007ffff6fcda56 in  () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#20 0x00007ffff6febdf1 in g_signal_emit_valist () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#21 0x00007ffff6fed0f3 in g_signal_emit () at /lib/x86_64-linux-gnu/libgobject-2.0.so.0
#22 0x00007ffff77ddc23 in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#23 0x00007ffff76991df in  () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#24 0x00007ffff769b3db in gtk_main_do_event () at /lib/x86_64-linux-gnu/libgtk-3.so.0
#25 0x00007ffff7383f79 in  () at /lib/x86_64-linux-gnu/libgdk-3.so.0
#26 0x00007ffff73b7106 in  () at /lib/x86_64-linux-gnu/libgdk-3.so.0
#27 0x00007ffff6ee217d in g_main_context_dispatch () at /lib/x86_64-linux-gnu/libglib-2.0.so.0
#28 0x0000555555d219a8 in glib_pollfds_poll () at ../util/main-loop.c:231
#29 os_host_main_loop_wait (timeout=<optimized out>) at ../util/main-loop.c:254
#30 main_loop_wait (nonblocking=nonblocking@entry=0) at ../util/main-loop.c:530
#31 0x0000555555ae96a1 in qemu_main_loop () at ../softmmu/runstate.c:725
#32 0x000055555582b4c2 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at ../softmmu/main.c:50

ps2_queue_noirq 的调用者可以从 guest ,也可以从 Host 到来,具体没有分析

host 如何注入中断的

isa_bus 和 gsi 就是直接连接到一起的:

    isa_bus_irqs(isa_bus, x86ms->gsi);

当想要注入中断的时候,调用函数 kbd_update_irq, 然后 gsi 转入到 pic / ioapic 中

guest 如何接受数据

#0  i8042_probe (dev=0xffff88810128f800) at drivers/input/serio/i8042.c:1539
#1  0xffffffff8168215a in platform_probe (_dev=0xffff88810128f810) at drivers/base/platform.c:1447
#2  0xffffffff8167ff86 in really_probe (dev=dev@entry=0xffff88810128f810, drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>) at drivers/base/dd.c:576
#3  0xffffffff816801c1 in driver_probe_device (drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>, dev=dev@entry=0xffff88810128f810) at drivers/base/dd.c:763
#4  0xffffffff8168048e in device_driver_attach (drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>, dev=dev@entry=0xffff88810128f810) at drivers/base/dd.c:1039
#5  0xffffffff816804f0 in __driver_attach (dev=0xffff88810128f810, data=0xffffffff825e6668 <i8042_driver+40>) at drivers/base/dd.c:1117
#6  0xffffffff8167e193 in bus_for_each_dev (bus=<optimized out>, start=start@entry=0x0 <fixed_percpu_data>, data=data@entry=0xffffffff825e6668 <i8042_driver+40>, fn=fn@
entry=0xffffffff816804a0 <__driver_attach>) at drivers/base/bus.c:305
#7  0xffffffff8167f975 in driver_attach (drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>) at drivers/base/dd.c:1133
#8  0xffffffff8167f342 in bus_add_driver (drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>) at drivers/base/bus.c:622
#9  0xffffffff81680d87 in driver_register (drv=drv@entry=0xffffffff825e6668 <i8042_driver+40>) at drivers/base/driver.c:171
#10 0xffffffff8168270e in __platform_driver_register (owner=<optimized out>, drv=0xffffffff825e6640 <i8042_driver>) at drivers/base/platform.c:894
#11 __platform_driver_probe (drv=0xffffffff825e6640 <i8042_driver>, probe=<optimized out>, module=<optimized out>) at drivers/base/platform.c:962
#12 0xffffffff81682baa in __platform_create_bundle (driver=driver@entry=0xffffffff825e6640 <i8042_driver>, probe=probe@entry=0xffffffff82b7b8f2 <i8042_probe>, res=res@e
ntry=0x0 <fixed_percpu_data>, n_res=n_res@entry=0, data=data@entry=0x0 <fixed_percpu_data>, size=size@entry=0, module=0x0 <fixed_percpu_data>) at drivers/base/platform.
c:1026
#13 0xffffffff82b7c3be in i8042_init () at drivers/input/serio/i8042.c:1629
#14 0xffffffff81000def in do_one_initcall (fn=0xffffffff82b7bf9b <i8042_init>) at init/main.c:1249
#15 0xffffffff82b3d26b in do_initcall_level (command_line=0xffff888100123400 "root", level=6) at ./include/linux/compiler.h:234
#16 do_initcalls () at init/main.c:1338
#17 do_basic_setup () at init/main.c:1358
#18 kernel_init_freeable () at init/main.c:1560
#19 0xffffffff81b7c249 in kernel_init (unused=<optimized out>) at init/main.c:1447
#20 0xffffffff810019b2 in ret_from_fork () at arch/x86/entry/entry_64.S:294
static struct platform_driver i8042_driver = {
	.driver		= {
		.name	= "i8042",
#ifdef CONFIG_PM
		.pm	= &i8042_pm_ops,
#endif
	},
	.remove		= i8042_remove,
	.shutdown	= i8042_shutdown,
};
struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.probe		= platform_probe,
	.remove		= platform_remove,
	.shutdown	= platform_shutdown,
	.dma_configure	= platform_dma_configure,
	.pm		= &platform_dev_pm_ops,
};

大多数设备都是挂载到总线上的,但是,并不是所有的,比如 i8042 之类1, 所以构建出来 platform 这种虚假的总线。 实际上,还是非常的让人迷惑,无论是物理设计 还是 qemu,i8042 都是接入到总线上才对啊。

初始化之类的东西就实在是太多了,具体内容就从 : i8042_interrupt 如何将数据到上层吧!

使用 strace bash 来分析 pselect 到底在做什么?

pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0, "e", 1)                         = 1
select(1, [0], NULL, [0], {tv_sec=0, tv_usec=0}) = 0 (Timeout)
write(2, "e", 1e)                        = 1
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0, "x", 1)                         = 1
select(1, [0], NULL, [0], {tv_sec=0, tv_usec=0}) = 0 (Timeout)
write(2, "x", 1x)                        = 1
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0, "i", 1)                         = 1
select(1, [0], NULL, [0], {tv_sec=0, tv_usec=0}) = 0 (Timeout)
write(2, "i", 1i)                        = 1
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0, "t", 1)                         = 1
select(1, [0], NULL, [0], {tv_sec=0, tv_usec=0}) = 0 (Timeout)
write(2, "t", 1t)                        = 1
pselect6(1, [0], NULL, NULL, NULL, {[], 8}) = 1 (in [0])
read(0, "\r", 1)                        = 1
write(2, "\n", 1)

在内核显示出来 pollwake 的机制

[   36.794135] Call Trace:
[   36.794140]  <IRQ>
[   36.794143]  dump_stack+0x64/0x7c
[   36.794150]  pollwake+0x2a/0x90
[   36.794157]  ? check_preempt_curr+0x3a/0x70
[   36.794162]  ? ttwu_do_wakeup.isra.0+0xd/0xd0
[   36.794167]  __wake_up_common+0x75/0x140
[   36.794172]  __wake_up_common_lock+0x77/0xb0
[   36.794178]  evdev_events+0x7c/0xa0
[   36.794184]  input_to_handler+0x90/0xf0
[   36.794190]  input_pass_values.part.0+0x119/0x140
[   36.794196]  input_handle_event+0x20e/0x5f0
[   36.794203]  input_event+0x4a/0x70
[   36.794208]  atkbd_interrupt+0x47f/0x640
[   36.794213]  serio_interrupt+0x42/0x90
[   36.794218]  i8042_interrupt+0x146/0x250
[   36.794223]  __handle_irq_event_percpu+0x38/0x150
[   36.794229]  handle_irq_event_percpu+0x2c/0x80
[   36.794234]  handle_irq_event+0x23/0x50
[   36.794252]  handle_edge_irq+0x79/0x190
[   36.794257]  __common_interrupt+0x39/0x90
[   36.794277]  common_interrupt+0x76/0xa0
[   36.794283]  </IRQ>
[   36.794285]  asm_common_interrupt+0x1e/0x40

evdev_events 的处理中间,其挂载到 evdev->client_list evdev_pass_values

#0  evdev_open (inode=0xffff888101283da8, file=0xffff8881015bad00) at drivers/input/evdev.c:472
#1  0xffffffff811e7378 in chrdev_open (inode=inode@entry=0xffff888101283da8, filp=filp@entry=0xffff8881015bad00) at fs/char_dev.c:414
#2  0xffffffff811de5c6 in do_dentry_open (f=f@entry=0xffff8881015bad00, inode=0xffff888101283da8, open=0xffffffff811e72e0 <chrdev_open>, open@entry=0x0 <fixed_percpu_da
ta>) at fs/open.c:826
#3  0xffffffff811dffb9 in vfs_open (path=path@entry=0xffffc90000507db0, file=file@entry=0xffff8881015bad00) at ./include/linux/dcache.h:555
#4  0xffffffff811f2c41 in do_open (op=0xffffc90000507ed4, op=0xffffc90000507ed4, file=0xffff8881015bad00, nd=0xffffc90000507db0) at fs/namei.c:3361
#5  path_openat (nd=nd@entry=0xffffc90000507db0, op=op@entry=0xffffc90000507ed4, flags=flags@entry=65) at fs/namei.c:3494
#6  0xffffffff811f461d in do_filp_open (dfd=dfd@entry=-100, pathname=pathname@entry=0xffff8881001f1000, op=op@entry=0xffffc90000507ed4) at fs/namei.c:3521
#7  0xffffffff811def2d in do_sys_openat2 (dfd=-100, filename=<optimized out>, how=how@entry=0xffffc90000507f18) at fs/open.c:1187
#8  0xffffffff811e03ff in do_sys_open (dfd=<optimized out>, filename=<optimized out>, flags=<optimized out>, mode=<optimized out>) at fs/open.c:1203
#9  0xffffffff81b79150 in do_syscall_64 (nr=<optimized out>, regs=0xffffc90000507f58) at arch/x86/entry/common.c:47
#10 0xffffffff81c0007c in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:112
#11 0x0000000000000000 in ?? ()
 */

这说明,其实当前打开设备是: evdev_open, 说明 evdev 也是存在一个 cdev

分析 input device 的注册过程

分析一下 evdev_connect

static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);

读取相当简单

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。

  1. https://events19.linuxfoundation.org/wp-content/uploads/2017/12/Introduction-to-Linux-Kernel-Driver-Programming-Michael-Opdenacker-Bootlin-.pdf