Linux键盘驱动详解 转_机械键盘linux驱动-程序员宅基地

技术标签: UBOOT  

转自

http://llq2993.blog.163.com/blog/static/3789655820092259534529/

 

Linux键盘驱动详解  

 

 

从什么开始说比较合理呀?就从硬件开始把:严格来说称不上什么键盘体系,但由于键盘的driver code比较的涩晦,所以就称之为键盘体系了。

后注:什么叫后注?也就是写完后想说点什么的意思呀!这篇文挡太长了(本来想写的更长,真的,还有一些文件都没有写上去呀),大家还是用文挡结构图来看把,厉害把,这么多,全部手写呀。

硬件相关硬件,其实有一些内容,但我实在不想一段段的翻译,大家想要的话,我把english文挡发给大家好了。

Keyboard Key

键盘代码

键盘模式

键盘模式有4种, Linux 下你可以用kbd_mode -参数 来设置和显示你的模式:

1 Scancode mode raw raw模式:将键盘端口上读出的扫描码放入缓冲区

2 Keycode mode (mediumraw) mediumraw模式:将扫描码过滤为键盘码放入缓冲区

3 ASCII mode (XLATE ) XLATE模式:识别各种键盘码的组合,转换为TTY终端代码放入缓冲区

4 UTF-8 MODE (UNICODE) Unicode 模式:UNICODE模式基本上与XLATE相同,只不过可以通过数字小键盘间接输入UNICODE代码。

键盘模块的上层漫游:

键盘模块的最上层严格的说应该是TTY设备,这在以后描述。对于使用者而言,keyboard.c是我们的最上层。

在keyboard.c中,不涉及底层操作,也不涉及到任何体系结构,他主要负责:键盘初始化、键盘tasklet的挂入、按键盘后的处理、keymap的装入、scancode的转化、与TTY设备的通信。

Keyboard.c是一个大杂烩,包含了大多数keyboardkey的处理,因此代码才变的庞大和复杂,我们可以修改它,让他变的很小(但这样做并不会使空间节省,因为keyboard.c的许多代码是动态加载的)。

Keyboard.c 中的int __init kbd_init(void) 函数是键盘代码执行的开始点,但keyboard.c并非是一定 执行的,你必须先定义CONFIG_VTCharacter devices ->nbsp;Virtual terminal)才有效,为什 么?因为kbd_init()是在tty_io.c tty_init()中被调用的。

Kbd_init()在进行一些基本初始化后,执行kbd_init_hw(),此函数可以看做是一个统一的上层界面,对于不同的体系或 相同体系下不同的board,他们的kbd_init_hw()的实现代码是不同的(同过CONFIG_XXX_XXX来实现),他的实现代码就是操作硬 件。 kbd_init_hw()会:检查硬件及有效性、分配资源和中断、操作寄存器的实现函数调用。

初始化一完成,就把keyboard_tasklet放到CPU tasklet中(注意到keyboard_tasklet 是开放出来的,等下可以看到怎么使用它)。

现在就等你按键了,当有按键时,会调用键盘中断处理函数,一般都是体系下的keyboard_interrupt(),此函数会调用到keyboard.c中的handle_scancode()

handle_scancode()就是我们的重点,这个函数最终决定了我们按下的key如何处理,在下面会分析他的流程,他主要做:与TTY的通信、keymap的装入、按键的处理。

handle_scancode()处理的结果就是把你按下的key发到不同的处理函数中,一般的,这些函数离最终的结果已经很近了。这其中,大多数的函数都会调用put_queue() 函数。

put_queue()的工作原理就是利用TTY, 它将经过转换的键盘功能码用tty_insert_flip_char()放入到当前 TTYflip buffer中,然后将缓冲区输出任务函数(flush_to_ldisc)添加到控制台任务队列(con_task_queue) 激活控制台软中断执行该任务函数. flush_to_ldisc()翻转读写缓冲区,将缓冲区接收数据传递给tty终端规程的 n_tty_receive_buf()接收函数,n_tty_receive_buf()处理输入字符,将输出字符缓冲在终端的循环缓冲区 (read_buf)之中.用户通过tty规程的read_chan()读取read_buf中的字符.当多个进程同时读取同一终端时, 使用tty- >tomic_read信号灯来竞争读取权.

Kbd_init()开始点:

int __init kbd_init(void)

