keyboard.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. #include "keyboard.h"
  2. #include "stdint.h"
  3. #include "ioqueue.h"
  4. #include "../kernel/global.h"
  5. #include "../lib/kernel/io.h"
  6. #include "../lib/kernel/print.h"
  7. #include "../kernel/interrupt.h"
  8. /*用转义字符定义部分控制字符*/
  9. #define esc '\033' // 八进制表示字符,也可以用十六进制'\x1b'
  10. #define backspace '\b'
  11. #define enter '\r'
  12. #define tab '\t'
  13. #define delete '\177' // 八进制表示字符,也可用十六进制 '\x7f'
  14. #define char_invisible 0
  15. #define ctrl_l_char char_invisible
  16. #define ctrl_r_char char_invisible
  17. #define shift_l_char char_invisible
  18. #define shift_r_char char_invisible
  19. #define alt_l_char char_invisible
  20. #define alt_r_char char_invisible
  21. #define caps_lock_char char_invisible
  22. /*定义控制字符的通码和断码*/
  23. #define shift_l_make 0x2a
  24. #define shift_r_make 0x36
  25. #define alt_l_make 0x38
  26. #define alt_r_make 0xe038
  27. #define alt_r_break 0xe0b8
  28. #define ctrl_l_make 0x1d
  29. #define ctrl_r_make 0xe01d
  30. #define ctrl_r_break 0xe09d
  31. #define caps_lock_make 0x3a
  32. // #define mac_command_make 0xe05b
  33. /*定义以下变量记录相应键是否按下的状态, ext_scancode 用于记录 makecode 是否以 0xe0 开头*/
  34. static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;
  35. /*定义键盘缓冲区*/
  36. struct ioqueue keyboard_buf;
  37. /*以通码 make_code 为索引的二维数组*/
  38. static char keymap[][2] = {
  39. /*扫描码未与 shift 组合*/
  40. /* 0x00 */ {0, 0},
  41. /* 0x01 */ {esc, esc},
  42. /* 0x02 */ {'1', '!'},
  43. /* 0x03 */ {'2', '@'},
  44. /* 0x04 */ {'3', '#'},
  45. /* 0x05 */ {'4', '$'},
  46. /* 0x06 */ {'5', '%'},
  47. /* 0x07 */ {'6', '^'},
  48. /* 0x08 */ {'7', '&'},
  49. /* 0x09 */ {'8', '*'},
  50. /* 0x0A */ {'9', '('},
  51. /* 0x0B */ {'0', ')'},
  52. /* 0x0C */ {'-', '_'},
  53. /* 0x0D */ {'=', '+'},
  54. /* 0x0E */ {backspace, backspace},
  55. /* 0x0F */ {tab, tab},
  56. /* 0x10 */ {'q', 'Q'},
  57. /* 0x11 */ {'w', 'W'},
  58. /* 0x12 */ {'e', 'E'},
  59. /* 0x13 */ {'r', 'R'},
  60. /* 0x14 */ {'t', 'T'},
  61. /* 0x15 */ {'y', 'Y'},
  62. /* 0x16 */ {'u', 'U'},
  63. /* 0x17 */ {'i', 'I'},
  64. /* 0x18 */ {'o', 'O'},
  65. /* 0x19 */ {'p', 'P'},
  66. /* 0x1A */ {'[', '{'},
  67. /* 0x1B */ {']', '}'},
  68. /* 0x1C */ {enter, enter},
  69. /* 0x1D */ {ctrl_l_char, ctrl_l_char},
  70. /* 0x1E */ {'a', 'A'},
  71. /* 0x1F */ {'s', 'S'},
  72. /* 0x20 */ {'d', 'D'},
  73. /* 0x21 */ {'f', 'F'},
  74. /* 0x22 */ {'g', 'G'},
  75. /* 0x23 */ {'h', 'H'},
  76. /* 0x24 */ {'j', 'J'},
  77. /* 0x25 */ {'k', 'K'},
  78. /* 0x26 */ {'l', 'L'},
  79. /* 0x27 */ {';', ':'},
  80. /* 0x28 */ {'\'', '"'},
  81. /* 0x29 */ {'`', '~'},
  82. /* 0x2A */ {shift_l_char, shift_l_char},
  83. /* 0x2B */ {'\\', '|'},
  84. /* 0x2C */ {'z', 'Z'},
  85. /* 0x2D */ {'x', 'X'},
  86. /* 0x2E */ {'c', 'C'},
  87. /* 0x2F */ {'v', 'V'},
  88. /* 0x30 */ {'b', 'B'},
  89. /* 0x31 */ {'n', 'N'},
  90. /* 0x32 */ {'m', 'M'},
  91. /* 0x33 */ {',', '<'},
  92. /* 0x34 */ {'.', '>'},
  93. /* 0x35 */ {'/', '?'},
  94. /* 0x36 */ {shift_r_char, shift_r_char},
  95. /* 0x37 */ {'*', '*'},
  96. /* 0x38 */ {alt_l_char, alt_l_char},
  97. /* 0x39 */ {' ', ' '},
  98. /* 0x3A */ {caps_lock_char, caps_lock_char}
  99. /* 其它按键暂不处理 */
  100. };
  101. // 键盘中断处理程序
  102. static void intr_keyboard_handler(void);
  103. void keyboard_init(void)
  104. {
  105. put_str("keyboard init start\n");
  106. // 1. 初始化键盘缓冲区
  107. ioqueue_init(&keyboard_buf);
  108. // 2. 注册键盘中断处理程序
  109. register_handler(0x21, intr_keyboard_handler);
  110. put_str("keyboard init done\n");
  111. return;
  112. }
  113. // 键盘中断处理程序
  114. static void intr_keyboard_handler(void)
  115. {
  116. /* 这次中断发生之前的上一次中断,以下任意三个键是否有按下 */
  117. // bool ctrl_down_last = ctrl_status;
  118. bool shift_down_last = shift_status;
  119. bool caps_lock_last = caps_lock_status;
  120. bool break_code;
  121. uint16_t scancode = inb(KBD_DATA_PORT); // 必须要读取输出缓冲寄存器,否则 8042 不再继续响应键盘中断
  122. /* 若 scancode 是 e0 开头的,表示此键的按下将产生多个扫描码,
  123. 所以马上结束此次中断处理函数,等待下一个扫描码进来*/
  124. if (scancode == 0xe0)
  125. {
  126. ext_scancode = true;
  127. return;
  128. }
  129. /*如果上次是 0xe0 开头的,将扫描码合并 */
  130. if (ext_scancode)
  131. {
  132. scancode = ((0xe000) | scancode);
  133. ext_scancode = false;
  134. }
  135. /* 若 scancode 高 bit 为 1,表示此键弹起(断码) */
  136. break_code = ((scancode & 0x0080) != 0);
  137. if (break_code)
  138. {
  139. // 由于 ctrol_r 和 ctrol_l 的 make_code 和 break_code 都是两字节
  140. // 所以可用下面的方法取 make_code
  141. // !多字节的扫描码暂不处理 TODO:
  142. uint16_t make_code = (scancode &= 0xff7f);
  143. /* 若是任意以下三个键弹起了,将状态设置为 false */
  144. if (make_code == ctrl_l_make || make_code == ctrl_r_make)
  145. {
  146. ctrl_status = false;
  147. }
  148. else if (make_code == shift_l_make || make_code == shift_r_make)
  149. {
  150. shift_status = false;
  151. }
  152. else if (make_code == alt_l_make || make_code == alt_r_make)
  153. {
  154. alt_status = false;
  155. }
  156. /* caps_lock 不是弹起后关闭,所以需要单独处理*/
  157. return; // 直接返回结束此次中断处理程序
  158. }
  159. /* 若为通码 makecode,只处理数组中定义的键及 alt_right 和 ctrl 键*/
  160. else if ((scancode > 0x00 && scancode < 0x3b) || (scancode == alt_r_make) || (scancode == ctrl_r_make))
  161. {
  162. bool shift = false; // 判断是否与 shift 组合,用来在一维数组中索引对应的字符
  163. /** 代表两个字母的键
  164. * 0x0e 数字 '0'~'9', 字符'-', 字符 '='
  165. * 0x29 字符 '`'
  166. * 0x1a 字符 '['
  167. * 0x1b 字符 ']'
  168. * 0x2b 字符 '\\'
  169. * 0x27 字符 ';'
  170. * 0x28 字符 '\'
  171. * 0x33 字符 ','
  172. * 0x34 字符 '.'
  173. * 0x35 字符 '/'
  174. * * */
  175. if ((scancode < 0x0e) || (scancode == 0x29) ||
  176. (scancode == 0x1a) || (scancode == 0x1b) ||
  177. (scancode == 0x2b) || (scancode == 0x27) ||
  178. (scancode == 0x28) || (scancode == 0x33) ||
  179. (scancode == 0x34) || (scancode == 0x35))
  180. {
  181. if (shift_down_last)
  182. shift = true; // 如果同时按下了 shift 键
  183. }
  184. else // 默认为字母键
  185. {
  186. if (shift_down_last && caps_lock_last)
  187. shift = false; // 同时按下 shift 和 capslock 键
  188. else if (shift_down_last || caps_lock_last)
  189. shift = true; // 按下 shift 键或者 capslock 键
  190. else
  191. shift = false;
  192. }
  193. // 将扫描码的高字节置 0,主要针对高字节是 e0 的扫描码
  194. uint8_t index = (scancode &= 0x00ff);
  195. char cur_chr = keymap[index][shift];
  196. /* 只处理 ASCII 码不为 0 的键 */
  197. if (cur_chr)
  198. {
  199. if (!ioq_full(&keyboard_buf))
  200. {
  201. put_char(cur_chr); // 打印字符
  202. ioq_putchar(&keyboard_buf, cur_chr); // 加入缓冲区
  203. }
  204. return;
  205. }
  206. /* 记录本次是否按下了下面几类控制键之一,供下次键入判断组合键 */
  207. if (scancode == alt_l_make || scancode == alt_r_make)
  208. alt_status = true;
  209. else if (scancode == shift_l_make || scancode == shift_r_make)
  210. shift_status = true;
  211. else if (scancode == ctrl_l_make || scancode == ctrl_r_make)
  212. ctrl_status = true;
  213. else if (scancode == caps_lock_make)
  214. // 不管之前是否有按下 caps_lock 键,当再次按下时则状态取反
  215. caps_lock_status = !caps_lock_status;
  216. }
  217. else
  218. {
  219. put_str("\n\nUnknown key. make key: 0x");
  220. put_int(scancode);
  221. put_char('\n');
  222. }
  223. return;
  224. }