返回列表 发帖

Keyboard Controller 简介

主板的键盘有一块专用的接口芯片,一般是采用一块单片微处理器8042(现在大多已集成在南桥或SIO里)。它控制整个键盘的工作,包括加电自检、键盘扫描码的缓冲以及与主板的通讯。INT 09HH/W中断,对应IRQ1INT 16H是一个S/W中断。当键盘的一个键被按下时,键盘接口芯片根据被按下的位置,INT 09H负责把键值转换成INT16H认识的值,返回给INT 16HINT 16H再把该值根据OS所选定的不同语系键盘而转换成相应的二进制字符传给OS或应用程序。当用户敲击键盘速度过快,使主CPU来不及处理时,则先将所键入的内容送往住存储器的键盘缓冲区,等CPU能处理时,便从缓冲区中取出,送入CPU进行分析和执行。一般在PC机的内存中安排了大约20个字符的键盘缓冲区。

8042
分输入缓冲和输出缓冲,它的数据传输在I/O60H64H进行。基本上,I/O 64H是命令和状态口,I/O 60H是数据口,它们同时可做读写动作,在读和写时有着不同的意义。I/O 64Hbit 01置位分别代表输出/输入缓冲满。如果发现输入缓冲满(即判断出I/O 64H[1]=1),要从I/O 60H将数据读完。BIOS在自检时如果确定输入/输出缓冲都没有问题,会发“AAH”给I/O 64H,让它自测试。等到输入缓冲空(说明上一个命令已执行完),输出缓冲满(KB控制器对自测试命令有反应),再读I/O 60H是否为“55H”(IBM PC/AT规范)。如果是,则表示KB没有问题,若等不到输出缓冲满,说明有问题。
       在写命令之前,必须对I/O 64H口送一个60H的值,并等到输入缓冲空,再操作I/O 60H。同样,在读状态之前,也必须对I/O 64H口送一个20H的值,并等到输出缓冲满(表示有状态输出),再操作I/O 60H。这时,我们可以把64H看作索引口,而60H看作数据口。
       键盘接口芯片除了接受来自键盘的信息外,还要负责A20地址线的切换,因为当CPU从实模式切换到保护模式时便是通过A20地址线的切换完成的。平常A20为“0”时,CPU工作于DOS的实模式;当A20切换为“1”时,便可进入保护模式。但由于键盘接口芯片切换A20地址线的速度不够快,目前多由主板上的芯片组以模拟方式取代,这样也就省去了一块键盘接口芯片。

4.2.1 Overview


键盘是计算机系统的重要输入设备,所有的IBM PC及其兼容机都有一个键盘。所以键盘驱动是一个面向IBM PC机OS的必不可少的部分。


当IBM从1981年开始,每次推出其新的PC机架构,同时也推出其新的键盘设计——最早的“IBM PC”,到稍后的“IBM XT”,所使用的键盘被称作"XT Keyboard",现在这种键盘已经完全过时,我们现在写键盘驱动程序时可以完全不用考虑它。随后1984年IBM推出了“IBM AT”,它所使用的键盘被称作“AT Keyboard”;1987年IBM推出的“IBM PS/2”使用的键盘被称作“PS/2 Keyboard”。“AT Keyboard”和“PS/2 Keyboard”大同小异,被称作IBM兼容键盘,所有的现代IBM PC/兼容机都支持他,它的接口相对简单,是本部分内容的重点。而当今最新的PC上都支持USB接口的键盘,但他的接口相对复杂的多,并且也不向后兼容,所以本部分内容不涉及它。



4.2.2 History


IBM从1981年发布它的第一款个人计算机“IBM PC”以来,它所使用的键盘也在不断的更新。如下表所示:


机型IBM PC/XTIBM ATIBM PS/2
发布年份198119841987
按键数8183-10183-101
串行协议单向双向双向
Scan code set122(增加了可选的Scan code set 3)
接口芯片825580428042
主机到键盘的命令数817

"PS/2 Keyboard"最初只是对“AT Keyboard”作了一些扩展,它完全兼容“AT Keyboard”。但对于数量众多的键盘生产商来说,他们所生产的键盘并非完全遵照"PS/2 Keyboard"或“AT Keyboard”的标准,而是只需要做到对它们兼容即可。比如,某些使用PS/2接口的键盘却只实现了7个主要的“主机到键盘的命令”,对于剩下了10个只是简单的回馈ACK。而某些使用AT接口的键盘却完全实现了“PS/2 Keyboard”的17个命令。所以,你所拥有的键盘即使是IBM兼容键盘,而未必会完全实现了标准所规定的所有功能。
所以,现在的IBM兼容键盘有如下特征:
  • 任意数量的按键(通常是101到104);
  • 双向串行协议;
  • 仅仅保证支持Scan code set 2;
  • 接口芯片可能不是8042,但保证都和8042兼容。
  • 对所有的17个命令都会回复“ACK”,但未必会完全实现它们。
“XT Keyboard”使用的协议和“AT-PS/2 Keyboard”完全不同,所以它们之间完全不兼容。但在过渡时期,有一些键盘生产厂商生产的"AT-PS/2 Keyboard",可以通过设置跳线,使其兼容"XT Keyboard"。这只不过在一个键盘里实现了两种方式,并不意味着“XT Keyboard”和"AT-PS/2 Keyboard"是兼容的。
由于“XT Keyboard”已经完全过时,我们下面不会再介绍“XT Keyboard”相关的细节,我们的内容定位于“AT-PS/2 Keyboard”。
4.2.3 Architechure
在IBM AT和IBM PS/2键盘系统中,CPU并不直接和Keyboard进行通信,而是通过一个8042芯片或者其它与之兼容的芯片。增加这么一个中间层,就可以屏蔽掉不同键盘之间实现的差别,并可以增加新的特性。如下图所示


