- // 用于区分0xfe,0xfa等指令返回码。
- if (scancode >= 0xFA) {
+ /*
+ * 判断键盘是否处在指令发送状态,防止误触发。(伪输入中断)
+ * 这是因为我们需要向ps/2设备发送指令(比如控制led灯),而指令会有返回码。
+ * 这就会有可能导致ps/2控制器在受到我们的命令后(在ps2_process_cmd中),
+ * 产生IRQ#1中断(虽然说这种情况取决于底层BIOS实现,但还是会发生,比如QEMU和bochs)。
+ * 所以这就是说,当IRQ#1中断产生时,我们的CPU正处在另一个ISR中。这样就会导致所有的外部中断被缓存在APIC内部的
+ * FIFO队列里,进行排队等待(APIC长度为二的队列 {IRR, TMR};参考 Intel Manual Vol.3A 10.8.4)
+ * 那么当ps2_process_cmd执行完后(内嵌在#APIC_TIMER_IV),CPU返回EOI给APIC,APIC紧接着将排在队里的IRQ#1发送给CPU
+ * 造成误触发。也就是说,我们此时读入的scancode实则上是上一个指令的返回代码。
+ *
+ * Problem 1 (Fixed):
+ * 但是这种方法有个问题,那就是,假若我们的某一个命令失败了一次,ps/2给出0xfe,我们重传,ps/2收到指令并给出0xfa。
+ * 那么这样一来,将会由两个连续的IRQ#1产生。而APIC是最多可以缓存两个IRQ,于是我们就会漏掉一个IRQ,依然会误触发。
+ * Solution:
+ * 累加掩码 ;)
+ *
+ * Problem 2:
+ * + 这种累加掩码的操作是基于只有一号IRQ产生的中断的假设,万一中间夹杂了别的中断?Race Condition!
+ * + 不很稳定x1,假如连续4次发送失败,那么就会导致累加的掩码上溢出,从而导致下述判断失败。
+ */
+#ifdef KBD_ENABLE_SPIRQ_FIX
+ if ((kbd_state.state & 0xc0)) {
+ kbd_state.state -= KBD_STATE_CMDPROCS;
+