windows-nt/Source/XPSP1/NT/drivers/wdm/input/hidparse/trnslate.c
2020-09-26 16:20:57 +08:00

816 lines
22 KiB
C

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
query.c
Abstract:
This module contains the code for Translating HID report packets.
Environment:
Kernel & user mode
Revision History:
Nov-96 : created by Kenneth Ray
--*/
#include <wtypes.h>
#include "hidsdi.h"
#include "hidparse.h"
NTSTATUS __stdcall
HidP_UsageListDifference (
IN PUSAGE PreviousUsageList,
IN PUSAGE CurrentUsageList,
OUT PUSAGE BreakUsageList,
OUT PUSAGE MakeUsageList,
IN ULONG UsageListLength
)
/*++
Routine Description:
Please see hidpi.h for description
Notes:
--*/
{
ULONG i,j;
ULONG test;
ULONG b; // an index into MakeUsageList
ULONG m; // an index into BreakUsageList
USHORT usage;
BOOLEAN found;
b = m = 0;
//
// This assumes that UsageListLength will be a small number.
// No keyboard today can generate more than 14 keys in one packet, and
// there is no anticipation for other devices with greater than 14 usages
// at once. For this reason the straight forward naive approach follows...
//
// These lists are not sorted.
//
//
// Find the Old usages.
//
for (i=0; i<UsageListLength; i++) {
usage = PreviousUsageList[i];
if (0 == usage) {
break; // Zeros Only at the end.
}
found = FALSE;
for (j=0; j<UsageListLength; j++) {
test = CurrentUsageList [j];
if (0 == test) {
break; // Zeros only at the end.
}
if (test == usage) {
found = TRUE;
break;
}
}
if (!found) {
BreakUsageList [b++] = usage;
}
}
//
// Find the New usages.
//
for (i=0; i<UsageListLength; i++) {
usage = CurrentUsageList[i];
if (0 == usage) {
break; // Zeros Only at the end.
}
found = FALSE;
for (j=0; j<UsageListLength; j++) {
test = PreviousUsageList [j];
if (0 == test) {
break; // Zeros only at the end.
}
if (test == usage) {
found = TRUE;
break;
}
}
if (!found) {
MakeUsageList [m++] = usage;
}
}
while (b < UsageListLength) {
BreakUsageList [b++] = 0;
}
while (m < UsageListLength) {
MakeUsageList [m++] = 0;
}
return HIDP_STATUS_SUCCESS;
}
NTSTATUS __stdcall
HidP_UsageAndPageListDifference (
IN PUSAGE_AND_PAGE PreviousUsageList,
IN PUSAGE_AND_PAGE CurrentUsageList,
OUT PUSAGE_AND_PAGE BreakUsageList,
OUT PUSAGE_AND_PAGE MakeUsageList,
IN ULONG UsageListLength
)
/*++
Routine Description:
Please see hidpi.h for description
Notes:
--*/
{
ULONG i,j;
ULONG b; // an index into MakeUsageList
ULONG m; // an index into BreakUsageList
BOOLEAN found;
USAGE_AND_PAGE usage;
USAGE_AND_PAGE test;
USAGE_AND_PAGE zero = {0,0};
b = m = 0;
//
// This assumes that UsageListLength will be a small number.
// No keyboard today can generate more than 14 keys in one packet, and
// there is no anticipation for other devices with greater than 14 usages
// at once. For this reason the straight forward naive approach follows...
//
// These lists are not sorted.
//
//
// Find the Old usages.
//
for (i=0; i<UsageListLength; i++) {
usage = PreviousUsageList[i];
if (HidP_IsSameUsageAndPage (zero, usage)) {
break; // Zeros Only at the end.
}
found = FALSE;
for (j=0; j<UsageListLength; j++) {
test = CurrentUsageList [j];
if (HidP_IsSameUsageAndPage (zero, test)) {
break; // Zeros only at the end.
}
if (HidP_IsSameUsageAndPage (test, usage)) {
found = TRUE;
break;
}
}
if (!found) {
BreakUsageList [b++] = usage;
}
}
//
// Find the New usages.
//
for (i=0; i<UsageListLength; i++) {
usage = CurrentUsageList[i];
if (HidP_IsSameUsageAndPage (zero, usage)) {
break; // Zeros Only at the end.
}
found = FALSE;
for (j=0; j<UsageListLength; j++) {
test = PreviousUsageList [j];
if (HidP_IsSameUsageAndPage (zero, test)) {
break; // Zeros only at the end.
}
if (HidP_IsSameUsageAndPage (test, usage)) {
found = TRUE;
break;
}
}
if (!found) {
MakeUsageList [m++] = usage;
}
}
while (b < UsageListLength) {
BreakUsageList [b++] = zero;
}
while (m < UsageListLength) {
MakeUsageList [m++] = zero;
}
return HIDP_STATUS_SUCCESS;
}
#define KPAD(_X_) 0x ## _X_ ## F0
#define SHFT(_X_) 0x ## _X_ ## F1
#define VEND(_X_) 0x ## _X_ ## F2
#define PTSC(_X_) 0x ## _X_ ## F3
#define NONE 0xFF
//
// A table to convert a Hid Keyboard usage into a scan code.
// The scan codes from F0 ~ FF are special, they are used to indicate that
// a secondary translation is required.
// This secondary translation is done by way of the secondary translation
// function found in the ScanCodeSubTable structure array below.
//
ULONG HidP_KeyboardToScanCodeTable [0x100] = {
//
// This is a straight lookup table
//
// + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07
// + 08 + 09 + 0A + 0B + 0C + 0D + 0E + OF
/*0x00*/ NONE, NONE, NONE, NONE, 0x1E, 0x30, 0x2E, 0x20,
/*0x08*/ 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26,
/*0x10*/ 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14,
/*0x18*/ 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x02, 0x03,
/*0x20*/ 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
/*0x28*/ 0x1C, 0x01, 0x0E, 0x0F, 0x39, 0x0C, 0x0D, 0x1A,
/*0x30*/ 0x1B, 0x2B, 0x2B, 0x27, 0x28, 0x29, 0x33, 0x34,
/*0x38*/ 0x35, SHFT(8), 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
/*0x40*/ 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, PTSC(0),SHFT(9),
/*0x48*/ 0x451DE1,KPAD(0), KPAD(1), KPAD(2), KPAD(3), KPAD(4), KPAD(5),KPAD(6),
/*0x50*/ KPAD(7), KPAD(8), KPAD(9), SHFT(A), 0x35E0, 0x37, 0x4A, 0x4E,
/*0x58*/ 0x1CE0, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47,
/*0x60*/ 0x48, 0x49, 0x52, 0x53, 0x56, 0x5DE0, 0x5EE0, 0x59,
/*0x68*/ 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
/*0x70*/ 0x6C, 0x6D, 0x6E, 0x76, NONE, NONE, NONE, NONE,
/*0x78*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0x80*/ NONE, NONE, NONE, NONE, NONE, 0x7E, NONE, 0x73,
/*0x88*/ 0x70, 0x7D, 0x79, 0x7B, 0x5C, NONE, NONE, NONE,
/*0x90*/ VEND(0), VEND(1), 0x78, 0x77, 0x76, NONE, NONE, NONE,
/*0x98*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xA0*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xA8*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xB0*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xB8*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xC0*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xC8*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xD0*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xD8*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xE0*/ SHFT(0), SHFT(1), SHFT(2), SHFT(3), SHFT(4), SHFT(5), SHFT(6),SHFT(7),
/*0xE8*/ NONE, 0x5EE0, 0x5FE0, 0x63E0, NONE, NONE, NONE, NONE,
/*KPAD*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
/*0xF8*/ NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE,
};
ULONG HidP_XlateKbdPadCodesSubTable[] = {
/* + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07 */
/* + 08 + 09 + 0A + 0B + 0C + 0D + 0E + OF */
/*0x48*/ 0x52E0, 0x47E0, 0x49E0, 0x53E0, 0x4FE0, 0x51E0, 0x4DE0,
/*0x50*/ 0x4BE0, 0x50E0, 0x48E0
};
ULONG HidP_XlateModifierCodesSubTable[] = {
//
// NOTE These modifier codes in this table are in a VERY SPECIAL order.
// that is: they are in the order of appearence in the
// _HIDP_KEYBOARD_SHIFT_STATE union.
//
// + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07
// + 08 + 09 + 0A + 0B + 0C + 0D + 0E + OF
// LCtrl LShft LAlt LGUI RCtrl RShft RAlt RGUI
/*0xE0*/ 0x1D, 0x2A, 0x38, 0x5BE0, 0x1DE0, 0x36, 0x38E0, 0x5CE0,
/*0x39 CAPS_LOCK*/ 0x3A,
/*0x47 SCROLL_LOCK*/ 0x46,
/*0x53 NUM_LOCK*/ 0x45
// This table is set up so that indices into this table greater than 7
// are sticky. HidP_ModifierCode uses this as an optimization for
// updating the Modifier state table.
//
};
ULONG HidP_BreakCodesAsMakeCodesTable[] = {
//
// Vendor scan codes that have the high bit set and are technically
// break codes, but are sent as make codes. No break code will be sent.
//
// + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07
// + 08 + 09 + 0A + 0B + 0C + 0D + 0E + OF
/*0x90*/ 0xF2, 0xF1
//
};
ULONG HidP_XlatePrtScrCodesSubTable[] = {
/* + 00 + 01 + 02 + 03 + 04 + 05 + 06 + 07 */
/* + 08 + 09 + 0A + 0B + 0C + 0D + 0E + OF */
/*0x40*/ 0x37E0
};
HIDP_SCANCODE_SUBTABLE HidP_KeyboardSubTables[0x10] = {
/* F0 */ {HidP_KeyboardKeypadCode, HidP_XlateKbdPadCodesSubTable},
/* F1 */ {HidP_ModifierCode, HidP_XlateModifierCodesSubTable},
/* F2 */ {HidP_VendorBreakCodesAsMakeCodes, HidP_BreakCodesAsMakeCodesTable},
/* F3 */ {HidP_PrintScreenCode, HidP_XlatePrtScrCodesSubTable},
/* F4 */ {NULL, NULL},
/* F5 */ {NULL, NULL},
/* F6 */ {NULL, NULL},
/* F7 */ {NULL, NULL},
/* F8 */ {NULL, NULL},
/* F9 */ {NULL, NULL},
/* FA */ {NULL, NULL},
/* FB */ {NULL, NULL},
/* FC */ {NULL, NULL},
/* FD */ {NULL, NULL},
/* FE */ {NULL, NULL},
/* FF */ {NULL, NULL}
};
#define HIDP_CONSUMER_TABLE_SIZE 16
ULONG HidP_ConsumerToScanCodeTable [HIDP_CONSUMER_TABLE_SIZE] = {
//
// This is an association table
//
// Usage -> Scancode
//
0x0224, 0x6AE0, // WWW Back
0x0225, 0x69E0, // WWW Forward
0x0226, 0x68E0, // WWW Stop
0x0227, 0x67E0, // WWW Refresh
0x0221, 0x65E0, // WWW Search
0x022A, 0x66E0, // WWW Favorites
0x0223, 0x32E0, // WWW Home
0x018A, 0x6CE0 // Mail
};
HIDP_SCANCODE_SUBTABLE HidP_ConsumerSubTables [1] = {
{NULL, NULL}
};
//
//BOOLEAN
//HidP_KbdPutKey (
// ULONG Code,
// HIDP_KEYBOARD_DIRECTION KeyAction,
// PHIDP_INSERT_SCANCODES Insert,
// PVOID Context)
//
// Add the scan codes to the callers buffer using the callback routine
// Insert.
//
// If we find a zero in the list then we are done with no error
// If we find a invalid code (anything that starts with an F, then
// we have a problem. No where in current published i8042 specs is there
// a scan code of F0 ~ FF.
//
// If we are breaking then we need to set the high byte.
//
BOOLEAN
HidP_KbdPutKey (
ULONG PassedCode,
HIDP_KEYBOARD_DIRECTION KeyAction,
PHIDP_INSERT_SCANCODES Insert,
PVOID Context)
{
PUCHAR pCode = (PCHAR)&PassedCode;
ULONG i;
for (i = 0; i < sizeof(ULONG); i++) {
//
// Some swell keyboard vendors have added Fx charaters to their
// keyboards which we now have to emulate.
//
// if ((0xF0 & *pCode) == 0xF0) {
// return FALSE;
// }
if (0 == pCode[i]) {
break;
}
if (HidP_Keyboard_Break == KeyAction) {
pCode[i] |= 0x80;
}
}
if (i) {
(*Insert)(Context, pCode, i);
}
return TRUE;
}
NTSTATUS
HidP_TranslateUsagesToI8042ScanCodes (
PUSAGE ChangedUsageList, // Those usages that changed
ULONG UsageListLength,
HIDP_KEYBOARD_DIRECTION KeyAction,
PHIDP_KEYBOARD_MODIFIER_STATE ModifierState,
PHIDP_INSERT_SCANCODES InsertCodesProcedure,
PVOID InsertCodesContext
)
/*++
Routine Description:
Please see hidpi.h for description
Notes:
--*/
{
PUSAGE usage;
ULONG i;
NTSTATUS status = HIDP_STATUS_SUCCESS;
for (i = 0, usage = ChangedUsageList;
i < UsageListLength;
i++, usage++) {
if (0 == *usage) {
// No more interesting usages. Zero terminated if not max length.
break;
}
status = HidP_TranslateUsage (*usage,
KeyAction,
ModifierState,
HidP_StraightLookup,
HidP_KeyboardToScanCodeTable,
HidP_KeyboardSubTables,
InsertCodesProcedure,
InsertCodesContext);
if (HIDP_STATUS_SUCCESS != status) {
break;
}
}
return status;
}
NTSTATUS __stdcall
HidP_TranslateUsageAndPagesToI8042ScanCodes (
PUSAGE_AND_PAGE ChangedUsageList, // Those usages that changed
ULONG UsageListLength,
HIDP_KEYBOARD_DIRECTION KeyAction,
PHIDP_KEYBOARD_MODIFIER_STATE ModifierState,
PHIDP_INSERT_SCANCODES InsertCodesProcedure,
PVOID InsertCodesContext
)
/*++
Routine Description:
Please see hidpi.h for description
Notes:
--*/
{
PUSAGE_AND_PAGE usage;
ULONG i;
NTSTATUS status = HIDP_STATUS_SUCCESS;
for (i = 0, usage = ChangedUsageList;
i < UsageListLength;
i++, usage++) {
if (0 == usage->Usage) {
break;
}
switch (usage->UsagePage) {
case HID_USAGE_PAGE_KEYBOARD:
status = HidP_TranslateUsage (usage->Usage,
KeyAction,
ModifierState,
HidP_StraightLookup,
HidP_KeyboardToScanCodeTable,
HidP_KeyboardSubTables,
InsertCodesProcedure,
InsertCodesContext);
break;
case HID_USAGE_PAGE_CONSUMER:
status = HidP_TranslateUsage (usage->Usage,
KeyAction,
ModifierState,
HidP_AssociativeLookup,
HidP_ConsumerToScanCodeTable,
HidP_ConsumerSubTables,
InsertCodesProcedure,
InsertCodesContext);
break;
default:
status = HIDP_STATUS_I8042_TRANS_UNKNOWN;
}
if (HIDP_STATUS_SUCCESS != status) {
break;
}
}
return status;
}
NTSTATUS __stdcall
HidP_TranslateUsage (
USAGE Usage,
HIDP_KEYBOARD_DIRECTION KeyAction,
PHIDP_KEYBOARD_MODIFIER_STATE ModifierState,
PHIDP_LOOKUP_TABLE_PROC LookupTableProc,
PULONG TranslationTable,
PHIDP_SCANCODE_SUBTABLE SubTranslationTable,
PHIDP_INSERT_SCANCODES InsertCodesProcedure,
PVOID InsertCodesContext
)
/*++
Routine Description:
Notes:
--*/
{
ULONG scancode;
PHIDP_SCANCODE_SUBTABLE table;
NTSTATUS status;
scancode = (* LookupTableProc) (TranslationTable, Usage);
if (0 == scancode) {
return HIDP_STATUS_I8042_TRANS_UNKNOWN;
}
if ((ModifierState->LeftControl || ModifierState->RightControl) &&
(scancode == 0x451DE1)) {
//
// The scancode of the pause key completely changes
// if the control key is down.
//
scancode = 0x46E0;
}
if ((0xF0 & scancode) == 0xF0) {
// Use a secondary table
table = &SubTranslationTable [scancode & 0xF];
if (table->ScanCodeFcn) {
if ((*table->ScanCodeFcn) (table->Table,
(UCHAR) ((scancode & 0xFF00) >> 8),
InsertCodesProcedure,
InsertCodesContext,
KeyAction,
ModifierState)) {
;
} else {
return HIDP_STATUS_I8042_TRANS_UNKNOWN;
}
} else {
return HIDP_STATUS_I8042_TRANS_UNKNOWN;
}
} else {
HidP_KbdPutKey (scancode,
KeyAction,
InsertCodesProcedure,
InsertCodesContext);
}
return HIDP_STATUS_SUCCESS;
}
BOOLEAN
HidP_KeyboardKeypadCode (
IN ULONG * Table,
IN UCHAR Index,
IN PHIDP_INSERT_SCANCODES Insert,
IN PVOID Context,
IN HIDP_KEYBOARD_DIRECTION KeyAction,
IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState
)
/*++
Routine Description:
Notes:
--*/
{
//
// The num lock key (if set then we add even more scan codes for these
// keys)
//
ULONG DarrylRis_Magic_Code = 0x2AE0;
BOOLEAN status = TRUE;
if ((ModifierState->NumLock) && (HidP_Keyboard_Make == KeyAction) ) {
status = HidP_KbdPutKey (DarrylRis_Magic_Code, KeyAction, Insert, Context);
}
if (!status) {
return status;
}
status = HidP_KbdPutKey (Table[Index], KeyAction, Insert, Context);
if (!status) {
return status;
}
if ((ModifierState->NumLock) && (HidP_Keyboard_Break == KeyAction) ) {
status = HidP_KbdPutKey (DarrylRis_Magic_Code, KeyAction, Insert, Context);
}
return status;
}
BOOLEAN
HidP_ModifierCode (
IN ULONG * Table,
IN UCHAR Index,
IN PHIDP_INSERT_SCANCODES Insert,
IN PVOID Context,
IN HIDP_KEYBOARD_DIRECTION KeyAction,
IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState
)
/*++
Routine Description:
Notes:
--*/
{
if (Index >> 3) {
//
// Indices greater than 8 are sticky.
//
switch (KeyAction) {
case HidP_Keyboard_Make:
if (!(ModifierState->ul & (1 << (Index+16)))) {
//
// Mark this as the first make.
//
ModifierState->ul |= (1 << (Index+16));
//
// Only toggle the state when this is the first make sent.
//
ModifierState->ul ^= (1 << Index);
}
break;
case HidP_Keyboard_Break:
//
// Clear the fist make field.
//
ModifierState->ul &= ~(1 << (Index+16));
break;
}
} else {
switch (KeyAction) {
case HidP_Keyboard_Make:
// The key is now on
ModifierState->ul |= (1 << Index);
break;
case HidP_Keyboard_Break:
// The key is now off
ModifierState->ul &= ~(1 << Index);
break;
}
}
return HidP_KbdPutKey (Table[Index], KeyAction, Insert, Context);
}
BOOLEAN
HidP_VendorBreakCodesAsMakeCodes (
IN ULONG * Table,
IN UCHAR Index,
IN PHIDP_INSERT_SCANCODES Insert,
IN PVOID Context,
IN HIDP_KEYBOARD_DIRECTION KeyAction,
IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState
)
{
//
// Vendor scan codes that have the high bit set and are technically
// break codes, but are sent as make codes. No break code will be sent.
//
UNREFERENCED_PARAMETER (ModifierState);
switch (KeyAction) {
case HidP_Keyboard_Make:
return HidP_KbdPutKey (Table[Index], KeyAction, Insert, Context);
case HidP_Keyboard_Break:
// do Nothing
return TRUE;
default:
return FALSE;
}
}
BOOLEAN
HidP_PrintScreenCode (
IN ULONG * Table,
IN UCHAR Index,
IN PHIDP_INSERT_SCANCODES Insert,
IN PVOID Context,
IN HIDP_KEYBOARD_DIRECTION KeyAction,
IN OUT PHIDP_KEYBOARD_MODIFIER_STATE ModifierState
)
/*++
Routine Description:
Notes:
--*/
{
BOOLEAN status = TRUE;
//
// Special casing for the printscreen key.
//
if (ModifierState->LeftAlt || ModifierState->RightAlt) {
//
// Alt key down.
//
status = HidP_KbdPutKey (0x54, KeyAction, Insert, Context);
} else if (ModifierState->LeftShift || ModifierState->RightShift ||
ModifierState->LeftControl || ModifierState->RightControl) {
//
// Shift or ctrl keys down.
//
status = HidP_KbdPutKey (Table[Index], KeyAction, Insert, Context);
} else {
//
// No modifier keys down. Add some extra "padding" to the make and break.
//
ULONG DarrylRis_Magic_Code = 0x2AE0;
if (HidP_Keyboard_Make == KeyAction) {
status = HidP_KbdPutKey (DarrylRis_Magic_Code, KeyAction, Insert, Context);
}
if (!status) {
return status;
}
status = HidP_KbdPutKey (Table[Index], KeyAction, Insert, Context);
if (!status) {
return status;
}
if (HidP_Keyboard_Break == KeyAction) {
status = HidP_KbdPutKey (DarrylRis_Magic_Code, KeyAction, Insert, Context);
}
}
return status;
}
ULONG
HidP_StraightLookup (
IN PULONG Table,
IN ULONG Usage
)
{
if (Usage > 0xFF) {
// We have
// have no translation for this usage.
return 0;
}
return Table[Usage];
}
ULONG
HidP_AssociativeLookup (
IN PULONG Table,
IN ULONG Usage
)
{
ULONG i;
for (i = 0; i < (HIDP_CONSUMER_TABLE_SIZE - 1); i+=2) {
if (Usage == Table[i]) {
return Table[i+1];
}
}
return 0;
}