我们使用的键盘,看似非常简单自然,但是其功能的实现却相当有学问,本文将以哈工大操作系统IO设备实验为基础,讲解一下linux0.11下键盘功能的实现,和简单修改的方法。
一,实现原理
键盘功能主要由如下几个程序实现:
keyboard.S : 大S,引入c++预编译,使得汇编程序可以用预处理命令。接受键盘中断反馈的键盘码。
tty_io.c :读汇编程序吃剩下的东西,嚼完往下扔。
console.c :对了,嚼完的被他接住了....他再嚼两下,吐到您的屏幕上。
也就是说,一个字符从敲击至到达屏幕,经过的路程简要来说是这样的:
keyboard.S ---read_queue----> tty_io.c --------write_queue-----> console.c ----调用__asm__()---> 屏幕。
不关心这些东西,想直接过实验的同学可以直接跳到五。
二,keyboard.S 程序和 f12 功能键
先看一下keyboard.s程序:
/*
* linux/kernel/keyboard.S
*
* (C) 1991 Linus Torvalds
* Thanks to Alfred Leung for US keyboard patches
* Wolfgang Thiel for German keyboard patches
* Marc Corsini for the French keyboard
*/
#include <linux/config.h>
.text
.globl keyboard_interrupt
/*
* these are for the keyboard read functions
*/
size = 1024 /* must be a power of two ! And MUST be the same as in tty_io.c !!!! */
head = 4
tail = 8
proc_list = 12
buf = 16
mode: .byte 0 /* caps, alt, ctrl and shift mode */
leds: .byte 2 /* num-lock, caps, scroll-lock mode (nom-lock on) */
e0: .byte 0
/*
* con_int is the real interrupt routine that reads the
* keyboard scan-code and converts it into the appropriate下方的中断服务函数,将按键扫描码,转换成ASCII码
* ascii character(s).
*/
keyboard_interrupt:/*这个是键盘中断函数,没几行代码,每次按键盘,就进入这里*/
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
push %ds
push %es
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
xor %al,%al /* %eax 寄存器放的,就是这次按键按下的按钮编码 */
inb $0x60,%al
cmpb $0xe0,%al /* 简单比较处理一下 */
je set_e0
cmpb $0xe1,%al
je set_e1
call key_table(,%eax,4)/* 将 eax 按钮号码交给 【key_table】 映射到相应处理函数。keytable 在程序末尾*/
movb $0,e0
e0_e1: inb $0x61,%al
jmp 1f
1: jmp 1f
1: orb $0x80,%al
jmp 1f
1: jmp 1f
1: outb %al,$0x61
jmp 1f
1: jmp 1f
1: andb $0x7F,%al
outb %al,$0x61
movb $0x20,%al
outb %al,$0x20
pushl $0
call do_tty_interrupt
addl $4,%esp
pop %es
pop %ds
popl %edx
popl %ecx
popl %ebx
popl %eax
iret
set_e0: movb $1,e0
jmp e0_e1
set_e1: movb $2,e0
jmp e0_e1
/*
* This routine fills the buffer with max 8 bytes, taken from
* %ebx:%eax. (%edx is high). The bytes are written in the
* order %al,%ah,%eal,%eah,%bl,%bh ... until %eax is zero.
*/
put_queue: /*写队列函数,调用时,把接收到的一组按钮吐到read_queue里面给tty_io*/
pushl %ecx
pushl %edx
movl table_list,%edx # read-queue for console
movl head(%edx),%ecx
1: movb %al,buf(%edx,%ecx)
incl %ecx
andl $size-1,%ecx
cmpl tail(%edx),%ecx # buffer full - discard everything
je 3f
shrdl $8,%ebx,%eax
je 2f
shrl $8,%ebx
jmp 1b
2: movl %ecx,head(%edx)
movl proc_list(%edx),%ecx
testl %ecx,%ecx
je 3f
movl $0,(%ecx)
3: popl %edx
popl %ecx
ret
/*控制特殊键盘按钮的处理函数 ,alt shift ctrl 什么的*/
ctrl: movb $0x04,%al
jmp 1f
alt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: orb %al,mode
ret
unctrl: movb $0x04,%al
jmp 1f
unalt: movb $0x10,%al
1: cmpb $0,e0
je 2f
addb %al,%al
2: notb %al
andb %al,mode
ret
lshift:
orb $0x01,mode
ret
unlshift:
andb $0xfe,mode
ret
rshift:
orb $0x02,mode
ret
unrshift:
andb $0xfd,mode
ret
caps: testb $0x80,mode
jne 1f
xorb $4,leds
xorb $0x40,mode
orb $0x80,mode
set_leds:
call kb_wait
movb $0xed,%al /* set leds command */
outb %al,$0x60
call kb_wait
movb leds,%al
outb %al,$0x60
ret
uncaps: andb $0x7f,mode
ret
scroll:
xorb $1,leds
jmp set_leds
num: xorb $2,leds
jmp set_leds
/*
* curosr-key/numeric keypad cursor keys are handled here.
* checking for numeric keypad etc.
*/
cursor: /*光标处理,不用管,代码省略*/
...
...
cur2: /* e0 forces cursor movement */
...
cur:
...
ok_cur:
...
#if defined(KBD_FR)
num_table:
.ascii "789 456 1230."
#else
num_table:
.ascii "789 456 1230,"
#endif
cur_table:
.ascii "HA5 DGC YB623"
/*
* 下面这个函数有意思了,他就是处理function1-12的函数,从下面的映射表key_table可以看出,任意F被按下都跳到这里处理
*/
func:
pushl %eax
pushl %ecx
pushl %edx
call show_stat /*这里调用了一个位于【sched.c】中的c语言系统调用函数