Keyboard驱动介绍

Keyboard驱动介绍

         最近手里面没啥事,就想看看一些Driver的MDD层。

         以前改过Keyboard Driver的PDD层,但是对它的MDD层还真是一片空白,这两天随便看了看Keyboard的MDD层,赶紧把东西记录下来,以防以过段时间忘记了。

         很多是个人理解,难免有错误的地方。

一.Keyboard Driver的加载过程
         系统启动过程中,GWES注册表HKEY_LOCAL_MACHINE/Hardware/DeviceMap/KEYBD 下的”Drivername”下去获取Keyboard Driver的名字,如果没找到,则使用默认的名字Keybddr.dll。

         加载的大概过程如下:

         首先GWES会去验证Keyboard Driver的导出接口是否存在;

         接下来去调用导出函数KeybdDriverInitializeEx(),对Keyboard Driver进行初始化。

二.有关Keyboard的几个概念
1.Device Layout
         以PC机键盘为例,有的键盘是101键,有的是108键(呵呵,记不清楚了,反正是有按键比较多的键盘),从外观上来看,它们的按键个数和键位不同,从功能上看,按键比较多的键盘支持更多的功能,其实这就是Keyboard Layout的含义。

         对于相同Layout不同厂家生产的键盘,相同按键产生的Scan Code必须是相同的,这也是PC机在不用装驱动的情况下可以支持任何市面上见到的键盘。

         对于WinCE系统而言,为了支持市面上常见的PC键盘,也需要支持这些标准的Layout,这也是WinCE中存在很多标准Layout的根本原因。

         具体到功能来说,Device Layout的功能就是负责Scan Code和Virtual Code的转换,以及Virtual Code的Remapping。比如,键盘上左边有一个Shift键,右边也有一个Shift键,它们的Scan Code和Virtual code不一样的,但是可以通过Remapping把它们的Virtual Code进行Remapping,转化为一样的Virtual Code。

         系统中的Layout都作了哪些工作,引用Help文档中的内容来进行说明:

The following list shows, in sequence, how the Layout Manager handles scan codes:

1.              PDD receives a scan code.

2.              PDD sends the scan code to the Layout Manager.

3.              Layout Manager converts the scan code to a virtual-key code based on the keyboard that sent the event and its current device layout.

4.              Layout Manager remaps the scan code based on the keyboard that sent the event and its current device layout.

5.              Layout Manager handles the auto-repeat functionality.

All keyboards share the same auto-repeat settings.

6.              Layout Manager calls keybd_event to send an event or events.
 


         Layout在注册表中的位置HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control /Layouts/{Layout name},观察会发现所有Layout下的DLL是同一个,这是因为默认情况下,DLL中包含了所有的Layout。

         另外,输入法也是要在HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control /Layouts/{Layout name}记录的DLL以及UI等信息,但实际上,并不是输入法真正的Layout,它实际使用的Layout是在通过注册表项"Keyboard Layout"指出来,例如双拼的注册表配置如下:

; Make your own IMEUI dll and change "UI Module" field to your dll name.

; Only need to export one API: void CALLBACK ImeGetUIClassName(LPTSTR);

; IME will call this API to copy your UI class name.

[HKEY_LOCAL_MACHINE/System/CurrentControlSet/Control/Layouts/e0010804]

; @CESYSGEN IF WCESHELLFE_MODULES_INTLL

; @CESYSGEN ENDIF

; @CESYSGEN IF WCESHELLFE_MODULES_INTLP

    "Layout Display Name"="@//Windows//intlp.cpl,-20483"

; @CESYSGEN ENDIF

    "Layout Text"="Microsoft CHS ShuangPin IME"

    "Ime File"="msimesp.dll"

    "UI Module"="msimeuic.dll"

    "Keyboard Layout"="00000409"
 


2.Input Language
         在Virtual Code和Unicode之间进行转换。

         Input Language的入口函数名字是:IL_{layout序号}。

3.Local
         引用Help文档来进行说明:

