925 lines
28 KiB
C
925 lines
28 KiB
C
/** @file
|
|
The helper library for HID descriptor parsing
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2013 - 2020, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include <Uefi.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/HidDescriptorLib.h>
|
|
|
|
STATIC HID_PARSER mHidParser;
|
|
STATIC struct {
|
|
UINT16 ClassCode;
|
|
UINT16 UsagePage;
|
|
UINT8 Usage;
|
|
UINT8 Value;
|
|
UINT8 Attr;
|
|
UINT8 Offset;
|
|
} mHidReportField[] = {
|
|
//
|
|
// The report field lookup table for touch panel
|
|
//
|
|
{ 0x0d00, HID_UP_GENDESK, HID_GD_X, 0xff, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldX },
|
|
{ 0x0d00, HID_UP_GENDESK, HID_GD_Y, 0xff, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldY },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_TIP_SWITCH, 0x01, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldTouchActive },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_SECONDARY_TIP_SWITCH, 0x01, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldAltActive },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_BARREL_SWITCH, 0x01, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldAltActive },
|
|
{ 0x0d00, HID_UP_BUTTON, 1, 0x01, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldTouchActive },
|
|
{ 0x0d00, HID_UP_BUTTON, 2, 0x01, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldAltActive },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_CONTACT_COUNT, 0xff, ATTR_TOUCH_PANEL_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanel.FieldContactCount },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_DEVICE_MODE, 0xff, ATTR_TOUCH_PANEL_MODE, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanelMode.FieldDeviceMode },
|
|
{ 0x0d00, HID_UP_DIGITIZERS, HID_DIGITIZERS_DEVICE_ID, 0xff, ATTR_TOUCH_PANEL_MODE, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.TouchPanelMode.FieldDeviceId },
|
|
{ 0x0d00, HID_UP_VENDOR, 0xff, 0xff, ATTR_VENDOR_FEATURE, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.VendorData.VendorDefined },
|
|
//
|
|
// The report field lookup table for pad
|
|
//
|
|
{ 0x0101, HID_UP_GENDESK, HID_GD_X, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldX },
|
|
{ 0x0101, HID_UP_GENDESK, HID_GD_Y, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldY },
|
|
{ 0x0101, HID_UP_GENDESK, HID_GD_WHEEL, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldZ },
|
|
{ 0x0101, HID_UP_BUTTON, 1, 0x01, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldButton1 },
|
|
{ 0x0101, HID_UP_BUTTON, 2, 0x01, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldButton2 },
|
|
//
|
|
// The report field lookup table for mouse
|
|
//
|
|
{ 0x0102, HID_UP_GENDESK, HID_GD_X, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldX },
|
|
{ 0x0102, HID_UP_GENDESK, HID_GD_Y, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldY },
|
|
{ 0x0102, HID_UP_GENDESK, HID_GD_WHEEL, 0xff, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldZ },
|
|
{ 0x0102, HID_UP_BUTTON, 1, 0x01, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldButton1 },
|
|
{ 0x0102, HID_UP_BUTTON, 2, 0x01, ATTR_MOUSE_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Mouse.FieldButton2 },
|
|
//
|
|
// The report field lookup table for keyboard
|
|
//
|
|
{ 0x0106, HID_UP_KEYBOARD, 0xff, 0x01, ATTR_KEYBOARD_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Keyboard.FieldModKeyCode },
|
|
{ 0x0106, HID_UP_KEYBOARD, 0xff, 0xff, ATTR_KEYBOARD_INPUT, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.Keyboard.FieldKeyCode },
|
|
{ 0x0106, HID_UP_LED, 0xff, 0x01, ATTR_KEYBOARD_LED, (UINT8)(UINTN)&((REPORT_GROUP*)0)->Data.KeyboardLed.FieldLed },
|
|
//
|
|
// End of table
|
|
//
|
|
{ 0x0000, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
/**
|
|
|
|
Get Next Item
|
|
|
|
@param StartPos Start Position
|
|
@param EndPos End Position
|
|
@param HidItem HidItem to return
|
|
|
|
@retval Position
|
|
|
|
**/
|
|
STATIC
|
|
UINT8 *
|
|
GetNextItem (
|
|
IN UINT8 *StartPos,
|
|
IN UINT8 *EndPos,
|
|
OUT HID_ITEM *HidItem
|
|
)
|
|
{
|
|
UINT8 Temp;
|
|
|
|
if ((EndPos - StartPos) <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
Temp = *StartPos;
|
|
StartPos++;
|
|
//
|
|
// bit 2,3
|
|
//
|
|
HidItem->Type = (UINT8) ((Temp >> 2) & 0x03);
|
|
//
|
|
// bit 4-7
|
|
//
|
|
HidItem->Tag = (UINT8) ((Temp >> 4) & 0x0F);
|
|
|
|
if (HidItem->Tag == HID_ITEM_TAG_LONG) {
|
|
//
|
|
// Long Items are not supported by HID rev1.0,
|
|
// although we try to parse it.
|
|
//
|
|
HidItem->Format = HID_ITEM_FORMAT_LONG;
|
|
|
|
if ((EndPos - StartPos) >= 2) {
|
|
HidItem->Size = *StartPos++;
|
|
HidItem->Tag = *StartPos++;
|
|
|
|
if ((EndPos - StartPos) >= HidItem->Size) {
|
|
HidItem->Data.LongData = StartPos;
|
|
StartPos += HidItem->Size;
|
|
return StartPos;
|
|
}
|
|
}
|
|
} else {
|
|
HidItem->Format = HID_ITEM_FORMAT_SHORT;
|
|
//
|
|
// bit 0, 1
|
|
//
|
|
HidItem->Size = (UINT8) (Temp & 0x03);
|
|
switch (HidItem->Size) {
|
|
|
|
case 0:
|
|
//
|
|
// No data
|
|
//
|
|
return StartPos;
|
|
|
|
case 1:
|
|
//
|
|
// One byte data
|
|
//
|
|
if ((EndPos - StartPos) >= 1) {
|
|
HidItem->Data.U8 = *StartPos++;
|
|
return StartPos;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
//
|
|
// Two byte data
|
|
//
|
|
if ((EndPos - StartPos) >= 2) {
|
|
HidItem->Data.U16 = *(UINT16*)StartPos;
|
|
StartPos += 2;
|
|
return StartPos;
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
//
|
|
// 4 byte data, adjust size
|
|
//
|
|
HidItem->Size++;
|
|
if ((EndPos - StartPos) >= 4) {
|
|
HidItem->Data.U32 = *(UINT32*)StartPos;
|
|
StartPos += 4;
|
|
return StartPos;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
|
|
Get Item Data
|
|
|
|
@param HidItem HID_ITEM
|
|
|
|
@retval HidItem Data
|
|
|
|
**/
|
|
STATIC
|
|
UINT32
|
|
GetItemData (
|
|
IN HID_ITEM *HidItem
|
|
)
|
|
{
|
|
//
|
|
// Get Data from HID_ITEM structure
|
|
//
|
|
switch (HidItem->Size) {
|
|
|
|
case 1:
|
|
return HidItem->Data.U8;
|
|
|
|
case 2:
|
|
return HidItem->Data.U16;
|
|
|
|
case 4:
|
|
return HidItem->Data.U32;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
|
|
Parse Local Item
|
|
|
|
@param LocalItem Local Item
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
ParseLocalItem (
|
|
IN HID_ITEM *LocalItem
|
|
)
|
|
{
|
|
if (LocalItem->Size == 0) {
|
|
//
|
|
// No expected data for local item
|
|
//
|
|
return ;
|
|
}
|
|
switch (LocalItem->Tag) {
|
|
case HID_LOCAL_ITEM_TAG_USAGE:
|
|
mHidParser.Local.Usage[mHidParser.Local.UsageIndex] = (UINT16)GetItemData (LocalItem);
|
|
if (mHidParser.Local.UsageIndex < MAXIMUM_USAGES - 1) mHidParser.Local.UsageIndex ++;
|
|
break;
|
|
case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM:
|
|
mHidParser.Local.UsageMin = (UINT16)GetItemData (LocalItem);
|
|
break;
|
|
case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM:
|
|
mHidParser.Local.UsageMax = (UINT16)GetItemData (LocalItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
ParseGlobalItem (
|
|
IN HID_ITEM *GlobalItem
|
|
)
|
|
{
|
|
switch (GlobalItem->Tag) {
|
|
case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM:
|
|
mHidParser.Global.LogicMax = (INT32)GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM:
|
|
mHidParser.Global.LogicMin = (INT32)GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_USAGE_PAGE:
|
|
mHidParser.Global.UsagePage = (UINT16)GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
|
|
mHidParser.Global.ReportSize = (UINT16)GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_REPORT_COUNT:
|
|
mHidParser.Global.ReportCount = (UINT16)GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_REPORT_ID:
|
|
mHidParser.Global.ReportId = (UINT8) GetItemData (GlobalItem);
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_PUSH:
|
|
CopyMem (
|
|
&mHidParser.GlobalStack[mHidParser.GlobalStackPtr],
|
|
&mHidParser.Global,
|
|
sizeof (HID_GLOBAL)
|
|
);
|
|
mHidParser.GlobalStackPtr ++;
|
|
break;
|
|
|
|
case HID_GLOBAL_ITEM_TAG_POP:
|
|
mHidParser.GlobalStackPtr --;
|
|
CopyMem (
|
|
&mHidParser.Global,
|
|
&mHidParser.GlobalStack[mHidParser.GlobalStackPtr],
|
|
sizeof (HID_GLOBAL)
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Usage searching
|
|
|
|
@param Usage Target
|
|
|
|
@retval Usage position
|
|
|
|
**/
|
|
STATIC
|
|
UINT8
|
|
SearchUsage (
|
|
IN UINT16 Usage
|
|
)
|
|
{
|
|
UINT8 Index;
|
|
|
|
if (Usage == 0xff) return 0;
|
|
if (mHidParser.Local.UsageIndex) {
|
|
for (Index = 0; Index < mHidParser.Local.UsageIndex; Index ++) {
|
|
if (mHidParser.Local.Usage[Index] == Usage) {
|
|
return Index;
|
|
}
|
|
}
|
|
} else if (mHidParser.Local.UsageMin && mHidParser.Local.UsageMax) {
|
|
if (Usage >= mHidParser.Local.UsageMin && Usage <= mHidParser.Local.UsageMax) {
|
|
return (UINT8)(Usage - mHidParser.Local.UsageMin);
|
|
}
|
|
}
|
|
return INVALID_USAGE_POSITION;
|
|
}
|
|
|
|
/**
|
|
|
|
Finds the report group data corresponding to the current Report ID. If no report group data
|
|
exists with the current Report ID, a new report group data with the current Report ID is started.
|
|
|
|
@param ReportFieldInfo REPORT_FIELD_INFO that stores all the report group data
|
|
@param CurrReportGroup the report group data corresponding to the current Report ID
|
|
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_NOT_FOUND
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FindCurrentReportGroup (
|
|
IN REPORT_FIELD_INFO *ReportFieldInfo,
|
|
IN UINTN DataType,
|
|
OUT REPORT_GROUP **CurrReportGroup
|
|
)
|
|
{
|
|
UINTN Count;
|
|
REPORT_GROUP *ReportGroup;
|
|
|
|
if (!(mHidParser.DataValid)) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
//
|
|
// Searching for stock report group
|
|
//
|
|
for (Count = 0, ReportGroup = ReportFieldInfo->ReportGroup; Count < ReportFieldInfo->Total; ReportGroup ++, Count ++) {
|
|
if (ReportGroup->Id == mHidParser.Global.ReportId && ReportGroup->DataType == DataType) {
|
|
//
|
|
// Return stock report group due to the ID exist in the array
|
|
//
|
|
*CurrReportGroup = ReportGroup;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
if (ReportFieldInfo->Total >= MAXIMUM_RERORT_GROUPS) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
//
|
|
// If current Report ID not found, start a new report group
|
|
//
|
|
ReportGroup = &ReportFieldInfo->ReportGroup[ReportFieldInfo->Total];
|
|
ReportGroup->Id = mHidParser.Global.ReportId;
|
|
ReportGroup->DataType = (UINT8)DataType;
|
|
if (ReportGroup->Id != 0) {
|
|
ReportGroup->DataSize = 8;
|
|
}
|
|
ReportFieldInfo->Total ++;
|
|
*CurrReportGroup = ReportGroup;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Fills in report field data. BitOffset and Usage are used together to calculate
|
|
the bit offset at which ReportField should start.
|
|
|
|
@param BitOffset The number of bits defined for the report data structure so far
|
|
@param Usage The usage corresponding to ReportField
|
|
@param ReportField The report field whose data should be updated
|
|
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_NOT_FOUND
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
FillInReportFieldData (
|
|
IN UINT16 BitOffset,
|
|
IN UINT16 DataAttr,
|
|
IN UINT16 Usage,
|
|
OUT REPORT_GROUP *ReportGroup
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT8 UsagePos;
|
|
UINT8 Attr;
|
|
UINT16 RootClassCode;
|
|
REPORT_FIELD *ReportField;
|
|
|
|
if (ReportGroup == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
UsagePos = SearchUsage (Usage);
|
|
if (UsagePos == INVALID_USAGE_POSITION) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
//
|
|
// Setup the class code used to identify the device class
|
|
//
|
|
RootClassCode = (mHidParser.TopLevelCollectionUsagePage << 8) | (mHidParser.CollectionStack[0].Usage);
|
|
if (mHidParser.TopLevelCollectionUsagePage == HID_UP_DIGITIZERS) {
|
|
//
|
|
// Don't care about page code if it is touch panel
|
|
//
|
|
RootClassCode &= 0xff00;
|
|
}
|
|
for (Index = 0, ReportField = NULL, Attr = 0; mHidReportField[Index].ClassCode != 0; Index ++) {
|
|
if (mHidReportField[Index].ClassCode == RootClassCode &&
|
|
mHidReportField[Index].UsagePage == mHidParser.Global.UsagePage &&
|
|
(mHidReportField[Index].Usage == 0xff || mHidReportField[Index].Usage == Usage) &&
|
|
(mHidReportField[Index].Value == 0xff || mHidReportField[Index].Value == mHidParser.Global.LogicMax)) {
|
|
ReportField = (REPORT_FIELD*)((UINT8*)ReportGroup + mHidReportField[Index].Offset);
|
|
Attr = mHidReportField[Index].Attr;
|
|
if (ReportField->DataValid) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (ReportField) {
|
|
ReportField->Max = mHidParser.Global.LogicMax;
|
|
ReportField->Min = mHidParser.Global.LogicMin;
|
|
ReportField->BitLength = (UINT8) mHidParser.Global.ReportSize;
|
|
ReportField->BitOffset = BitOffset + (UsagePos * mHidParser.Global.ReportSize);
|
|
ReportField->DataValid = TRUE;
|
|
ReportField->ValueType = (DataAttr & HID_MAIN_ITEM_RELATIVE) ? RELATIVE_VALUE : ABSOLUTE_VALUE;
|
|
if (!ReportGroup->DataValid) {
|
|
ReportGroup->DataValid = TRUE;
|
|
ReportGroup->DataClass = RootClassCode;
|
|
ReportGroup->DataAttr = Attr;
|
|
}
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Updates report group data.
|
|
|
|
@param ReportFieldInfo REPORT_FIELD_INFO that stores all the report group data
|
|
@param MainItem HID_ITEM to parse
|
|
|
|
@retval EFI_INVALID_PARAMETER
|
|
@retval EFI_NOT_FOUND
|
|
@retval EFI_SUCCESS
|
|
|
|
**/
|
|
STATIC
|
|
EFI_STATUS
|
|
UpdateReportGroupData (
|
|
IN REPORT_FIELD_INFO *ReportFieldInfo,
|
|
IN HID_ITEM *MainItem
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
REPORT_GROUP *ReportGroup;
|
|
UINT16 *BitOffset;
|
|
UINT16 DataAttr;
|
|
|
|
if ((ReportFieldInfo == NULL) ||
|
|
(MainItem == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
ReportGroup = NULL;
|
|
Status = FindCurrentReportGroup (ReportFieldInfo, MainItem->Tag, &ReportGroup);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
BitOffset = &ReportGroup->DataSize;
|
|
DataAttr = (UINT16) GetItemData (MainItem);
|
|
//
|
|
// If the item tag has only constant bits, there is no new data to save so just update the bit
|
|
// offset and return.
|
|
//
|
|
if ((DataAttr & HID_MAIN_ITEM_CONSTANT) == HID_MAIN_ITEM_CONSTANT) {
|
|
*BitOffset += (mHidParser.Global.ReportSize * mHidParser.Global.ReportCount);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
switch (MainItem->Tag) {
|
|
case HID_MAIN_ITEM_TAG_INPUT:
|
|
switch (mHidParser.Global.UsagePage) {
|
|
case HID_UP_GENDESK:
|
|
if (mHidParser.TopLevelCollectionUsagePage == HID_UP_DIGITIZERS &&
|
|
(((DataAttr & HID_MAIN_ITEM_RELATIVE) == HID_MAIN_ITEM_RELATIVE) ||
|
|
(((UINT32) mHidParser.Global.LogicMin) >= ((UINT32) mHidParser.Global.LogicMax)))) {
|
|
//
|
|
// Touch panel should report absolute X and Y values; otherwise, the report data will be
|
|
// interpreted incorrectly and result in undefined behaviour.
|
|
//
|
|
break;
|
|
}
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_GD_X, ReportGroup);
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_GD_Y, ReportGroup);
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_GD_WHEEL, ReportGroup);
|
|
break;
|
|
case HID_UP_DIGITIZERS:
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_TIP_SWITCH, ReportGroup);
|
|
Status = FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_SECONDARY_TIP_SWITCH, ReportGroup);
|
|
if (EFI_ERROR (Status)) {
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_BARREL_SWITCH, ReportGroup);
|
|
}
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_CONTACT_COUNT, ReportGroup);
|
|
break;
|
|
case HID_UP_BUTTON:
|
|
FillInReportFieldData (*BitOffset, DataAttr, 1, ReportGroup);
|
|
FillInReportFieldData (*BitOffset, DataAttr, 2, ReportGroup);
|
|
break;
|
|
case HID_UP_KEYBOARD:
|
|
FillInReportFieldData (*BitOffset, DataAttr, 0xff, ReportGroup);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case HID_MAIN_ITEM_TAG_OUTPUT:
|
|
switch (mHidParser.Global.UsagePage) {
|
|
case HID_UP_LED:
|
|
FillInReportFieldData (*BitOffset, DataAttr, 0xff, ReportGroup);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case HID_MAIN_ITEM_TAG_FEATURE:
|
|
switch (mHidParser.Global.UsagePage) {
|
|
case HID_UP_DIGITIZERS:
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_DEVICE_MODE, ReportGroup);
|
|
FillInReportFieldData (*BitOffset, DataAttr, HID_DIGITIZERS_DEVICE_ID, ReportGroup);
|
|
break;
|
|
|
|
case HID_UP_LED:
|
|
FillInReportFieldData (*BitOffset, DataAttr, 0xff, ReportGroup);
|
|
break;
|
|
|
|
default:
|
|
if (mHidParser.Global.UsagePage >= 0xFF00 && mHidParser.Global.UsagePage <= 0xFFFF) {
|
|
FillInReportFieldData (*BitOffset, DataAttr, 0xff, ReportGroup);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return EFI_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (ReportGroup->DataValid) {
|
|
if (ReportGroup->DataAttr == ATTR_TOUCH_PANEL_MODE) {
|
|
if (!ReportFieldInfo->DeviceConfigurationReportGroup &&
|
|
mHidParser.TopLevelCollectionUsagePage == HID_UP_DIGITIZERS &&
|
|
mHidParser.CollectionStack[0].Usage == HID_DIGITIZERS_DEVICE_CONFIGURATION) {
|
|
ReportFieldInfo->DeviceConfigurationReportGroup = ReportGroup;
|
|
}
|
|
if (!ReportFieldInfo->FirstFeatureID) {
|
|
ReportFieldInfo->FirstFeatureID = ReportGroup->Id;
|
|
}
|
|
}
|
|
}
|
|
*BitOffset += (mHidParser.Global.ReportSize * mHidParser.Global.ReportCount);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Determines whether the given UsagePage is defined (according to Chapter 3 Table 1 Usage Page
|
|
Summary of the document USB HID Usage Tables version 1.12).
|
|
|
|
@param UsagePage HID Usage Page
|
|
|
|
@retval TRUE
|
|
@retval FALSE The UsagePage is either undefined, reserved, or vendor-defined.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsUsagePageValid (
|
|
UINT16 UsagePage
|
|
)
|
|
{
|
|
if (((UsagePage > 0x00) && (UsagePage < 0x0E)) ||
|
|
(UsagePage == 0x0F) ||
|
|
(UsagePage == 0x10) ||
|
|
(UsagePage == 0x14) ||
|
|
(UsagePage == 0x40) ||
|
|
((UsagePage > 0x7F) && (UsagePage < 0x88)) ||
|
|
((UsagePage > 0x8B) && (UsagePage < 0x8F)) ||
|
|
((UsagePage > 0x8F) && (UsagePage < 0x92))) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
|
|
Parse Main Item
|
|
|
|
@param ReportFieldInfo REPORT_FIELD_INFO that stores all the report group data
|
|
@param MainItem HID_ITEM to parse
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
ParseMainItem (
|
|
IN REPORT_FIELD_INFO *ReportFieldInfo,
|
|
IN HID_ITEM *MainItem
|
|
)
|
|
{
|
|
UINT16 Usage;
|
|
|
|
switch (MainItem->Tag) {
|
|
case HID_MAIN_ITEM_TAG_INPUT:
|
|
case HID_MAIN_ITEM_TAG_OUTPUT:
|
|
case HID_MAIN_ITEM_TAG_FEATURE:
|
|
if (mHidParser.DataValid) {
|
|
UpdateReportGroupData (ReportFieldInfo, MainItem);
|
|
}
|
|
break;
|
|
|
|
case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION:
|
|
Usage = (mHidParser.Local.UsageIndex > 0) ? mHidParser.Local.Usage[mHidParser.Local.UsageIndex - 1] : 0;
|
|
mHidParser.CollectionStack[mHidParser.CollectionStackPtr].Type = (UINT16)GetItemData (MainItem);
|
|
mHidParser.CollectionStack[mHidParser.CollectionStackPtr].Usage = Usage;
|
|
if (!mHidParser.DataValid && mHidParser.CollectionStackPtr == 0) {
|
|
mHidParser.TopLevelCollectionUsagePage = mHidParser.Global.UsagePage;
|
|
//
|
|
// Parse all collections whose usage pages are not undefined, reserved, or vendor-defined.
|
|
//
|
|
mHidParser.DataValid = IsUsagePageValid (mHidParser.Global.UsagePage);
|
|
}
|
|
mHidParser.CollectionStackPtr ++;
|
|
break;
|
|
|
|
case HID_MAIN_ITEM_TAG_END_COLLECTION:
|
|
mHidParser.CollectionStackPtr --;
|
|
if (mHidParser.DataValid && mHidParser.CollectionStackPtr == 0) {
|
|
mHidParser.DataValid = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
//
|
|
// Reset local parameters
|
|
//
|
|
mHidParser.Local.UsageIndex = 0;
|
|
mHidParser.Local.UsageMin = 0;
|
|
mHidParser.Local.UsageMax = 0;
|
|
}
|
|
|
|
/**
|
|
|
|
Parse Hid Item
|
|
|
|
@param ReportFieldInfo REPORT_FIELD_INFO that stores all the report group data
|
|
@param HidItem HidItem to parse
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
ParseHidItem (
|
|
IN REPORT_FIELD_INFO *ReportFieldInfo,
|
|
IN HID_ITEM *HidItem
|
|
)
|
|
{
|
|
switch (HidItem->Type) {
|
|
|
|
case HID_ITEM_TYPE_MAIN:
|
|
//
|
|
// For Main Item, parse main item
|
|
//
|
|
ParseMainItem (ReportFieldInfo, HidItem);
|
|
break;
|
|
|
|
case HID_ITEM_TYPE_GLOBAL:
|
|
//
|
|
// For global Item, parse global item
|
|
//
|
|
ParseGlobalItem (HidItem);
|
|
break;
|
|
|
|
case HID_ITEM_TYPE_LOCAL:
|
|
//
|
|
// For Local Item, parse local item
|
|
//
|
|
ParseLocalItem (HidItem);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Get Report Field Value according the report field description
|
|
|
|
@param ReportData Data payload
|
|
@param ReportDataSize Data size
|
|
@param Field The format of data
|
|
|
|
@retval report field value
|
|
|
|
**/
|
|
UINTN
|
|
GetReportFieldValue (
|
|
IN UINT8 *ReportData,
|
|
IN UINTN ReportDataSize,
|
|
IN REPORT_FIELD *Field
|
|
)
|
|
{
|
|
UINT8 BitOffsetInAByte;
|
|
UINTN *StartAddress;
|
|
UINTN MaskToKeepBits;
|
|
UINTN DataN;
|
|
|
|
if ((ReportData == NULL) ||
|
|
(Field == NULL) ||
|
|
(!(Field->DataValid)) ||
|
|
(((UINTN) Field->BitOffset + Field->BitLength)) > (ReportDataSize * 8)) {
|
|
return 0;
|
|
}
|
|
BitOffsetInAByte = (Field->BitOffset % 8);
|
|
if ((Field->BitLength + BitOffsetInAByte) > (sizeof (UINTN) * 8)) {
|
|
return 0;
|
|
}
|
|
StartAddress = (UINTN *) (ReportData + (Field->BitOffset / 8));
|
|
DataN = *StartAddress;
|
|
DataN >>= BitOffsetInAByte;
|
|
MaskToKeepBits = ((UINTN) 1 << Field->BitLength) - 1;
|
|
return DataN &= MaskToKeepBits;
|
|
}
|
|
|
|
/**
|
|
|
|
Set Report Field Value according the report field description
|
|
|
|
@param ReportData Data payload
|
|
@param ReportDataSize Data size
|
|
@param Field The format of data
|
|
|
|
@retval EFI_SUCCESS Success
|
|
@retval EFI_INVALID_PARAMETER Invalid parameter
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SetReportFieldValue (
|
|
OUT UINT8 *ReportData,
|
|
IN UINTN ReportDataSize,
|
|
IN REPORT_FIELD *Field,
|
|
IN UINTN NewFieldValue
|
|
)
|
|
{
|
|
UINT8 BitOffsetInAByte;
|
|
UINTN MaskToKeepBits;
|
|
UINTN *StartAddress;
|
|
|
|
if ((ReportData == NULL) ||
|
|
(Field == NULL) ||
|
|
(!(Field->DataValid)) ||
|
|
(((UINTN) Field->BitOffset + Field->BitLength)) > (ReportDataSize * 8)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
BitOffsetInAByte = (Field->BitOffset % 8);
|
|
if ((Field->BitLength + BitOffsetInAByte) > (sizeof (UINTN) * 8)) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
MaskToKeepBits = ((UINTN)1 << Field->BitLength) - 1;
|
|
MaskToKeepBits <<= BitOffsetInAByte;
|
|
NewFieldValue <<= BitOffsetInAByte;
|
|
NewFieldValue &= MaskToKeepBits;
|
|
StartAddress = (UINTN *) (ReportData + (Field->BitOffset / 8));
|
|
*StartAddress &= (~MaskToKeepBits);
|
|
*StartAddress |= NewFieldValue;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Parse Report Descriptor
|
|
|
|
@param ReportDescriptor Report descriptor to parse
|
|
@param ReportSize Report descriptor size
|
|
@param ReportFieldInfo REPORT_FIELD_INFO that stores all the report group data
|
|
|
|
@retval EFI_DEVICE_ERROR Report descriptor error
|
|
@retval EFI_SUCCESS Success
|
|
|
|
**/
|
|
EFI_STATUS
|
|
ParseReportDescriptor (
|
|
IN UINT8 *ReportDescriptor,
|
|
IN UINTN ReportSize,
|
|
IN OUT REPORT_FIELD_INFO *ReportFieldInfo
|
|
)
|
|
{
|
|
UINT8 *DescriptorEnd;
|
|
UINT8 *ptr;
|
|
HID_ITEM HidItem;
|
|
REPORT_GROUP *ReportGroup;
|
|
UINTN Count;
|
|
UINTN MaximumSize;
|
|
|
|
ZeroMem (&mHidParser, sizeof (HID_PARSER));
|
|
ReportFieldInfo->Total = 0;
|
|
DescriptorEnd = ReportDescriptor + ReportSize;
|
|
ptr = GetNextItem (ReportDescriptor, DescriptorEnd, &HidItem);
|
|
while (ptr != NULL) {
|
|
if (HidItem.Format != HID_ITEM_FORMAT_SHORT) {
|
|
//
|
|
// Long Format Item is not supported at current HID revision
|
|
//
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
ParseHidItem (ReportFieldInfo, &HidItem);
|
|
ptr = GetNextItem (ptr, DescriptorEnd, &HidItem);
|
|
}
|
|
//
|
|
// Find out the report group in biggest reported data size
|
|
//
|
|
Count = ReportFieldInfo->Total;
|
|
ReportGroup = ReportFieldInfo->ReportGroup;
|
|
MaximumSize = 0;
|
|
while (Count > 0) {
|
|
if (ReportGroup->DataValid) {
|
|
//
|
|
// Convert the report data size in bits to size in bytes.
|
|
//
|
|
ReportGroup->DataSize = (ReportGroup->DataSize + 7) / 8;
|
|
if (ReportGroup->DataType == HID_MAIN_ITEM_TAG_INPUT && ReportGroup->DataSize > MaximumSize) {
|
|
MaximumSize = ReportGroup->DataSize;
|
|
ReportFieldInfo->BiggestInputReportGroup = ReportGroup;
|
|
} else if (ReportGroup->DataType == HID_MAIN_ITEM_TAG_FEATURE && ReportGroup->DataSize > ReportFieldInfo->MaximumFeatureLength) {
|
|
ReportFieldInfo->MaximumFeatureLength = ReportGroup->DataSize;
|
|
}
|
|
}
|
|
ReportGroup++;
|
|
Count--;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Finding the class code to match the desire type
|
|
|
|
@param ReportDescriptor Report descriptor to parse
|
|
@param ReportSize Report descriptor size
|
|
@param ClassCode The target usage page and usage code
|
|
|
|
@retval TRUE Matched
|
|
@retval FALSE Not found
|
|
|
|
**/
|
|
BOOLEAN
|
|
MatchHidDeviceType (
|
|
IN UINT8 *ReportDescriptor,
|
|
IN UINTN ReportSize,
|
|
IN UINT16 ClassCode
|
|
)
|
|
{
|
|
UINT8 *DescriptorEnd;
|
|
UINT8 *ptr;
|
|
HID_ITEM HidItem;
|
|
UINT8 CurrentUsagePage = 0;
|
|
UINT8 CurrentUsage = 0;
|
|
UINT8 TargetUsagePage = (UINT8)(ClassCode >> 8);
|
|
UINT8 TargetUsage = (UINT8)(ClassCode & 0xff);
|
|
|
|
DescriptorEnd = ReportDescriptor + ReportSize;
|
|
|
|
ptr = GetNextItem (ReportDescriptor, DescriptorEnd, &HidItem);
|
|
|
|
while (ptr != NULL) {
|
|
if (HidItem.Format != HID_ITEM_FORMAT_SHORT) {
|
|
//
|
|
// Long Format Item is not supported at current HID revision
|
|
//
|
|
return FALSE;
|
|
}
|
|
switch (HidItem.Type) {
|
|
|
|
case HID_ITEM_TYPE_GLOBAL:
|
|
//
|
|
// For global Item, parse global item
|
|
//
|
|
if (HidItem.Tag == HID_GLOBAL_ITEM_TAG_USAGE_PAGE) {
|
|
CurrentUsagePage = (UINT8) GetItemData (&HidItem);
|
|
}
|
|
break;
|
|
|
|
case HID_ITEM_TYPE_LOCAL:
|
|
//
|
|
// For Local Item, parse local item
|
|
//
|
|
if (HidItem.Tag == HID_LOCAL_ITEM_TAG_USAGE) {
|
|
CurrentUsage = (UINT8) GetItemData (&HidItem);
|
|
}
|
|
//
|
|
// Check Touch device by Usage Page is DIGITIZERS and Usage is Touch Screen,
|
|
// or Usage Page is DIGITIZERS and Usage is PEN
|
|
//
|
|
if (CurrentUsagePage == TargetUsagePage && CurrentUsage == TargetUsage) {
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
}
|
|
ptr = GetNextItem (ptr, DescriptorEnd, &HidItem);
|
|
}
|
|
return FALSE;
|
|
}
|