CPU直接和8042芯片进行通信,以实现对整个键盘的控制;键盘从外界输入得到的数据也可以通过8042芯片通知给CPU,然后CPU可以通过8042芯片读取这些数据。另外,CPU也直接向8042芯片发送命令,以使用8042芯片自身所提供的功能。

键盘自身也有自己的芯片(Intel 8048及其兼容芯片),此芯片的功能主要是检索来自于Key Matrix的外界输入(击键(Press key)或释放键(Release Key))所产生的Scan code,并将这些Scan code存放于键盘自身的内部缓冲;还负责和外部系统(i8042)之间的通信,以及自身的控制(Self Test, Reset, etc)等等。

4.2.4 Mechanism
对于PC机的操作用户来说,与键盘的接口就是键盘上的按键。操作键盘的方式就是敲击这些按键。对于键盘系统而言,操作用户对键盘的敲击分为两种动作:Press key和Release key。这两个动作之间,还有一个时间段,被称为Press key delay。我们将这2个动作和1个时间段称为一个“击键过程”。

你可以设想一下这个动作——按下一个键,保持一段时间,再松开这个键——这就是你在大多数情况下快速击键动作的一个慢镜头,它明确的出现了2个动作和1个时间段。但无论你的击键动作有多快,都是上述2个动作和1个时间段的组合

对于除了Pause键之外的所有键而言,键盘针对Press Key和Release Key两个动作会分别产生两个Scan code,被称作Make Code和Break Code。在这两个动作之间的时间段里,会按照一定的频率产生Repeat code。在大多数情况下,由于你的击键速度非常快,所以不会产生Repeat code;但在任何情况下,肯定会产生Make code和Break Code


键盘都有一个Repeat code的“产生延迟”设置,这个“产生延迟”指的是两次产生“Repeat code”之间的时间间隔,比如,如果"产生延迟"被设置为0.25秒,则当一个键被保持Press状态时,键盘系统会每0.25秒产生一个针对此键的Repeat code

有时候,你会同时按下多个键,对于键盘系统而言,针对这些键的Press key动作总有现有之分。但你有可能对这些按键按下之后会保持一段时间才放开。这时候,键盘总是对你最后发生Press key动作的键产生Repeat code。而对你之前按下而没有松开的键,在它的当前“击键过程”内不会再产生Repeat code,即使在它之后被按下的键都已经完全松开

比如,你现在按下了"A"键,产生了一个Press key的动作,键盘系统会为之产生一个Make code;随后,你保持按着"A"键不放开,键盘系统将会按照一定的频率产生针对"A"键的Repeat code。这个时候,你由按下了"B"键,键盘随即停止产生"A"键的Repeat code,然后产生一个"B"键的Make code。从此以后,在"A"键的当前“击键过程”中,"A"的Repeat code再也不会产生,即使"B"在"A"被Release之前松开也是这样。而之后如果“B”被保持按着的话,则键盘系统会按照一定的频率产生"B"的Repeat code,直到"B"被松开,或又有一个键被Press为止。但无论"A"或"B"在任何时候被松开,都必然会产生一个Break code


结论是:在键盘被打开的情况下,只要一个键发生了Press key的动作,就一定会产生一个Make code;只要一个键(除了"Pause/Break"键)发生了Rlease key的动作,就一定会产生一个Break code;无论它们在什么时候发生。而对于Repeat Code,则有两个条件,一是,到当前的时刻为止,最后被按下的键;二是,这个最后被按下的键,在被松开之前被按的时间超过键盘所设置的Repeat code“产生延迟”。

在键盘系统中,由两根线,Data line和Clock line,用来控制对Scan code的检索和传递。如果Data line和Clock line都处于高电平状态,则每次8048每次检索到一个Scan code,就会立即将其发送给8042芯片。如果Data line为高电平,而Clock line为低电平,则每次8048每次检索到一个Scan code,不会立即将其发送给8042芯片,而是先将其存放在键盘的内部缓冲中,等Clock line变成高电平后,再将缓冲中的Scan code发送给8042。如果Data line为低电平,则8048停止对Scan code的检索,转而等待接收来自于8042的命令,这种情况下,如果Clock line为高电平,8048则会将接到的命令的回复数据发送给8042,否则,则无法回复这些命令。所以在8042需要向8048发送命令时,必须保证Clock line为高电平状态

如果8042芯片收到一个来自于8048芯片的Scan code或者命令回复字节,经过处理后(可能存在的解码操作),会将其放入8042的Output buffer中,8042芯片会首先将状态寄存器(Status Register)的OBF(Output Buffer Full)标志设置,随后将Output port的IBF(bit-4)设为1,表示将产生一个IRQ1,然后将Clock line置为低电平,以禁止8042进一步接收8048的数据;然后发送一个IRQ1给Intel 8059A可编程中断控制器,由它将中断提交给CPU,CPU收到此IRQ后,将调用此IRQ对应的ISR(中断服务程序,这就是我们键盘Driver的一个重要部分)。随后此ISR可以从8042的数据端口60H中将Output buffer数据读取出来,并进行进一步的处理。当Output buffer中的数据被读取出来之后,8042会将状态寄存器的OBF标志清0,然后将Clock line置为高电平,以允许进一步接收8048发送来的数据






4.2.5  Scan Code Set



迄今为止,IBM PC键盘共有3套Scan Code Set,最早的IBM PC/XT使用Scan Code Set 1,在随后的系统中,默认的都是Scan Code Set 2,后来出现了Scan Code Set 3,但并非所有的键盘都支持。为了保证正确性,我们应该以Scan Code Set 2为开发对象