Pairing of an input language with an input method. The input locale identifier is a number. For example, the input locale identifier for a standard United States 101 keyboard is 00000409. The low word is the language identifier and the high word is a type identifier. The input locale identifier for a Dvorak keyboard is 00010409.

An input locale and an input locale handle differ. Once an input locale is loaded, Layout Manager generates a handle to an input locale (HKL) for the input locale that can be used with the keyboard APIs.
 


         注册表HKEY_CURRENT_USER/Keyboard Layout/Preload下指定了默认的Local,而HKEY_CURRENT_USER/Keyboard Layout/Preload/{number no more than 15}指出了可用的Local。

三.Keyboard中相关信息的获取方式
1.MDD层如何获取Device Layout信息
         理论上,Layout Manager可以管理多个PDD层,这些PDD层会组成一个链表,并用全局变量g_rgpfnPddEntries来表示所有PDD层的入口,在目前我们的项目上其值为:

PFN_KEYBD_PDD_ENTRY g_rgpfnPddEntries[] =

{

         PS2_NOP_Entry, Matrix_Entry, NULL

};
 


         GWES调用函数KeybdDriverInitializeEx()初始化Keyboard Driver的时候,会去引用PDD层的入口函数,获取到KEYBD_PDD后将其填充到MDD层的指针变量g_pPdds指向的KEYBD_PDD_INFO链表中,链表的长度取决于PDD层入口的个数。

         下面看一下KEYBD_PDD_INFO的定义:

typedef struct tagKEYBD_PDD_INFO {

    BOOL                fValid;

    PKEYBD_PDD          pKeybdPdd;

    DEVICE_LAYOUT_INFO  dli;

} KEYBD_PDD_INFO, *PKEYBD_PDD_INFO;
 


         其成员pKeybdPdd就指向了从PDD层获取的KEYBD_PDD,接下来看一下KEYBD_PDD的定义:

typedef struct tagKEYBD_PDD {

    WORD wPddMask; // Matches the keyboard layout with its PDD

    LPCTSTR pszName; // Used to identify PDD to user

    PFN_KEYBD_PDD_POWER_HANDLER pfnPowerHandler;

    PFN_KEYBD_PDD_TOGGLE_LIGHTS pfnToggleLights;

} KEYBD_PDD, *PKEYBD_PDD;
 


         有必要指出的是其第一个成员wPddMask,后面从注册表中查询Keyboard入口并获取Device Layout的时候,需要利用它判断系统中存在的所有Device Layout并找到最合适的一个。

         好了,接下来我们看一下函数KeybdDriverInitializeEx()的代码。

//----------------------------------------------------------------------------

//

// KeybdDriverInitializeEx

//

// Initializes the layout manager and any keyboard PDDs.

//

//----------------------------------------------------------------------------

extern "C"

void

KeybdDriverInitializeEx(

                        PFN_KEYBD_EVENT_CALLBACK_EX pfnKeybdEventCallbackEx // @parm The callback into the input system.

                        )

