#endif
}
initialize_kbd()函数:
这个函数如果逐层展开,会比较庞大,我们就分析最上层的把。
首先,要reset键盘,即对键盘的KBDDATA寄存器写KBD_CMD_RESET数据,然后读状态位,假如超时,那么可能是没有AT键盘挂上。
这里有一个很重要的问题就是你在对register读的时候,先收到KBD_REPLY_ACK数据标志,然后再受到数据,因此你需要读2次。
另,这些硬件代码实在是太多了,我想,如果大家有什么不懂来问我把,我吃不削写了。
这里涉及到很多的键盘命令/键盘标志,你可以参考pc_keyb.h文件,它包括了特殊的键盘命令操作,你可能需要对键盘的原理有所了解才看的懂。
staticchar*__initinitialize_kbd(void)
{
intstatus;
#if0/*@@@*/
/*
*Testthekeyboardinterface.
*Thisseemstobetheonlywaytogetitgoing.
*Ifthetestissuccessfulax55isplacedintheinputbuffer.
*/
kbd_write_command_w(KBD_CCMD_SELF_TEST);
if(kbd_wait_for_input()!
=0x55)
return"Keyboardfailedselftest";
/*
*Performakeyboardinterfacetest.Thiscausesthecontroller
*totestthekeyboardclockanddatalines.Theresultsofthe
*testareplacedintheinputbuffer.
*/
kbd_write_command_w(KBD_CCMD_KBD_TEST);
if(kbd_wait_for_input()!
=0x00)
return"Keyboardinterfacefailedselftest";
/*
*Enablethekeyboardbyallowingthekeyboardclocktorun.
*/
kbd_write_command_w(KBD_CCMD_KBD_ENABLE);
#endif
/*
*Resetkeyboard.Ifthereadtimesout
*thentheassumptionisthatnokeyboardis
*pluggedintothemachine.
*Thisdefaultsthekeyboardtoscan-codeset2.
*
*SetuptotryagainifthekeyboardasksforRESEND.
*/
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"Keyboardresetfailed,noACK";
}while
(1);
if(kbd_wait_for_input()!
=KBD_REPLY_POR)
return"Keyboardresetfailed,noPOR";
/*
*Setkeyboardcontrollermode.Duringthis,thekeyboardshouldbe
*inthedisabledstate.
*
*SetuptotryagainifthekeyboardasksforRESEND.
*/
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"Disablekeyboard:
noACK";
}while
(1);
#if0/*@@@*/
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);
/*ibmpowerpcportablesneedthistousescan-codeset1--Cort*/
kbd_write_command_w(KBD_CCMD_READ_MODE);
if(!
(kbd_wait_for_input()&KBD_MODE_KCC)){
/*
*Ifthecontrollerdoesnotsupportconversion,
*Setthekeyboardtoscan-codeset1.
*/
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"Enablekeyboard:
noACK";
/*
*Finally,setthetypematicratetomaximum.
*/
kbd_write_output_w(KBD_CMD_SET_RATE);
if(kbd_wait_for_input()!
=KBD_REPLY_ACK)
return"Setrate:
noACK";
kbd_write_output_w(0x00);
if(kbd_wait_for_input()!
=KBD_REPLY_ACK)
return"Setrate:
noACK";
returnNULL;
}
键盘中断:
在做完init后,接下来就等待keypress事件了,如果产生了keypress,好,调到keyboard_interrupt()来处理,在sa1111中就是调用handle_kbd_event()函数。
handle_kbd_event()函数:
首先去读statusport,看是不是有按键事件,然后判断是键盘还是鼠标的,如果是键盘的,读出scancode,然后判断statusregister的第8位是否为1,如果是1(表示正确,即keypress时状态),那么调用handle_keyboard_event(scancode)。
staticinlinevoidhandle_keyboard_event(unsignedcharscancode)
{
#ifdefCONFIG_VT
kbd_exists=1;
if(do_acknowledge(scancode))/*我们希望读出的不是键盘的CMD数据,正确,返回1,否则0*/
handle_scancode(scancode,!
(scancode&0x80));/*这就是我们要注意的最重要的函数,我们看到scancode&0x80,为什么要这样,因为KBDDATAregister最高位是判断键是press还是release,如果是1就是release。
后面的7位才是我们要的数据。
*/
#endif
tasklet_schedule(&keyboard_tasklet);/*一不做,二不修,我们再一次把keyboard_tasklet放到tasklet中,确保bottomhalf的执行。
*/
}
handle_scancode()函数:
进入handle_scancode(),你准备好了吗?
,首先我们要看到此函数是kernelEXPORT出去的函数,也就是说我们可以在user-level程序中调用他,不过,如果把这段代码改写一下,在user-level中调用才有意义的多。
Handle_scancode()主要是:
判断key是按下还是释放,然后把当前的tty_driver赋给变量tty,tty_driver可看成/dev/tty*的输出设备,不要和tty_struct混乱起来。
voidhandle_scancode(unsignedcharscancode,intdown)
{
unsignedcharkeycode;
charup_flag=down?
0:
0200;/*判断,如果是press那么up_flag=0,否则up_flag=0200*/
charraw_mode;/*键盘模式*/
pm_access(pm_kbd);
add_keyboard_randomness(scancode|up_flag);
tty=ttytab?
ttytab[fg_console]:
NULL;/*把当前的TTY参数传给变量tty,由tty来控制操作*/
if(tty&&(!
tty->driver_data)){/*我们如果直接操作tty是危险的,因为我们不知道tty是否被打开了,所以我们通过tty->driver_data来判断,tty打开的时候,tty->driver_data是被设置的,tty关闭的时候,他被clear*/
/*
*Wetouchthettystructureviathettytabarray
*withoutknowingwhetherornotttyisopen,which
*isinherentlydangerous.Wecurrentlyrelyonthat
*factthatconsole_opensetstty->driver_datawhen
*itopensit,andclearsitwhenitclosesit.
*/
tty=NULL;
}
kbd=kbd_table+fg_console;/*kbd_table是kbd[]的指针,所以kbd就是当前tty的kbd_struct的指针*/
if((raw_mode=(kbd->kbdmode==VC_RAW))){/*我们判断当前的kbd模式是否为VC_RAW,我们知道如果是的话,我们就可以把scancode直接放到tty_flip_buffe