{

int i;

struct kbd_struct kbd0; /* 定义一个kbd_struct, 他用于保存当前键盘LED灯状态、缺省keymap表、键盘复合锁定状态、一些功能灯的定义、键盘模式定义、及modeflags模式*/

extern struct tty_driver console_driver; /* console_driver这个全局变量是很重要的,他维护着庞大的TTY/Console对象,承当TTY对外的输入和输出 */

kbd0.ledflagstate = kbd0.default_ledflagstate = KBD_DEFLEDS; /* 缺省不亮灯 */

kbd0.ledmode = LED_SHOW_FLAGS; /* 缺省,用于显示flags */

kbd0.lockstate = KBD_DEFLOCK; /* 缺省,表示用key_map的第一个表,即没有lock*/

kbd0.slockstate = 0; /* 没有粘键 */

kbd0.modeflags = KBD_DEFMODE; /* modeflags=0 */

kbd0.kbdmode = VC_XLATE; /* ASCII模式 */

for (i = 0 ; i

kbd_table[i] = kbd0;

ttytab = console_driver.table; /* ttytab是一个很重要的指针,他维护着当前各个控制台的tty_struct 表(即相当于一个2维表),tty_struct可看成/dev/tty*的输入设备,只有在/dev/tty*打开时它才接收输入数据*/

/* 以下就是涉及到硬件的操作,由于我们没有KBD硬件,因此我们可能希望屏蔽所产生的error信息*/

#ifdef CONFIG_SA1100_ROSETTA

button_setup(); /* initialize button hardware */

#else

kbd_init_hw(); /*下面会分析他*/

#endif

/* 我们希望把keyboard_tasklet 挂到CPU的运行队列中去,下面就是 */

tasklet_enable(&eyboard_tasklet);

tasklet_schedule(&eyboard_tasklet);

/* 下面register一个电源管理的KBD设备?操作函数为NULL? */

pm_kbd = pm_register(PM_SYS_DEV, PM_SYS_KBC, NULL);

return 0;

}

kbd_init_hw()函数:

我们已经说了,kbd_init_hw() 相当与一个统一的界面,不同硬件操作代码是不同的,因为是涉及硬件的操作。

arm体系而言,目前的kbd_init_hw() 提供了4种硬件代码,象如果是ADS板,就要提供ADS的相应代码。

SA1100体系的此函数放在文件include/asm-arm/arch-sa1100/keyboard.h 中。大家可以看到在这个 文件中除了kbd_init_hw() 外,还有kbd_setkeycode() / kbd_getkeycode() / kbd_translate()/ ……等函数都是函数名相同,处理代码不同。而这些函数都会在keyboard.c中被调用到。

SA1100定义了 “CONFIG_SA1100_BRUTUS / CONFIG_SA1100_GRAPHICSCLIENT/ CONFIG_SA1111 / other board” 4种已知硬件的操作代码,对于Assabet就会调用other board的代码,也就是无论什么函数都为NULL(因为没有这个硬件呀!), CONFIG_SA1100_GRAPHICSCLIENT也就是ADS板的处理代码(SmartIO芯片决定的),CONFIG_SA1111也有自己 的一块KBD芯片,而且功能居然和PC的差不多。

那现在我们怎么办呀(我们的板不知道有没有KBD模块芯片)?我们来挑一个最复杂的来分析把。那就是SA1111了。

我们从kbd_init_hw() 进入把:

void __init sa1111_init_hw(void)

{

kbd_request_region(); /* 分配kbd端口,其实根本就不用分配了,对PC来说,0x60~0x68就是KBDIO口,对sa1100更不用分配了,所以此函数为NULL*/

SKPCR |= (1

/* 以下初始化KBD4register,以便开始下面的test工作 */

KBDCLKDIV = 0;

KBDPRECNT = 127;

KBDCR = 0x08;

mdelay(50);

KBDDATA = 0xff;

mdelay(50);

/* Flush any pending input. */

kbd_clear_input();

if (kbd_startup_reset) { /* int kbd_startup_reset =1,所以肯定执行 */

char *msg = initialize_kbd(); /* initialize_kbd() 真正开始init 硬件,出错会return错误message,显示在系统启动的时候 */

if (msg)

printk(KERN_WARNING "nitialize_kbd: %s\n" msg);

}

#if defined CONFIG_PSMOUSE

psaux_init();

#endif

/* allocate the IRQ, keyboard_interrupt 是处理函数 */

kbd_request_irq(keyboard_interrupt);

#if 0 /*@@@*/

for (;;) {

if (KBDSTAT KBD_STAT_RXF)

printk("_%.2x " KBDDATA 0xff);

if (MSESTAT MSE_STAT_RXF)

printk("_%.2x " MSEDATA 0xff);

}

#endif

#if 0 /*@@@*/

timer_table[COMTROL_TIMER].fn = pckbd_timer;

timer_table[COMTROL_TIMER].expires = jiffies + 2*HZ/100;

timer_active |= 1

#endif

}

initialize_kbd()函数:

这个函数如果逐层展开,会比较庞大,我们就分析最上层的把。

首先,要reset键盘,即对键盘的 KBDDATA寄存器写KBD_CMD_RESET数据,然后读状态位,假如超时,那么可能是没有AT 键盘挂上。这里有一个很重要的问题就是你在对register读的时候,先收到KBD_REPLY_ACK数据标志,然后再受到数据,因此你需要读2次。

另,这些硬件代码实在是太多了,我想,如果大家有什么不懂来问我把,我吃不削写了。这里涉及到很多的 键盘命令/键盘标志,你可以参考pc_keyb.h文件,它包括了特殊的键盘命令操作,你可能需要对键盘的原理有所了解才看的懂。

static char * __init initialize_kbd(void)

{

int status;

#if 0 /*@@@*/

/*

* Test the keyboard interface.

* This seems to be the only way to get it going.

* If the test is successful a x55 is placed in the input buffer.

*/

kbd_write_command_w(KBD_CCMD_SELF_TEST);

if (kbd_wait_for_input() != 0x55)

return "eyboard failed self test"

/*

* Perform a keyboard interface test. This causes the controller

* to test the keyboard clock and data lines. The results of the

* test are placed in the input buffer.

*/

kbd_write_command_w(KBD_CCMD_KBD_TEST);

if (kbd_wait_for_input() != 0x00)

return "eyboard interface failed self test"

/*

* Enable the keyboard by allowing the keyboard clock to run.

*/

kbd_write_command_w(KBD_CCMD_KBD_ENABLE);

#endif

/*

* Reset keyboard. If the read times out

* then the assumption is that no keyboard is

* plugged into the machine.

* This defaults the keyboard to scan-code set 2.

*

* Set up to try again if the keyboard asks for RESEND.

*/

do {

kbd_write_output_w(KBD_CMD_RESET);

status = kbd_wait_for_input();

if (status == KBD_REPLY_ACK)

break;

if (status != KBD_REPLY_RESEND)

return "eyboard reset failed, no ACK"

} while (1);

if (kbd_wait_for_input() != KBD_REPLY_POR)

return "eyboard reset failed, no POR"

/*

* Set keyboard controller mode. During this, the keyboard should be

* in the disabled state.

*

* Set up to try again if the keyboard asks for RESEND.

*/

do {

kbd_write_output_w(KBD_CMD_DISABLE);

status = kbd_wait_for_input();

if (status == KBD_REPLY_ACK)

break;

if (status != KBD_REPLY_RESEND)

return "isable keyboard: no ACK"

} while (1);

#if 0 /*@@@*/

kbd_write_command_w(KBD_CCMD_WRITE_MODE);

kbd_write_output_w(KBD_MODE_KBD_INT

| KBD_MODE_SYS

| KBD_MODE_DISABLE_MOUSE

| KBD_MODE_KCC);

/* ibm powerpc portables need this to use scan-code set 1 -- Cort */

kbd_write_command_w(KBD_CCMD_READ_MODE);

if (!(kbd_wait_for_input() KBD_MODE_KCC)) {

/*

* If the controller does not support conversion,

* Set the keyboard to scan-code set 1.

*/

kbd_write_output_w(0xF0);

kbd_wait_for_input();

kbd_write_output_w(0x01);

kbd_wait_for_input();

}

#else

kbd_write_output_w(0xf0);

kbd_wait_for_input();

kbd_write_output_w(0x01);

kbd_wait_for_input();

#endif

kbd_write_output_w(KBD_CMD_ENABLE);

if (kbd_wait_for_input() != KBD_REPLY_ACK)

return "nable keyboard: no ACK"

/*

* Finally, set the typematic rate to maximum.

*/

kbd_write_output_w(KBD_CMD_SET_RATE);

if (kbd_wait_for_input() != KBD_REPLY_ACK)

return "et rate: no ACK"

kbd_write_output_w(0x00);

if (kbd_wait_for_input() != KBD_REPLY_ACK)

return "et rate: no ACK"

return NULL;

}

键盘中断:

在做完init后,接下来就等待key press事件了,如果产生了key press,好,调到keyboard_interrupt()来处理,在sa1111中就是调用handle_kbd_event()函数。

handle_kbd_event ()函数:首先去读status port,看是不是有按键事件,然后判断是键盘还是鼠标的,如果是键盘的,读 scancode,然后判断status register的第8位是否为1,如果是1(表示正确,即key press时状态),那么调用 handle_keyboard_event(scancode)

static inline void handle_keyboard_event(unsigned char scancode)

{

#ifdef CONFIG_VT

kbd_exists = 1;

if (do_acknowledge(scancode)) /* 我们希望读出的不是键盘的CMD数据,正确,返回1,否则0 */

handle_scancode(scancode, !(scancode 0x80)); /* 这就是我 们要注意的最重要的函数,我们看到scancode 0x80,为什么要这样,因为KBDDATA register最高位是判断键是press还是 release,如果是1就是release。后面的7位才是我们要的数据。 */

#endif

tasklet_schedule(&eyboard_tasklet); /* 一不做,二不修,我们再一次把keyboard_tasklet放到tasklet中,确保bottom half的执行。*/

}

handle_scancode()函数:

进入handle_scancode() 你准备好了吗?,首先我们要看到此函数是kernel EXPORT出去的函数,也就是说我们可以在user-level程序中调用他,不过,如果把这段代码改写一下,在user-level中调用才有意义的多。

Handle_scancode()主要是:判断key是按下还是释放,然后把当前的tty_driver赋给变量ttytty_driver可看成/dev/tty*的输出设备,不要和tty_struct混乱起来。

void handle_scancode(unsigned char scancode, int down)

{

unsigned char keycode;

char up_flag = down ? 0 : 0200; /* 判断,如果是press那么up_flag=0,否则up_flag=0200 */

char raw_mode; /* 键盘模式 */

pm_access(pm_kbd);

add_keyboard_randomness(scancode | up_flag);

tty = ttytab? ttytab[fg_console]: NULL; /* 把当前的TTY参数传给变量 tty, tty来控制操作 */

if (tty & (!tty->river_data)) { /* 我们如果直接操作tty是危险的,因为我们不知道tty是否被打 开了,所以我们通过tty->river_data来判断,tty打开的时候,tty->river_data是被设置的,tty关闭的时 候,他被clear */

/*

* We touch the tty structure via the ttytab array

* without knowing whether or not tty is open, which

* is inherently dangerous. We currently rely on that

* fact that console_open sets tty->river_data when

* it opens it, and clears it when it closes it.

*/

tty = NULL;

}

kbd = kbd_table + fg_console; /*kbd_tablekbd[]的指针,所以kbd就是当前ttykbd_struct的指针 */

if ((raw_mode = (kbd->bdmode == VC_RAW))) { /* 我们判断当前的kbd模式是否为VC_RAW,我们知道如果是的话,我们就可以把scancode 直接放到tty_flip_buffer中。 */

put_queue(scancode | up_flag);

/* we do not return yet, because we want to maintain

the key_down array, so that we have the correct

values when finishing RAW mode or when changing VT' */

}

/*

* scancode 转化为 keycode kbd_translate()函数,

* 我们分析sa1111的。分析在下面

*/

if (!kbd_translate(scancode, &eycode, raw_mode))

return;

if (0) printk(__FUNCTION__ " scancode=%d keycode=%d raw_mode=%d\n" scancode, keycode, raw_mode);

/*

* 变量keycode包含的是键控代码(u-char

* 注意到keycode 不允许为0 (on m68k 0 是允许的).

* 我们明了了键的 up/down 状态,我们要把状态传进去

* 并且如果是MEDIUMRAW 模式,我们要返回keycode的值

*/

kbd_processkeycode(keycode, up_flag, 0);

}

sa1111_translate()函数:

因为scancode 转化成keycode 是这样的重要,所以我才不的不把他拿出来单独讲解了。我们只有知道scancodekeycode 的区别,我们才可以做下面的事情。

们知道我们从键盘的 IO口读出的是scancode,那我们怎么知道每个键的scancode 呀?通过:Linux下的showkey ? Cs我们就可以知道每个键的scancode 如果想知道相应的keycode,你重要用showkey就可以了。(后面的代码我写了一个模仿 showkey的程序)

通过测试,我们知道1~88键按下的scancode“0x**”,而89~128是以 “0xe0(0xe1) 0x**”表示的。如果是释放,只要加上个128就可以了。

然后我们来读以下的代码:

int sa1111_translate(unsigned char scancode, unsigned char *keycode,

char raw_mode)

{

static int prev_scancode = 0;

/* special prefix scancodes.. */

if (scancode == 0xe0 || scancode == 0xe1) {

prev_scancode = scancode;

return 0;

}

/* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */

if (scancode == 0x00 || scancode == 0xff) {

prev_scancode = 0;

return 0;

}

scancode & 0x7f;

if (prev_scancode) {

/*

* usually it will be 0xe0, but a Pause key generates

* e1 1d 45 e1 9d c5 when pressed, and nothing when released

*/

if (prev_scancode != 0xe0) {

if (prev_scancode == 0xe1 & scancode == 0x1d) {

prev_scancode = 0x100;

return 0;

} else if (prev_scancode == 0x100 & scancode == 0x45) {

*keycode = E1_PAUSE;

prev_scancode = 0;

} else {

#ifdef KBD_REPORT_UNKN

if (!raw_mode)

printk(KERN_INFO "eyboard: unknown e1 escape sequence\n";

#endif

prev_scancode = 0;

return 0;

}

} else {

prev_scancode = 0;

/*

* The keyboard maintains its own internal caps lock and

* num lock statuses. In caps lock mode E0 AA precedes make

* code and E0 2A follows break code. In num lock mode,

* E0 2A precedes make code and E0 AA follows break code.

* We do our own book-keeping, so we will just ignore these.

*/

/*

* For my keyboard there is no caps lock mode, but there are

* both Shift-L and Shift-R modes. The former mode generates

* E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.

* So, we should also ignore the latter. - [email protected]

*/

if (scancode == 0x2a || scancode == 0x36)

return 0;

if (e0_keys[scancode])

*keycode = e0_keys[scancode];

else {

#ifdef KBD_REPORT_UNKN

if (!raw_mode)

printk(KERN_INFO "eyboard: unknown scancode e0 %02x\n"

scancode);

#endif

return 0;

}

}

} else if (scancode > SC_LIM) {

/* This happens with the FOCUS 9000 keyboard

Its keys PF1..PF12 are reported to generate

55 73 77 78 79 7a 7b 7c 74 7e 6d 6f

Moreover, unless repeated, they do not generate

key-down events, so we have to zero up_flag below */

/* Also, Japanese 86/106 keyboards are reported to

generate 0x73 and 0x7d for \ - and \ | respectively. */

/* Also, some Brazilian keyboard is reported to produce

0x73 and 0x7e for \ ? and KP-dot, respectively. */

*keycode = high_keys[scancode - SC_LIM];

if (!*keycode) {

if (!raw_mode) {

#ifdef KBD_REPORT_UNKN

printk(KERN_INFO "eyboard: unrecognized scancode (%02x)"

"nbsp;- ignored\n" scancode);

#endif

}

return 0;

}

} else

*keycode = scancode;

return 1;

}

因为涉及到high_keys[] e0_keys[],因此我们把代码拿进来读一读:

static unsigned char high_keys[128 - SC_LIM] = {

RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */

0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */

0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */

FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */

FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */

};

static unsigned char e0_keys[128] = {

0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */

0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */

0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */

E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */

E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */

E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */

E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */

0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */

0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */

0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */

//0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */

0, 0, 0, 0, 0, E0_BACKSLASH, 0, 0, /* 0x70-0x77 */

0, 0, 0, E0_YEN, 0, 0, 0, 0 /* 0x78-0x7f */

};

我们可以知道scancode keycode的转化了。

对于1~88scancode,就等于keycode 比如 ’a’ scancode 0x1e0x9e,对应的keycode 就是30158

89-128scancode*keycode = e0_keys[scancode]来转化,我们可以举列,比如小键盘的回车: scancode = 0xe0 0x1c,(大家别奇怪怎么发送,它会先发送0xe0 然后发送0x1c,根据上面代码,流程应为: prev_scancode = 0xe0 -->nbsp;scancode = 0x1c -->nbsp;prev_scancode = 0 -->nbsp;执行 *keycode = e0_keys[scancode]; -->nbsp;找到e0_keys[] 0x1c个元素,即第28个元素,即E0_KPENTER,即为96#define E0_KPENTER 96)。

另对于键是按下还是放开,keycode是相同的。

kbd_processkeycode()函数:

这才是我们真正要仔细考虑和分析的函数,keyboard.c文件的庞大就是由于它的存在,kbd_processkeycode()最主要的工作就是根据 keycodekeymap 来决定最终的处理函数。

在这个函数里有几个很重要的变量:

1. 一个就是tty,它的值就是当前tty设备的环境;

2. u_short keysym 是我们真正要用的表示一个键的唯一的值,它是一个32位的数,高16位表示typetype的基数是0xf0;低16 表示键的值;现在你就知道了为什么我们要不费其烦的把scancode转化成keycode,就是因为我们要用keycode 来找到其对应的 key_map[]中的元素。

3. u_char type 就是keysym的高16位值,这个值一定大于0xf0。我们引入他是因为我们需要对不同的见类型做不同的处理代码,比如方向键,小键盘的键,功能键等的处理代码都是不同的。

4. int shift_final key_maps[]是一个2维表,那我们怎么知道我们要用哪张表呀,答案就是这个变量,他对kbd_struct中相应的变量进行读取,得到这个值,然后就用此值来决定使用哪张表,见下面代码。

5. ushort *key_maps[MAX_NR_KEYMAPS] =

{

plain_map, shift_map, altgr_map, 0,

ctrl_map, shift_ctrl_map, 0, 0,

alt_map, 0, 0, 0,

ctrl_alt_map, 0

};这个变量维护着所有的可能出现的表,每个表都有自己的128个元素。

6. ushort *key_map key_map就指向key_maps[]中具体的一张表了。见下面的代码

static void

kbd_processkeycode(unsigned char keycode, char up_flag, int autorepeat)

{

char raw_mode = (kbd->bdmode == VC_RAW); /* 键盘模式 */

if (up_flag) { /* 放开键 */

rep = 0;

if(!test_and_clear_bit(keycode, key_down))

up_flag = kbd_unexpected_up(keycode);

} else { /* 按下键 */

rep = test_and_set_bit(keycode, key_down);

/* If the keyboard autorepeated for us, ignore it.

* We do our own autorepeat processing.

*/

if (rep & !autorepeat)

return;

}

/* 以下是关与重发的处理,我们暂时忽略他 */

if (kbd_repeatkeycode == keycode || !up_flag || raw_mode) {

kbd_repeatkeycode = -1;

del_timer(&ey_autorepeat_timer);

}

do_poke_blanked_console = 1;

tasklet_schedule(&onsole_tasklet);

#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */

if (keycode == SYSRQ_KEY) {

sysrq_pressed = !up_flag;

return;

} else if (sysrq_pressed) {

if (!up_flag) {

handle_sysrq(kbd_sysrq_xlate[keycode], kbd_pt_regs, kbd, tty);

return;

}

}

#endif

/*

* Calculate the next time when we have to do some autorepeat

* processing. Note that we do not do autorepeat processing

* while in raw mode but we do do autorepeat processing in

* medium raw mode.

*/

if (!up_flag & !raw_mode) {

kbd_repeatkeycode = keycode;

if (vc_kbd_mode(kbd, VC_REPEAT)) {

if (rep)

key_autorepeat_timer.expires = jiffies + kbd_repeatinterval;

else

key_autorepeat_timer.expires = jiffies + kbd_repeattimeout;

add_timer(&ey_autorepeat_timer);

}

}

if (kbd->bdmode == VC_MEDIUMRAW) {

/* soon keycodes will require more than one byte */

put_queue(keycode + up_flag);

raw_mode = 1; /* Most key classes will be ignored */

}

/* 以下就是我们的重要的部分了,下面的代码必须被执行,由于代码展开的话比较琐碎,我把重要的地方用蓝色来表示 */

/*

* Small change in philosophy: earlier we defined repetition by

* rep = keycode == prev_keycode;

* prev_keycode = keycode;

* but now by the fact that the depressed key was down already.

* Does this ever make a difference? Yes.

*/

/*

* Repeat a key only if the input buffers are empty or the

* characters get echoed locally. This makes key repeat usable

* with slow applications and under heavy loads.

*/

if (!rep ||

(vc_kbd_mode(kbd,VC_REPEAT) & tty &

(L_ECHO(tty) || (tty->river.chars_in_buffer(tty) == 0))))

{

u_short keysym;

u_char type;

/* the XOR below used to be an OR */

/* 我们要计算出当前的键盘状态,键盘状态你别告诉我你不知道呀,就是有没有按住“shift / ctrl / alt / shift+alt /……”呀,当然还有正常状态,也就是什么都 没有按住(=0 */

int shift_final = (shift_state | kbd->lockstate) ^

kbd->ockstate;

ushort *key_map = key_maps[shift_final]; /* key_maps[shift_final] 的定义在 defkeymap.c中,他就是大名鼎鼎的key表,下面会详细的介绍,那么这个时候,key_map就指向具体的一张表了。 */

if (key_map != NULL) {

keysym = key_map[keycode]; /* 我们已经把scancode转化成了keycode,很多人问为什么,就是要在这里用呀,根据keycode找到一个相对应的keysym的值,keysym才是我们真正要用的值呀! */

type = KTYP(keysym); /* 分析一下那些key_maps表,你就知道了,type要取出这些32位数的高16位,为什么?因为, 16位是表示键类型用的,那什么叫键类型,键类型就在include/linux/keyboard.h 中定义,如KT_LATIN表示一般键, KT_CUR表示方向键(就4个,你别说不知道哪4个呀!),…………. */

if (0) printk(__FUNCTION__ " keysym=%#x type=%d shift_final=%d\n" keysym, type, shift_final);

if (type > 0xf0) /* 一定会执行了 */

{

type -= 0xf0; /* 我们要减掉一个基数0xf0,当然了,你也可以不减,那就直接判断呀。 */

if (raw_mode & ! (TYPES_ALLOWED_IN_RAW_MODE (1

return;

if (type == KT_LETTER) /* 大写键比较特殊,再次转化以后在执行 */

{

type = KT_LATIN; /* 一般的键的type */

if (vc_kbd_led(kbd, VC_CAPSLOCK))

{

key_map = key_maps[shift_final ^ (1

if (key_map)

keysym = key_map[keycode];

}

}

if (0) printk(__FUNCTION__ " key_handler=%p keysym=%#x up_flag=%d\n" key_handler[type], keysym, up_flag);

(*key_handler[type])(keysym 0xff, up_flag); /* 这就是重点中的重点了,针对不同的键,有不同的操 作。比如’a’和方向键的操作就是不同的,在keyboard.c中我们定义了很多的针对不同的键的不同处理函数,并且放在 static k_hand key_handler[16]中,根据type我们选择相应的处理函数,这些处理函数都带2个参数,第一个就是32位数 keysym的低16位,另一个就是up_flag标志。这句代码会去调用诸如:do_cur()即方向键的处理代码,do_shift()即按住 shift 键时的处理代码。 */

if (type != KT_SLOCK)

kbd->lockstate = 0;

} else {

/* maybe only if (kbd->bdmode == VC_UNICODE) ? */

if (!up_flag & !raw_mode)

to_utf8(keysym); /* */

}

} else {

/* maybe beep? */

/* we have at least to update shift_state */

#if 1 /* how? two almost equivalent choices follow */

compute_shiftstate();

kbd->lockstate = 0; /* play it safe */

#else

keysym = U(plain_map[keycode]);

type = KTYP(keysym);

if (type == KT_SHIFT)

(*key_handler[type])(keysym 0xff, up_flag);

#endif

}

}

rep = 0;

}

各种不同的键类型:

#define KT_LATIN 0 /* we depend on this being zero */

#define KT_LETTER 11 /* symbol that can be acted upon by CapsLock */

#define KT_FN 1 /* 功能键 */

#define KT_SPEC 2 /* 专用键 */

#define KT_PAD 3 /* 小键盘上的键 */

#define KT_DEAD 4 /* 没有使用过 */

#define KT_CONS 5 /* 没有使用过*/

#define KT_CUR 6 /* 方向键 */

#define KT_SHIFT 7 /* shift按下时?*/

#define KT_META 8

#define KT_ASCII 9

#define KT_LOCK 10 /* shift, alt, ctrl 是否被锁住 */

#define KT_SLOCK 12

ushort *key_maps[MAX_NR_KEYMAPS] = {

plain_map, shift_map, altgr_map, 0,

ctrl_map, shift_ctrl_map, 0, 0,

alt_map, 0, 0, 0,

ctrl_alt_map, 0

};

处理函数:

由于处理函数比较多,而且都涉及到tty的操作,因此我把处理函数的代码分析另外写一份文挡。

处理函数 Keysym Keycode 函数值 对应事件

Do_self 0xf01b 1 0x1b27 单击Esc

0xf031 2 0x31(49) 单击1

处理函数 Keysym Keycode Fuc_buf的值 对应事件

do_fn 0xf100 59 '033' '' '' '' 0, 单击F1

0xf101 60 '033' '' '' '' 0, 单击F2

处理函数

Do_spec Keysym Keycode 下一处理函数 对应事件

0xf200 0,89-95,120-127 NULL 没有定义

0xf201 28 Enter() 单击CR(回车)

0xf203 70 shift_map show_mem() Ctl+scrolllock

处理函数 Keysym Keycode 下一处理函数 对应事件

do_pad 0xf300 82 do_fn(KVAL(K_INSERT), 0) 单击kp_0

numlock 开、关分别处理

pad_chars[0]=0

0xf305 76 applkey('' vc_kbd_mode(kbd, VC_APPLIC)); 单击kp_5

numlock 开、关分别处理

pad_chars[5]=5

一段自己读取键盘按键处理的代码(模块式):

以下这段代码能够在X86上很好的运行,他主要是重新分配键盘的IRQ,读取键盘的数据寄存器,安排队列的运行,最终打印出scancode的值和按键状态。

#include

#include

#include

#include

/* Bottom Half - 一旦内核模块认为它做任何事都是安全的时候这将被内核调用。 */

static void got_char(void *scancode)

{

printk("can Code %x %s.n"

(int) *((char *) scancode) 0x7F,

*((char *) scancode) 0x80 ? "eleased"nbsp;: "ressed";

}

/* 这个函数为键盘中断服务。它读取来自键盘的相关信息然后安排当内核认为bottom half安全的时候让它运行 */

void irq_handler(int irq,

void *dev_id,

struct pt_regs *regs)

{

/* 这些变量是静态的,因为它们需要对 bottom half 可见(通过指针)。 */

static unsigned char scancode;

static struct tq_struct task = {NULL, 0, got_char, &cancode};

unsigned char status;

/* Read keyboard status */

status = inb(0x64);

scancode = inb(0x60);

/* 安排 bottom half 运行 */

#if LINUX_VERSION_CODE >nbsp;KERNEL_VERSION(2,2,0)

queue_task(&ask, &q_immediate);

#else

queue_task_irq(&ask, &q_immediate);

#endif

mark_bh(IMMEDIATE_BH);

}

/* 初始化模块--登记 IRQ 句柄 */

int init_module()

{

/* 既然键盘的句柄不能和我们的共存,在我们做事情前我们不得不关闭它(释放它的 IRQ)。

* 因为我们不知道它在哪儿,所以以后没有办法恢复它--因此当我们做完时计算机将被重新启动。

*/

free_irq(1, NULL);

/* 请求 IRQ 1,键盘的 IRQ,指向我们的 irq_handler */

return request_irq(

1, /* PC上的键盘的 IRQ */

irq_handler, /* 我们的句柄 */

SA_SHIRQ,

/* SA_SHIRQ 意味着我们将另一个句柄用于这个 IRQ

*

* SA_INTERRUPT 能使句柄为一个快速中断。

*/

"est_keyboard_irq_handler" NULL);

}

/* 清除 */

void cleanup_module()

{

/* 它在这儿只是为了完全。它是完全不相关的,因为我们没有办法恢复通常的键盘中断因此计算机完全没用 * 了,需要被重新启动。 */

free_irq(1, NULL);

}

一段功能和showkey同样强大的代码:

这段代码是参考了一个样本的,很有意义的代码,他演示了任何写一个应用程序,如何考虑到X的情况,如何使用信号来处理代码,如何使你的应用程序支持参数,以及如何使用tty参数,当然更重要的是如何实现showkey的功能(甚至实现了键盘模式设置和显示的代码)。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define VERSION "ia showkey 0.01"

int tmp; /* for debugging */

#define OPTION "edium raw"

extern int getfd();

int fd;

int oldkbmode;

struct termios old;

/*

* restore kbmode unconditially to XLATE,

* thus making the console unusable when it was called under X.

*/

void get_mode(void) {

char *m;

if (ioctl(fd, KDGKBMODE, &ldkbmode)) {

perror("DGKBMODE";

exit(1);

}

switch(oldkbmode)

{

case K_RAW:

m = "AW" break;

case K_XLATE:

m = "LATE" break;

case K_MEDIUMRAW:

m = "EDIUMRAW" break;

default:

m = "UNKNOWN?" break;

}

printf("b mode was %s\n" m);

if (oldkbmode != K_XLATE) {

printf(" if you are trying this under X, it might not work\n";

printf("ince the X server is also reading /dev/console ]\n";

}

printf("n";

}

void clean_up(void)

{

if (ioctl(fd, KDSKBMODE, oldkbmode)) {

perror("DSKBMODE";

exit(1);

}

tmp = tcsetattr(fd, 0, &ld);

if (tmp)

printf("csetattr = %d\n" tmp);

close(fd);

}

void die(int x) {

printf("aught signal %d, cleaning up...\n" x);

clean_up();

exit(1);

}

void watch_dog(int x) {

clean_up();

exit(0);

}

void usage(void) {

fprintf(stderr, "

showkey version "nbsp;VERSION "nbsp;("nbsp;OPTION "

usage: showkey [options...]

valid options are:

-h --help display this help text

-s --scancodes display only the raw scan-codes

-k --keycodes display only the interpreted keycodes (default)

";

exit(1);

}

int

main (int argc, char *argv[]) {

const char *short_opts = "sk"

const struct option long_opts[] = {

{ "elp" no_argument, NULL, ''nbsp;},

{ "cancodes" no_argument, NULL, ''nbsp;},

{ "eycodes" no_argument, NULL, ''nbsp;},

{ NULL, 0, NULL, 0 }

};

int c;

int show_keycodes = 1;

struct termios new;

unsigned char buf[16];

int i, n;

while ((c = getopt_long(argc, argv,

short_opts, long_opts, NULL)) != -1) {

switch (c) {

case ''

show_keycodes = 0;

break;

case ''

show_keycodes = 1;

break;

case ''

case ''

usage();

}

}

if (optind

usage();

fd = getfd();

/* the program terminates when there is no input for 10 secs */

signal(SIGALRM, watch_dog);

/*

if we receive a signal, we want to exit nicely, in

order not to leave the keyboard in an unusable mode

*/

signal(SIGHUP, die);

signal(SIGINT, die);

signal(SIGQUIT, die);

signal(SIGILL, die);

signal(SIGTRAP, die);

signal(SIGABRT, die);

signal(SIGIOT, die);

signal(SIGFPE, die);

signal(SIGKILL, die);

signal(SIGUSR1, die);

signal(SIGSEGV, die);

signal(SIGUSR2, die);

signal(SIGPIPE, die);

signal(SIGTERM, die);

#ifdef SIGSTKFLT

signal(SIGSTKFLT, die);

#endif

signal(SIGCHLD, die);

signal(SIGCONT, die);

signal(SIGSTOP, die);

signal(SIGTSTP, die);

signal(SIGTTIN, die);

signal(SIGTTOU, die);

get_mode();

tmp = tcgetattr(fd, &ld);

if (tmp)

printf("cgetattr = %d\n" tmp);

tmp = tcgetattr(fd, &ew);

if (tmp)

printf("cgetattr = %d\n" tmp);

new.c_lflag & ~ (ICANON | ECHO | ISIG);

new.c_iflag = 0;

new.c_cc[VMIN] = sizeof(buf);

new.c_cc[VTIME] = 1; /* 0.1 sec intercharacter timeout */

tmp = tcsetattr(fd, TCSAFLUSH, &ew);

if (tmp)

printf("csetattr = %d\n" tmp);

if (ioctl(fd, KDSKBMODE,

show_keycodes ? K_MEDIUMRAW : K_RAW)) {

perror("DSKBMODE";

exit(1);

}

printf("ress any key (program terminates after 10s of last keypress)...\n";

while (1) {

alarm(10);

n = read(fd, buf, sizeof(buf));

for (i = 0; i

if (!show_keycodes)

printf("x%02x " buf[i]);

else

printf("eycode %3d %s\n"

buf[i] 0x7f,

buf[i] 0x80 ? "elease"nbsp;: "ress";

}

if (!show_keycodes)

printf("n";

}

clean_up();

exit(0);

}

scancodekeycode一一对应:

以下其实就是defkeymap.map文件,你可以找出相应关系:

# Default kernel keymap. This uses 7 modifier combinations.

keymaps 0-2,4-5,8,12

# Change the above line into

# keymaps 0-2,4-6,8,12

# in case you want the entries

# altgr control keycode 83 = Boot

# altgr control keycode 111 = Boot

# below.

#

# In fact AltGr is used very little, and one more keymap can

# be saved by mapping AltGr to Alt (and adapting a few entries):

# keycode 100 = Alt

#

keycode 1 = Escape Escape

alt keycode 1 = Meta_Escape

keycode 2 = one exclam

alt keycode 2 = Meta_one

keycode 3 = two at at

control keycode 3 = nul

shift control keycode 3 = nul

alt keycode 3 = Meta_two

keycode 4 = three numbersign

control keycode 4 = Escape

alt keycode 4 = Meta_three

keycode 5 = four dollar dollar

control keycode 5 = Control_backslash

alt keycode 5 = Meta_four

keycode 6 = five percent

control keycode 6 = Control_bracketright

alt keycode 6 = Meta_five

keycode 7 = six asciicircum

control keycode 7 = Control_asciicircum

alt keycode 7 = Meta_six

keycode 8 = seven ampersand braceleft

control keycode 8 = Control_underscore

alt keycode 8 = Meta_seven

keycode 9 = eight asterisk bracketleft

control keycode 9 = Delete

alt keycode 9 = Meta_eight

keycode 10 = nine parenleft bracketright

alt keycode 10 = Meta_nine

keycode 11 = zero parenright braceright

alt keycode 11 = Meta_zero

keycode 12 = minus underscore backslash

control keycode 12 = Control_underscore

shift control keycode 12 = Control_underscore

alt keycode 12 = Meta_minus

keycode 13 = equal plus

alt keycode 13 = Meta_equal

keycode 14 = Delete Delete

control keycode 14 = BackSpace

alt keycode 14 = Meta_Delete

keycode 15 = Tab Tab

alt keycode 15 = Meta_Tab

keycode 16 = q

keycode 17 = w

keycode 18 = e

altgr keycode 18 = Hex_E

keycode 19 = r

keycode 20 = t

keycode 21 = y

keycode 22 = u

keycode 23 = i

keycode 24 = o

keycode 25 = p

keycode 26 = bracketleft braceleft

control keycode 26 = Escape

alt keycode 26 = Meta_bracketleft

keycode 27 = bracketright braceright asciitilde

control keycode 27 = Control_bracketright

alt keycode 27 = Meta_bracketright

keycode 28 = Return

alt keycode 28 = Meta_Control_m

keycode 29 = Control

keycode 30 = a

altgr keycode 30 = Hex_A

keycode 31 = s

keycode 32 = d

altgr keycode 32 = Hex_D

keycode 33 = f

altgr keycode 33 = Hex_F

keycode 34 = g

keycode 35 = h

keycode 36 = j

keycode 37 = k

keycode 38 = l

keycode 39 = semicolon colon

alt keycode 39 = Meta_semicolon

keycode 40 = apostrophe quotedbl

control keycode 40 = Control_g

alt keycode 40 = Meta_apostrophe

keycode 41 = grave asciitilde

control keycode 41 = nul

alt keycode 41 = Meta_grave

keycode 42 = Shift

keycode 43 = backslash bar

control keycode 43 = Control_backslash

alt keycode 43 = Meta_backslash

keycode 44 = z

keycode 45 = x

keycode 46 = c

altgr keycode 46 = Hex_C

keycode 47 = v

keycode 48 = b

altgr keycode 48 = Hex_B

keycode 49 = n

keycode 50 = m

keycode 51 = comma less

alt keycode 51 = Meta_comma

keycode 52 = period greater

control keycode 52 = Compose

alt keycode 52 = Meta_period

keycode 53 = slash question

control keycode 53 = Delete

alt keycode 53 = Meta_slash

keycode 54 = Shift

keycode 55 = KP_Multiply

keycode 56 = Alt

keycode 57 = space space

control keycode 57 = nul

alt keycode 57 = Meta_space

keycode 58 = Caps_Lock

keycode 59 = F1 F11 Console_13

control keycode 59 = F1

alt keycode 59 = Console_1

control alt keycode 59 = Console_1

keycode 60 = F2 F12 Console_14

control keycode 60 = F2

alt keycode 60 = Console_2

control alt keycode 60 = Console_2

keycode 61 = F3 F13 Console_15

control keycode 61 = F3

alt keycode 61 = Console_3

control alt keycode 61 = Console_3

keycode 62 = F4 F14 Console_16

control keycode 62 = F4

alt keycode 62 = Console_4

control alt keycode 62 = Console_4

keycode 63 = F5 F15 Console_17

control keycode 63 = F5

alt keycode 63 = Console_5

control alt keycode 63 = Console_5

keycode 64 = F6 F16 Console_18

control keycode 64 = F6

alt keycode 64 = Console_6

control alt keycode 64 = Console_6

keycode 65 = F7 F17 Console_19

control keycode 65 = F7

alt keycode 65 = Console_7

control alt keycode 65 = Console_7

keycode 66 = F8 F18 Console_20

control keycode 66 = F8

alt keycode 66 = Console_8

control alt keycode 66 = Console_8

keycode 67 = F9 F19 Console_21

control keycode 67 = F9

alt keycode 67 = Console_9

control alt keycode 67 = Console_9

keycode 68 = F10 F20 Console_22

control keycode 68 = F10

alt keycode 68 = Console_10

control alt keycode 68 = Console_10

keycode 69 = Num_Lock

shift keycode 69 = Bare_Num_Lock

keycode 70 = Scroll_Lock Show_Memory Show_Registers

control keycode 70 = Show_State

alt keycode 70 = Scroll_Lock

keycode 71 = KP_7

alt keycode 71 = Ascii_7

altgr keycode 71 = Hex_7

keycode 72 = KP_8

alt keycode 72 = Ascii_8

altgr keycode 72 = Hex_8

keycode 73 = KP_9

alt keycode 73 = Ascii_9

altgr keycode 73 = Hex_9

keycode 74 = KP_Subtract

keycode 75 = KP_4

alt keycode 75 = Ascii_4

altgr keycode 75 = Hex_4

keycode 76 = KP_5

alt keycode 76 = Ascii_5

altgr keycode 76 = Hex_5

keycode 77 = KP_6

alt keycode 77 = Ascii_6

altgr keycode 77 = Hex_6

keycode 78 = KP_Add

keycode 79 = KP_1

alt keycode 79 = Ascii_1

altgr keycode 79 = Hex_1

keycode 80 = KP_2

alt keycode 80 = Ascii_2

altgr keycode 80 = Hex_2

keycode 81 = KP_3

alt keycode 81 = Ascii_3

altgr keycode 81 = Hex_3

keycode 82 = KP_0

alt keycode 82 = Ascii_0

altgr keycode 82 = Hex_0

keycode 83 = KP_Period

# altgr control keycode 83 = Boot

control alt keycode 83 = Boot

keycode 84 = Last_Console

keycode 85 =

keycode 86 = less greater bar

alt keycode 86 = Meta_less

keycode 87 = F11 F11 Console_23

control keycode 87 = F11

alt keycode 87 = Console_11

control alt keycode 87 = Console_11

keycode 88 = F12 F12 Console_24

control keycode 88 = F12

alt keycode 88 = Console_12

control alt keycode 88 = Console_12

keycode 89 =

keycode 90 =

keycode 91 =

keycode 92 =

keycode 93 =

keycode 94 =

keycode 95 =

keycode 96 = KP_Enter

keycode 97 = Control

keycode 98 = KP_Divide

keycode 99 = Control_backslash

control keycode 99 = Control_backslash

alt keycode 99 = Control_backslash

keycode 100 = AltGr

keycode 101 = Break

keycode 102 = Find

keycode 103 = Up

keycode 104 = Prior

shift keycode 104 = Scroll_Backward

keycode 105 = Left

alt keycode 105 = Decr_Console

keycode 106 = Right

alt keycode 106 = Incr_Console

keycode 107 = Select

keycode 108 = Down

keycode 109 = Next

shift keycode 109 = Scroll_Forward

keycode 110 = Insert

keycode 111 = Remove

# altgr control keycode 111 = Boot

control alt keycode 111 = Boot

keycode 112 = Macro

keycode 113 = F13

keycode 114 = F14

keycode 115 = Help

keycode 116 = Do

keycode 117 = F17

keycode 118 = KP_MinPlus

keycode 119 = Pause

keycode 120 =

keycode 121 =

keycode 122 =

keycode 123 =

keycode 124 =

keycode 125 =

keycode 126 =

keycode 127 =

string F1 = "033[[A"

string F2 = "033[[B"

string F3 = "033[[C"

string F4 = "033[[D"

string F5 = "033[[E"

string F6 = "033[17~"

string F7 = "033[18~"

string F8 = "033[19~"

string F9 = "033[20~"

string F10 = "033[21~"

string F11 = "033[23~"

string F12 = "033[24~"

string F13 = "033[25~"

string F14 = "033[26~"

string F15 = "033[28~"

string F16 = "033[29~"

string F17 = "033[31~"

string F18 = "033[32~"

string F19 = "033[33~"

string F20 = "033[34~"

string Find = "033[1~"

string Insert = "033[2~"

string Remove = "033[3~"

string Select = "033[4~"

string Prior = "033[5~"

string Next = "033[6~"

string Macro = "033[M"

string Pause = "033[P"

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''#39; ''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '

compose 'quot;'nbsp;''nbsp;to ''

compose ''nbsp;''nbsp;to '

compose ''nbsp;''nbsp;to '' 

 

 

 

 

  1. static unsigned char usb_kbd_keycode[256] = {  
  2.       0,  0,  0,  0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,  
  3.      50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44,  2,  3,  
  4.       4,  5,  6,  7,  8,  9, 10, 11, 28,  1, 14, 15, 57, 12, 13, 26,  
  5.      27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,  
  6.      65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,  
  7.     105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,  
  8.      72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,  
  9.     191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,  
  10.     115,114,  0,  0,  0,121,  0, 89, 93,124, 92, 94, 95,  0,  0,  0,  
  11.     122,123, 90, 91, 85,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  12.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  13.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  14.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  15.       0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  
  16.      29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,  
  17.     150,158,159,128,136,177,178,176,142,152,173,140  
  18. };  
  19.   

 

 

 

 

 

 

 

keycode对照表:
字母和数字键的键码值(keyCode)
按键 键码 按键 键码 按键 键码 按键 键码
A 65 J 74 S 83 1 49
B 66 K 75 T 84 2 50
C 67 L 76 U 85 3 51
D 68 M 77 V 86 4 52
E 69 N 78 W 87 5 53
F 70 O 79 X 88 6 54
G 71 P 80 Y 89 7 55
H 72 Q 81 Z 90 8 56
I 73 R 82 0 48 9 57

数字键盘上的键的键码值(keyCode) 功能键键码值(keyCode)
按键 键码 按键 键码 按键 键码 按键 键码
0 96 8 104 F1 112 F7 118
1 97 9 105 F2 113 F8 119
2 98 * 106 F3 114 F9 120
3 99 + 107 F4 115 F10 121
4 100 Enter 108 F5 116 F11 122
5 101 - 109 F6 117 F12 123
6 102 . 110
7 103 / 111

控制键键码值(keyCode)
按键 键码 按键 键码 按键 键码 按键 键码
BackSpace 8 Esc 27 Right Arrow 39 -_ 189
Tab 9 Spacebar 32 Dw Arrow 40 .> 190
Clear 12 Page Up 33 Insert 45 /? 191
Enter 13 Page Down 34 Delete 46 `~ 192
Shift 16 End 35 Num Lock 144 [{ 219
Control 17 Home 36 ;: 186 \| 220
Alt 18 Left Arrow 37 =+ 187 ]} 221
Cape Lock 20 Up Arrow 38 ,< 188 ‘” 222

多媒体键码值(keyCode)
按键 键码 按键 键码 按键 键码 按键 键码
音量加 175
音量减 174
停止 179
静音 173
浏览器 172
邮件 180
搜索 170
收藏 171

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/yu704645129/article/details/85862436

智能推荐

【基础算法】反转链表的三种方法_链表反转-程序员宅基地

文章浏览阅读3.3k次,点赞13次,收藏20次。【基础算法】反转链表的三种方法_链表反转

使用遗传算法优化的BP神经网络实现自变量降维_bp神经网络优化-程序员宅基地

文章浏览阅读317次。本次利用遗传算法筛选出最具有代表的自变量,再利用BP神经网络进行预测。_bp神经网络优化

第十四届蓝桥杯青少组选拔赛Python真题 (2022年11月27日),包含答案_python 蓝桥杯真题-程序员宅基地

文章浏览阅读2.5k次,点赞2次,收藏6次。初始客户编号为 1、2、3 的客户分别在 1、2、3 窗口同时办理业务;窗口 3 用时 2 分钟办理完 3 号客户的业务,变为空闲状态,并按顺序呼叫 4 号客户,4 号客户用时 4 分钟窗口 1 用时 3 分钟办理完 1 号客户的业务,变为空闲状态,并按顺序呼叫 5 号客户,5 号客户用时 7 分钟。例如: N=3.M=7、从编号3 的位置到综号 7 的位置共有5 条路线,分别为: (3->5->7),(3-5->6->7,(3->4-5->7(3->4->5->6>7) ,(3->4>6->7)。_python 蓝桥杯真题

基于RK3399 Android11适配OV13850 MIPI摄像头_camera_etc.mk-程序员宅基地

文章浏览阅读1.4k次,点赞22次,收藏21次。基于RK3399 Android11 适配MIPI摄像头 OV13850_camera_etc.mk

2020公文格式模板及范文_公文写作格式+请示、报告、会议纪要基本模板-程序员宅基地

文章浏览阅读6.4k次。一、排版1.WORD文档页面设置,页边距:上3.7cm,下3.5cm,左2.8cm,右2.6cm。具体操作过程中,根据版式要求,也可适当调整。2.字体要求:文章标题为二号方正小标宋、居中,标题内容多,可分多行,排成梯形或菱形,标题内容换行时注意词意完整,标题行距选择磅值约30-34之间,可根据版面自行设定。正文为三号仿宋,行距选择1.5倍行距,也可根据页面做适当调整。3.正文结束空2-3..._请示的页边距

Pandas-处理文本字符串(拼接)_pandas字符串拼接-程序员宅基地

文章浏览阅读6.8k次。Pandas提供了不同的方法将序列或索引与他们自己或者其他的对象进行拼接,所有的方法都是基于各自的cat()方法1.将单个序列拼接为一个完整字符串输出:2. 如果没有额外声明,sep即分隔符默认为空字串,即sep='':输出:3.默认情况下,缺失值会被忽略。使用na_rep参数,可以对缺失值进行赋值:输出:4.拼接序列和其他类列表型对象为新的序列cat()的第一个参数为类列表对象,但必须要确保长度与序列或索引相同.输出:..._pandas字符串拼接

随便推点

LLaMA 2 - 最全资源汇总,你想要的都有_llama-2-7b-chat-gguf 百度网盘-程序员宅基地

文章浏览阅读553次。LLaMA 2 是 Meta 开发的大型语言模型,是 LLaMA 1 的后继者。LLaMA 2 可通过 AWS、Hugging Face 等提供商免费用于研究和商业用途。LLaMA 2 预训练模型接受了 2 万亿个标记的训练,上下文长度是 LLaMA 1 的两倍。其微调模型已经接受了超过 100 万个人工注释的训练。本文包含 LLama 2 所有相关资源,可帮助您快速入门。它包括以下链接:LLaMA 2 是什么?Lllama 2在线体验Llama2 背后的研究Llama 2 基准测试有多好。_llama-2-7b-chat-gguf 百度网盘

《C语言程序设计》谭浩强-学习笔记以及课后习题答案(考前复习/考研/专升本)_谭浩强课后习题csdn-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏26次。《C语言程序设计》谭浩强-学习笔记-课后习题答案(考前复习/考研/专升本/)_谭浩强课后习题csdn

Knowledge Distillation by On-the-Fly Native Ensemble论文解读_on the fly 蒸馏-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏2次。1. 网络结构: Gate为全连接网络,用来学习哪个网络更重要。目前利用全连接网络选择网络部件重要性的方法很流行。“三个臭皮匠顶个诸葛亮?”,感觉很像bagging方法。2. 损失函数: 训练时softmax都有温度T=3蒸馏,测试时就恢复T=1。 最终的Loss 第一项代表各个分支的损失,第二项代表最后Teacher的损失,第三项代表各..._on the fly 蒸馏

Mac 屏幕录制 权限 没有可勾选或添加的App选项 产生原因和解决办法_mac屏幕录制没有微信选项-程序员宅基地

文章浏览阅读4.1w次,点赞12次,收藏39次。遇到问题:安装软件需要获取截屏和屏幕录制的功能权限,但是发现打开系统设置->安全性与隐私-屏幕录制,右边竟然没有可勾选或可添加的App选项产生原因:经过网络搜索关键字,发现是因为系统升级到10.5,MacCataLina过程中位于/Library/Application Support/com.apple.TCC目录下的TCC.db文件损坏了。期间遇到的错误提示(Error: table access has 7 columns but 12 values were supplied)..._mac屏幕录制没有微信选项

python一些练手小项目_pycharm 练手程序-程序员宅基地

文章浏览阅读260次。python一些练手小项目参考Pycharm+django2.2+python3.6+MySQL实现简单的考试报名系统Pycharm+Django之使用模型django基础之数据库操作使用pycharm调试django项目_pycharm 练手程序

汇编语言 第三版 王爽 实验四_汇编语言第三版实验4第三题-程序员宅基地

文章浏览阅读8.2k次,点赞4次,收藏17次。百度文库答案有误。特写此博客。_汇编语言第三版实验4第三题

推荐文章

热门文章

相关标签