i8042 : 键盘
i8042
- https://wiki.osdev.org/%228042%22_PS/2_Controller
- 在哪里可以找到 intel 的手册
- i8042 为什么在 /proc/interrupts 下存在两个项 ? (同时有键盘和鼠标)
- drivers/input/serio/i8042.c
- drivers/input/keyboard/atkbd.c
TODO
- 那么 pci 设备是怎么处理中断的呀 ?
- i8042 根本不是一个 pci 设备
整理
pc_basic_device_initi8257_dma_initpc_superio_initi8042 = isa_create_simple(isa_bus, "i8042");
应该是,首先发送一个中断,然后驱动读去吧。
这个注册只能说是相当的直白了:
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 注册的,为什么内核就确定是就是 1 和 12
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_get_irq 上为什么会注册 6 和 7
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
-
创建了在 /dev/input/ 下存在一堆 event 设备
input_register_device: 将设备注册到 input_dev_list 上input_register_handler: 将 handler 注册到 input_handler_list 上
static LIST_HEAD(input_dev_list);
static LIST_HEAD(input_handler_list);
- 实际上,handler 和 input device 都很多
- 不过从上面的 backtrace 分析,handler 使用的 evdev
- 当 input device 和 handler 链接的时候,会创建 /dev/input/event{num} 出来
evdev_events调用evdev_attach_client让该设备挂载到上面去- 当存在中断到来的时候,这些 client 都会接受到数据
读取相当简单
- 但是不知道为什么拷贝是一个 struct
input_event结构体>>> bt #0 input_event_to_user (buffer=buffer@entry=0x7ffdd8102980 "\023\b\236`", event=event@entry=0xffffc90000507e50) at drivers/input/input-compat.c:40 #1 0xffffffff817cc63b in evdev_read (file=0xffff888101639100, buffer=0x7ffdd8102980 "\023\b\236`", count=24, ppos=<optimized out>) at drivers/input/evdev.c:588 #2 0xffffffff811e2ed6 in vfs_read (file=file@entry=0xffff888101639100, buf=buf@entry=0x7ffdd8102980 "\023\b\236`", count=count@entry=24, pos=pos@entry=0x0 <fixed_percp u_data>) at fs/read_write.c:494 #3 0xffffffff811e32e2 in ksys_read (fd=<optimized out>, buf=0x7ffdd8102980 "\023\b\236`", count=24) at fs/read_write.c:634 #4 0xffffffff81b79150 in do_syscall_64 (nr=<optimized out>, regs=0xffffc90000507f58) at arch/x86/entry/common.c:47 #5 0xffffffff81c0007c in entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:112
本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。
-
https://events19.linuxfoundation.org/wp-content/uploads/2017/12/Introduction-to-Linux-Kernel-Driver-Programming-Michael-Opdenacker-Bootlin-.pdf ↩