kernel源码(十七)字符设备-keyboard.S

键盘扫描码与ASCII码转换及Linux键盘中断处理

0 键盘扫描码

当我们按下键盘的时候,键盘上的微控制器产生一个键盘扫描码传送给计算机,计算机把这个键盘扫描码传唤为ASCII字符。

下图为AT键盘的扫描码对照表

 键盘工作的大概过程:当用户在键盘上键入一个字符时,会引起键盘中断,此时键盘中断处理程序就会从键盘控制器读入对应的键盘扫描码,然后会将键盘扫描码翻译为对应的字符,放入tty读队列read_q中。然后调用中断处理的C函数do_tty_interrupt(),它又直接调用行规则函数copy_to_cooked()对该字符进行过滤处理,并放入tty辅助队列secondary中,同时把该字符放入tty写队列write_q中,并调用写控制台函数con_write()。如果设置了控制台回显属性,则该字符会显式到屏幕上。教材中代码介绍之前的原理描述部分讲解的不错,可以多看看。

这个文件代码比较多,但是相对来说代码没有那么复杂。我们阅读的时候,抓住一些关键代码就可以了,可以不必去深究每一行代码。比较重要的地方(后面随着理解的深入会慢慢补充):

  1. 键盘硬件中断过程涉及到三个队列:读缓冲队列read_q(键盘录入的内容)、辅助缓冲队列secondary(经过加工的内容)、写缓冲队列write_q
  2. 键盘处理程序的入口:_keyboard_interrupt
  3. 当键盘按下时,中断控制器8259A会向CPU的INTR引脚发送1号中断IRQ1,接着CPU做必要的现场保护,然后从0x60地址(0x60端口)处读取键盘扫描码
  4. 根据键盘扫描码的值,去key_table[]数组相应位置处执行相应按键处理子程序,对于常规按键,这个按键处理子程序就是_do_self
  5. _do_self中调用put_queue,把扫描码对应的字符放到读缓冲队列read_q中

00 tty tty0 tty1-6 console

在Linux 系统中,计算机显示器通常被称为控制台终端(Console)。它仿真了类型为Linux的一种终端(TERM=Linux),并且有一些设备特殊文件与之相关联:tty0、tty1、tty2 等。

当你在控制台上登录时,使用的是tty1,注意这里是控制台登陆,非远程ssh登陆,远程ssh登陆对应的终端设备名为/dev/pts/0

使用Alt+[F1—F6]组合键时,我们就可以切换到tty1-tty6上面去。tty1–tty6等称为虚拟终端。

而tty0则是当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上(这时也叫控制台终端)。也就是说当我们使用ALT+Fn切换到特定虚拟终端上面去后,当前在哪个tty(比如tty3)上,tty0就是tty3的别名。

当我们使用ALT+F3切换到tty3时,控制台终端就是tty3,系统信息会打印到tty3上(系统信息其实会打印到显示器上,而显示器其实就是控制台,也就是/dev/console,又因为tty0是当前虚拟终端的一个别名,因此这里/dev/tty0、/dev/tty3、/dev/console是一样的)

 1 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 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
    xorl %al,%al        /* %eax is scan code */
    inb $0x60,%al
    cmpb $0xe0,%al
    je set_e0
    cmpb $0xe1,%al
    je set_e1
    call key_table(,%eax,4)
    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:
    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

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:
    subb $0x47,%al
    jb 1f
    cmpb $12,%al
    ja 1f
    jne cur2        /* check for ctrl-alt-del */
    testb $0x0c,mode
    je cur2
    testb $0x30,mode
    jne reboot
cur2:    cmpb $0x01,e0        /* e0 forces cursor movement */
    je cur
    testb $0x02,leds    /* not num-lock forces cursor */
    je cur
    testb $0x03,mode    /* shift forces cursor */
    jne cur
    xorl %ebx,%ebx
    movb num_table(%eax),%al
    jmp put_queue
1:    ret

cur:    movb cur_table(%eax),%al
    cmpb $'9,%al
    ja ok_cur
    movb $'~,%ah
ok_cur:    shll $16,%eax
    movw $0x5b1b,%ax
    xorl %ebx,%ebx
    jmp put_queue

#if defined(KBD_FR)
num_table:
    .ascii "789 456 1230."
#else
num_table:
    .ascii "789 456 1230,"
#endif
cur_table:
    .ascii "HA5 DGC YB623"

/*
 * this routine handles function keys
 */
func:
    pushl %eax
    pushl %ecx
    pushl %edx
    call _show_stat
    popl %edx
    popl %ecx
    popl %eax
    subb $0x3B,%al
    jb end_func
    cmpb $9,%al
    jbe ok_func
    subb $18,%al
    cmpb $10,%al
    jb end_func
    cmpb $11,%al
    ja end_func
ok_func:
    cmpl $4,%ecx        /* check that there is enough room */
    jl end_func
    movl func_table(,%eax,4),%eax
    xorl %ebx,%ebx
    jmp put_queue
end_func:
    ret

/*
 * function keys send F1:'esc [ [ A' F2:'esc [ [ B' etc.
 */
func_table:
    .long 0x415b5b1b,0x425b5b1b,0x435b5b1b,0x445b5b1b
    .long 0x455b5b1b,0x465b5b1b,0x475b5b1b,0x485b5b1b
    .long 0x495b5b1b,0x4a5b5b1b,0x4b5b5b1b,0x4c5b5b1b

#if    defined(KBD_FINNISH)
key_map:
    .byte 0,27
    .ascii "1234567890+'"
    .byte 127,9
    .ascii "qwertyuiop}"
    .byte 0,13,0
    .ascii "asdfghjkl|{
     
     "
    .byte 0,0
    .ascii "'zxcvbnm,.-"
    .byte 0,'*,0,32        /* 36-39 */
    .fill 16,1,0        /* 3A-49 */
    .byte '-,0,0,0,'+    /* 4A-4E */
    .byte 0,0,0,0,0,0,0    /* 4F-55 */
    .byte '<
    .fill 10,1,0

shift_map:
    .byte 0,27
    .ascii "!\"#$%&/()=?`"
    .byte 127,9
    .ascii "QWERTYUIOP]^"
    .byte 13,0
    .ascii "ASDFGHJKL\\["
    .byte 0,0
    .ascii "*ZXCVBNM;:_"
    .byte 0,'*,0,32        /* 36-39 */
    .fill 16,1,0        /* 3A-49 */
    .byte '-,0,0,0,'+    /* 4A-4E */
    .byte 0,0,0,0,0,0,0    /* 4F-55 */
    .byte '>
    .fill 10,1,0

alt_map:
    .byte 0,0
    .ascii "\0@\0$\0\0{[]}\\\0"
    .byte 0,0
    .byte 0,0,0,0,0,0,0,0,0,0,0
    .byte '~,13,0
    .byte 0,0,0,0,0,0,0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值