keyboard.docx

上传人:b****7 文档编号:10270603 上传时间:2023-02-09 格式:DOCX 页数:51 大小:38.35KB
下载 相关 举报
keyboard.docx_第1页
第1页 / 共51页
keyboard.docx_第2页
第2页 / 共51页
keyboard.docx_第3页
第3页 / 共51页
keyboard.docx_第4页
第4页 / 共51页
keyboard.docx_第5页
第5页 / 共51页
点击查看更多>>
下载资源
资源描述

keyboard.docx

《keyboard.docx》由会员分享,可在线阅读,更多相关《keyboard.docx(51页珍藏版)》请在冰豆网上搜索。

keyboard.docx

keyboard

键盘体系

author:

sniff

从什么开始说比较合理呀?

就从硬件开始把:

严格来说称不上什么键盘体系,但由于键盘的drivercode比较的涩晦,所以就称之为键盘体系了。

后注:

什么叫后注?

也就是写完后想说点什么的意思呀!

这篇文挡太长了(本来想写的更长,真的,还有一些文件都没有写上去呀),大家还是用“文挡结构图”来看把,厉害把,这么多,全部手写呀。

硬件相关

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

KeyboardKey

键盘代码

键盘模式

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

1)Scancodemode(raw)raw模式:

将键盘端口上读出的扫描码放入缓冲区

2)Keycodemode(mediumraw)mediumraw模式:

将扫描码过滤为键盘码放入缓冲区

3)ASCIImode(XLATE)XLATE模式:

识别各种键盘码的组合,转换为TTY终端代码放入缓冲区

4)UTF-8MODE(UNICODE)Unicode模式:

UNICODE模式基本上与XLATE相同,只不过可以通过数字小键盘间接输入UNICODE代码。

键盘模块的上层漫游:

键盘模块的最上层严格的说应该是TTY设备,这在以后描述。

对于使用者而言,keyboard.c是我们的最上层。

在keyboard.c中,不涉及底层操作,也不涉及到任何体系结构,他主要负责:

键盘初始化、键盘tasklet的挂入、按键盘后的处理、keymap的装入、scancode的转化、与TTY设备的通信。

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

Keyboard.c中的int__initkbd_init(void)函数是键盘代码执行的开始点,但keyboard.c并非是一定执行的,你必须先定义CONFIG_VT(Characterdevices->Virtualterminal)才有效,为什么?

因为kbd_init()是在tty_io.c的tty_init()中被调用的。

★Kbd_init()在进行一些基本初始化后,执行kbd_init_hw(),此函数可以看做是一个统一的上层界面,对于不同的体系或相同体系下不同的board,他们的kbd_init_hw()的实现代码是不同的(同过CONFIG_XXX_XXX来实现),他的实现代码就是操作硬件。

kbd_init_hw()会:

检查硬件及有效性、分配资源和中断、操作寄存器的实现函数调用。

初始化一完成,就把keyboard_tasklet放到CPUtasklet中(注意到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()放入到当前TTY的flipbuffer中,然后将缓冲区输出任务函数(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->atomic_read信号灯来竞争读取权.

Kbd_init()开始点:

int__initkbd_init(void)

{

inti;

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

externstructtty_driverconsole_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信息*/

#ifdefCONFIG_SA1100_ROSETTA

button_setup();/*initializebuttonhardware*/

#else

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

#endif

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

tasklet_enable(&keyboard_tasklet);

tasklet_schedule(&keyboard_tasklet);

/*下面register一个电源管理的KBD设备?

操作函数为NULL?

*/

pm_kbd=pm_register(PM_SYS_DEV,PM_SYS_KBC,NULL);

return0;

}

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/otherboard”4种已知硬件的操作代码,对于Assabet就会调用otherboard的代码,也就是无论什么函数都为NULL(因为没有这个硬件呀!

),CONFIG_SA1100_GRAPHICSCLIENT也就是ADS板的处理代码(SmartIO芯片决定的),CONFIG_SA1111也有自己的一块KBD芯片,而且功能居然和PC的差不多。

★那现在我们怎么办呀(我们的板不知道有没有KBD模块芯片)?

我们来挑一个最复杂的来分析把。

那就是SA1111了。

我们从kbd_init_hw()进入把:

void__initsa1111_init_hw(void)

{

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

SKPCR|=(1<<6);/*即0x4000,0000+0x0200处的register操作*/

/*以下初始化KBD的4个register,以便开始下面的test工作*/

KBDCLKDIV=0;

KBDPRECNT=127;

KBDCR=0x08;

mdelay(50);

KBDDATA=0xff;

mdelay(50);

/*Flushanypendinginput.*/

kbd_clear_input();

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

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

if(msg)

printk(KERN_WARNING"initialize_kbd:

%s\n",msg);

}

#ifdefinedCONFIG_PSMOUSE

psaux_init();

#endif

/*allocatetheIRQ,keyboard_interrupt是处理函数*/

kbd_request_irq(keyboard_interrupt);

#if0/*@@@*/

for(;;){

if(KBDSTAT&KBD_STAT_RXF)

printk("K_%.2x",KBDDATA&0xff);

if(MSESTAT&MSE_STAT_RXF)

printk("M_%.2x",MSEDATA&0xff);

}

#endif

#if0/*@@@*/

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文件,它包括了特殊的键盘命令操作,你可能需要对键盘的原理有所了解才看的懂。

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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 商务科技

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1