这一节主要主要是键盘。
所需要的文件在Github:https://github.com/yongkangluo/Ubuntu20.04OS/tree/main/Files/Lec7-ExternalInterrupt
- 初始化8042控制器
- 将键盘的硬件数据抽象封装
- 将封装好的键盘事件传送到上层
在初始化8042时,不仅仅需要发送命令,同时要等待返回代码。
static uint8_t ps2_issue_cmd(char cmd, uint16_t arg) {
// 发送命令
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, cmd, arg);
char result;
// 等待PS/2控制器返回。通过轮询(polling)状态寄存器的 bit 0
// 如置位,则表明返回代码此时就在 0x60 IO口上等待读取。
// 判断是否成功
while(!((result = io_inb(PS2_PORT_CTRL_STATUS)) & PS2_STATUS_OFULL));
return io_inb(PS2_PORT_ENC_CMDREG);
}
static void ps2_post_cmd(uint8_t port, char cmd, uint16_t arg) {
char result;
// 等待PS/2输入缓冲区清空,这样我们才可以写入命令
while((result = io_inb(PS2_PORT_CTRL_STATUS)) & PS2_STATUS_IFULL);
io_outb(port, cmd);
if (!(arg & PS2_NO_ARG)) {
// 所有参数一律通过0x60传入。
io_outb(PS2_PORT_ENC_CMDREG, (uint8_t)(arg & 0x00ff));
}
}
//关中断
cpu_disable_interrupt();
// 1、禁用任何的PS/2设备
// 读取20 读取配置字节
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT1_DISABLE, PS2_NO_ARG);
// 禁用0 1
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT2_DISABLE, PS2_NO_ARG);
// 2、清空控制器缓冲区
io_inb(PS2_PORT_ENC_DATA);
char result;
// 3、屏蔽所有PS/2设备(端口1&2)IRQ,并且禁用键盘键码转换功能
result = ps2_issue_cmd(PS2_CMD_READ_CFG, PS2_NO_ARG);
result = result & ~(PS2_CFG_P1INT | PS2_CFG_P2INT | PS2_CFG_TRANSLATION);
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_WRITE_CFG, result);
// 4、控制器自检
result = ps2_issue_cmd(PS2_CMD_SELFTEST, PS2_NO_ARG);
if (result != PS2_RESULT_TEST_OK) {
kprintf(KERROR "Controller self-test failed.");
goto done;
}
// 5、设备自检(端口1自检,通常是我们的键盘)
result = ps2_issue_cmd(PS2_CMD_SELFTEST_PORT1, PS2_NO_ARG);
if (result != 0) {
kprintf(KERROR "Interface test on port 1 failed.");
goto done;
}
// 6、开启位于端口1的 IRQ,并启用端口1。不用理会端口2,那儿一般是鼠标。
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_PORT1_ENABLE, PS2_NO_ARG);
result = ps2_issue_cmd(PS2_CMD_READ_CFG, PS2_NO_ARG);
result = result | PS2_CFG_P1INT;
ps2_post_cmd(PS2_PORT_CTRL_CMDREG, PS2_CMD_WRITE_CFG, result);
// 至此,PS/2控制器和设备已完成初始化,可以正常使用。
键盘事件被封装成:
struct kdb_keyinfo_pkt {
kbd_scancode_t scancode;//扫描码
kbd_keycode_t keycode;//键码
kbd_kstate_t state;//键盘状态
time_t timestamp;//时间戳
};
注意:大小写锁定,shift、ctrl键等,不会被设置成信报,而是设置为状态。
设计要点:
- 哪些键是按下的,哪些是释放的
- 一些开关状态,大小写锁定
- 一些短暂状态,识别组合键
- 跟踪shift
- 一些扫描码由两个字节构成
向上层报告:
利用一个有界的缓存队列。使用一个读指针和写指针。