• [自制操作系统] 第17回 编写键盘驱动


    目录
    一、前景回顾
    二、实现键盘输入的中断函数
    三、编写键盘驱动
    四、实现环形输入缓冲区
    五、运行测试

     

    一、前景回顾

      上一回我们完成了锁的实现,并且利用锁优化了终端输出函数。这一回我们来实现键盘的输入,为后面的用户交互功能打好基础。

    二、实现键盘输入的中断函数

      首先我们需要知道键盘是属于外设,所以对应的中断属于外部中断。在讲中断那一章节时,我们知道了外部中断的处理流程,不过对于键盘的输入中断,还需要增加一点点东西。

       

      8048是键盘上的芯片,其主要任务就是监控哪个键被按下,一旦有按键信息,8048就将按键信息传递给键盘控制器8042(8042通常是Intel 8042或兼容芯片,集成在主机内部的主板上),再由8042发送中断信号给8259A。最重要的一点是,键盘的中断号。

       

      我们可以看到键盘对应的是IR1口,这个是硬件上决定的,所以我们无法更改。除此之外,在我们的程序中,我们将IR0口的中断号设置为0x20,后面依次递增,所以我们可以知道键盘的中断号为0x21。这里我们不管按键信息如何,我们只需要知道一旦有按键按下,就会有中断触发,所以我们尝试写一下按键的中断处理函数。

      在project/kernel目录下新建keyboard.c、keyboard.h文件,除此之外还需要修改interrupt.c文件。

     1 #include "keyboard.h"
     2 #include "print.h"
     3 #include "interrupt.h"
     4 #include "io.h"
     5 #include "global.h"
     6 #include "stdint.h"
     7 
     8 #define KBD_BUF_PORT  0x60
     9 
    10 static void intr_keyboard_handler(void)
    11 {
    12     put_str("k\n");
    13     inb(KBD_BUF_PORT);
    14 }
    15 
    16 /*键盘初始化*/
    17 void keyboard_init(void)
    18 {
    19     put_str("keyboard init start\n");
    20     register_handler(0x21, intr_keyboard_handler);
    21     put_str("keyboard init done\n");
    22 }
    keyboard.c
    1 #ifndef  __KERNEL_KEYBOARD_H
    2 #define  __KERNEL_KEYBOARD_H
    3 
    4 void keyboard_init(void);
    5 static void intr_keyboard_handler(void);
    6 #endif
    keyboard.h
     1 ...
     2 
     3 /* 初始化可编程中断控制器8259A */
     4 static void pic_init(void) {
     5    /* 初始化主片 */
     6    outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
     7    outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
     8    outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. 
     9    outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI
    10 
    11    /* 初始化从片 */
    12    outb (PIC_S_CTRL, 0x11);    // ICW1: 边沿触发,级联8259, 需要ICW4.
    13    outb (PIC_S_DATA, 0x28);    // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
    14    outb (PIC_S_DATA, 0x02);    // ICW3: 设置从片连接到主片的IR2引脚
    15    outb (PIC_S_DATA, 0x01);    // ICW4: 8086模式, 正常EOI
    16 
    17    /*只打开键盘中断*/
    18    outb (PIC_M_DATA, 0xfd);
    19    outb (PIC_S_DATA, 0xff);
    20 
    21    put_str("pic_init done\n");
    22 }
    23 
    24 ...
    interrupt.c

      最后编译运行,可以看到我们一旦按下按键,屏幕便会打印信息,而且释放按键也会打印信息。当然这是后面需要讲解的内容,总之到现在,我们已经成功实现了按键的中断处理函数。

       

    三、编写键盘驱动

      现在来说说为什么按下按键和释放按键都会触发中断。其实这是硬件所决定的,一个键的状态要么是按下,要么是弹起,因此一个按键有两个编码,按键被按下时的编码是通码,按键被释放时的编码是断码。

      无论是按下键或是松开键,当键的状态改变后,键盘中的8048芯片把按键对应的扫描码(通码或者断码)发送到主板上的8042芯片,由8042处理后保存在自己的寄存器中,然后向8259A发送中断信号,这样处理器便去执行键盘中断处理程序,将8042处理过的扫描码从它的寄存器中读取出来,随后将扫描码转换成对应的ASCII码。

      所以我们的函数中需要自己完善这么一个映射关系,也就是扫描码到ASCII码的映射。这里就偷懒直接抄书上的,我也没有去仔细看了,总之能知道整个流程就行了。

      所以进一步完善我们的keyboard.c文件如下。

      1 #include "keyboard.h"
      2 #include "print.h"
      3 #include "interrupt.h"
      4 #include "io.h"
      5 #include "global.h"
      6 #include "stdint.h"
      7 #include "ioqueue.h"
      8 
      9 #define KBD_BUF_PORT  0x60
     10 
     11 /*用转移字符定义部分控制字符*/
     12 #define esc        '\033'
     13 #define backspace  '\b'
     14 #define tab        '\t'
     15 #define enter      '\r'
     16 #define delete     '\0177'
     17 
     18 /*以下不可见字符一律为0*/
     19 #define char_invisible  0
     20 #define ctrl_l_char     char_invisible
     21 #define ctrl_r_char     char_invisible
     22 #define shift_l_char    char_invisible
     23 #define shift_r_char    char_invisible
     24 #define alt_l_char      char_invisible
     25 #define alt_r_char      char_invisible
     26 #define caps_lock_char  char_invisible
     27 
     28 /*定义控制字符的通码和断码*/
     29 #define shift_l_make     0x2a
     30 #define shift_r_make     0x36
     31 #define alt_l_make       0x38
     32 #define alt_r_make       0xe038
     33 #define alt_r_break      0xe0b8
     34 #define ctrl_l_make      0x1d
     35 #define ctrl_r_make      0xe01d
     36 #define ctrl_r_break     0xe09d
     37 #define caps_lock_make   0x3a
     38  
     39 /*定义以下变量记录相应键是否按下的状态*/
     40 static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;
     41 
     42 
     43 /*以通码make_code为索引的二维数组*/
     44 static char keymap[][2] = {
     45 /*扫描码未与shift组合*/
     46 /* 0x00 */    {0,    0},        
     47 /* 0x01 */    {esc,    esc},        
     48 /* 0x02 */    {'1',    '!'},        
     49 /* 0x03 */    {'2',    '@'},        
     50 /* 0x04 */    {'3',    '#'},        
     51 /* 0x05 */    {'4',    '$'},        
     52 /* 0x06 */    {'5',    '%'},        
     53 /* 0x07 */    {'6',    '^'},        
     54 /* 0x08 */    {'7',    '&'},        
     55 /* 0x09 */    {'8',    '*'},        
     56 /* 0x0A */    {'9',    '('},        
     57 /* 0x0B */    {'0',    ')'},        
     58 /* 0x0C */    {'-',    '_'},        
     59 /* 0x0D */    {'=',    '+'},        
     60 /* 0x0E */    {backspace, backspace},    
     61 /* 0x0F */    {tab,    tab},        
     62 /* 0x10 */    {'q',    'Q'},        
     63 /* 0x11 */    {'w',    'W'},        
     64 /* 0x12 */    {'e',    'E'},        
     65 /* 0x13 */    {'r',    'R'},        
     66 /* 0x14 */    {'t',    'T'},        
     67 /* 0x15 */    {'y',    'Y'},        
     68 /* 0x16 */    {'u',    'U'},        
     69 /* 0x17 */    {'i',    'I'},        
     70 /* 0x18 */    {'o',    'O'},        
     71 /* 0x19 */    {'p',    'P'},        
     72 /* 0x1A */    {'[',    '{'},        
     73 /* 0x1B */    {']',    '}'},        
     74 /* 0x1C */    {enter,  enter},
     75 /* 0x1D */    {ctrl_l_char, ctrl_l_char},
     76 /* 0x1E */    {'a',    'A'},        
     77 /* 0x1F */    {'s',    'S'},        
     78 /* 0x20 */    {'d',    'D'},        
     79 /* 0x21 */    {'f',    'F'},        
     80 /* 0x22 */    {'g',    'G'},        
     81 /* 0x23 */    {'h',    'H'},        
     82 /* 0x24 */    {'j',    'J'},        
     83 /* 0x25 */    {'k',    'K'},        
     84 /* 0x26 */    {'l',    'L'},        
     85 /* 0x27 */    {';',    ':'},        
     86 /* 0x28 */    {'\'',    '"'},        
     87 /* 0x29 */    {'`',    '~'},        
     88 /* 0x2A */    {shift_l_char, shift_l_char},    
     89 /* 0x2B */    {'\\',    '|'},        
     90 /* 0x2C */    {'z',    'Z'},        
     91 /* 0x2D */    {'x',    'X'},        
     92 /* 0x2E */    {'c',    'C'},        
     93 /* 0x2F */    {'v',    'V'},        
     94 /* 0x30 */    {'b',    'B'},        
     95 /* 0x31 */    {'n',    'N'},        
     96 /* 0x32 */    {'m',    'M'},        
     97 /* 0x33 */    {',',    '<'},        
     98 /* 0x34 */    {'.',    '>'},        
     99 /* 0x35 */    {'/',    '?'},
    100 /* 0x36    */    {shift_r_char, shift_r_char},    
    101 /* 0x37 */    {'*',    '*'},        
    102 /* 0x38 */    {alt_l_char, alt_l_char},
    103 /* 0x39 */    {' ',    ' '},        
    104 /* 0x3A */    {caps_lock_char, caps_lock_char}
    105 };
    106 
    107 /*键盘中断处理程序*/
    108 static void intr_keyboard_handler(void)
    109 {
    110     bool ctrl_down_last = ctrl_status;
    111     bool shift_down_last = shift_status;
    112     bool caps_lock_last = caps_lock_status;
    113 
    114 
    115     bool break_code;
    116     uint16_t scancode = inb(KBD_BUF_PORT);
    117 
    118     /*若扫描码scancode是以e0开头的,表示此键的按下将产生多个扫描码
    119     所以马上结束此次中断处理函数,等待下一个扫描码进入*/
    120     if (scancode == 0xe0) {
    121         ext_scancode = true;
    122         return;
    123     }
    124 
    125     /*如果赏赐是以0xe0开头的,将扫描码合并*/
    126     if (ext_scancode) {
    127         scancode = ((0xe00) | scancode);
    128         ext_scancode = false;
    129     }
    130 
    131     break_code = ((scancode & 0x0080) != 0);
    132     if (break_code) {
    133         uint16_t make_code = (scancode &= 0xff7f); //多字节不处理
    134         if(make_code == ctrl_l_make || make_code == ctrl_r_make) {
    135             ctrl_status = false;
    136         } 
    137         else if (make_code == shift_l_make || make_code == shift_r_make) {
    138             shift_status = false;
    139         }
    140         else if (make_code == alt_l_make || make_code == alt_r_make) {
    141             alt_status = false;
    142         }
    143         return;
    144     }
    145     else if((scancode > 0x00 && scancode < 0x3b) || (scancode == alt_r_make) || (scancode == ctrl_r_make)) {
    146         bool shift = false; //先默认设置成false
    147         if ((scancode < 0x0e) || (scancode == 0x29) || (scancode == 0x1a) || \
    148         (scancode == 0x1b) || (scancode == 0x2b) || (scancode == 0x27) || \
    149         (scancode == 0x28) || (scancode == 0x33) || (scancode == 0x34) || \
    150         (scancode == 0x35))
    151         {
    152             if (shift_down_last) {
    153                 shift = true;
    154             }    
    155         } else {
    156             if (shift_down_last && caps_lock_last) {
    157                 shift = false; //效果确实是这样子的 我试了一下
    158             }    
    159             else if(shift_down_last || caps_lock_last) {
    160                 shift = true; //其中任意一个都是大写的作用
    161             } 
    162             else shift = false;
    163         }
    164         
    165         uint8_t index = (scancode & 0x00ff);
    166         char cur_char = keymap[index][shift];
    167 
    168     put_char(cur_char);
    169     }
    170     return;
    171 }
    172 
    173 
    174 /*键盘初始化*/
    175 void keyboard_init(void)
    176 {
    177     put_str("keyboard init start\n");
    178     register_handler(0x21, intr_keyboard_handler);
    179     put_str("keyboard init done\n");
    180 }
    keyboard.c

       

      此时可以发现,我们在键盘上按下键,屏幕上能相应地输出字符。

    四、实现环形输入缓冲区

      虽然我们已经实现了键盘驱动,但是目前能实现的功能仅仅是在屏幕上输出我们所按下的按键,但是并没有什么实用的地方。我们在键盘上操作是为了能和系统进行交互,而交互过程一般都是键入各种shell命令,然后shell解析并且执行。

      所以我们需要实现一个缓冲区,在按键的中断处理函数中将输入的按键信息保存在缓冲区中,将来实现的shell进程在该缓冲区中读取数据并且输出到屏幕上,等到我们按下了回车后,就将前面读取到的字符解析去处理。虽然我们还没有实现shell进程,但是我们可以新建线程来读取数据,测试缓冲区的功能。

      所以下面的代码便是实现缓冲区,在project/kernel目录下新建ioqueue.c和ioqueue.h文件。

     1 #include "ioqueue.h"
     2 #include "interrupt.h"
     3 #include "global.h"
     4 #include "debug.h"
     5 #include "thread.h"
     6 #include "stdbool.h"
     7 #include "stddef.h"
     8 
     9 /*初始化io队列ioq*/
    10 void ioqueue_init(struct ioqueue *ioq)
    11 {
    12     lock_init(&ioq->lock);
    13     ioq->consumer = ioq->producer = NULL;
    14     ioq->head = ioq->tail = 0;      /*队列的首尾指针都指向缓冲区数组的第0个位置*/      
    15 }
    16 
    17 /*返回pos在缓冲区的下一个位置*/
    18 static int32_t next_pos(int32_t pos)
    19 {
    20     return ((pos + 1) % bufsize);
    21 }
    22 
    23 /*判断队列是否已满*/
    24 bool ioq_full(struct ioqueue *ioq)
    25 {
    26     //return ((ioq->head + 1) % bufsize == ioq->tail) ? true : false;
    27     ASSERT(intr_get_status() == INTR_OFF);
    28     return next_pos(ioq->head) == ioq->tail;
    29 }
    30 
    31 /*判断队列是否为空*/
    32 bool ioq_empty(struct ioqueue *ioq)
    33 {
    34     ASSERT(intr_get_status() == INTR_OFF);
    35     return ioq->head == ioq->tail;
    36 }
    37 
    38 /*使当前生产者或消费者在此缓冲区上等待*/
    39 static void ioq_wait(struct task_struct **waiter)
    40 {
    41     ASSERT(*waiter == NULL && waiter != NULL);
    42     *waiter = running_thread();
    43     thread_block(TASK_BLOCKED);
    44 }
    45 
    46 /*唤醒waiter*/
    47 static void wakeup(struct task_struct **waiter)
    48 {
    49     ASSERT(*waiter != NULL);
    50     thread_unblock(*waiter);
    51     *waiter = NULL;
    52 }
    53 
    54  
    55 /*消费者从ioq队列中获取一个字符*/
    56 char ioq_getchar(struct ioqueue *ioq)
    57 {
    58     ASSERT(intr_get_status() == INTR_OFF);
    59 
    60     while (ioq_empty(ioq)) {
    61         lock_acquire(&ioq->lock);
    62         ioq_wait(&ioq->consumer);
    63         lock_release(&ioq->lock);
    64     }
    65 
    66     char byte = ioq->buf[ioq->tail];
    67     ioq->tail = next_pos(ioq->tail);
    68 
    69     if (ioq->producer != NULL) {
    70         wakeup(&ioq->producer);
    71     }
    72     return byte;
    73 }
    74 
    75 
    76 /*生产者往ioq队列中写入一个字符byte*/
    77 void ioq_putchar(struct ioqueue *ioq, char byte)
    78 {
    79     while (ioq_full(ioq)) {
    80         lock_acquire(&ioq->lock);
    81         ioq_wait(&ioq->producer);
    82         lock_release(&ioq->lock);
    83     }
    84     ioq->buf[ioq->head] = byte; 
    85     ioq->head = next_pos(ioq->head);
    86 
    87     if (ioq->consumer != NULL) {
    88         wakeup(&ioq->consumer);
    89     }
    90 }
    ioqueue.c
     1 #ifndef  __KERNEL_IOQUEUE_H
     2 #define  __KERNEL_IOQUEUE_H
     3 #include "sync.h"
     4 #include "stdint.h"
     5 
     6 #define bufsize 64
     7 
     8 /*环形队列*/
     9 struct ioqueue {
    10 /*生产者消费问题*/
    11     struct lock lock;
    12     struct task_struct *producer;
    13     struct task_struct *consumer;
    14     char buf[bufsize];
    15     int32_t head;
    16     int32_t tail;
    17 };
    18 
    19 void ioq_putchar(struct ioqueue *ioq, char byte);
    20 char ioq_getchar(struct ioqueue *ioq);
    21 static void wakeup(struct task_struct **waiter);
    22 static void ioq_wait(struct task_struct **waiter);
    23 bool ioq_empty(struct ioqueue *ioq);
    24 bool ioq_full(struct ioqueue *ioq);
    25 static int32_t next_pos(int32_t pos);
    26 void ioqueue_init(struct ioqueue *ioq);
    27 
    28 #endif
    ioqueue.h

    五、运行测试

      上面我们已经实现了环形输入缓冲区,接下来我们在main函数中新建两个线程,这两个线程不停地从缓冲区中一个字节一个字节地取数据,如果没有便阻塞,直到缓冲区中又有数据。除此之外还需要修改interrupt.c文件,我们前面只开启了键盘中断,现在加入线程调度,所以需要开启时钟中断。修改的代码一并如下:

     1 ...
     2 
     3 /* 初始化可编程中断控制器8259A */
     4 static void pic_init(void) {
     5    /* 初始化主片 */
     6    outb (PIC_M_CTRL, 0x11);   // ICW1: 边沿触发,级联8259, 需要ICW4.
     7    outb (PIC_M_DATA, 0x20);   // ICW2: 起始中断向量号为0x20,也就是IR[0-7] 为 0x20 ~ 0x27.
     8    outb (PIC_M_DATA, 0x04);   // ICW3: IR2接从片. 
     9    outb (PIC_M_DATA, 0x01);   // ICW4: 8086模式, 正常EOI
    10 
    11    /* 初始化从片 */
    12    outb (PIC_S_CTRL, 0x11);    // ICW1: 边沿触发,级联8259, 需要ICW4.
    13    outb (PIC_S_DATA, 0x28);    // ICW2: 起始中断向量号为0x28,也就是IR[8-15] 为 0x28 ~ 0x2F.
    14    outb (PIC_S_DATA, 0x02);    // ICW3: 设置从片连接到主片的IR2引脚
    15    outb (PIC_S_DATA, 0x01);    // ICW4: 8086模式, 正常EOI
    16 
    17    /*打开键盘和时钟中断*/
    18    outb (PIC_M_DATA, 0xfc);
    19    outb (PIC_S_DATA, 0xff);
    20    
    21    put_str("pic_init done\n");
    22 }
    23 
    24 ...
    interrupt.c
     1 #include "print.h"
     2 #include "init.h"
     3 #include "memory.h"
     4 #include "thread.h"
     5 #include "list.h"
     6 #include "interrupt.h"
     7 #include "console.h"
     8 #include "ioqueue.h"
     9 #include "keyboard.h"
    10 
    11 void k_thread_a(void *arg);
    12 void k_thread_b(void *arg);
    13 
    14 int main(void)
    15 {
    16     put_str("HELLO KERNEL\n");
    17     init_all();
    18     thread_start("k_thread_a", 31, k_thread_a, "ThreadA_");
    19     thread_start("k_thread_b", 8, k_thread_b, "ThreadB_");
    20     intr_enable();
    21     while(1);
    22 }
    23 
    24 /*在线程中运行的函数k_thread_a*/
    25 void k_thread_a(void *arg)
    26 {
    27     char *para = arg;
    28     while(1) {
    29         enum intr_status old_status = intr_disable();
    30         if (!ioq_empty(&kbd_buf)) {
    31             console_put_str(arg);
    32             char byte = ioq_getchar(&kbd_buf);
    33             console_put_char(byte);
    34             console_put_str("\n");
    35         }
    36         intr_set_status(old_status);
    37     }
    38 }
    39 
    40 /*在线程中运行的函数k_thread_b*/
    41 void k_thread_b(void *arg)
    42 {
    43     char *para = arg;
    44     while(1) {
    45         enum intr_status old_status = intr_disable();
    46         if (!ioq_empty(&kbd_buf)) {
    47             console_put_str(arg);
    48             char byte = ioq_getchar(&kbd_buf);
    49             console_put_char(byte);
    50             console_put_str("\n");
    51         }
    52         intr_set_status(old_status);
    53     }
    54 }
    main.c
    1 #ifndef  __KERNEL_KEYBOARD_H
    2 #define  __KERNEL_KEYBOARD_H
    3 
    4 void keyboard_init(void);
    5 static void intr_keyboard_handler(void);
    6 extern struct ioqueue kbd_buf;
    7 #endif
    keyboard.h 
     1 #include "init.h"
     2 #include "print.h"
     3 #include "interrupt.h"
     4 #include "timer.h"
     5 #include "memory.h"
     6 #include "thread.h"
     7 #include "list.h"
     8 #include "console.h"
     9 #include "keyboard.h"
    10 
    11 void init_all(void)
    12 {
    13     put_str("init_all\n");
    14     idt_init();
    15     timer_init();
    16     mem_init();
    17     thread_init();
    18     console_init();
    19     keyboard_init();
    20 }
    init.c 
      1 #include "keyboard.h"
      2 #include "print.h"
      3 #include "interrupt.h"
      4 #include "io.h"
      5 #include "global.h"
      6 #include "stdint.h"
      7 #include "ioqueue.h"
      8 
      9 #define KBD_BUF_PORT  0x60
     10 
     11 /*用转移字符定义部分控制字符*/
     12 #define esc        '\033'
     13 #define backspace  '\b'
     14 #define tab        '\t'
     15 #define enter      '\r'
     16 #define delete     '\0177'
     17 
     18 /*以下不可见字符一律为0*/
     19 #define char_invisible  0
     20 #define ctrl_l_char     char_invisible
     21 #define ctrl_r_char     char_invisible
     22 #define shift_l_char    char_invisible
     23 #define shift_r_char    char_invisible
     24 #define alt_l_char      char_invisible
     25 #define alt_r_char      char_invisible
     26 #define caps_lock_char  char_invisible
     27 
     28 /*定义控制字符的通码和断码*/
     29 #define shift_l_make     0x2a
     30 #define shift_r_make     0x36
     31 #define alt_l_make       0x38
     32 #define alt_r_make       0xe038
     33 #define alt_r_break      0xe0b8
     34 #define ctrl_l_make      0x1d
     35 #define ctrl_r_make      0xe01d
     36 #define ctrl_r_break     0xe09d
     37 #define caps_lock_make   0x3a
     38  
     39 /*定义以下变量记录相应键是否按下的状态*/
     40 static bool ctrl_status, shift_status, alt_status, caps_lock_status, ext_scancode;
     41 
     42 struct ioqueue kbd_buf;
     43 
     44 /*以通码make_code为索引的二维数组*/
     45 static char keymap[][2] = {
     46 /*扫描码未与shift组合*/
     47 /* 0x00 */    {0,    0},        
     48 /* 0x01 */    {esc,    esc},        
     49 /* 0x02 */    {'1',    '!'},        
     50 /* 0x03 */    {'2',    '@'},        
     51 /* 0x04 */    {'3',    '#'},        
     52 /* 0x05 */    {'4',    '$'},        
     53 /* 0x06 */    {'5',    '%'},        
     54 /* 0x07 */    {'6',    '^'},        
     55 /* 0x08 */    {'7',    '&'},        
     56 /* 0x09 */    {'8',    '*'},        
     57 /* 0x0A */    {'9',    '('},        
     58 /* 0x0B */    {'0',    ')'},        
     59 /* 0x0C */    {'-',    '_'},        
     60 /* 0x0D */    {'=',    '+'},        
     61 /* 0x0E */    {backspace, backspace},    
     62 /* 0x0F */    {tab,    tab},        
     63 /* 0x10 */    {'q',    'Q'},        
     64 /* 0x11 */    {'w',    'W'},        
     65 /* 0x12 */    {'e',    'E'},        
     66 /* 0x13 */    {'r',    'R'},        
     67 /* 0x14 */    {'t',    'T'},        
     68 /* 0x15 */    {'y',    'Y'},        
     69 /* 0x16 */    {'u',    'U'},        
     70 /* 0x17 */    {'i',    'I'},        
     71 /* 0x18 */    {'o',    'O'},        
     72 /* 0x19 */    {'p',    'P'},        
     73 /* 0x1A */    {'[',    '{'},        
     74 /* 0x1B */    {']',    '}'},        
     75 /* 0x1C */    {enter,  enter},
     76 /* 0x1D */    {ctrl_l_char, ctrl_l_char},
     77 /* 0x1E */    {'a',    'A'},        
     78 /* 0x1F */    {'s',    'S'},        
     79 /* 0x20 */    {'d',    'D'},        
     80 /* 0x21 */    {'f',    'F'},        
     81 /* 0x22 */    {'g',    'G'},        
     82 /* 0x23 */    {'h',    'H'},        
     83 /* 0x24 */    {'j',    'J'},        
     84 /* 0x25 */    {'k',    'K'},        
     85 /* 0x26 */    {'l',    'L'},        
     86 /* 0x27 */    {';',    ':'},        
     87 /* 0x28 */    {'\'',    '"'},        
     88 /* 0x29 */    {'`',    '~'},        
     89 /* 0x2A */    {shift_l_char, shift_l_char},    
     90 /* 0x2B */    {'\\',    '|'},        
     91 /* 0x2C */    {'z',    'Z'},        
     92 /* 0x2D */    {'x',    'X'},        
     93 /* 0x2E */    {'c',    'C'},        
     94 /* 0x2F */    {'v',    'V'},        
     95 /* 0x30 */    {'b',    'B'},        
     96 /* 0x31 */    {'n',    'N'},        
     97 /* 0x32 */    {'m',    'M'},        
     98 /* 0x33 */    {',',    '<'},        
     99 /* 0x34 */    {'.',    '>'},        
    100 /* 0x35 */    {'/',    '?'},
    101 /* 0x36    */    {shift_r_char, shift_r_char},    
    102 /* 0x37 */    {'*',    '*'},        
    103 /* 0x38 */    {alt_l_char, alt_l_char},
    104 /* 0x39 */    {' ',    ' '},        
    105 /* 0x3A */    {caps_lock_char, caps_lock_char}
    106 };
    107 
    108 /*键盘中断处理程序*/
    109 static void intr_keyboard_handler(void)
    110 {
    111     bool ctrl_down_last = ctrl_status;
    112     bool shift_down_last = shift_status;
    113     bool caps_lock_last = caps_lock_status;
    114 
    115 
    116     bool break_code;
    117     uint16_t scancode = inb(KBD_BUF_PORT);
    118 
    119     /*若扫描码scancode是以e0开头的,表示此键的按下将产生多个扫描码
    120     所以马上结束此次中断处理函数,等待下一个扫描码进入*/
    121     if (scancode == 0xe0) {
    122         ext_scancode = true;
    123         return;
    124     }
    125 
    126     /*如果赏赐是以0xe0开头的,将扫描码合并*/
    127     if (ext_scancode) {
    128         scancode = ((0xe00) | scancode);
    129         ext_scancode = false;
    130     }
    131 
    132     break_code = ((scancode & 0x0080) != 0);
    133     if (break_code) {
    134         uint16_t make_code = (scancode &= 0xff7f); //多字节不处理
    135         if(make_code == ctrl_l_make || make_code == ctrl_r_make) {
    136             ctrl_status = false;
    137         } 
    138         else if (make_code == shift_l_make || make_code == shift_r_make) {
    139             shift_status = false;
    140         }
    141         else if (make_code == alt_l_make || make_code == alt_r_make) {
    142             alt_status = false;
    143         }
    144         return;
    145     }
    146     else if((scancode > 0x00 && scancode < 0x3b) || (scancode == alt_r_make) || (scancode == ctrl_r_make)) {
    147         bool shift = false; //先默认设置成false
    148         if ((scancode < 0x0e) || (scancode == 0x29) || (scancode == 0x1a) || \
    149         (scancode == 0x1b) || (scancode == 0x2b) || (scancode == 0x27) || \
    150         (scancode == 0x28) || (scancode == 0x33) || (scancode == 0x34) || \
    151         (scancode == 0x35))
    152         {
    153             if (shift_down_last) {
    154                 shift = true;
    155             }    
    156         } else {
    157             if (shift_down_last && caps_lock_last) {
    158                 shift = false; //效果确实是这样子的 我试了一下
    159             }    
    160             else if(shift_down_last || caps_lock_last) {
    161                 shift = true; //其中任意一个都是大写的作用
    162             } 
    163             else shift = false;
    164         }
    165         
    166         uint8_t index = (scancode & 0x00ff);
    167         char cur_char = keymap[index][shift];
    168     
    169         if (cur_char) {
    170             if (!ioq_full(&kbd_buf)) {
    171                 ioq_putchar(&kbd_buf, cur_char);
    172             }
    173             return;
    174         }
    175     
    176     if(scancode == ctrl_l_make || scancode == ctrl_r_make)        
    177         ctrl_status = true;
    178     else if(scancode == shift_l_make || scancode == shift_r_make)
    179             shift_status = true;
    180     else if(scancode == alt_l_make || scancode == alt_r_make)
    181         alt_status = true;
    182     else if(scancode == caps_lock_make)
    183         caps_lock_status = !caps_lock_status;
    184     else put_str("unknown key\n");
    185     }
    186     return;
    187 }
    188 
    189 
    190 /*键盘初始化*/
    191 void keyboard_init(void)
    192 {
    193     put_str("keyboard init start\n");
    194     ioqueue_init(&kbd_buf);
    195     register_handler(0x21, intr_keyboard_handler);
    196     put_str("keyboard init done\n");
    197 }
    keyboard.c

      

       本回到此结束,预知后事如何,请看下回分解。

  • 相关阅读:
    DDoS攻击、CC攻击
    Bean的四种实例化方式以及BeanFactory和FactoryBean的区别
    <计算机网络自顶向下> TCP拥塞
    使用ceph-deploy部署Ceph集群
    项目整合管理论文
    通过 WiFi 信标进行基于边缘的被动人群监控
    商业合作保密协议---技术开发
    Java poi 后台导出Excel
    IOC操作bean管理XML方式(注入空值和特殊符号)
    这样做时间轴,让你的PPT更出彩!
  • 原文地址:https://www.cnblogs.com/Lizhixing/p/15956006.html