HACK#49 关闭键盘的LED来省电

为了尽量减少电能消耗,关闭键盘LED灯等,对键盘进行控制。

小时候,父母经常教育我“房间里没人的时候要关灯”。突然看到键盘上还有LED灯闪着美丽的光芒却无人欣赏。要省电就必须关掉这些没有必要的LED。使用无线键盘等时,为了抑制电能消耗,可能会想要关闭显示键盘NumLock是否启用的LED等。这种情况下禁用NumLock也可以,这里介绍的是启用NumLock的同时关闭LED的方法。首先介绍怎样控制键盘,即PS/2键盘的规格。

PS/2键盘

我第一次接触电脑的时候,说起键盘,都是PS/2连接的。我家的电脑至今还有PS/2连接的键盘。AT键盘和PS/2键盘是IBM发布的主流键盘。PS/2键盘是作为能够与AT键盘互换的键盘发布的。PS/2是AT的扩展,添加了一些命令。笔者家用的键盘是PS/2兼容的键盘,但是操作系统启动时显示的是AT互换键盘。也有不少键盘不支持PS/2的所有命令。IBM原本只是将Intel i8042作为键盘输入的编码用控制器使用。

因此,PS/2兼容键盘一般是以PS/2兼容模式连接到Intel i8042互换芯片组的。PS/2连接的键盘,是通过PC主板上的i8042互换控制器(板载的微控制器)与键盘中的微控制器(键盘微控制器)进行通信来控制键盘。可以使用in命令、out命令经由0x60、0x64的I/O端口从CPU访问控制器。

通过读出(in命令)I/O端口0x64,就可以检查板载微控制器的状态。板载微控制器的Status寄存器各位的意义如表6-26所示。

HACK#49 关闭键盘的LED来省电 - 图1

针对I/O端口0x64执行Write(out命令)就可以向板载控制器发送命令。针对I/O端口0x60的Write命令被发送到键盘微控制器。

从键盘微控制器发送的数据,从I/O端口0x60读出。用于读取一般键盘输入等的数据。Status的第0位和第1位用于信号交换(握手)。在向I/O端口0x60、0x64进行写入(发送命令)前需要确认第0位是否为0(Output Buffer empty)。当第1位为1(Input Buffer full)时,可以从I/O端口0x60读取数据。细节参见表6-27。

HACK#49 关闭键盘的LED来省电 - 图2

命令0x20和0x60对板载微控制器的命令字节进行读写。这个命令字节的各位含义如表6-28所示。

HACK#49 关闭键盘的LED来省电 - 图3

向I/O端口0x60写入表示直接向键盘微控制器发送命令。向I/O端口0x60写入必须在确认Status的第0位为0后进行。向键盘微控制器发送的命令如表6-29所示。

HACK#49 关闭键盘的LED来省电 - 图4

HACK#49 关闭键盘的LED来省电 - 图5

通过上面的介绍,你应该已经了解LED的控制方法了。下面介绍如何控制LED的代码作为参考。


1#include<stdio.h>

2#include<sys/io.h>

3#define KBD_CMD_PORT 0x60

4#define OBD_STS_PORT 0x64

5

6#define OBS_FULL 0x1

7#define SEND_LED 0xED

8

9#define SCROLL_LOCK(1<<0)

10#define NUM_LOCK(1<<1)

11#define CAPS_LOCK(1<<2)

12

13 void send_cmd(cmd, port)

14{

15 char sts;

16 do{

17 sts=inb(OBD_STS_PORT);

18}while(sts&OBS_FULL);

19 outb(cmd, port);

20 usleep(100000);

21}

22

23 int main(void)

24{

25 ioperm(KBD_CMD_PORT,1,1);

26 ioperm(OBD_STS_PORT,1,1);

27

28/Turn on a LED of CAPS_LOCK/

29 send_cmd(SEND_LED, KBD_CMD_PORT);

30 send_cmd(CAPS_LOCK, KBD_CMD_PORT);

31

32/Turn on a LED of NUM_LOCK/

33 send_cmd(SEND_LED, KBD_CMD_PORT);

34 send_cmd(NUM_LOCK, KBD_CMD_PORT);

35

36/Turn on a LED of SCROLL_LOCK/

37 send_cmd(SEND_LED, KBD_CMD_PORT);

38 send_cmd(SCROLL_LOCK, KBD_CMD_PORT);

39

40/Turn off LEDs/

41 send_cmd(SEND_LED, KBD_CMD_PORT);

42 send_cmd(0,KBD_CMD_PORT);

43

44 ioperm(KBD_CMD_PORT,1,0);

45 ioperm(OBD_STS_PORT,1,0);

46 return 0;

47}


首先在第25、26行允许I/O端口0x60、0x64的访问。然后使用send_cmd()函数,在写入指定的I/O端口(port)前获取板载控制器的Status,确认Output Buffer为empty,确认后向I/O端口写入值(cmd)。在第29行为了点亮CAPS_LOCK的LED灯,发送命令(0xED)。然后写入用来亮灯的CAPS_LOCK的LED值(4)。按同样方法依次点亮NUM_LOCK、SCROLL_LOCK后,将所有的LED熄灭。