{

    SETFNAME(_T("KeybdDriverInitializeEx"));

   

    HKL hkl;

    PFN_KEYBD_PDD_ENTRY *ppfnKeybdPddEntry;

    UINT uiCurrPdd;

    TCHAR szDefaultName[KL_NAMELENGTH]= _T("");

   

    PREFAST_DEBUGCHK(pfnKeybdEventCallbackEx != NULL);

   

    InitializeCriticalSection(&g_csAccessInputLocaleInfo);

    LockConfig();

   

    // Set up critical sections, threads, events, etc.

    InitializeCriticalSection(&g_csEventCallback);

    g_hevBeginEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    g_hevEndEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

    g_hevExitNotficationThread = CreateEvent(NULL, FALSE, FALSE, NULL);

    g_hEventThread = CreateThread(NULL, 0, KeybdEventThreadProc, NULL, 0, NULL);

    HANDLE hNotificationThread = CreateThread(NULL, 0, KbdNotificationThread, NULL, 0, NULL);

    CloseHandle(hNotificationThread); // We do not need this handle anymore

   

    if ((g_hevBeginEvent == NULL) ||

        (g_hevEndEvent == NULL) ||

        (g_hevExitNotficationThread == NULL) ||

        (g_hEventThread == NULL))

    {

        ERRORMSG(1, (_T("Unable to set up keyboard layout manager/r/n")));

        if (g_hevBeginEvent != NULL) CloseHandle(g_hevBeginEvent);

        if (g_hevEndEvent != NULL) CloseHandle(g_hevEndEvent);

        if (g_hevExitNotficationThread != NULL) CloseHandle(g_hevExitNotficationThread);

        if (g_hEventThread != NULL) CloseHandle(g_hEventThread);

        DeleteCriticalSection(&g_csEventCallback);

       

        goto EXIT;

    }

   

    // Initialize our PDD data structures

    // Get a count of PDDs

    PREFAST_DEBUGCHK(g_rgpfnPddEntries != NULL);

    DEBUGCHK(g_cPdds == 0);

 

    // 获取PDD层的全局变量g_rgpfnPddEntries

    ppfnKeybdPddEntry = &g_rgpfnPddEntries[0];

 

    // 计算PDD层总共提供了多少个Entry,意思应该是说可以支持多个Entry,即支持多个键盘

    // PDD层的总数是g_cPdds

    while (*ppfnKeybdPddEntry != NULL) {

        ++g_cPdds;

        ++ppfnKeybdPddEntry;

    }

   

    // Allocate our PDD list

    // 在MDD层分配空间对PDD List进行管理

    DEBUGCHK(g_pPdds == NULL);

    g_pPdds =

        (PKEYBD_PDD_INFO) LocalAlloc(LPTR, sizeof(KEYBD_PDD_INFO) * g_cPdds);

    if (g_pPdds == NULL) {

        ERRORMSG(1, (_T("Out of memory/r/n")));

        goto EXIT;

    }   

   

    // Initialize each PDD

    // 通过引用pdd层的全局变量g_rgpfnPddEntries来调用pdd层的所有初始化函数

    // 另外,通过调用pdd层的初始化函数从pdd层获取获取KEYBD_PDD变量的值,并放到g_pPdds中

    for (uiCurrPdd = 0; uiCurrPdd < g_cPdds; ++uiCurrPdd)

    {

        // 获取刚才为管理PDD List而分配空间,第uiCurrPdd部分

        PKEYBD_PDD_INFO pKeybdPddInfo = pKeybdPddInfoFromId(uiCurrPdd);

       

        // 调用PDD层的第uiCurrPdd个Entry

        // 其中一个目的是获取PDD层的PKEYBD_PDD,一个典型的PKEYBD_PDD如

        //static KEYBD_PDD PS2NOPPdd = {

        //  PS2_NOP_PDD,

        //_T("PS/2 NOP"),

        //NULL, // Don't give the layout manager any PDD function pointers

        //NULL

        //};

        BOOL fNoErr = (*g_rgpfnPddEntries[uiCurrPdd])

            (uiCurrPdd, KeybdEventCallback, &pKeybdPddInfo->pKeybdPdd);

       

        if (fNoErr == FALSE) {

            // Something bad happened during PDD initialization. Mark it

            // as invalid.

            ERRORMSG(1, (_T("Keyboard: PDD %u initialization failed/r/n"),

                uiCurrPdd));           

            pKeybdPddInfo->fValid = FALSE;

            continue;

        }

       

        DEBUGCHK(pKeybdPddInfo->pKeybdPdd != NULL);

       

        PKEYBD_PDD pKeybdPdd = pKeybdPddInfo->pKeybdPdd;

       

        if (ValidateKeybdPdd(pKeybdPdd) == FALSE) {

            ERRORMSG(1, (_T("Keyboard: Invalid keybd PDD information for PDD %u/r/n"),

                uiCurrPdd));

            pKeybdPddInfo->fValid = FALSE;

            continue;

        } 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值