Scan Code Set 1和Scan Code Set 2是不相同的,但你可以让8042芯片帮你将从8048芯片得到的属于Scan Code Set 2的Scan Code转换为Scan Code Set 1中对应的Scan Code,这样,你的驱动程序只需要都以一种方式——Scan Code set 1处理就行了。设置Scan Code 转换的方法为将8042 Command Byte的bit-6清0。如果你设置了Scan Code转换,则8042在得到一个来自于8048的Scan Code之后,会首先将其转换为Scan Code set 1中对应的Scan Code,然后再将其放入8042的Output buffer中。这样Keyboard Driver从中读出的时候,就已经是属于Scan Code Set 1的Scan Code了

需要注意的是,Scan Code和ASCII码完全不相同,所以Keyboard Driver的一个重要任务是将Scan Code和ASCII之间建立一种映射关系,将从8042读到的Scan Code转换为ASCII码。这就提供了一个特性:你可以通过建立不同的映射表,将键映射成你所喜欢的方式。比如你完全可以将键“A”映射为字母“B”的ASCII码。事实上,你也可以其映射到扩展ASCII码上,以实现其它语言的输入,比如简体中文——假如你的键盘驱动中实现了GB2312的映射,则你敲击两次键盘则可以输入一个汉字


[size=+0][size=+0][size=+0]4.2.5.1 Scan code set 1

在Scan Code Set 1中,对于绝大多数键而言,其Make Code,Break Code,以及Repeat Code都是单字节的。其规则为:如果Make Code为nn,则其Repeat code与Make Code相同也是nn,而其Break Code则是将nn与80h进行按位OR运算,也就是将Make Code的最高位bit-7设置为1。比如:键"A"的Make Code位1Eh,其Repeat Code也为1Eh,而其Break Code则为1Eh|80h=9Eh。
还有一些键的Scan Code是双字节的。其规则为:它们的第一个字节都是E0h,对于第2个字节,其规则与单字节Scan Code的规则一样。

对PrtSc/SysRq键而言,其make code = E02AE037,repeat code = E037, break code = E0B7E0AA。

Pause/Break键没有Repeat Code,也没有Break Code,只有Make Code。其Make Code很长,为E11D45E19DC5


101-, 102-, and 104-key keyboards:
  

KEYMAKEBREAK-----KEYMAKEBREAK-----KEYMAKEBREAK
A1E9E90A8A[1A9A
B30B0`2989INSERTE0,52E0,D2
C2EAE-0C8CHOMEE0,47E0,C7
D20A0=0D8DPG UPE0,49E0,C9
E1292\2BABDELETEE0,53E0,D3
F21A1BKSP0E8EENDE0,4FE0,CF
G22A2SPACE39B9PG DNE0,51E0,D1
H23A3TAB0F8FU ARROWE0,48E0,C8
I1797CAPS3ABAL ARROWE0,4BE0,CB
J24A4L SHFT2AAAD ARROWE0,50E0,D0
K25A5L CTRL1D9DR ARROWE0,4DE0,CD
L26A6L GUIE0,5BE0,DBNUM45C5
M32B2L ALT38B8KP /E0,35E0,B5
N31B1R SHFT36B6KP *37B7
O1898R CTRLE0,1DE0,9DKP -4ACA
P1999R GUIE0,5CE0,DCKP +4ECE
Q1019R ALTE0,38E0,B8KP ENE0,1CE0,9C
R1393APPSE0,5DE0,DDKP .53D3
S1F9FENTER1C9CKP 052D2
T1494ESC0181KP 14FCF
U1696F13BBBKP 250D0
V2FAFF23CBCKP 351D1
W1191F33DBDKP 44BCB
X2DADF43EBEKP 54CCC
Y1595F53FBFKP 64DCD
Z2CACF640C0KP 747C7
00B8BF741C1KP 848C8
10282F842C2KP 949C9
20383F943C3]1B9B
30484F1044C4;27A7
40585F1157D7'28A8
50686F1258D8,33B3
60787PRNT
SCRN
E0,2A,
E0,37
E0,B7,
E0,AA
.34B4
70888SCROLL46C6/35B5
80989PAUSEE1,1D,45
E1,9D,C5
-NONE-
ACPI Scan Codes:
  
KeyMake CodeBreak Code
PowerE0, 5EE0, DE
SleepE0, 5FE0, DF
WakeE0, 63E0, E3
Windows Multimedia Scan Codes:
  
KeyMake CodeBreak Code
Next TrackE0, 19E0, 99
Previous TrackE0, 10E0, 90
StopE0, 24E0, A4
Play/PauseE0, 22E0, A2
MuteE0, 20E0, A0
Volume UpE0, 30E0, B0
Volume DownE0, 2EE0, AE
Media SelectE0, 6DE0, ED
E-MailE0, 6CE0, EC
CalculatorE0, 21E0, A1
My ComputerE0, 6BE0, EB
WWW SearchE0, 65E0, E5
WWW HomeE0, 32E0, B2
WWW BackE0, 6AE0, EA
WWW ForwardE0, 69E0, E9
WWW StopE0, 68E0, E8
WWW RefreshE0, 67E0, E7
WWW FavoritesE0, 66E0, E6
[size=+0][size=+0][size=+0]4.2.5.2 Scan code set 2
[size=+0][size=+0][size=+0]在Scan Code Set 2中,对于绝大多数键而言,其Make Code,Repeat Code都是单字节的,而其Break Code为双字节的。其规则为:如果Make Code为nn,则其Repeat code与Make Code相同也是nn,而其Break Code的第一个字节为F0,而第二个字节与Make Code相同。比如:键"A"的Make Code位1Ch,其Repeat Code也为1Ch,而其Break Code则为F01Ch。
还有一些键的Make Code,Repeat Code是双字节的,其Break Code则是3字节的。其规则为:它们的第一个字节都是E0h,对于后两个字节,其规则与单字节Scan Code的规则一样。

对PrtSc/SysRq键而言,其Make code = E012E07C, repeat code = E07C, break code = E0F07CE0F012

Pause/Break键没有Repeat Code,也没有Break Code,只有Make Code。其Make Code很长,为E11477E1F014F077


101-, 102-, and 104-key keyboards:
  
KEYMAKEBREAK-----KEYMAKEBREAK-----KEYMAKEBREAK
A1CF0,1C946F0,46[54FO,54
B32F0,32`0EF0,0EINSERTE0,70E0,F0,70
C21F0,21-4EF0,4EHOMEE0,6CE0,F0,6C
D23F0,23=55FO,55PG UPE0,7DE0,F0,7D
E24F0,24\5DF0,5DDELETEE0,71E0,F0,71
F2BF0,2BBKSP66F0,66ENDE0,69E0,F0,69
G34F0,34SPACE29F0,29PG DNE0,7AE0,F0,7A
H33F0,33TAB0DF0,0DU ARROWE0,75E0,F0,75
I43F0,43CAPS58F0,58L ARROWE0,6BE0,F0,6B
J3BF0,3BL SHFT12FO,12D ARROWE0,72E0,F0,72
K42F0,42L CTRL14FO,14R ARROWE0,74E0,F0,74
L4BF0,4BL GUIE0,1FE0,F0,1FNUM77F0,77
M3AF0,3AL ALT11F0,11KP /E0,4AE0,F0,4A
N31F0,31R SHFT59F0,59KP *7CF0,7C
O44F0,44R CTRLE0,14E0,F0,14KP -7BF0,7B
P4DF0,4DR GUIE0,27E0,F0,27KP +79F0,79
Q15F0,15R ALTE0,11E0,F0,11KP ENE0,5AE0,F0,5A
R2DF0,2DAPPSE0,2FE0,F0,2FKP .71F0,71
S1BF0,1BENTER5AF0,5AKP 070F0,70
T2CF0,2CESC76F0,76KP 169F0,69
U3CF0,3CF105F0,05KP 272F0,72
V2AF0,2AF206F0,06KP 37AF0,7A
W1DF0,1DF304F0,04KP 46BF0,6B
X22F0,22F40CF0,0CKP 573F0,73
Y35F0,35F503F0,03KP 674F0,74
Z1AF0,1AF60BF0,0BKP 76CF0,6C
045F0,45F783F0,83KP 875F0,75
116F0,16F80AF0,0AKP 97DF0,7D
21EF0,1EF901F0,01]5BF0,5B
326F0,26F1009F0,09;4CF0,4C
425F0,25F1178F0,78'52F0,52
52EF0,2EF1207F0,07,41F0,41
636F0,36PRNT
SCRN
E0,12,
E0,7C
E0,F0,
7C,E0,
F0,12
.49F0,49
73DF0,3DSCROLL7EF0,7E/4AF0,4A
83EF0,3EPAUSEE1,14,77,
E1,F0,14,
F0,77
-NONE-

