/** @file Touch Host Controller Human Interface Device API @copyright INTEL CONFIDENTIAL Copyright 2019 - 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains an 'Intel Peripheral Driver' and is uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement. @par Specification Reference: **/ #include "ThcPrivate.h" #include "ThcDriver.h" /** Performs Sanity Checks before write transfer starts @param[in] ThcDev Context of Thc device @param[in] Length Size of the buffer @retval EFI_NOT_READY THC is not ready @retval EFI_ALREADY_STARTED HID transaction is still active @retval EFI_NOT_AVAILABLE_YET THC read transaction is ongoing @retval EFI_SUCCESS All checks passed **/ EFI_STATUS STATIC HidSanityCheck ( IN THC_DEV *ThcDev, IN UINT32 Length ) { if (ThcDev->ThcProtocol.DeviceStatus != EFI_SUCCESS) { DEBUG ((DEBUG_ERROR, "ThcProtocol HidSanityCheck Error Thc Status: %r\n", ThcDev->ThcProtocol.DeviceStatus)); return EFI_NOT_READY; } if (ThcDev->HidActive == TRUE) { DEBUG ((DEBUG_ERROR, "ThcProtocol HidSanityCheck Error HID transaction is still active: %r\n", EFI_ALREADY_STARTED)); return EFI_ALREADY_STARTED; } if (ThcDev->ReadDone == FALSE) { DEBUG ((DEBUG_ERROR, "ThcProtocol HidSanityCheck Error THC is still recieving data: %r\n", EFI_NOT_AVAILABLE_YET)); return EFI_NOT_AVAILABLE_YET; } return EFI_SUCCESS; } /** Wrapper function for Set/Get Feauture support @param[in] ThcDev Context of Thc device @param[in] WriteData Write Data Header @param[in] Buffer Data Container that will be sent to external device @param[in] Timeout 0 - No timeout, do not wait for response 1 or higher - will wait for that amount of time and copy response results to the same buffer. @param[in] Length Size of buffer. @retval EFI_NOT_READY THC is not ready @retval EFI_ALREADY_STARTED HID transaction is still active @retval EFI_NOT_AVAILABLE_YET THC read transaction is ongoing @retval EFI_TIMEOUT a) Response did not come in time OR b) DMA transaction did not finish in time @retval EFI_BUFFER_TOO_SMALL THC DMA buffer is unable to fit that much data @retval EFI_SUCCESS Hid Operation Completed **/ EFI_STATUS STATIC HidGetSetFeature ( IN THC_DEV *ThcDev, IN THC_WRITE_DATA_HDR *WriteData, IN OUT UINT8 *Buffer, IN UINTN Timeout, IN UINT32 Length ) { EFI_STATUS Status; // // Sanity checks before transfer // Status = HidSanityCheck (ThcDev, Length); if (EFI_ERROR (Status)) { return Status; } if (Timeout > 0) { ThcDev->HidDataAvailable = FALSE; ThcDev->HidActive = TRUE; ThcDev->HidBuffer = Buffer; } Status = ThcDmaWriteTx (ThcDev, Buffer, WriteData); if (EFI_ERROR (Status)) { goto THC_HID_EXIT; } // // Wait for Response only if Timeout was set // if (Timeout > 0) { THC_LOCAL_DEBUG(L"ThcProtocol HidGetSetFeature Waiting for Response \n") DEBUG ((DEBUG_INFO, "ThcProtocol HidGetSetFeature Waiting for Response \n")); do { if (ThcDev->HidDataAvailable == TRUE && ThcDev->ReadDone == TRUE) { Status = EFI_SUCCESS; break; } MicroSecondDelay (1000); Timeout--; } while (Timeout > 0); if (Timeout == 0) { Status = EFI_TIMEOUT; THC_LOCAL_DEBUG(L"ThcProtocol HidGetSetFeature Response Timeout\n") DEBUG ((DEBUG_ERROR, "ThcProtocol HidGetSetFeature Response Timeout\n")); } } THC_HID_EXIT: // // Restore Thc Hid state to default // ThcDev->HidDataAvailable = FALSE; ThcDev->HidActive = FALSE; ThcDev->HidBuffer = NULL; return Status; } /** Performs SetFeature function as described in Human Interface Device specification. @param[in] This Pointer to instance of protocol. @param[in] Length Size of the buffer. @param[in] Buffer On input, contains data to be sent to external device. If timeout is set BIOS expects response for which same buffer is consumed. @param[in] Timeout 0 - No timeout, do not wait for response 1 or higher - will wait for that amount of time and copy response results to the same buffer. @retval EFI_NOT_READY THC is not ready @retval EFI_ALREADY_STARTED HID transaction is still active @retval EFI_NOT_AVAILABLE_YET THC read transaction is ongoing @retval EFI_TIMEOUT a) Response did not come in time OR b) DMA transaction did not finish in time @retval EFI_BUFFER_TOO_SMALL THC DMA buffer is unable to fit that much data @retval EFI_SUCCESS Set feature completed **/ EFI_STATUS EFIAPI HidSetFeature ( IN THC_PROTOCOL *This, IN UINT32 Length, IN OUT UINT8 *Buffer, IN UINTN Timeout ) { THC_DEV *ThcDev; THC_WRITE_DATA_HDR WriteData; EFI_STATUS Status; ThcDev = THC_CONTEXT_FROM_THC_PROTOCOL (This); WriteData.WriteDataType = SET_FEATURES_DATA; WriteData.WriteDataLength = Length; Status = HidGetSetFeature (ThcDev, &WriteData, Buffer, Timeout, Length); return Status; } /** Performs GetFeature function as described in Human Interface Device specification. @param[in] This Pointer to instance of protocol. @param[in] Length Size of buffer. @param[in] Buffer On input, contains ReportId in 1st byte. On output, filled with Feature data from external device. @param[in] Timeout 0 - No timeout, do not wait for response 1 or higher - will wait for that amount of time and copy response results to the same buffer. @retval EFI_NOT_READY THC is not ready @retval EFI_ALREADY_STARTED HID transaction is still active @retval EFI_NOT_AVAILABLE_YET THC read transaction is ongoing @retval EFI_TIMEOUT a) Response did not come in time OR b) DMA transaction did not finish in time @retval EFI_BUFFER_TOO_SMALL THC DMA buffer is unable to fit that much data @retval EFI_SUCCESS Get feature completed **/ EFI_STATUS EFIAPI HidGetFeature ( IN THC_PROTOCOL *This, IN UINT32 Length, IN OUT UINT8 *Buffer, IN UINTN Timeout ) { THC_DEV *ThcDev; THC_WRITE_DATA_HDR WriteData; EFI_STATUS Status; ThcDev = THC_CONTEXT_FROM_THC_PROTOCOL (This); WriteData.WriteDataType = GET_FEATURES_DATA; WriteData.WriteDataLength = Length; Status = HidGetSetFeature (ThcDev, &WriteData, Buffer, Timeout, Length); return Status; } /** Enables end point device. - Reads all Touch Panels devices registers - Sets TouchEnable @param[in] ThcDev Context of Thc device @retval EFI_SUCCESS Enabling completed @retval EFI_DEVICE_ERROR TSSDONE not set or ERR set @retval EFI_TIMEOUT Timeout **/ EFI_STATUS EFIAPI HidEnableAfterReset ( IN THC_PROTOCOL *This ) { THC_DEV *ThcDev; EFI_STATUS Status; ThcDev = THC_CONTEXT_FROM_THC_PROTOCOL (This); Status = ThcEnableAfterReset (ThcDev); return Status; } /* HID's InputReportDescriptor consists of variable length tokens. On entry, Descriptor points to InputReportDescriptor's start or a boundary between two tokens somewhere inside descriptor. Size means number of bytes before end of Descriptor. Function returns a single Token. On exit, Descriptor points next boundary between tokens, just after the token that was returned. Size is decreased by as many bytes as Descriptor pointer was moved forward. Tokens in Long Item format are only partially supported; they will return invalid value but Descriptor pointer and Size will be updated correctly to allow further parsing */ HID_TOKEN STATIC HidGetToken ( IN OUT UINT8 **Descriptor, IN OUT UINT32 *Size ) { HID_TOKEN Token; UINT32 Length; UINT8 DataSize; Token.ID = ((**Descriptor)>>2); Token.Value = 0; DataSize = (**Descriptor)&0x3; if (**Descriptor==0xFE) { // Long Item format Token.ID = *(*Descriptor+2); //don't care about value - Long Items are not supported Length = *(*Descriptor+1); (*Descriptor)+=Length; (*Size)-=Length; return Token; } switch(DataSize) { case 0: Token.Value = 0; break; case 1: Token.Value = *(*Descriptor+1); break; case 2: Token.Value = *(*Descriptor+1) + (*(*Descriptor+2)<<8); break; case 3: Token.Value = *(*Descriptor+1) + ((*(*Descriptor+2))<<8) + ((*(*Descriptor+3))<<16) + ((*(*Descriptor+4))<<24); DataSize = 4; break; default: ; } // move descriptor pointer and decrease size by datasize + 1 byte for ID (*Descriptor) += (1+DataSize); (*Size) -= (1+DataSize); return Token; } /* Stack contains a set of data retrieved from parsing InputReportDescriptor. This function checks if that set of data constitutes a valid InputData dictionary, and if so then puts it into ReportTable, table of dictionaries. Unless this is the 1st dictionary for a particular device, this means allocating new bigger table and deallocating old table @param[in] Stack A pointer to the PARSER_STACK @param[in/out] ReportTable Pointer to the final report table that contains all the Reports */ VOID STATIC HidExportReport ( IN HID_PARSER_STACK *Stack, IN OUT HID_INPUT_REPORT_TABLE *ReportTable ) { HID_INPUT_REPORT_FORMAT *ArrayOfReports; if (Stack->TouchPanelUsage) { ReportTable->TouchPanel = 1; } ArrayOfReports = (HID_INPUT_REPORT_FORMAT*) AllocatePool(sizeof(HID_INPUT_REPORT_FORMAT) * (ReportTable->Quantity + 1)); ASSERT (ArrayOfReports != NULL); if (ArrayOfReports != NULL) { if (ReportTable->Quantity != 0) { CopyMem(ArrayOfReports, ReportTable->Report, sizeof(HID_INPUT_REPORT_FORMAT) * ReportTable->Quantity); FreePool(ReportTable->Report); } ReportTable->Report = ArrayOfReports; CopyMem(&(ReportTable->Report[ReportTable->Quantity]), &(Stack->TempReport), sizeof(HID_INPUT_REPORT_FORMAT)); ReportTable->Quantity++; } } /* Stack is pointer to a not-yet-complete InputReport dictionary. This function consumes Token retrieved from InputReportDescriptor stream and uses it to update the dictionary. Once a dictionary is completed, it calls ExportReport() which puts the dictionary in a table of dictionaries. Then it clears internal data and prepares to build a new one. This implementation of Descriptor parser ignores all types of data except for information on how to decode Button presses and X/Y touch coordinates, as this is the only info relevant for touchpanels. @param[in/out] Stack A pointer to the PARSER_STACK @param[in] Token Current Token @param[in/out] ReportTable Pointer to the final report table that contains all the Reports */ VOID STATIC HidUpdateStack( IN OUT HID_PARSER_STACK *Stack, IN HID_TOKEN Token, IN OUT HID_INPUT_REPORT_TABLE *ReportTable ) { switch (Token.ID) { case USE_PAGE: Stack->GlobalUsage = Token.Value; break; case USAGE: if (Token.Value > 0xFFFF) { //special case for 32 bit usages - they indicate Usage + Use Page together Stack->GlobalUsage = Token.Value >> 16; Token.Value = Token.Value & 0xFFFF; } Stack->Usages++; if (Stack->GlobalUsage == DIGITIZER && Token.Value == TOUCHPANEL) { Stack->TouchPanelUsage = 1; } if (Stack->GlobalUsage == DIGITIZER && Token.Value == TIP_SWITCH) { Stack->UsageB = Stack->Usages;} if (Stack->GlobalUsage == DESKTOP && Token.Value == X_AXIS) { Stack->UsageX = Stack->Usages; } if (Stack->GlobalUsage == DESKTOP && Token.Value == Y_AXIS) { Stack->UsageY = Stack->Usages; } break; case LOG_MIN: Stack->LogMin = Token.Value; break; case LOG_MAX: Stack->LogMax = Token.Value; break; case REP_SIZE: Stack->ReportSize = Token.Value; break; case REP_COUNT: Stack->ReportCount = Token.Value; break; case REPORT_ID: // // Is new report? // if (Stack->TempReport.Id != 0xFFFF) { // && Stack->TempReport.Id != Token.Value HidExportReport (Stack, ReportTable); } Stack->Usages = 0; Stack->UsageX = 0; Stack->UsageY = 0; Stack->UsageB = 0; Stack->LogMin = 0; Stack->LogMax = 0; Stack->TouchPanelUsage = 0; Stack->ReportSize = 0; Stack->ReportCount = 0; Stack->TempReport.Id = Token.Value; // // New Report does not indicate new collection set! // ZeroMem (Stack->TempReport.Collection, sizeof(HID_INPUT_REPORT_COLLECTION) * MAX_HID_COLLECTION); Stack->TempReport.CollectionCount = 1; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection = FALSE; break; case INPUT: if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopB == 0 && Stack->UsageB != 0 && Stack->Usages == Stack->UsageB) { Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartB = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopB = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount); Stack->LogMax = 0; Stack->LogMin = 0; } if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopX == 0 && Stack->UsageX != 0 && Stack->Usages == Stack->UsageX) { Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartX = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopX = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount); Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMaxX = Stack->LogMax; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMinX = Stack->LogMin; Stack->LogMax = 0; Stack->LogMin = 0; } if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopY == 0 && Stack->UsageY != 0 && Stack->Usages == Stack->UsageY) { Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStartY = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitStopY = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal + (Stack->ReportSize * Stack->ReportCount); Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMaxY = Stack->LogMax; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].LogMinY = Stack->LogMin; Stack->LogMax = 0; Stack->LogMin = 0; } Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal += (Stack->ReportSize * Stack->ReportCount); break; case COLLECTION: if (Token.Value == APPLICATION) { } else if (Token.Value == LOGICAL) { if (Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection == TRUE) { Stack->TempReport.CollectionCount += 1; Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].BitsTotal = Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 2].BitsTotal; } Stack->TempReport.Collection[Stack->TempReport.CollectionCount - 1].ValidCollection = TRUE; } break; case END_COLLECTION: Stack->Usages = 0; Stack->UsageB = 0; Stack->UsageX = 0; Stack->UsageY = 0; Stack->LogMin = 0; Stack->LogMax = 0; Stack->ReportSize = 0; Stack->ReportCount = 0; break; default:; } } /* Retrieves single bit from multi-byte stream @param[in] InputStream Data @param[in] Bit Bit offset */ UINT32 STATIC HidAccessBit ( IN UINT8 *InputStream, IN UINT32 Bit ) { return ( ( InputStream[Bit/8] & (1<<(Bit%8)) ) ? 1:0 ); } /** Dump report table - shows the amount of parsed Reports along with their collections @param[in] ReportTable Pointer to the final report table that contains all the Reports **/ VOID STATIC HidShowReportTable ( IN HID_INPUT_REPORT_TABLE ReportTable ) { UINT32 Index; UINT32 Collections; for (Index = 0; Index < ReportTable.Quantity; Index++) { DEBUG((DEBUG_INFO, "Report Id_%x \n", ReportTable.Report[Index].Id)); THC_LOCAL_DEBUG(L"Report Id_%x \n", ReportTable.Report[Index].Id) for (Collections = 0; Collections < ReportTable.Report[Index].CollectionCount; Collections++) { DEBUG((DEBUG_INFO, " Collection Id_%x ", Collections)); DEBUG((DEBUG_INFO, "MaxX_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxX)); DEBUG((DEBUG_INFO, "MinX_%x ", ReportTable.Report[Index].Collection[Collections].LogMinX)); DEBUG((DEBUG_INFO, "MaxY_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxY)); DEBUG((DEBUG_INFO, "MinY_%x ", ReportTable.Report[Index].Collection[Collections].LogMinY)); DEBUG((DEBUG_INFO, "BitB_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartB, ReportTable.Report[Index].Collection[Collections].BitStopB)); DEBUG((DEBUG_INFO, "BitX_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartX, ReportTable.Report[Index].Collection[Collections].BitStopX)); DEBUG((DEBUG_INFO, "BitY_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartY, ReportTable.Report[Index].Collection[Collections].BitStopY)); DEBUG((DEBUG_INFO, "\n")); THC_LOCAL_DEBUG(L" Collection Id_%x ", Collections) THC_LOCAL_DEBUG(L"MaxX_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxX) THC_LOCAL_DEBUG(L"MinX_%x ", ReportTable.Report[Index].Collection[Collections].LogMinX) THC_LOCAL_DEBUG(L"MaxY_%x ", ReportTable.Report[Index].Collection[Collections].LogMaxY) THC_LOCAL_DEBUG(L"MinY_%x ", ReportTable.Report[Index].Collection[Collections].LogMinY) THC_LOCAL_DEBUG(L"BitB_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartB, ReportTable.Report[Index].Collection[Collections].BitStopB) THC_LOCAL_DEBUG(L"BitX_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartX, ReportTable.Report[Index].Collection[Collections].BitStopX) THC_LOCAL_DEBUG(L"BitY_%d-%d ", ReportTable.Report[Index].Collection[Collections].BitStartY, ReportTable.Report[Index].Collection[Collections].BitStopY) THC_LOCAL_DEBUG(L"\n") } } } /** Parses HID Descriptor and creates Report Tables @param[in] ThcDev Context of Thc device @param[in] Descriptor Pointer to the descriptor @param[in] DescriptorLength Size of the descriptor to parse **/ VOID HidParseDescriptor ( IN THC_DEV *ThcDev, IN UINT8 *Descriptor, IN UINT32 DescriptorLength ) { UINT8 *MovingPointer; HID_PARSER_STACK ParseStack; HID_TOKEN Token; DEBUG((DEBUG_INFO, "%a \n", __FUNCTION__)); ZeroMem (&ParseStack, sizeof(HID_PARSER_STACK)); MovingPointer = Descriptor; ParseStack.TempReport.Id = 0xFFFF; //Invalid/Default report while (DescriptorLength > 0) { Token = HidGetToken (&MovingPointer, &DescriptorLength); HidUpdateStack (&ParseStack, Token, &(ThcDev->InputReportTable) ); } HidExportReport (&ParseStack, &(ThcDev->InputReportTable) ); HidShowReportTable (ThcDev->InputReportTable); } /* This function uses dictionaries to parse incoming InputReport and convert it into X/Y coordinates plus Button info. @param[in] ReportTable Report Table with all supported HID reports @param[in] InputStream Pointer to the HID report @param[in] Output Result X/Y/B data @param[in] MinMax X/Y Min and Max data @retval EFI_SUCCESS Parsing completed @retval EFI_NOT_FOUND Corresponding Report ID was not found in the Report Table. */ EFI_STATUS HidParseInput ( IN HID_INPUT_REPORT_TABLE ReportTable, IN UINT8 *InputStream, IN HID_TOUCH_OUTPUT *Output, IN HID_XY_BOUNDARY *MinMax ) { UINT32 Index; UINT32 BitIndex; UINT8* RawData; UINT32 Collections; Output->X = 0; Output->Y = 0; Output->B = 0; MinMax->MaxX = 0; MinMax->MinX = 0; MinMax->MaxY = 0; MinMax->MinY = 0; RawData = InputStream; RawData++; //Skip Report ID // // Incoming data is parsed using proper dictionary selected by ReportID. In cases where // ReportID is not provided, it is guaranteed that only a single dictionary exists // // Dictionary describes which bits from input stream hold X / Y / Button data. // There's no guarantee coordinates will be byte-aligned, therefore we use bit access // for (Index = 0; Index < ReportTable.Quantity; Index++) { if (InputStream[0] == ReportTable.Report[Index].Id) { Collections = 0; for (Collections = 0; Collections < ReportTable.Report[Index].CollectionCount; Collections++) { // // ignore other collections if already got valid data // if (Output->X != 0 && Output->Y != 0) { return EFI_SUCCESS; } Output->B = 0; Output->X = 0; Output->Y = 0; MinMax->MaxX = 0; MinMax->MinX = 0; MinMax->MaxY = 0; MinMax->MinY = 0; for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartX; BitIndexX += HidAccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartX); } MinMax->MinX = ReportTable.Report[Index].Collection[Collections].LogMinX; MinMax->MaxX = ReportTable.Report[Index].Collection[Collections].LogMaxX; for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartY; BitIndexY += HidAccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartY); } MinMax->MinY = ReportTable.Report[Index].Collection[Collections].LogMinY; MinMax->MaxY = ReportTable.Report[Index].Collection[Collections].LogMaxY; for (BitIndex = ReportTable.Report[Index].Collection[Collections].BitStartB; BitIndexB += HidAccessBit(RawData, BitIndex) << (BitIndex - ReportTable.Report[Index].Collection[Collections].BitStartB); } } return EFI_SUCCESS; } } return EFI_NOT_FOUND; }