alder_lake_bios/Insyde/InsydeModulePkg/Bus/I2c/I2cTouchPanelDxe/I2cTouchPanel.c

1443 lines
40 KiB
C

/** @file
I2C Touch Panel driver
;******************************************************************************
;* Copyright (c) 2014 - 2021, 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 "I2cTouchPanel.h"
#include <Library/TimerLib.h>
#define I2C_TP_EVENT_EXPIRD_PERIOD_TIME 100 // 100 ms
/**
Get HID Descriptor
@param [in] I2cIo Protocol instance pointer.
@param [in] HidDescReg HID descriptor register
@param [out] HidDescriptor Point to HID Descriptor
@retval EFI_SUCCESS This driver supports this device.
@retval EFI_DEVICE_ERROR No ack for this command
**/
STATIC
EFI_STATUS
GetHidDescriptor (
IN EFI_I2C_IO_PROTOCOL *I2cIo,
IN UINT32 HidDescReg,
OUT HID_DESCRIPTOR *HidDescriptor
)
{
EFI_STATUS Status;
EFI_I2C_REQUEST_PACKET *RequestPacket;
EFI_I2C_OPERATION *Operation;
UINT8 Buffer[sizeof(EFI_I2C_REQUEST_PACKET) + sizeof(EFI_I2C_OPERATION)];
RequestPacket = (EFI_I2C_REQUEST_PACKET*)Buffer;
Operation = RequestPacket->Operation;
RequestPacket->OperationCount = 2;
Operation[0].Flags = 0;
Operation[0].LengthInBytes = sizeof(UINT16);
Operation[0].Buffer = (UINT8*)&HidDescReg;
Operation[1].Flags = I2C_FLAG_READ;
Operation[1].LengthInBytes = sizeof(HID_DESCRIPTOR);
Operation[1].Buffer = (UINT8*)HidDescriptor;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
RequestPacket,
NULL
);
return Status;
}
/**
Get Report Descriptor
@param [in] I2cIo Protocol instance pointer.
@param [in] HidDescriptor Point to HID Descriptor
@param [out] ReportDesc Point to Report Descriptor
@retval EFI_SUCCESS This driver supports this device.
@retval EFI_DEVICE_ERROR No ack for this command
@retval EFI_INVALID_PARAMETER ReportDesc == NULL
@retval EFI_OUT_OF_RESOURCES Memory Overflow
**/
STATIC
EFI_STATUS
GetReportDescriptor (
IN EFI_I2C_IO_PROTOCOL *I2cIo,
IN HID_DESCRIPTOR *HidDescriptor,
OUT UINT8 **ReportDesc
)
{
EFI_I2C_REQUEST_PACKET *RequestPacket;
EFI_I2C_OPERATION *Operation;
UINT8 Buffer[sizeof(EFI_I2C_REQUEST_PACKET) + sizeof(EFI_I2C_OPERATION)];
UINT16 ReportDescReg;
EFI_STATUS Status;
if (ReportDesc == NULL) {
return EFI_INVALID_PARAMETER;
}
*ReportDesc = AllocateZeroPool (HidDescriptor->ReportDescLength);
if (*ReportDesc == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Get the Report Descriptor
//
RequestPacket = (EFI_I2C_REQUEST_PACKET*)Buffer;
Operation = RequestPacket->Operation;
ReportDescReg = HidDescriptor->ReportDescRegister;
RequestPacket->OperationCount = 2;
Operation[0].Flags = 0;
Operation[0].LengthInBytes = sizeof(UINT16);
Operation[0].Buffer = (UINT8*)&ReportDescReg;
Operation[1].Flags = I2C_FLAG_READ;
Operation[1].LengthInBytes = HidDescriptor->ReportDescLength;
Operation[1].Buffer = *ReportDesc;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
RequestPacket,
NULL
);
return Status;
}
/**
Set the power state
@param TpDev Touch panel instance
@param PowerState Power state
@retval EFI_SUCCESS
**/
STATIC
EFI_STATUS
SetPower (
IN I2C_TOUCH_PANEL_DEV *TpDev,
IN UINT16 PowerState
)
{
UINT16 Request[2];
EFI_STATUS Status;
EFI_I2C_REQUEST_PACKET RequestPacket;
EFI_I2C_IO_PROTOCOL *I2cIo;
if ((TpDev == NULL) ||
((I2cIo = TpDev->I2cIo) == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Set the power state
//
ZeroMem (&RequestPacket, sizeof (RequestPacket));
Request[0] = TpDev->HidDescriptor.CommandRegister;
Request[1] = SET_POWER | PowerState;
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = 0;
RequestPacket.Operation[0].LengthInBytes = 4;
RequestPacket.Operation[0].Buffer = (UINT8*)Request;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
return Status;
}
/**
Reset the device
@param TpDev Touch panel instance
@retval EFI_SUCCESS
**/
STATIC
EFI_STATUS
ResetDevice (
IN I2C_TOUCH_PANEL_DEV *TpDev
)
{
UINT16 Request[2];
EFI_STATUS Status;
EFI_I2C_REQUEST_PACKET RequestPacket;
EFI_I2C_IO_PROTOCOL *I2cIo;
if ((TpDev == NULL) ||
((I2cIo = TpDev->I2cIo) == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Reset the device
//
ZeroMem (&RequestPacket, sizeof (RequestPacket));
Request[0] = TpDev->HidDescriptor.CommandRegister;
Request[1] = RESET;
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = 0;
RequestPacket.Operation[0].LengthInBytes = 4;
RequestPacket.Operation[0].Buffer = (UINT8*)Request;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Receive the status with flush the transmission buffer
//
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = I2C_FLAG_READ;
RequestPacket.Operation[0].LengthInBytes = 2;
RequestPacket.Operation[0].Buffer = (UINT8*)Request;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
return Status;
}
/**
Flush data buffer before operation
@param TpDev Touch panel instance
@retval EFI_SUCCESS
**/
STATIC
EFI_STATUS
FlushDataBuffer (
IN I2C_TOUCH_PANEL_DEV *TpDev
)
{
EFI_STATUS Status;
EFI_I2C_REQUEST_PACKET RequestPacket;
EFI_I2C_IO_PROTOCOL *I2cIo;
if ((TpDev == NULL) ||
((I2cIo = TpDev->I2cIo) == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Receive the status with flush the transmission buffer
//
ZeroMem (&RequestPacket, sizeof (RequestPacket));
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = I2C_FLAG_READ;
RequestPacket.Operation[0].LengthInBytes = TpDev->HidDescriptor.MaxInputLength;
RequestPacket.Operation[0].Buffer = TpDev->ReportData;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
return Status;
}
/**
I2C touch panel driver need receive next event in fixed time when
receive a touch event and user still touch.
Otherwise it will take as finger up, but no receive release event,
I2C touch panel driver will simulator a release event.
@param TpDev Touch panel instance
**/
VOID
I2cTouchPanelEventExpiredHandler (
I2C_TOUCH_PANEL_DEV *TpDev
)
{
EFI_TPL OldTpl;
UINT64 Tick;
UINTN BufferIn;
if (TpDev->EventExpiredTime == 0) {
return ;
}
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
Tick = GetPerformanceCounter();
if (Tick < TpDev->EventExpiredTime) {
gBS->RestoreTPL (OldTpl);
return ;
}
TpDev->EventExpiredTime = 0;
if (TpDev->State[TpDev->BufferIn].ActiveButtons == 0) {
gBS->RestoreTPL (OldTpl);
return ;
}
TpDev->StateChanged = TRUE;
TpDev->EventExpiredTime = 0;
TpDev->State[TpDev->BufferIn].ActiveButtons = 0;
BufferIn = TpDev->BufferIn + 1;
if (BufferIn == STATE_BUFFER_SIZE) BufferIn = 0;
if (BufferIn != TpDev->BufferOut) {
//
// Copy last state to new position for use of check procedure of last button state
//
CopyMem (
&TpDev->State[BufferIn],
&TpDev->State[TpDev->BufferIn],
sizeof (EFI_ABSOLUTE_POINTER_STATE)
);
TpDev->BufferIn = BufferIn;
}
gBS->RestoreTPL(OldTpl);
}
/**
It is called whenever there is data received from async interrupt
transfer.
@param Event Wait Event
@param Context Passed parameter to event handler
**/
STATIC
VOID
EFIAPI
I2cTouchPanelInterruptOnCompleted (
IN EFI_EVENT Event,
IN VOID *Context
)
{
I2C_TOUCH_PANEL_DEV *TpDev;
REPORT_GROUP *ReportGroup;
UINTN DataLength;
UINTN Index;
EFI_ABSOLUTE_POINTER_STATE AbsPtrState;
UINT8 *ReportData;
EFI_I2C_IO_PROTOCOL *I2cIo;
EFI_I2C_REQUEST_PACKET RequestPacket;
UINTN BufferIn;
TpDev = (I2C_TOUCH_PANEL_DEV*)Context;
I2cIo = TpDev->I2cIo;
DataLength = *(UINT16*)TpDev->ReportData;
if (TpDev->TransferResult != EFI_SUCCESS || DataLength == 0) goto Exit;
ReportData = TpDev->ReportData + sizeof (UINT16);
DataLength -= sizeof (UINT16);
//
// Since Report ID is optional, our saved value of Report ID can be 0 and it means that no
// Report ID item tags were present in the report descriptor. If no Report ID was declared,
// we assume only one report group exists and continue using the first report group.
//
if (TpDev->ReportId == 0) {
ReportGroup = &(TpDev->ReportFieldInfo.ReportGroup[0]);
} else {
ReportGroup = NULL;
for (Index = 0; Index < TpDev->ReportFieldInfo.Total; Index++) {
if ((TpDev->ReportFieldInfo.ReportGroup[Index].Id == (*(UINT8*)ReportData)) &&
(TpDev->ReportFieldInfo.ReportGroup[Index].DataValid)) {
ReportGroup = &TpDev->ReportFieldInfo.ReportGroup[Index];
break;
}
}
if (!ReportGroup) goto Exit;
if (TpDev->ReportId != ReportGroup->Id) {
//
// Reset the Mode parameters due to report ID changed
//
TpDev->ReportId = ReportGroup->Id;
//
// Set limit by report descriptor
//
TpDev->Mode.AbsoluteMinX = ReportGroup->Data.TouchPanel.FieldX.Min;
TpDev->Mode.AbsoluteMinY = ReportGroup->Data.TouchPanel.FieldY.Min;
TpDev->Mode.AbsoluteMaxX = ReportGroup->Data.TouchPanel.FieldX.Max;
TpDev->Mode.AbsoluteMaxY = ReportGroup->Data.TouchPanel.FieldY.Max;
TpDev->Mode.Attributes = 0;
if (ReportGroup->Data.TouchPanel.FieldAltActive.DataValid) {
TpDev->Mode.Attributes |= EFI_ABSP_SupportsAltActive;
}
}
}
if (DataLength < ReportGroup->DataSize) {
goto Exit;
}
//
// Get TouchPanelData data
//
AbsPtrState.CurrentX = (UINT32) GetReportFieldValue (ReportData, DataLength, &ReportGroup->Data.TouchPanel.FieldX);
AbsPtrState.CurrentY = (UINT32) GetReportFieldValue (ReportData, DataLength, &ReportGroup->Data.TouchPanel.FieldY);
AbsPtrState.CurrentZ = 0;
AbsPtrState.ActiveButtons = 0;
if (GetReportFieldValue (ReportData, DataLength, &ReportGroup->Data.TouchPanel.FieldTouchActive)) {
AbsPtrState.ActiveButtons |= EFI_ABSP_TouchActive;
}
if (GetReportFieldValue (ReportData, DataLength, &ReportGroup->Data.TouchPanel.FieldAltActive)) {
AbsPtrState.ActiveButtons |= EFI_ABS_AltActive;
}
TpDev->State[TpDev->BufferIn].CurrentX = AbsPtrState.CurrentX;
TpDev->State[TpDev->BufferIn].CurrentY = AbsPtrState.CurrentY;
TpDev->State[TpDev->BufferIn].CurrentZ = AbsPtrState.CurrentZ;
I2cTouchPanelEventExpiredHandler (TpDev);
if (AbsPtrState.ActiveButtons != 0) {
TpDev->EventExpiredTime = GetPerformanceCounter() + TpDev->EventExpiredPeriodTime;
} else {
TpDev->EventExpiredTime = 0;
}
if ((TpDev->State[TpDev->BufferIn].ActiveButtons != 0) || (AbsPtrState.ActiveButtons != 0)) {
TpDev->StateChanged = TRUE;
if (TpDev->State[TpDev->BufferIn].ActiveButtons != AbsPtrState.ActiveButtons) {
TpDev->State[TpDev->BufferIn].ActiveButtons = AbsPtrState.ActiveButtons;
//
// Advance the internal buffer only when button state changed to reduce the dragging phenomenon
//
BufferIn = TpDev->BufferIn + 1;
if (BufferIn == STATE_BUFFER_SIZE) BufferIn = 0;
if (BufferIn != TpDev->BufferOut) {
//
// Copy last state to new position for use of check procedure of last button state
//
CopyMem (
&TpDev->State[BufferIn],
&TpDev->State[TpDev->BufferIn],
sizeof (EFI_ABSOLUTE_POINTER_STATE)
);
TpDev->BufferIn = BufferIn;
}
}
}
Exit:
//
// Starting next transmission
//
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = I2C_FLAG_READ;
RequestPacket.Operation[0].LengthInBytes = TpDev->HidDescriptor.MaxInputLength;
RequestPacket.Operation[0].Buffer = TpDev->ReportData;
I2cIo->QueueRequest (
I2cIo,
0,
TpDev->OnCompleteEvent,
&RequestPacket,
&TpDev->TransferResult
);
}
/**
Event notification function for ABSOLUTE_POINTER.WaitForInput event
Signal the event if there is input from Touch Panel
@param Event Wait Event
@param Context Passed parameter to event handler
**/
STATIC
VOID
EFIAPI
I2cTouchPanelWaitForInput (
IN EFI_EVENT Event,
IN VOID *Context
)
{
I2C_TOUCH_PANEL_DEV *TpDev;
TpDev = (I2C_TOUCH_PANEL_DEV *) Context;
//
// Someone is waiting on the TouchPanel event, if there's
// input from Touch Panel, signal the event
//
if (TpDev->StateChanged) {
gBS->SignalEvent (Event);
}
}
/**
Get the Touch Panel state, see ABSOLUTE POINTER PROTOCOL.
@param This Protocol instance pointer.
@param Touch PanelState Current Touch Panel state
@retval EFI_SUCCESS
@retval EFI_DEVICE_ERROR
@retval EFI_NOT_READY
**/
STATIC
EFI_STATUS
EFIAPI
I2cGetTouchPanelState (
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This,
OUT EFI_ABSOLUTE_POINTER_STATE *TouchPanelState
)
{
I2C_TOUCH_PANEL_DEV *TpDev;
EFI_TPL OldTpl;
if (TouchPanelState == NULL) {
return EFI_DEVICE_ERROR;
}
TpDev = I2C_TOUCH_PANEL_DEV_FROM_ABSOLUTE_POINTER_PROTOCOL (This);
I2cTouchPanelEventExpiredHandler (TpDev);
if (!TpDev->StateChanged) {
return EFI_NOT_READY;
}
CopyMem (
TouchPanelState,
&TpDev->State[TpDev->BufferOut],
sizeof (EFI_ABSOLUTE_POINTER_STATE)
);
OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
if (TpDev->BufferOut != TpDev->BufferIn) {
TpDev->BufferOut ++;
if (TpDev->BufferOut == STATE_BUFFER_SIZE) TpDev->BufferOut = 0;
}
if (TpDev->BufferOut == TpDev->BufferIn) {
TpDev->StateChanged = FALSE;
}
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
}
/**
Reset the Touch Panel device, see ABSOLUTE POINTER PROTOCOL.
@param This Protocol instance pointer.
@param ExtendedVerification Ignored here
@retval EFI_SUCCESS
**/
STATIC
EFI_STATUS
EFIAPI
I2cTouchPanelReset (
IN EFI_ABSOLUTE_POINTER_PROTOCOL *This,
IN BOOLEAN ExtendedVerification
)
{
I2C_TOUCH_PANEL_DEV *TpDev;
TpDev = I2C_TOUCH_PANEL_DEV_FROM_ABSOLUTE_POINTER_PROTOCOL (This);
ZeroMem (
TpDev->State,
sizeof (EFI_ABSOLUTE_POINTER_STATE) * STATE_BUFFER_SIZE
);
TpDev->StateChanged = FALSE;
TpDev->BufferIn = 0;
TpDev->BufferOut = 0;
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
I2cGetReportRequest (
IN I2C_TOUCH_PANEL_DEV *TpDev,
IN UINT8 ReportId,
IN UINT16 ReportType,
IN UINTN ReportLen,
IN UINT8 *Report
)
{
UINT8 Buffer[sizeof(EFI_I2C_REQUEST_PACKET) + sizeof(EFI_I2C_OPERATION)];
UINT8 Request[8];
EFI_STATUS Status;
EFI_I2C_OPERATION *Operation;
EFI_I2C_REQUEST_PACKET *RequestPacket;
UINT32 Length;
EFI_I2C_IO_PROTOCOL *I2cIo;
if ((TpDev == NULL) ||
((I2cIo = TpDev->I2cIo) == NULL)) {
return EFI_INVALID_PARAMETER;
}
*(UINT16*)Request = TpDev->HidDescriptor.CommandRegister;
if (ReportId < 0x0f) {
*(UINT16*)(Request + 2) = ReportType | ReportId;
*(UINT16*)(Request + 4) = TpDev->HidDescriptor.DataRegister;
Length = 6;
} else {
*(UINT16*)(Request + 2) = ReportType | 0x0f;
Request[4] = ReportId;
*(UINT16*)(Request + 5) = TpDev->HidDescriptor.DataRegister;
Length = 7;
}
RequestPacket = (EFI_I2C_REQUEST_PACKET*)Buffer;
Operation = RequestPacket->Operation;
RequestPacket->OperationCount = 2;
Operation[0].Flags = 0;
Operation[0].LengthInBytes = Length;
Operation[0].Buffer = Request;
Operation[1].Flags = I2C_FLAG_READ;
Operation[1].LengthInBytes = (UINT32)(ReportLen + 2);
Operation[1].Buffer = Report;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
RequestPacket,
NULL
);
return Status;
}
EFI_STATUS
I2cHidInitReports (
IN I2C_TOUCH_PANEL_DEV *TpDev
)
{
EFI_STATUS Status;
UINT8 *ReportData;
UINTN Index;
REPORT_GROUP *ReportGroup;
if ((TpDev == NULL) || (TpDev->I2cIo == NULL)) {
return EFI_INVALID_PARAMETER;
}
ReportData = AllocatePool (TpDev->ReportFieldInfo.MaximumFeatureLength + 16);
if (ReportData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Accroding to Linux source code,
// get Feature Report to make some touch panel device work
//
for (Index = 0; Index < TpDev->ReportFieldInfo.Total; Index++) {
ReportGroup = &TpDev->ReportFieldInfo.ReportGroup[Index];
if (ReportGroup->DataValid &&
ReportGroup->DataClass == TOUCH_PANEL_CLASS &&
ReportGroup->DataType == HID_MAIN_ITEM_TAG_FEATURE &&
ReportGroup->DataSize > 0 &&
ReportGroup->DataAttr == ATTR_VENDOR_FEATURE) {
Status = I2cGetReportRequest (
TpDev,
(UINT8) ReportGroup->Id,
GET_REPORT_FEATURE,
ReportGroup->DataSize,
ReportData
);
}
}
FreePool (ReportData);
return EFI_SUCCESS;
}
/**
Set device into single input mode
@param TpDev Touch panel instance
@retval EFI_SUCCESS
@retval others
**/
STATIC
EFI_STATUS
SetDeviceMode (
IN I2C_TOUCH_PANEL_DEV *TpDev
)
{
EFI_STATUS Status;
REPORT_GROUP *ReportGroup;
UINTN Length;
UINT8 *ReportData;
EFI_I2C_REQUEST_PACKET RequestPacket;
EFI_I2C_IO_PROTOCOL *I2cIo;
UINT8 Request[8];
if ((TpDev == NULL) ||
((I2cIo = TpDev->I2cIo) == NULL) ||
((ReportGroup = TpDev->ReportFieldInfo.DeviceConfigurationReportGroup) == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Since Device Configuration must have its own report (see HUTRR34.pdf), Report ID must be
// reported by the USB touch device and can not be the reserved value of 0.
//
if ((ReportGroup->Id == 0) || (!(ReportGroup->DataValid))) {
return EFI_UNSUPPORTED;
}
ReportData = AllocatePool (ReportGroup->DataSize + 16);
if (ReportData == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Get the current mode
//
*(UINT16*)ReportData = TpDev->HidDescriptor.CommandRegister;
if (ReportGroup->Id < 0x0f) {
*(UINT16*)(ReportData + 2) = GET_REPORT_FEATURE | ReportGroup->Id;
*(UINT16*)(ReportData + 4) = TpDev->HidDescriptor.DataRegister;
Length = 6;
} else {
*(UINT16*)(ReportData + 2) = GET_REPORT_FEATURE | 0x0f;
ReportData[4] = ReportGroup->Id;
*(UINT16*)(ReportData + 5) = TpDev->HidDescriptor.DataRegister;
Length = 7;
}
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = 0;
RequestPacket.Operation[0].LengthInBytes = (UINT32)Length;
RequestPacket.Operation[0].Buffer = ReportData;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
Length = ReportGroup->DataSize + 2;
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = I2C_FLAG_READ;
RequestPacket.Operation[0].LengthInBytes = (UINT32)Length;
RequestPacket.Operation[0].Buffer = ReportData;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
if (GetReportFieldValue (ReportData + 2, ReportGroup->DataSize, &ReportGroup->Data.TouchPanelMode.FieldDeviceMode) == HID_DIGITIZERS_DEVICE_MODE_SINGLE_INPUT) {
//
// Exit if current mode is in single input mode
//
goto EXIT;
}
Status = SetReportFieldValue (
ReportData + 2,
ReportGroup->DataSize,
&ReportGroup->Data.TouchPanelMode.FieldDeviceMode,
HID_DIGITIZERS_DEVICE_MODE_SINGLE_INPUT
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
if (ReportGroup->Data.TouchPanelMode.FieldDeviceId.DataValid) {
//
// Use a Device ID of 0 to configure all the report groups (i.e. top-level collections)
// at the same time regardless of Device ID (see HUTRR34.pdf).
//
Status = SetReportFieldValue (
ReportData + 2,
ReportGroup->DataSize,
&ReportGroup->Data.TouchPanelMode.FieldDeviceId,
0
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
}
//
// Set the mode to single input mode
//
*(UINT16*)Request = TpDev->HidDescriptor.CommandRegister;
if (ReportGroup->Id < 0x0f) {
Length = 6;
*(UINT16*)(Request + 2) = SET_REPORT_FEATURE | ReportGroup->Id;
*(UINT16*)(Request + 4) = TpDev->HidDescriptor.DataRegister;
} else {
Length = 7;
*(UINT16*)(Request + 2) = SET_REPORT_FEATURE | 0x0f;
Request[4] = ReportGroup->Id;
*(UINT16*)(Request + 5) = TpDev->HidDescriptor.DataRegister;
}
CopyMem (ReportData + Length, ReportData, *(UINT16*)ReportData);
CopyMem (ReportData, Request, Length);
Length += *(UINT16*)(ReportData + Length);
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = 0;
RequestPacket.Operation[0].LengthInBytes = (UINT32)Length;
RequestPacket.Operation[0].Buffer = ReportData;
Status = I2cIo->QueueRequest (
I2cIo,
0,
NULL,
&RequestPacket,
NULL
);
if (EFI_ERROR (Status)) {
return Status;
}
EXIT:
FreePool (ReportData);
return Status;
}
/**
Free the resource allocated by I2C Touch Panel instance
@param TpDev I2C Touch panel instance
@param Controller I2C I/O Controller Handle
**/
STATIC
VOID
FreeResource (
IN I2C_TOUCH_PANEL_DEV *TpDev,
IN EFI_HANDLE Controller
)
{
//
// Always close protocol even the protocol has not being opened
//
gBS->CloseProtocol (
Controller,
&gEfiI2cIoProtocolGuid,
gI2cTouchPanelDriverBinding.DriverBindingHandle,
Controller
);
//
// Free resources in the instance
//
if (TpDev) {
if (TpDev->OnCompleteEvent) {
gBS->CloseEvent (TpDev->OnCompleteEvent);
}
if (TpDev->AbsolutePointerProtocol.WaitForInput) {
gBS->CloseEvent (TpDev->AbsolutePointerProtocol.WaitForInput);
}
gBS->UninstallProtocolInterface (
Controller,
&gEfiAbsolutePointerProtocolGuid,
&TpDev->AbsolutePointerProtocol
);
if (TpDev->ReportData) {
FreePool (TpDev->ReportData);
}
if (TpDev->ControllerNameTable) {
FreeUnicodeStringTable (TpDev->ControllerNameTable);
}
FreePool (TpDev);
}
}
/**
Verify the controller type
This routine determines if an Touch Panel is available.
This routine is called by the UEFI driver framework during connect
processing.
@param [in] DriverBinding Protocol instance pointer.
@param [in] Controller Handle of device to test.
@param [in] RemainingDevicePath Not used.
@retval EFI_SUCCESS This driver supports this device.
@retval other This driver does not support this device.
**/
EFI_STATUS
EFIAPI
I2cTouchPanelDriverSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
EFI_I2C_IO_PROTOCOL *I2cIo;
H2O_I2C_HID_DEVICE *I2cHidDevice;
//
// Determine if the I2C I/O is available
//
Status = gBS->OpenProtocol (
Controller,
&gEfiI2cIoProtocolGuid,
(VOID**) &I2cIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR ( Status )) {
return Status;
}
//
// The I2C I/O is available
//
gBS->CloseProtocol (
Controller,
&gEfiI2cIoProtocolGuid,
This->DriverBindingHandle,
Controller
);
//
// Inspect the H2O I2C HID specific data in the controller
//
Status = gBS->OpenProtocol (
Controller,
&gI2cHidDeviceInfoGuid,
(VOID**) &I2cHidDevice,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
if (I2cHidDevice->ClassType != TOUCH_PANEL_CLASS) {
Status = EFI_NOT_FOUND;
}
return Status;
}
/**
Connect to the I2C Touch Panel
This routine initializes an instance of the I2C driver for this
controller.
This routine is called by the UEFI driver framework during connect
processing if the controller passes the tests in I2cBusDriverSupported.
@param [in] DriverBinding Protocol instance pointer.
@param [in] Controller Handle of device to work with.
@param [in] RemainingDevicePath Not used, always produce all possible children.
@retval EFI_SUCCESS This driver is added to Controller.
@retval other This driver does not support this device.
**/
EFI_STATUS
EFIAPI
I2cTouchPanelDriverStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
I2C_TOUCH_PANEL_DEV *TpDev;
EFI_I2C_IO_PROTOCOL *I2cIo;
H2O_I2C_HID_DEVICE *I2cHidDevice;
UINT8 *ReportDescriptor;
UINTN Index;
REPORT_GROUP *ReportGroup;
EFI_I2C_REQUEST_PACKET RequestPacket;
UINT64 CpuFreq;
TpDev = NULL;
//
// Occupy the I2C I/O protocol
//
Status = gBS->OpenProtocol (
Controller,
&gEfiI2cIoProtocolGuid,
(VOID**) &I2cIo,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_BY_DRIVER
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Get the H2O I2C HID specific data in the controller
//
Status = gBS->OpenProtocol (
Controller,
&gI2cHidDeviceInfoGuid,
(VOID**) &I2cHidDevice,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
goto Error;
}
TpDev = AllocateZeroPool (sizeof (I2C_TOUCH_PANEL_DEV));
if (TpDev == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
TpDev->Signature = I2C_TOUCH_PANEL_DEV_SIGNATURE;
TpDev->I2cIo = I2cIo;
CopyMem (&TpDev->I2cHid, I2cHidDevice, I2cHidDevice->Length);
//
// Get HID descriptor
//
Status = GetHidDescriptor (I2cIo, TpDev->I2cHid.DescReg, &TpDev->HidDescriptor);
if (EFI_ERROR (Status)) {
goto Error;
}
if (TpDev->HidDescriptor.HIDDescLength != sizeof(HID_DESCRIPTOR)) {
Status = EFI_DEVICE_ERROR;
goto Error;
}
TpDev->I2cHid.VendorID = TpDev->HidDescriptor.VendorID;
TpDev->I2cHid.ProductID = TpDev->HidDescriptor.ProductID;
TpDev->I2cHid.VersionID = TpDev->HidDescriptor.VersionID;
//
// Set the VID/PID/Revision into I2C Hid Device inforamtion for use of OEM specific usage
//
CopyMem (I2cHidDevice, &TpDev->I2cHid, I2cHidDevice->Length);
TpDev->ReportData = AllocateZeroPool (TpDev->HidDescriptor.MaxInputLength);
if (TpDev->ReportData == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Error;
}
//
// Power on the device
//
SetPower (TpDev, POWER_ON);
//
// Reset the device
//
ResetDevice (TpDev);
//
// Flush the data buffer before operation
//
FlushDataBuffer (TpDev);
//
// Get report descriptor
//
Status = GetReportDescriptor ( I2cIo, &TpDev->HidDescriptor, &ReportDescriptor);
if (EFI_ERROR (Status)) {
goto Error;
}
Status = ParseReportDescriptor (
ReportDescriptor,
TpDev->HidDescriptor.ReportDescLength,
&TpDev->ReportFieldInfo
);
FreePool (ReportDescriptor);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Searching for touch panel report
//
for (Index = 0, ReportGroup = NULL; Index < TpDev->ReportFieldInfo.Total; Index ++) {
if (TpDev->ReportFieldInfo.ReportGroup[Index].DataValid &&
TpDev->ReportFieldInfo.ReportGroup[Index].DataClass == TOUCH_PANEL_CLASS &&
TpDev->ReportFieldInfo.ReportGroup[Index].DataType == HID_MAIN_ITEM_TAG_INPUT &&
TpDev->ReportFieldInfo.ReportGroup[Index].DataSize > 0) {
ReportGroup = &TpDev->ReportFieldInfo.ReportGroup[Index];
break;
}
}
if (ReportGroup == NULL) {
Status = EFI_UNSUPPORTED;
goto Error;
}
TpDev->ReportId = ReportGroup->Id;
TpDev->Mode.AbsoluteMinX = ReportGroup->Data.TouchPanel.FieldX.Min;
TpDev->Mode.AbsoluteMinY = ReportGroup->Data.TouchPanel.FieldY.Min;
TpDev->Mode.AbsoluteMaxX = ReportGroup->Data.TouchPanel.FieldX.Max;
TpDev->Mode.AbsoluteMaxY = ReportGroup->Data.TouchPanel.FieldY.Max;
TpDev->Mode.Attributes = 0;
if (ReportGroup->Data.TouchPanel.FieldAltActive.DataValid) {
TpDev->Mode.Attributes |= EFI_ABSP_SupportsAltActive;
}
//
// If Device Configuration feature report is found, this touch device has multiple operating
// modes (mouse, single-input, multi-input, etc.). Some devices only send "correct" report data
// (i.e. absolute coordinates for digitizer as opposed to relative displacements for mouse) when
// they are operating in single-input mode. However, some other devices do not change operating
// mode after SetDeviceMode (), but can still work with their report data; thus, we do not check
// the return status of SetDeviceMode ().
//
if (TpDev->ReportFieldInfo.DeviceConfigurationReportGroup != NULL) {
SetDeviceMode (TpDev);
}
I2cHidInitReports (TpDev);
//
// Install AbsolutePointer protocol
//
TpDev->AbsolutePointerProtocol.GetState = I2cGetTouchPanelState;
TpDev->AbsolutePointerProtocol.Reset = I2cTouchPanelReset;
TpDev->AbsolutePointerProtocol.Mode = &TpDev->Mode;
Status = gBS->CreateEvent (
EVT_NOTIFY_WAIT,
TPL_NOTIFY,
I2cTouchPanelWaitForInput,
TpDev,
&TpDev->AbsolutePointerProtocol.WaitForInput
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Install Absolute Pointer Protocol for the I2C touch panel device.
//
Status = gBS->InstallProtocolInterface (
&Controller,
&gEfiAbsolutePointerProtocolGuid,
EFI_NATIVE_INTERFACE,
&TpDev->AbsolutePointerProtocol
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// Create event for interrupt on completed handler
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
I2cTouchPanelInterruptOnCompleted,
TpDev,
&TpDev->OnCompleteEvent
);
if (EFI_ERROR (Status)) {
goto Error;
}
//
// calcaute I2C_TP_CHECK_RELEASE_STATE_PERIOD ms
//
CpuFreq = GetPerformanceCounterProperties (NULL, NULL);
TpDev->EventExpiredPeriodTime = DivU64x32 (MultU64x32 (CpuFreq, I2C_TP_EVENT_EXPIRD_PERIOD_TIME), 1000u);
//
// Allocate and setup controller name table for ComponentName/2 protocol
//
AddUnicodeString2 (
LANGUAGE_CODE_ENGLISH_ISO639,
gI2cTouchPanelComponentName.SupportedLanguages,
&TpDev->ControllerNameTable,
CONTROLLER_DRIVER_NAME,
TRUE
);
AddUnicodeString2 (
LANGUAGE_CODE_ENGLISH_RFC4646,
gI2cTouchPanelComponentName2.SupportedLanguages,
&TpDev->ControllerNameTable,
CONTROLLER_DRIVER_NAME,
FALSE
);
//
// Issue async I2C request for data transfer
//
RequestPacket.OperationCount = 1;
RequestPacket.Operation[0].Flags = I2C_FLAG_READ;
RequestPacket.Operation[0].LengthInBytes = TpDev->HidDescriptor.MaxInputLength;
RequestPacket.Operation[0].Buffer = TpDev->ReportData;
Status = I2cIo->QueueRequest (
I2cIo,
0,
TpDev->OnCompleteEvent,
&RequestPacket,
&TpDev->TransferResult
);
Error:
if (EFI_ERROR (Status)) {
FreeResource (TpDev, Controller);
}
return Status;
}
/**
Disconnect from the I2C host controller.
This routine disconnects from the Touch Panel
This routine is called by DriverUnload when the Touch Panel
is being unloaded.
@param [in] DriverBinding Protocol instance pointer.
@param [in] Controller Handle of device to stop driver on.
@param [in] NumberOfChildren How many children need to be stopped.
@param [in] ChildHandleBuffer Not used.
@retval EFI_SUCCESS This driver is removed Controller.
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
@retval other This driver was not removed from this device.
**/
EFI_STATUS
EFIAPI
I2cTouchPanelDriverStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_STATUS Status;
I2C_TOUCH_PANEL_DEV *TpDev;
EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointerProtocol;
//
// Disconnect any connected drivers and locate the context
// structure
//
Status = gBS->OpenProtocol (
Controller,
&gEfiAbsolutePointerProtocolGuid,
(VOID**) &AbsolutePointerProtocol,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
TpDev = I2C_TOUCH_PANEL_DEV_FROM_ABSOLUTE_POINTER_PROTOCOL (AbsolutePointerProtocol);
FreeResource (TpDev, Controller);
return Status;
}
/**
Driver binding protocol support
**/
EFI_DRIVER_BINDING_PROTOCOL gI2cTouchPanelDriverBinding = {
I2cTouchPanelDriverSupported,
I2cTouchPanelDriverStart,
I2cTouchPanelDriverStop,
0x12,
NULL,
NULL
};
/**
Entry point for EFI drivers.
@param ImageHandle EFI_HANDLE
@param SystemTable EFI_SYSTEM_TABLE
@retval EFI_SUCCESS
@retval others
**/
EFI_STATUS
EFIAPI
I2cTouchPanelEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Install binding protocols
//
return EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gI2cTouchPanelDriverBinding,
ImageHandle,
&gI2cTouchPanelComponentName,
&gI2cTouchPanelComponentName2
);
}
/**
This is the default unload handle for all the I2C tocuh panel drivers.
Disconnect the driver specified by ImageHandle from all the devices in the handle database.
Uninstall all the protocols installed in the driver entry point.
@param[in] ImageHandle The drivers' driver image.
@retval EFI_SUCCESS The image is unloaded.
@retval Others Failed to unload the image.
**/
EFI_STATUS
EFIAPI
I2cTouchPanelUnload (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_HANDLE *DeviceHandleBuffer;
UINTN DeviceHandleCount;
UINTN Index;
UINTN Index2;
EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
//
// Get the list of all the handles in the handle database.
// If there is an error getting the list, then the unload
// operation fails.
//
Status = gBS->LocateHandleBuffer (
AllHandles,
NULL,
NULL,
&DeviceHandleCount,
&DeviceHandleBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < DeviceHandleCount; Index++) {
Status = gBS->HandleProtocol (
DeviceHandleBuffer[Index],
&gEfiDriverBindingProtocolGuid,
(VOID **) &DriverBinding
);
if (EFI_ERROR (Status)) {
continue;
}
if (DriverBinding->ImageHandle != ImageHandle) {
continue;
}
//
// Disconnect the driver specified by ImageHandle from all
// the devices in the handle database.
//
for (Index2 = 0; Index2 < DeviceHandleCount; Index2++) {
Status = gBS->DisconnectController (
DeviceHandleBuffer[Index2],
DriverBinding->DriverBindingHandle,
NULL
);
}
//
// Uninstall all the protocols installed in the driver entry point
//
gBS->UninstallProtocolInterface (
DriverBinding->DriverBindingHandle,
&gEfiDriverBindingProtocolGuid,
DriverBinding
);
Status = gBS->HandleProtocol (
DeviceHandleBuffer[Index],
&gEfiComponentNameProtocolGuid,
(VOID **) &ComponentName
);
if (!EFI_ERROR (Status)) {
gBS->UninstallProtocolInterface (
DriverBinding->DriverBindingHandle,
&gEfiComponentNameProtocolGuid,
ComponentName
);
}
Status = gBS->HandleProtocol (
DeviceHandleBuffer[Index],
&gEfiComponentName2ProtocolGuid,
(VOID **) &ComponentName2
);
if (!EFI_ERROR (Status)) {
gBS->UninstallProtocolInterface (
DriverBinding->DriverBindingHandle,
&gEfiComponentName2ProtocolGuid,
ComponentName2
);
}
}
//
// Free the buffer containing the list of handles from the handle database
//
if (DeviceHandleBuffer != NULL) {
FreePool (DeviceHandleBuffer);
}
return EFI_SUCCESS;
}