- /*
- By Marcus Xing
- kernel/keyboard.c
- 与键盘的输入输出有关的代码
- */
- #include "type.h"
- #include "const.h"
- #include "ipc.h"
- #include "protect.h"
- #include "proc.h"
- #include "console.h"
- #include "tty.h"
- #include "global.h"
- #include "keyboard.h"
- #include "keymap.h"
- #include "proto.h"
- /* 内部函数和内部变量声明 */
- /* 键盘中断处理程序,只能由本文件调用 */
- static void Keyboard_Handler(int int_vec_no);
- /* 从缓冲区中得到一个扫描码,只能由本文件调用*/
- static u8 Get_Byte_From_Buffer();
- /* 缓冲区结构变量,只能由本文件使用 */
- static KB_Input_Buffer KB_Input_Buf;
- /* 状态变量,只能由本文件调用 */
- static int Shift_L; /* 是否按下左边的SHIFT键 */
- static int Shift_R; /* 是否按下右边的SHIFT键 */
- static int Ctrl_L; /* 是否按下左边的CTRL键 */
- static int Ctrl_R; /* 是否按下右边的CTRL键 */
- static int Alt_L; /* 是否按下左边的ALT键 */
- static int Alt_R; /* 是否按下右边的ALT键 */
- static int With_E0; /* 判断2扫描码并第一个码为E0的变量 */
- static int Caps_Lock; /* 是否按下Caps_Lock键 */
- static int Num_Lock; /* 是否按下Num Lock键 */
- static int Scroll_Lock; /* 是否按下Scroll Lock键 */
- /* 仅限于本文件调用的功能函数声明 */
- static void Keyboard_Handler(int int_vec_no);
- static u8 Get_Byte_From_Buffer();
- static void KB_Wait();
- static void KB_Ack();
- static void Set_LED();
- /*-----------------------------------------------------------------Init_Keyboard
- 初始化键盘
- */
- void Init_Keyboard()
- {
- /* 三盏灯全灭 */
- Caps_Lock = Num_Lock = Scroll_Lock = 0;
- Set_LED();
- /* 初始化缓冲区结构 */
- KB_Input_Buf.head = KB_Input_Buf.tail = KB_Input_Buf.buffer;
- KB_Input_Buf.size = 0;
- /* 键盘中断的处理函数的填充 */
- IRQ_Handler_Table[1] = Keyboard_Handler;
- /* 激活键盘中断 */
- Enable_IRQ(1);
- }
- /*-----------------------------------------------------------------Keyboard_Read
- 读键盘函数,由终端进程调用
- */
- void Keyboard_Read(TTY *tty)
- {
- u8 scan_code = Get_Byte_From_Buffer(); /* 从缓冲区读取一个扫描码 */
- int make; /* 判断是否按下,按下为1,不按下为0 */
- int col = 0; /* 决定读取keymap表中当前行的列数 */
- u32 *row = 0; /* 指向当前码的行的第一列 */
- u32 key = 0; /* 存放当前码对应的表中的值 */
- if(scan_code == 0xe0)
- {
- scan_code = Get_Byte_From_Buffer();
- /*
- 判断是否按下的是PRINTSCREEN键
- 此键比较特殊,是惟一的按下弹起都是4个码的键
- */
- if(scan_code == 0x2a)
- {
- scan_code = Get_Byte_From_Buffer();
- if(scan_code == 0xe0)
- {
- if(Get_Byte_From_Buffer() == 0x37)
- {
- key = PRINTSCREEN;
- make = 1;
- }
- }
- }
- /* 判断是否是PRINTSCREEN键弹起 */
- else if(scan_code == 0xb7)
- {
- if(Get_Byte_From_Buffer() == 0xe0)
- {
- if(Get_Byte_From_Buffer() == 0xaa)
- {
- key = PRINTSCREEN;
- make = 0;
- }
- }
- }
- /* 否则是以0xe0开头的其它键,按下弹起均为2个码 */
- else
- {
- With_E0 = 1; /* 定位到第2列的标志置1 */
- }
- }
- /*
- 判断是否按下了PAUSE键,此键是惟一以0xe1开头的键
- PAUSE键有6个扫描码,没有BREAK CODE
- */
- else if(scan_code == 0xe1)
- {
- int flag = 1;
- int i = 0;
- u32 pause_scan_code[6] = {0xe1,0x1d,0x45,0xe1,0x9d,0xc5};
- for(;i < 6;i++)
- {
- /* 如果不匹配,标志置0,跳出 */
- if(Get_Byte_From_Buffer() != pause_scan_code[i])
- {
- flag = 0;
- break;
- }
- }
- /* 如果匹配,key赋值 */
- if(flag)
- {
- key = PAUSEBREAK;
- make = 1;
- }
- }
- /* 如果不是PRINTSCREEN和PAUSE键的话 */
- if((key != PRINTSCREEN) && (key != PAUSEBREAK))
- {
- /*
- 是按下还是弹起,结果赋给make,MAKE CODE | FLAG_BREAK = BREAK CODE
- 如果按下或弹起的是以0xe0开头的键,此时的scan_code为第2个码
- */
- make = (scan_code & FLAG_BREAK) ? 0 : 1;
- /* 指向当前码的行的第一列 */
- row = &Key_Map[(scan_code & 0x7f) * MAP_COLS];
- /* 如果Caps Lock灯亮着再按shift+字母键,则还是小写 */
- int caps;
- caps = Shift_L || Shift_R;
- if(Caps_Lock)
- {
- if(row[0] >= 'a' && row[0] <= 'z')
- {
- caps = !caps;
- }
- }
- /* 如果SHIFT键还没放开,读当前列的第2列 */
- if(caps)
- {
- col = 1;
- }
- /* 判断是否在此前按下了以0xe0码开头的键 */
- if(With_E0)
- {
- col = 2;
- With_E0 = 0;
- }
- key = row[col]; /* 从表中取出值 */
- switch(key)
- {
- /* 按下或释放左SHIFT键 */
- case SHIFT_L:
- /* 如果按下,Shift_L置1,否则置0 */
- if(!With_E0)
- {
- Shift_L = make;
- }
- break;
- /* 如果按下,Shift_R置1,否则置0 */
- case SHIFT_R:
- if(!With_E0)
- {
- Shift_R = make;
- }
- break;
- /* 如果按下,Ctrl_L置1,否则置0 */
- case CTRL_L:
- Ctrl_L = make;
- break;
- /* 按下或释放右CTRL键 */
- case CTRL_R:
- /* 如果按下,Ctrl_R置1,否则置0 */
- Ctrl_R = make;
- break;
- /* 按下或释放左ALT键 */
- case ALT_L:
- /* 如果按下,Alt_L置1,否则置0 */
- Alt_L = make;
- break;
- /* 按下或释放右ALT键 */
- case ALT_R:
- /* 如果按下,Alt_R置1,否则置0 */
- Alt_R = make;
- break;
- /* 处理Caps Lock键 */
- case CAPS_LOCK:
- /* 如果按下,则亮灭转换下 */
- if(make)
- {
- Caps_Lock = !Caps_Lock;
- Set_LED();
- }
- break;
- /* 处理Num Lock键 */
- case NUM_LOCK:
- /* 如果按下,则亮灭转换下 */
- if(make)
- {
- Num_Lock = !Num_Lock;
- Set_LED();
- }
- break;
- /* 处理Scroll Lock键 */
- case SCROLL_LOCK:
- /* 如果按下,则亮灭转换下 */
- if(make)
- {
- Scroll_Lock = !Scroll_Lock;
- Set_LED();
- }
- break;
- /* 处理其它键 */
- default:
- break;
- }
- }
- int pad = 0;
- /* 如果按下 */
- if(make)
- {
- /* 按下的是小键盘上的键 */
- if(key >= PAD_SLASH && key <= PAD_9)
- {
- pad = 1; /* 标志按下的是小键盘的键 */
- /* 以下代码逻辑简单,不注释了 */
- switch(key)
- {
- case PAD_SLASH:
- key = '/';
- break;
- case PAD_STAR:
- key = '*';
- break;
- case PAD_MINUS:
- key = '-';
- break;
- case PAD_PLUS:
- key = '+';
- break;
- case PAD_ENTER:
- key = '/n';
- break;
- case PAD_DOT:
- if(Num_Lock)
- {
- key = '.';
- }
- else
- {
- key = '/b';
- }
- break;
- default:
- if(Num_Lock && key >= PAD_0 && key <= PAD_9)
- {
- key = key - PAD_0 + '0';
- }
- else
- {
- switch(key)
- {
- case PAD_UP:
- key = UP;
- break;
- case PAD_DOWN:
- key = DOWN;
- break;
- case PAD_LEFT:
- key = LEFT;
- break;
- case PAD_RIGHT:
- key = RIGHT;
- break;
- case PAD_HOME:
- key = HOME;
- break;
- case PAD_END:
- key = END;
- break;
- case PAD_PAGEUP:
- key = PAGEUP;
- break;
- case PAD_PAGEDOWN:
- key = PAGEDOWN;
- break;
- case PAD_INS:
- key = INSERT;
- break;
- default:
- break;
- }
- }
- break;
- }
- }
- }
- /*
- 如果按下,把SHIFT,CTRL,ALT键的信息附在key后
- 则把key交给tty.c中的函数处理,无论key是否可打印
- */
- if(make)
- {
- key |= (Shift_L ? FLAG_SHIFT_L : 0);
- key |= (Shift_R ? FLAG_SHIFT_R : 0);
- key |= (Ctrl_L ? FLAG_CTRL_L : 0);
- key |= (Ctrl_R ? FLAG_CTRL_R : 0);
- key |= (Alt_L ? FLAG_ALT_L : 0);
- key |= (Alt_R ? FLAG_ALT_R : 0);
- key |= (pad ? FLAG_PAD : 0); /* 是否是小键盘按下的加以区分 */
- /* 分离功能,此函数只负责解析,怎么处理交给In_Process */
- In_Process(tty,key);
- }
- }
- /*--------------------------------------------------------------Keyboard_Handler
- 键盘中断处理程序
- */
- static void Keyboard_Handler(int int_vec_no)
- {
- u8 scan_code;
- scan_code = In_Byte(IN_BUFFER_8042); /* 读取扫描码到scan_code中 */
- /* 如果缓冲区还有空间,则把扫描码放入缓冲区队尾指示的位置 */
- if(KB_Input_Buf.size < BUFFER_SIZE)
- {
- *KB_Input_Buf.tail = scan_code; /* 放到队尾 */
- KB_Input_Buf.tail++; /* 尾指针前进一个位置 */
- /* 如果尾指针超出缓冲区,则回到缓冲区首地址 */
- if(KB_Input_Buf.tail == KB_Input_Buf.buffer + BUFFER_SIZE)
- {
- KB_Input_Buf.tail = KB_Input_Buf.buffer;
- }
- KB_Input_Buf.size++; /* 缓冲区大小加1 */
- }
- else
- {
- /* 没有空间则直接忽略 */
- }
- }
- /*----------------------------------------------------------Get_Byte_From_Buffer
- 从缓冲区中得到一个扫描码
- */
- static u8 Get_Byte_From_Buffer()
- {
- u8 scan_code;
- /* 如果缓冲区没有扫描码,等待着 */
- while(KB_Input_Buf.size <= 0){}
- /*
- 下面的开中断和关中断是为了保证两个函数调用之间
- 的代码保持原子性,一气呵成,因为可能会被键盘中
- 断打断,而键盘中断处理程序又会操作缓冲区,造成
- 混乱
- */
- Disable_Int(); /* 关中断 */
- scan_code = *KB_Input_Buf.head; /* 队首指针指示的码给scan_code */
- KB_Input_Buf.head++; /* 头指针前进一个位置 */
- /* 如果队首指针超出缓冲区,则回到缓冲区首地址 */
- if(KB_Input_Buf.head == KB_Input_Buf.buffer + BUFFER_SIZE)
- {
- KB_Input_Buf.head = KB_Input_Buf.buffer;
- }
- KB_Input_Buf.size--; /* 缓冲区大小减1 */
- Enable_Int(); /* 开中断 */
- return scan_code;
- }
- /*-----------------------------------------------------------------------KB_Wait
- 判断8042的输入缓冲区是否为空,不为空就死等到空为止
- 向8042的输入缓冲区填数据是向8048发送命令
- */
- static void KB_Wait()
- {
- u8 status;
- do
- {
- status = In_Byte(STATUS_REG_8042); /* 从0x64端口读取8042的状态 */
- }while(status & 2); /* 第1位如果为0,则可以向输入缓冲区写数据 */
- }
- /*------------------------------------------------------------------------KB_Ack
- 设置LED时是否收到8048的反馈,如果没有接收到则死等
- */
- void KB_Ack()
- {
- u8 data;
- do
- {
- data = In_Byte(IN_BUFFER_8042); /* 从8042输出缓冲区中读取反馈 */
- }while(data != KB_ACK); /* 如果不是8048的反馈KB_ACK则死等 */
- }
- /*-----------------------------------------------------------------------Set_LED
- 设置LED,设置LED的命令字中,0表示灭,1表示亮
- 第0位表示Scroll Lock,第1位表示Num Lock,第2位表示Caps_Lock
- */
- static void Set_LED()
- {
- u8 led = Scroll_Lock | (Num_Lock << 1) | (Caps_Lock << 2); /* 组合命令字节 */
- KB_Wait(); /* 判断输入缓冲区是否空闲 */
- Out_Byte(OUT_BUFFER_8042,KB_SET_LED_COM); /* 表明要控制LED了 */
- KB_Ack(); /* 等待回应 */
- KB_Wait(); /* 判断输入缓冲区是否空闲 */
- Out_Byte(OUT_BUFFER_8042,led); /* 写命令 */
- KB_Ack(); /* 等待回应 */
- }