ACPI Scan Codes:
  
KeyMake CodeBreak Code
PowerE0, 37E0, F0, 37
SleepE0, 3FE0, F0, 3F
WakeE0, 5EE0, F0, 5E
Windows Multimedia Scan Codes:
  
KeyMake CodeBreak Code
Next TrackE0, 4DE0, F0, 4D
Previous TrackE0, 15E0, F0, 15
StopE0, 3BE0, F0, 3B
Play/PauseE0, 34E0, F0, 34
MuteE0, 23E0, F0, 23
Volume UpE0, 32E0, F0, 32
Volume DownE0, 21E0, F0, 21
Media SelectE0, 50E0, F0, 50
E-MailE0, 48E0, F0, 48
CalculatorE0, 2BE0, F0, 2B
My ComputerE0, 40E0, F0, 40
WWW SearchE0, 10E0, F0, 10
WWW HomeE0, 3AE0, F0, 3A
WWW BackE0, 38E0, F0, 38
WWW ForwardE0, 30E0, F0, 30
WWW StopE0, 28E0, F0, 28
WWW RefreshE0, 20E0, F0, 20
WWW FavoritesE0, 18E0, F0, 18
[size=+0][size=+0][size=+0]4.2.5.3 Scan code set 3

Scan Code Set 3的Scan Code只有一种规则,与Scan Code Set 2的单字节Make Code的规则一样。这非常有利于Keyboard driver的实现。可惜的就是只有一部分键盘支持它
KEYMAKEBREAK-----KEYMAKEBREAK-----KEYMAKEBREAK
A1CF0,1C
946F0,46
[54F0,54
B32F0,32
`0EF0,0E
INSERT67F0,67
C21F0,21
-4EF0,4E
HOME6EF0,6E
D23F0,23
=55F0,55
PG UP6FF0,6F
E24F0,24
\5CF0,5C
DELETE64F0,64
F2BF0,2B
BKSP66F0,66
END65F0,65
G34F0,34
SPACE29F0,29
PG DN6DF0,6D
H33F0,33
TAB0DF0,0D
U ARROW63F0,63
I43F0,48
CAPS14F0,14
L ARROW61F0,61
J3BF0,3B
L SHFT12F0,12
D ARROW60F0,60
K42F0,42
L CTRL11F0,11
R ARROW6AF0,6A
L4BF0,4B
L WIN8BF0,8B
NUM76F0,76
M3AF0,3A
L ALT19F0,19
KP /4AF0,4A
N31F0,31
R SHFT59F0,59
KP *7EF0,7E
O44F0,44
R CTRL58F0,58
KP -4EF0,4E
P4DF0,4D
R WIN8CF0,8C
KP +7CF0,7C
Q15F0,15
R ALT39F0,39
KP EN79F0,79
R2DF0,2D
APPS8DF0,8D
KP .71F0,71
S1BF0,1B
ENTER5AF0,5A
KP 070F0,70
T2CF0,2C
ESC08F0,08
KP 169F0,69
U3CF0,3C
F107F0,07
KP 272F0,72
V2AF0,2A
F20FF0,0F
KP 37AF0,7A
W1DF0,1D
F317F0,17
KP 46BF0,6B
X22F0,22
F41FF0,1F
KP 573F0,73
Y35F0,35
F527F0,27
KP 674F0,74
Z1AF0,1A
F62FF0,2F
KP 76CF0,6C
045F0,45
F737F0,37
KP 875F0,75
116F0,16
F83FF0,3F
KP 97DF0,7D
21EF0,1E
F947F0,47
]5BF0,5B
326F0,26
F104FF0,4F
;4CF0,4C
425F0,25
F1156F0,56
'52F0,52
52EF0,2E
F125EF0,5E
,41F0,41
636F0,36
PRNT
SCRN
57F0,57
.49F0,49

3DF0,3D
SCROLL5FF0,5F
/4AF0,4A
83EF0,3E
PAUSE62F0,62





4.2.6  8042 Controller



4.2.4节所描述的过程就是如何把操作用户的外部输入转化为系统软件(也就是操作系统)可以获取的数据的过程。除了上述的外部输入的转化过程之外,操作系统还可以通过向8042芯片发送命令来分别控制8042芯片和8048芯片

下图表现的就是8042芯片的内部结构,图中Processor不是指计算机的CPU,而是8042芯片自身的处理器






8042芯片除了被用来控制键盘,作为键盘和CPU之间的桥梁之外,还可以被用来控制A20 Gate,以决定CPU是否可以访问以MB为单位的偶数内存;以及向系统发送Reset信号,让主机重新启动。另外,8042芯片还可以支持PS/2类型的鼠标,但这一点本部分不会进行讨论

8042有4个寄存器
  • 1个8-bit长的Input buffer;Write-Only

  • 1个8-bit长的Output buffer; Read-Only

  • 1个8-bit长的Status Register;Read-Only

  • 1个8-bit长的Control Register;Read/Write
其中Control Register在上图中没有表现,又被称作Command Byte。
另外,8042有2个端口:60h和64h  
PortR/WFunction
0x60ReadRead Output Buffer
0x60WriteWrite Input Buffer(8042 Data&8048 Command)
0x64ReadRead Status Register
0x64WriteWrite Input Buffer(8042 Command)
上面提到的4个寄存器中,前3个寄存器都可以通过60h和64h直接访问,但第4个寄存器只能够通过向64h端口发送命令,然后通过60h端口存取。


Status Register中存放的是一些与缓冲状态,数据状态,及键盘状态有关的状态信息,是一个8-bit长的积存器。它对于OS来说是只读的,并且只能够通过64h端口读取,任何时候,只要读取64h端口,都会读到Status Register的内容。

bit

Meaning

0

output register (60h) has data for system

1

input register (60h/64h) has data for 8042

2

system flag (set to 0 after power on reset)

3

data in input register is command (1) or data (0)

4

1=keyboard enabled, 0=keyboard disabled (via switch)

5

1=transmit timeout (data transmit not complete)

6

1=receive timeout (data transmit not complete)

7

1=even parity rec'd, 0=odd parity rec'd (should be odd)

Status Register

Input buffer被用来向8042芯片发送命令与数据,以控制8042芯片和8048芯片。Input buffer可以通过60h端口和64h端口写入,其中通过64h端口写入的是用来控制8042芯片的命令,而通过60h写入的数据有两种:一种是通过64h端口发送的被用来控制8042芯片的命令所需要的进一步数据;另一种则是直接发给键盘用来控制8048芯片的命令。



Output buffer被存放可以通过60h端口读取的数据。这些数据分为2大类:一类是那些通过64h端口发送的,被用来控制8042芯片的命令的返回结果;另一类则是8048芯片所发过来的数据。后者的数据又分为Scan code,和对那些通过60h端口发送给8048的命令的回复结果。


8042自身有少量的RAM,以及一些ROM,这些内存与我们常说的内存(称为系统内存)没有任何关系,它们和系统内存不使用相同的地址空间。这些RAM和ROM是8042芯片处理器自身运算的需要。


除了上述元素之外,8042还有3个内部端口:Input port, Output port和Test Port。Output port有System Reset和A20 Gate两个与键盘无关的重要的控制位,其它的位都是向8048芯片输出,让8048芯片参考的控制位。程序员最好不要动这些除了System Reset和A20 Gate之外的控制位。IBM AT和IBM PS/2的这3个端口的格式有少许不同,主要因为在IBM PS/2上,8048同时支持PS/2鼠标。

PinNamePS/2 FunctionAT Function
0P10Keyboard Data Undefined
1P11Mouse DataUndefined
2P12Undefined Undefined
3P13Undefined Undefined
4P14External RAM
1: Enable external RAM
0: Disable external RAM
External RAM
1: Enable external RAM
0: Disable external RAM
5P15Manufacturing Setting
1: Setting enabled
0: Setting disabled
Manufacturing Setting
1: Setting enabled
0: Setting disabled
6P16Display Type Switch
1: Color display
0: Monochrome
Display Type Switch
1: Color display
0: Monochrome
7P17Keyboard Inhibit Switch
1: Keyboard enabled
0: Keyboard inhibited
Keyboard Inhibit Switch
1: Keyboard enabled
0: Keyboard inhibited
Input Port
PinNamePS/2 FunctionAT Function
0P20System Reset
1: Normal
0: Reset computer
System Reset
1: Normal
0: Reset computer
1P21A20 Gate
1: Enable
0: Disable
A20 Gate
1: Enable
0: Disable
2P22Mouse Data:
1: Pull Data low
0: High-Z
Undefined
3P23Mouse Clock:
1: Pull Clock low
0: High-Z
Undefined
4P24Keyboard IBF interrupt:
1: Assert IRQ 1
0: De-assert IRQ 1
Output Buffer Full
5P25Mouse IBF interrupt:
1: Assert IRQ 12
0: De-assert IRQ 12
Input Buffer Empty
6P26Keyboard Clock
1: Pull Clock low
0: High-Z
Keyboard Clock
1: Pull Clock low
0: High-Z
7P27Keyboard Data:
1: Pull Data low
0: High-Z
Keyboard Data:
1: Pull Data low
0: High-Z
Output Port
PinNameFunction
0T0Keyboard Clock
1T1Mouse Clock
2--Undefined
3--Undefined
4--Undefined
5--Undefined
6--Undefined
7--Undefined
Test Port

Command Byte不能直接通过60h和64端口读取,若要访问它,必须首先通过64h端口向8042发布相应命令(20h/read,60h/write),然后再通过60h存取。


bit

Meaning

0

1=enable output register full interrupt

1

should be 0

2

1=set status register system, 0=clear

3

1=override keyboard inhibit, 0=allow inhibit

4

disable keyboard I/O by driving clock line low

5

disable auxiliary device, drives clock line low

6

IBM scancode translation 0=AT, 1=PC/XT

7

reserved, should be 0

Commnand Byte


4.2.7 Command

通过8042芯片,可以:

Ÿ
8042芯片发布命令(通过64h),并通过60h读取命令的返回结果(如果有的话),或通过60h端口写入命令所需的数据(如果需要的话)。

Ÿ
读取Status Register的内容(通过64h);

Ÿ
向8048发布命令(通过60h);

Ÿ
读取来自于Keyboard的数据(通过60h)。这些数据包括Scan Code(由按键和释放键引起的),对8048发送的命令的确认字节(ACK)及回复数据

再次强调一遍,Command(命令)分为发送给8042芯片的命令和发送给8048的命令。它们是不相同的,并且使用的端口也是不相同的(分别为64h60h)。



  • 64h端口(读操作)

64h端口进行读操作,会读取Status Register的内容。

inb %0x64

执行这个指令之后,AL寄存器中存放的就是Status Register的内容。

  • 64h端口(写操作)
64h端口写入的字节,被认为是对8042芯片发布的命令(Command):
Ÿ
写入的字节将会被存放在Input Register中;

Ÿ
同时会引起Status RegisterBit-3自动被设置为1,表示现在放在Input Register中的数据是一个Command,而不是一个Data

Ÿ
在向64h端口写某些命令之前必须确保键盘是被禁止的,因为这些被写入的命令的返回结果将会放到Output Register中,而键盘如果不被禁止,则也会将数据放入到Output Register中,会引起相互之间的数据覆盖;

Ÿ
在向64h端口写数据之前必须确保Input Register是空的(通过判断Status RegisterBit-1是否为0)。


void wait_input_empty(void)
{
   char __b;

   do{
     __b = inb(0x64);
   }while(!(__b&0x02));
}

void disable_keyboard(void)
{
   wait_input_empty();
   outb(0x64, 0xAD);
}
[size=+0]
  • 60h端口(读操作)
60h端口进行读操作,将会读取Output Register的内容。Output Register的内容可能是:
Ÿ           来自于8048的数据。这些数据包括Scan Code,对8048发送的命令的确认字节(ACK)及回复数据
Ÿ           通过64h端口对8042发布的命令的返回结果。

在向60h端口读取数据之前必须确保Output Register中有数据(通过判断Status RegisterBit-0是否为1)。
void wait_output_full(void)
{
   char __b;

   do{
     __b = inb(0x64);
   }while(__b&0x01);
}

unsigned char read_output(void)
{
   wait_output_full();
   return inb(0x60);
}
  • 60h端口(写操作)
60h端口写入的字节,有两种可能:
1.如果之前通过64h端口向8042芯片发布的命令需要进一步的数据,则此时写入的字节就被认为是数据;
2.否则,此字节被认为是发送给8048的命令。

在向60h端口写数据之前,必须确保Input Register是空的(通过判断Status RegisterBit-1是否为0)。


[size=+0]4.2.7.1 发给8042的命令

  • 20h
准备读取8042芯片的Command Byte;其行为是将当前8042 Command Byte的内容放置于Output Register中,下一个从60H端口的读操作将会将其读取出来。


unsigned char read_command_byte(void)
{
   wait_input_empty();
   outb(0x64,0x20);
   wait_output_full();
   return inb(0x60);   
}
  • 60h
准备写入8042芯片的Command Byte;下一个通过60h写入的字节将会被放入Command Byte


void write_command_byte(unsigned char command_byte)
{
   wait_input_empty();
   outb(0x64,0x60);
   wait_input_empty();
   outb(0x60,command_byte);
}


  • A4h
测试一下键盘密码是否被设置;测试结果放置在Output Register,然后可以通过60h读取出来。测试结果可以有两种值:FAh=密码被设置;F1h=没有密码。
bool is_set_password(void)
{
   wait_input_empty();
   outb(0x64,0xA4);
   wait_output_full();
   return inb(0x60)==0xFA?true:false;   
}
  • A5h
设置键盘密码。其结果被按照顺序通过60h端口一个一个被放置在Input Register中。密码的最后是一个空字节(内容为0)。
void set_password(unsigned char* password)
{
   char* p = password;

   if(p == NULL)
      return;

   wait_input_empty();
   outb(0x64,0xA5);

   do{
      wait_input_empty();
      outb(0x60, *p);
   }while(*p++ != 0);
}
  • A6h
让密码生效。在发布这个命令之前,必须首先使用A5h命令设置密码。
void enable_password(void)
{
   if(!is_set_password())
      return;

   wait_input_empty();
   outb(0x64,0xA6);   
}
  • AAh
自检。诊断结果放置在Output Register中,可以通过60h读取。55h=OK


bool is_test_ok(void)
{
   wait_input_empty();
   outb(0x64,0xAA);


   wait_output_full();
   return inb(0x60)==0x55?true:false;   
}


  • ADh
禁止键盘接口。Command Bytebit-4被设置。当此命令被发布后,Keyboard将被禁止发送数据到Output Register
void disable_keyboard(void)
{
   wait_input_empty();
   outb(0x64,0xAD);

}
  • AEh
打开键盘接口。Command Bytebit-4被清除。当此命令被发布后,Keyboard将被允许发送数据到Output Register
void enable_keyboard(void)
{
   wait_input_empty();
   outb(0x64,0xAE);

}
  • C0h
准备读取Input PortInput Port的内容被放置于Output Register中,随后可以通过60h端口读取。
unsigned char read_input_port(void)
{
   wait_input_empty();
   outb(0x64,0xC0);

   wait_output_full();

   return inb(0x60);
}
  • D0h
准备读取Outport端口。结果被放在Output Register中,随后通过60h端口读取出来。
unsigned char read_output_port(void)
{
   wait_input_empty();
   outb(0x64,0xD0);

   wait_output_full();

   return inb(0x60);
}
  • D1h
准备写Output端口。随后通过60h端口写入的字节,会被放置在Output Port中。
void write_output_port(unsigned char __c)
{
   wait_input_empty();
   outb(0x64,0xD1);

   wait_input_empty();
   outb(0x60,__c);

}


  • D2h
准备写数据到Output Register中。随后通过60h写入到Input Register的字节会被放入到Output Register中,此功能被用来模拟来自于Keyboard发送的数据。如果中断被允许,则会触发一个中断。
void put_data_to_output_register(unsigned char __data)
{
   wait_input_empty();
   outb(0x64,0xD2);

   wait_input_empty();
   outb(0x60,__c);
}

4.2.7.2 发给8048的命令


  • EDh
设置LEDKeyboard收到此命令后,一个LED设置会话开始。Keyboard首先回复一个ACKFAh),然后等待从60h端口写入的LED设置字节,如果等到一个,则再次回复一个ACK,然后根据此字节设置LED。然后接着等待。。。直到等到一个非LED设置字节(高位被设置),此时LED设置会话结束。
  • EEh
诊断Echo。此命令纯粹为了检测Keyboard是否正常,如果正常,当Keyboard收到此命令后,将会回复一个EEh字节。
  • F0h
选择Scan code setKeyboard系统共可能有3Scan code set。当Keyboard收到此命令后,将回复一个ACK,然后等待一个来自于60h端口的Scan code set代码。系统必须在此命令之后发送给Keyboard一个Scan code set代码。当Keyboard收到此代码后,将再次回复一个ACK,然后将Scan code set设置为收到的Scan code set代码所要求的。
  • F2
读取Keyboard ID。由于8042芯片后不仅仅能够接Keyboard。此命令是为了读取8042后所接的设备ID。设备ID2个字节,Keyboard ID83ABh。当键盘收到此命令后,会首先回复一个ACK,然后,将2字节的Keyboard ID一个一个回复回去。
  • F3h
设置Typematic Rate/Delay。当Keyboard收到此命令后,将回复一个ACK。然后等待来自于60h的设置字节。一旦收到,将回复一个ACK,然后将Keyboard Rate/Delay设置为相应的值。
  • F4h
清理键盘的Output Buffer。一旦Keyboard收到此命令,将会将Output buffer清空,然后回复一个ACK。然后继续接受Keyboard的击键。
  • F5h
设置默认状态(w/Disable)。一旦Keyboard收到此命令,将会将Keyboard完全初始化成默认状态。之前所有对它的设置都将失效——Output buffer被清空,Typematic Rate/Delay被设置成默认值。然后回复一个ACK,接着等待下一个命令。需要注意的是,这个命令被执行后,键盘的击键接受是禁止的。如果想让键盘接受击键输入,必须Enable Keyboard
  • F6h
设置默认状态。和F5命令唯一不同的是,当此命令被执行之后,键盘的击键接收是允许的。
  • FEh
Resend。如果Keyboard收到此命令,则必须将刚才发送到8042 Output Register中的数据重新发送一遍。当系统检测到一个来自于Keyboard的错误之后,可以使用自命令让Keyboard重新发送刚才发送的字节。
  • FFh
Reset Keyboard。如果Keyboard收到此命令,则首先回复一个ACK,然后启动自身的Reset程序,并进行自身基本正确性检测(BAT-Basic Assurance Test)。等这一切结束之后,将返回给系统一个单字节的结束码(AAh=Success, FCh=Failed),并将键盘的Scan code set设置为2


4.2.6.3 8048到8042的数据


  • 00h/FFh
当击键或释放键时检测到错误时,则在Output Bufer后放入此字节,如果Output Buffer已满,则会将Output Buffer的最后一个字节替代为此字节。使用Scan code set 1时使用00hScan code 2Scan Code 3使用FFh
  • AAh
BAT完成代码。如果键盘检测成功,则会将此字节发送到8042 Output Register中。
  • EEh
Echo响应。Keyboard使用EEh响应从60h发来的Echo请求。
  • F0h
Scan code set 2Scan code set 3中,被用作Break Code的前缀。
  • FAh
ACK。当Keyboard任何时候收到一个来自于60h端口的合法命令或合法数据之后,都回复一个FAh
  • FCh
BAT失败代码。如果键盘检测失败,则会将此字节发送到8042 Output Register中。
  • FEh
Resend。当Keyboard任何时候收到一个来自于60h端口的非法命令或非法数据之后,或者数据的奇偶交验错误,都回复一个FEh,要求系统重新发送相关命令或数据。
  • 83ABh
当键盘收到一个来自于60hF2h命令之后,会依次回复83hABh83AB是键盘的ID
  • Scan code
除了上述那些特殊字节以外,剩下的都是Scan code。

TOP

补充的好,赞一个

TOP

8042控制命令的下達方式:

    ;
    ; ========================================================
    ; 以下為8042界面函數之內部呼叫函數
    ; ========================================================
    ;
    Flush8042       PROC
                    ; 清除所有的輸出緩衝器資料
                    push ax
    FlushOBF:       in   al,64h
                    test al,1
                    jz   NotOBF
                    in   al,60h
                    jmp  short FlushOBF
    NotOBF:         pop  ax
                    retn
    Flush8042       ENDP
    ;
    WaitIBF         PROC
                    ; 等待8042輸入緩衝器有空
    WaitIBFLoop:    push ax
                    in   al,64h
                    test al,2
                    pop  ax
                    jnz  WaitIBFLoop
                    retn
    WaitIBF         ENDP
    ;
    WaitOBF         PROC
                    ; 等待8042輸出緩衝器有資料送來
    WaitOBFLoop:    push ax
                    in   al,64h
                    test al,1
                    pop  ax
                    jz   WaitOBFLoop
                    retn
    WaitOBF         ENDP
    ;
    Read8042Data    PROC
                    ; 讀取8042回應資料
                    ; 傳回: AL = 回應資料
                    call WaitOBF        ; 等待資料回庄
                    in   al,60h
                    retn
    Read8042Data    ENDP
    ;
    Write8042Data   PROC
                    ; 送出資料給8042
                    ; 參數: AL = 8042系統命令或資料
                    ; 備註: 本函數亦為送出系統命令或資料的函數
                    call WaitIBF        ; 等待輸入緩衝區有空
                    out  60h,al         ; 送出資料
                    call WaitIBF        ; 確認8042收到
                    retn
    Write8042Data   ENDP
    ;
    RealSend8042Cmd PROC
                    ; 送出一般命令碼給8042
                    ; 參數: AL = 8042一般控制命令
                    ; 備註: 8042命令之參數或傳回值由外界處理
                    call WaitIBF        ; 等待輸入緩衝區有空
                    out  64h,al         ; 送出命令
                    call WaitIBF        ; 確認8042收到
                    retn
    RealSend8042Cmd ENDP
    ;
    RealSend8042Sys PROC
                    ; 送出系統命令碼或參數給8042
                    ; 參數: AL = 8042系統控制命令或參數
                    ; 傳回: AL = 8042回應值
                    ; 備註: 除回音外,其餘命令或參數應檢查是否傳回ACK
                    call Flush8042
                    call Write8042Data
                    call Read8042Data
                    retn
    RealSend8042Sys ENDP
    ;
    ; ========================================================
    ; 以下為8042界面函數,使用前必須使用CLI將岔斷禁能
    ; ========================================================
    ;
    Send8042Cmd     PROC
                    ; 送出一般命令碼給8042
                    ; 參數: AL = 8042一般控制命令
                    call RealSend8042Cmd
                    retn
    Send8042Cmd     ENDP
    ;
    Read8042Cmd     PROC
                    ; 送出讀取命令給8042
                    ; 參數: AL = 8042讀取命令
                    ; 傳回: AL = 讀取值
                    call Flush8042
                    call RealSend8042Cmd
                    call Read8042Data
                    retn
    Read8042Cmd     ENDP
    ;
    Write8042Cmd    PROC
                    ; 送出寫入命令給8042
                    ; 參數: AL = 8042寫入命令
                    ;       AH = 寫入資料
                    ; 備註: AX值會被破壞
                    call RealSend8042Cmd
                    xchg al,ah
                    call Write8042Data
                    retn
    Write8042Cmd    ENDP
    ;
    Echo8042        PROC
                    ; 送出8042回音命令
                    ; 傳回: AL = EEh
                    mov  al,0EEh        ; 回音命令
                    call RealSend8042Sys
                    retn
    Echo8042        ENDP
    ;

TOP

非常好!!曾经trace进int09,和int16的整个过程,代码蛮复杂的,兼容这个兼容那个,头都看大了
就是有几张图没有显示,能把图补上么?

TOP

奇怪图贴不上去~直接上传吧~现在好了呵呵
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
Let's study together!

TOP

非常感谢lz!!
再问一下:文章中有3个图,你上传的只有2个图,是少一个图还是其中2个图是同一个?

TOP

好像少了4.2.6的图?

TOP

LZ能不能把这本书上传啊!期待!!!

TOP

不好意思~ 没检查仔细~再补。。。

这个不是书~只是我以前收集的部分零散资料。呵呵,觉得不错,就贴出来和大家分享。
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
Let's study together!

TOP

哈哈,对60H和64H不懂的可以来看看了
修得一身好灵气,万事花开万事春。-- 争取每日一帖!

TOP

谢谢lz
先前有看过,但没有那么详细,一饱眼福

TOP

谢谢LZ

好东西,以前想找都很难的,顶一个!

TOP

專業......
推依下

TOP

这篇文章确实很好的说!
修得一身好灵气,万事花开万事春。-- 争取每日一帖!

TOP

原帖由 biosren 于 2008-5-30 08:56 发表
谢谢lz
先前有看过,但没有那么详细,一饱眼福
studying

TOP

LZ,能不能附带上传你最开始的介绍中涉及的IBM PC/AT规范啊
我本将心向明月。。。。。。。

TOP

这个规范好像不怎么好找,手头没有噢
Let's study together!

TOP

最近遇到个Bug,看看能不能找到点什么?
对了,有知道usb键盘工作流程的吗?

TOP

回复 4# 的帖子

Flush8042       PROC
                    ; 清除所有的輸出緩衝器資料
                    push ax
    FlushOBF:       in   al,64h
                    test al,1
                    jz   NotOBF
                    in   al,60h
                    jmp  short FlushOBF
    NotOBF:         pop  ax
                    retn
    Flush8042       ENDP

有一個地方沒有看明白 就是 in   al,60h 執行之後馬上又被 in   al,64h覆蓋掉了  不是跟沒有這段代碼一樣嗎? 請指教
世界上分爲10种人,懂二進制和不懂二進制的

TOP

返回列表