/** @file ;****************************************************************************** ;* Copyright (c) 2012 - 2018, 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 "UsbTouchPanel.h" #include // // Prototypes // Driver model protocol interface // EFI_STATUS EFIAPI UsbTouchPanelDriverBindingEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); EFI_STATUS EFIAPI UsbTouchPanelDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ); EFI_STATUS EFIAPI UsbTouchPanelDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ); EFI_STATUS EFIAPI UsbTouchPanelDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ); EFI_DRIVER_BINDING_PROTOCOL gUsbTouchPanelDriverBinding = { UsbTouchPanelDriverBindingSupported, UsbTouchPanelDriverBindingStart, UsbTouchPanelDriverBindingStop, 0x12, NULL, NULL }; // // helper functions // STATIC BOOLEAN IsUsbTouchPanel ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_USB_CORE_PROTOCOL *UsbCore ); STATIC EFI_STATUS InitializeUsbTouchPanelDevice ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice ); STATIC VOID EFIAPI UsbTouchPanelWaitForInput ( IN EFI_EVENT Event, IN VOID *Context ); STATIC VOID EFIAPI DisconnectUsbTouchPanel ( IN EFI_EVENT Event, IN VOID *Context ); // // Touch Panel interrupt handler // STATIC EFI_STATUS EFIAPI OnTouchPanelInterruptComplete ( IN VOID *Data, IN UINTN DataLength, IN VOID *Context, IN UINT32 Result ); // // Touch Panel Protocol // STATIC EFI_STATUS EFIAPI GetTouchPanelState ( IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, OUT EFI_ABSOLUTE_POINTER_STATE *TouchPanelState ); STATIC EFI_STATUS EFIAPI UsbTouchPanelReset ( IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, IN BOOLEAN ExtendedVerification ); STATIC VOID EFIAPI UsbTouchPanelRecoveryHandler ( IN UINTN Event, IN VOID *Context ); STATIC VOID TouchPanelReportStatusCode ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice, IN EFI_STATUS_CODE_TYPE CodeType, IN EFI_STATUS_CODE_VALUE Value ); /** Entry point for EFI drivers. @param ImageHandle EFI_HANDLE @param SystemTable EFI_SYSTEM_TABLE @retval EFI_SUCCESS @retval others **/ EFI_STATUS EFIAPI UsbTouchPanelDriverBindingEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { // // Install binding protocols // return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gUsbTouchPanelDriverBinding, ImageHandle, &gUsbTouchPanelComponentName, &gUsbTouchPanelComponentName2 ); } /** Test to see if this driver supports ControllerHandle. Any ControllerHandle that has UsbHcProtocol installed will be supported. @param This Protocol instance pointer. @param Controller Handle of device to test @param RemainingDevicePath Not used @retval EFI_SUCCESS This driver supports this device. @retval EFI_UNSUPPORTED This driver does not support this device. **/ EFI_STATUS EFIAPI UsbTouchPanelDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS OpenStatus; EFI_USB_IO_PROTOCOL *UsbIo; EFI_STATUS Status; EFI_DEV_PATH_PTR Node; EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_USB_CORE_PROTOCOL *UsbCore; // // Check Device Path // if (RemainingDevicePath != NULL) { Node.DevPath = RemainingDevicePath; if (Node.DevPath->Type != MESSAGING_DEVICE_PATH || Node.DevPath->SubType != MSG_USB_CLASS_DP || DevicePathNodeLength(Node.DevPath) != sizeof(USB_CLASS_DEVICE_PATH) || Node.UsbClass->DeviceClass != CLASS_HID) { return EFI_UNSUPPORTED; } } OpenStatus = gBS->OpenProtocol ( Controller, &gEfiUsbIoProtocolGuid, (VOID **)&UsbIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (OpenStatus) && (OpenStatus != EFI_ALREADY_STARTED)) { return EFI_UNSUPPORTED; } if (OpenStatus == EFI_ALREADY_STARTED) { return EFI_ALREADY_STARTED; } // // Get the device path for CheckIgnoredDevice // Status = gBS->HandleProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Setup the data for UsbLegacy // Status = gBS->LocateProtocol ( &gEfiUsbCoreProtocolGuid, NULL, (VOID **)&UsbCore ); if (EFI_ERROR (Status)) { goto ErrorExit; } #if (KEEP_USBIO_FOR_IGNORED_DEVICE == 1) // // Filter out the USB devices which in the UsbIgnoreDevice list // Status = UsbCore->CheckIgnoredDevice(DevicePath, UsbIo); if (EFI_ERROR (Status)) { goto ErrorExit; } #endif // // Use the USB I/O protocol interface to see the Controller is // the Touch Panel controller that can be managed by this driver. // Status = EFI_SUCCESS; if (!IsUsbTouchPanel (UsbIo, UsbCore)) { Status = EFI_UNSUPPORTED; } ErrorExit: gBS->CloseProtocol ( Controller, &gEfiUsbIoProtocolGuid, This->DriverBindingHandle, Controller ); return Status; } /** Starting the Usb Bus Driver @param This Protocol instance pointer. @param Controller Handle of device to test @param RemainingDevicePath Not used @retval EFI_SUCCESS This driver supports this device. @retval EFI_UNSUPPORTED This driver does not support this device. @retval EFI_DEVICE_ERROR This driver cannot be started due to device error @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources @retval EFI_ALREADY_STARTED Thios driver has been started **/ EFI_STATUS EFIAPI UsbTouchPanelDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_CORE_PROTOCOL *UsbCore; EFI_USB_ENDPOINT_DESCRIPTOR EndpointDesc; USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; UINT8 EndpointNumber; UINT8 Index; UINT8 EndpointAddr; UINT8 PollingInterval; UINT8 PacketSize; BOOLEAN EndpointFound; EFI_EVENT LegacyBootEvent; EFI_EVENT ExitBootServicesEvent; POST_CODE (BDS_CONNECT_USB_DEVICE); UsbTouchPanelDevice = NULL; UsbCore = NULL; Status = EFI_SUCCESS; LegacyBootEvent = NULL; ExitBootServicesEvent = NULL; Status = gBS->OpenProtocol ( Controller, &gEfiUsbIoProtocolGuid, (VOID **)&UsbIo, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Locate EFI_USB_CORE_PROTOCOL for further use // Status = gBS->LocateProtocol ( &gEfiUsbCoreProtocolGuid, NULL, (VOID **)&UsbCore ); if (EFI_ERROR (Status)) { goto ErrorExit; } Status = UsbCore->AllocateBuffer ( sizeof (USB_TOUCH_PANEL_DEV), ALIGNMENT_32, (VOID **)&UsbTouchPanelDevice ); if (EFI_ERROR(Status)) { Status = EFI_OUT_OF_RESOURCES; goto ErrorExit; } UsbTouchPanelDevice->UsbIo = UsbIo; UsbTouchPanelDevice->UsbCore = UsbCore; UsbTouchPanelDevice->Signature = USB_TOUCH_PANEL_DEV_SIGNATURE; // // Get the Device Path Protocol on Controller's handle // Status = gBS->OpenProtocol ( Controller, &gEfiDevicePathProtocolGuid, (VOID **) &UsbTouchPanelDevice->DevicePath, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Get interface & endpoint descriptor // UsbIo->UsbGetInterfaceDescriptor ( UsbIo, &UsbTouchPanelDevice->InterfaceDescriptor ); EndpointNumber = UsbTouchPanelDevice->InterfaceDescriptor.NumEndpoints; for (Index = 0, EndpointFound = FALSE; Index < EndpointNumber; Index++) { UsbIo->UsbGetEndpointDescriptor ( UsbIo, Index, &EndpointDesc ); if ((EndpointDesc.Attributes & 0x03) == 0x03 && (EndpointDesc.EndpointAddress & 0x80)) { // // We only care interrupt endpoint here // UsbTouchPanelDevice->IntEndpointDescriptor = EndpointDesc; EndpointFound = TRUE; break; } } if (!EndpointFound) { // // No interrupt endpoint, then error // Status = EFI_UNSUPPORTED; goto ErrorExit; } Status = InitializeUsbTouchPanelDevice (UsbTouchPanelDevice); if (EFI_ERROR (Status)) { TouchPanelReportStatusCode ( UsbTouchPanelDevice, EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_PERIPHERAL_TOUCH_PANEL | EFI_P_EC_INTERFACE_ERROR) ); goto ErrorExit; } UsbTouchPanelDevice->AbsolutePointerProtocol.GetState = GetTouchPanelState; UsbTouchPanelDevice->AbsolutePointerProtocol.Reset = UsbTouchPanelReset; UsbTouchPanelDevice->AbsolutePointerProtocol.Mode = &UsbTouchPanelDevice->Mode; Status = gBS->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, UsbTouchPanelWaitForInput, UsbTouchPanelDevice, &((UsbTouchPanelDevice->AbsolutePointerProtocol).WaitForInput) ); if (EFI_ERROR (Status)) { goto ErrorExit; } Status = gBS->InstallProtocolInterface ( &Controller, &gEfiAbsolutePointerProtocolGuid, EFI_NATIVE_INTERFACE, &UsbTouchPanelDevice->AbsolutePointerProtocol ); if (EFI_ERROR (Status)) { Status = EFI_DEVICE_ERROR; goto ErrorExit; } // // After Enabling Async Interrupt Transfer on this Touch Panel Device // we will be able to get key data from it. Thus this is deemed as // the enable action of the Touch Panel // TouchPanelReportStatusCode ( UsbTouchPanelDevice, EFI_PROGRESS_CODE, (EFI_PERIPHERAL_TOUCH_PANEL | EFI_P_PC_ENABLE) ); // // submit async interrupt transfer // EndpointAddr = UsbTouchPanelDevice->IntEndpointDescriptor.EndpointAddress; PollingInterval = UsbTouchPanelDevice->IntEndpointDescriptor.Interval; PacketSize = (UINT8)UsbTouchPanelDevice->IntEndpointDescriptor.MaxPacketSize; if (PacketSize < UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup->DataSize) { PacketSize = (UINT8)UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup->DataSize; } Status = UsbIo->UsbAsyncInterruptTransfer ( UsbIo, EndpointAddr, TRUE, PollingInterval, PacketSize, OnTouchPanelInterruptComplete, UsbTouchPanelDevice ); if (!EFI_ERROR (Status)) { UsbTouchPanelDevice->ControllerNameTable = NULL; AddUnicodeString2 ( LANGUAGE_CODE_ENGLISH_ISO639, gUsbTouchPanelComponentName.SupportedLanguages, &UsbTouchPanelDevice->ControllerNameTable, CONTROLLER_DRIVER_NAME, TRUE ); AddUnicodeString2 ( LANGUAGE_CODE_ENGLISH_RFC4646, gUsbTouchPanelComponentName2.SupportedLanguages, &UsbTouchPanelDevice->ControllerNameTable, CONTROLLER_DRIVER_NAME, FALSE ); Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, DisconnectUsbTouchPanel, (VOID *) Controller, &gEfiEventLegacyBootGuid, &LegacyBootEvent ); if (EFI_ERROR (Status)) { goto ErrorExit; } Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, DisconnectUsbTouchPanel, (VOID *) Controller, &gEfiEventExitBootServicesGuid, &ExitBootServicesEvent ); if (EFI_ERROR (Status)) { goto ErrorExit; } return EFI_SUCCESS; } // // If submit error, uninstall that interface // Status = EFI_DEVICE_ERROR; gBS->UninstallProtocolInterface ( Controller, &gEfiAbsolutePointerProtocolGuid, &UsbTouchPanelDevice->AbsolutePointerProtocol ); ErrorExit: if (EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gEfiUsbIoProtocolGuid, This->DriverBindingHandle, Controller ); if (UsbTouchPanelDevice != NULL) { if ((UsbTouchPanelDevice->AbsolutePointerProtocol).WaitForInput != NULL) { gBS->CloseEvent ((UsbTouchPanelDevice->AbsolutePointerProtocol).WaitForInput); } UsbCore->FreeBuffer ( sizeof (USB_TOUCH_PANEL_DEV), UsbTouchPanelDevice ); } if (LegacyBootEvent != NULL) { gBS->CloseEvent (LegacyBootEvent); } if (ExitBootServicesEvent != NULL) { gBS->CloseEvent (ExitBootServicesEvent); } } return Status; } /** Stop this driver on ControllerHandle. Support stoping any child handles created by this driver. @param This Protocol instance pointer. @param Controller Handle of device to stop driver on @param NumberOfChildren Number of Children in the ChildHandleBuffer @param ChildHandleBuffer List of handles for the children we need to stop. @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR @retval others **/ EFI_STATUS EFIAPI UsbTouchPanelDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { EFI_STATUS Status; USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; EFI_ABSOLUTE_POINTER_PROTOCOL *AbsolutePointerProtocol; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_CORE_PROTOCOL *UsbCore; // // Get our context back. // Status = gBS->OpenProtocol ( Controller, &gEfiAbsolutePointerProtocolGuid, (VOID **)&AbsolutePointerProtocol, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return EFI_UNSUPPORTED; } UsbTouchPanelDevice = USB_TOUCH_PANEL_DEV_FROM_TOUCH_PANEL_PROTOCOL (AbsolutePointerProtocol); UsbIo = UsbTouchPanelDevice->UsbIo; UsbCore = UsbTouchPanelDevice->UsbCore; // // Set the UsbIo to NULL to preventing async interrupt transfer restarted // by recovery handler after deleting the async interrupt transfer // UsbTouchPanelDevice->UsbIo = NULL; gBS->CloseProtocol ( Controller, &gEfiAbsolutePointerProtocolGuid, This->DriverBindingHandle, Controller ); // // Remove the recovery handler before deleting the async interrupt transfer to preventing // async interrupt transfer restarted by recovery handler // UsbCore->RemovePeriodicTimer(UsbTouchPanelDevice->RecoveryPollingHandle); // // Uninstall the Asyn Interrupt Transfer from this device // will disable the Touch Panel data input from this device // TouchPanelReportStatusCode ( UsbTouchPanelDevice, EFI_PROGRESS_CODE, (EFI_PERIPHERAL_TOUCH_PANEL | EFI_P_PC_DISABLE) ); // // Delete Touch Panel Async Interrupt Transfer // UsbIo->UsbAsyncInterruptTransfer ( UsbIo, UsbTouchPanelDevice->IntEndpointDescriptor.EndpointAddress, FALSE, UsbTouchPanelDevice->IntEndpointDescriptor.Interval, 0, NULL, NULL ); gBS->CloseEvent (UsbTouchPanelDevice->AbsolutePointerProtocol.WaitForInput); Status = gBS->UninstallProtocolInterface ( Controller, &gEfiAbsolutePointerProtocolGuid, &UsbTouchPanelDevice->AbsolutePointerProtocol ); if (EFI_ERROR (Status)) { return Status; } gBS->CloseProtocol ( Controller, &gEfiUsbIoProtocolGuid, This->DriverBindingHandle, Controller ); if (UsbTouchPanelDevice->ControllerNameTable) { FreeUnicodeStringTable (UsbTouchPanelDevice->ControllerNameTable); } UsbCore->FreeBuffer ( sizeof (USB_TOUCH_PANEL_DEV), UsbTouchPanelDevice ); return EFI_SUCCESS; } /** Event notification function for disconnect USB Touch Panel @param Event Legacy Boot event or Exit Boot Service event @param Context Controller handler of USB Touch Panel **/ STATIC VOID EFIAPI DisconnectUsbTouchPanel ( IN EFI_EVENT Event, IN VOID *Context ) { gBS->DisconnectController ((EFI_HANDLE) Context, NULL, NULL); } /** Tell if a Usb Controller is a Touch Panel @param UsbIo Protocol instance pointer. @retval TRUE It is a Touch Panel @retval FALSE It is not a Touch Panel **/ BOOLEAN IsUsbTouchPanel ( IN EFI_USB_IO_PROTOCOL *UsbIo, IN EFI_USB_CORE_PROTOCOL *UsbCore ) { EFI_STATUS Status; EFI_USB_INTERFACE_DESCRIPTOR InterfaceDescriptor; EFI_USB_HID_DESCRIPTOR TouchPanelHidDesc; UINT8 *ReportDesc; UINT16 DescriptorLength; BOOLEAN TouchDeviceFound; REPORT_FIELD_INFO ReportFieldInfo; UINTN Index; // // Get the Default interface descriptor, now we only // suppose it is interface 1 // Status = UsbIo->UsbGetInterfaceDescriptor ( UsbIo, &InterfaceDescriptor ); if (EFI_ERROR (Status)) { return FALSE; } if ((InterfaceDescriptor.InterfaceClass == CLASS_HID) && ((InterfaceDescriptor.InterfaceProtocol == PROTOCOL_MOUSE) || (InterfaceDescriptor.InterfaceProtocol == PROTOCOL_NONE))) { Status = UsbCore->UsbGetHidDescriptor ( UsbIo, InterfaceDescriptor.InterfaceNumber, &TouchPanelHidDesc ); if (EFI_ERROR (Status) || (InterfaceDescriptor.InterfaceProtocol == PROTOCOL_NONE && TouchPanelHidDesc.HidClassDesc[0].DescriptorType != 0x22)) { return FALSE; } DescriptorLength = TouchPanelHidDesc.HidClassDesc[0].DescriptorLength; Status = UsbCore->AllocateBuffer ( DescriptorLength, ALIGNMENT_32, (VOID **)&ReportDesc ); if (EFI_ERROR(Status)) { return FALSE; } TouchDeviceFound = FALSE; for (Index = 0; Index < 3; Index ++) { Status = UsbCore->UsbGetReportDescriptor ( UsbIo, InterfaceDescriptor.InterfaceNumber, DescriptorLength, ReportDesc ); if (!EFI_ERROR (Status)) { break; } else if (Status == EFI_DEVICE_ERROR) { // // Stall 10ms to waiting for potential signal unstable // UsbCore->Stall (10 * 1000); } } if (!EFI_ERROR (Status) && !MatchHidDeviceType (ReportDesc, DescriptorLength, TOUCH_PAD_CLASS_CODE) && (MatchHidDeviceType (ReportDesc, DescriptorLength, MOUSE_CLASS_CODE) || MatchHidDeviceType (ReportDesc, DescriptorLength, POINTER_CLASS_CODE) || MatchHidDeviceType (ReportDesc, DescriptorLength, TOUCH_SCREEN_CLASS_CODE) || MatchHidDeviceType (ReportDesc, DescriptorLength, PEN_CLASS_CODE))) { // // Check the X/Y value type, we only support the absolute value due to it is requirement for AbsolutePointer protocol // ZeroMem (&ReportFieldInfo, sizeof (REPORT_FIELD_INFO)); Status = ParseReportDescriptor ( ReportDesc, DescriptorLength, &ReportFieldInfo ); if (EFI_ERROR (Status)) { return FALSE; } // // Searching for qualified mouse report // for (Index = 0; Index < ReportFieldInfo.Total; Index ++) { if (ReportFieldInfo.ReportGroup[Index].DataValid) { if ((ReportFieldInfo.ReportGroup[Index].DataAttr == ATTR_MOUSE_INPUT && ReportFieldInfo.ReportGroup[Index].Data.Mouse.FieldX.ValueType == ABSOLUTE_VALUE && ReportFieldInfo.ReportGroup[Index].Data.Mouse.FieldY.ValueType == ABSOLUTE_VALUE) || (ReportFieldInfo.ReportGroup[Index].DataAttr == ATTR_TOUCH_PANEL_INPUT && ReportFieldInfo.ReportGroup[Index].Data.TouchPanel.FieldX.ValueType == ABSOLUTE_VALUE && ReportFieldInfo.ReportGroup[Index].Data.TouchPanel.FieldY.ValueType == ABSOLUTE_VALUE)) { TouchDeviceFound = TRUE; break; } } } } UsbCore->FreeBuffer ( DescriptorLength, ReportDesc ); if (TouchDeviceFound) return TRUE; } return FALSE; } EFI_STATUS SetDeviceMode ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice ) { EFI_STATUS Status; REPORT_GROUP *ReportGroup; EFI_USB_CORE_PROTOCOL *UsbCore; UINT8 *ReportData; if ((UsbTouchPanelDevice == NULL) || (UsbTouchPanelDevice->UsbIo == NULL) || (UsbTouchPanelDevice->UsbCore == NULL) || (UsbTouchPanelDevice->ReportFieldInfo.DeviceConfigurationReportGroup == NULL)) { return EFI_INVALID_PARAMETER; } ReportGroup = UsbTouchPanelDevice->ReportFieldInfo.DeviceConfigurationReportGroup; // // 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; } UsbCore = UsbTouchPanelDevice->UsbCore; Status = UsbCore->AllocateBuffer ( ReportGroup->DataSize, ALIGNMENT_32, (VOID **)&ReportData ); if (EFI_ERROR (Status)) { return Status; } // // Set Device Mode to single input // ReportData[0] = ReportGroup->Id; Status = SetReportFieldValue ( ReportData, 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, ReportGroup->DataSize, &ReportGroup->Data.TouchPanelMode.FieldDeviceId, 0 ); if (EFI_ERROR (Status)) { goto EXIT; } } Status = UsbCore->UsbSetReportRequest ( UsbTouchPanelDevice->UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, ReportGroup->Id, HID_FEATURE_REPORT, ReportGroup->DataSize, ReportData ); EXIT: UsbCore->FreeBuffer ( ReportGroup->DataSize, ReportData ); return Status; } /** The workaround for some incompatible device @param UsbTouchPanelDevice Device instance to be initialized @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Init error. @retval EFI_OUT_OF_RESOURCES Can't allocate memory **/ STATIC EFI_STATUS GetFeatureData ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice ) { EFI_STATUS Status; EFI_USB_CORE_PROTOCOL *UsbCore; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_DEVICE_DESCRIPTOR DeviceDescriptor; UINT8 *FeatureData; UINT16 *LangIDTable; UINT16 TableSize; UINT16 *String; if (UsbTouchPanelDevice->ReportFieldInfo.FirstFeatureID != 0 && UsbTouchPanelDevice->ReportFieldInfo.MaximumFeatureLength != 0) { UsbIo = UsbTouchPanelDevice->UsbIo; UsbCore = UsbTouchPanelDevice->UsbCore; Status = UsbIo->UsbGetDeviceDescriptor ( UsbIo, &DeviceDescriptor ); if (EFI_ERROR (Status)) { return Status; } if (DeviceDescriptor.StrProduct != 0) { Status = UsbCore->AllocateBuffer ( UsbTouchPanelDevice->ReportFieldInfo.MaximumFeatureLength, ALIGNMENT_32, (VOID **)&FeatureData ); if (EFI_ERROR (Status)) { return Status; } UsbCore->UsbGetReportRequest ( UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, (UINT8)UsbTouchPanelDevice->ReportFieldInfo.FirstFeatureID, HID_FEATURE_REPORT, (UINT16)UsbTouchPanelDevice->ReportFieldInfo.MaximumFeatureLength, FeatureData ); Status = UsbIo->UsbGetSupportedLanguages ( UsbIo, &LangIDTable, &TableSize ); if (EFI_ERROR (Status)) { goto ErrorExit; } UsbCore->UsbGetReportRequest ( UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, (UINT8)UsbTouchPanelDevice->ReportFieldInfo.FirstFeatureID, HID_FEATURE_REPORT, (UINT16)UsbTouchPanelDevice->ReportFieldInfo.MaximumFeatureLength, FeatureData ); UsbIo->UsbGetStringDescriptor ( UsbIo, LangIDTable[0], DeviceDescriptor.StrProduct, &String ); ErrorExit: UsbCore->FreeBuffer ( UsbTouchPanelDevice->ReportFieldInfo.MaximumFeatureLength, FeatureData ); } } return EFI_SUCCESS; } /** Initialize the Usb Touch Panel Device. @param UsbTouchPanelDevice Device instance to be initialized @retval EFI_SUCCESS Success @retval EFI_DEVICE_ERROR Init error. @retval EFI_OUT_OF_RESOURCES Can't allocate memory **/ STATIC EFI_STATUS InitializeUsbTouchPanelDevice ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice ) { EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_CORE_PROTOCOL *UsbCore; EFI_STATUS Status; EFI_USB_HID_DESCRIPTOR TouchPanelHidDesc; UINT8 *ReportDesc; REPORT_GROUP *ReportGroup; UsbIo = UsbTouchPanelDevice->UsbIo; UsbCore = UsbTouchPanelDevice->UsbCore; // // Get HID descriptor // Status = UsbCore->UsbGetHidDescriptor ( UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, &TouchPanelHidDesc ); if (EFI_ERROR (Status)) { return Status; } // // Get Report descriptor // if (TouchPanelHidDesc.HidClassDesc[0].DescriptorType != 0x22) { return EFI_UNSUPPORTED; } Status = UsbCore->AllocateBuffer ( TouchPanelHidDesc.HidClassDesc[0].DescriptorLength, ALIGNMENT_32, (VOID **)&ReportDesc ); if (EFI_ERROR(Status)) { return EFI_OUT_OF_RESOURCES; } Status = UsbCore->UsbGetReportDescriptor ( UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, TouchPanelHidDesc.HidClassDesc[0].DescriptorLength, ReportDesc ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Initialize with "not found" values before parsing. // UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup = NULL; UsbTouchPanelDevice->ReportFieldInfo.DeviceConfigurationReportGroup = NULL; // // Parse report descriptor // Status = ParseReportDescriptor ( ReportDesc, TouchPanelHidDesc.HidClassDesc[0].DescriptorLength, &UsbTouchPanelDevice->ReportFieldInfo ); if (EFI_ERROR (Status)) { goto ErrorExit; } // // Since we do not know which report data will be sent by the USB device (and we will interpret // the different report data by the report group with the correct Report ID anyway), the default // Mode parameters are set based on the report group with the biggest input data size for now. // ReportGroup = UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup; if ((ReportGroup == NULL) || (!(ReportGroup->DataValid)) || (ReportGroup->DataSize == 0)) { Status = EFI_UNSUPPORTED; goto ErrorExit; } UsbTouchPanelDevice->ReportId = ReportGroup->Id; UsbTouchPanelDevice->Mode.AbsoluteMinX = ReportGroup->Data.TouchPanel.FieldX.Min; UsbTouchPanelDevice->Mode.AbsoluteMinY = ReportGroup->Data.TouchPanel.FieldY.Min; UsbTouchPanelDevice->Mode.AbsoluteMaxX = ReportGroup->Data.TouchPanel.FieldX.Max; UsbTouchPanelDevice->Mode.AbsoluteMaxY = ReportGroup->Data.TouchPanel.FieldY.Max; UsbTouchPanelDevice->Mode.Attributes = 0; if (ReportGroup->Data.TouchPanel.FieldAltActive.DataValid) { UsbTouchPanelDevice->Mode.Attributes |= EFI_ABSP_SupportsAltActive; } // // Set indefinite Idle rate for USB Touch Panel // UsbCore->UsbSetIdleRequest ( UsbIo, UsbTouchPanelDevice->InterfaceDescriptor.InterfaceNumber, 0, 0 ); // // 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 (UsbTouchPanelDevice->ReportFieldInfo.DeviceConfigurationReportGroup != NULL) { SetDeviceMode (UsbTouchPanelDevice); } // // Some incompatible device will postpone the first event around 10 seconds. To launch GetFeature // command twice with first feature report ID and maximum feature length, and launch GetString to // get vendor string to workaround this issue // GetFeatureData (UsbTouchPanelDevice); if (UsbTouchPanelDevice->RecoveryPollingHandle) { UsbCore->RemovePeriodicTimer(UsbTouchPanelDevice->RecoveryPollingHandle); UsbTouchPanelDevice->RecoveryPollingHandle = NULL; } ErrorExit: UsbCore->FreeBuffer ( TouchPanelHidDesc.HidClassDesc[0].DescriptorLength, ReportDesc ); return Status; } /** It is called whenever there is data received from async interrupt transfer. @param Data Data received. @param DataLength Length of Data @param Context Passed in context @param Result Async Interrupt Transfer result @retval EFI_SUCCESS @retval EFI_DEVICE_ERROR **/ STATIC EFI_STATUS EFIAPI OnTouchPanelInterruptComplete ( IN VOID *Data, IN UINTN DataLength, IN VOID *Context, IN UINT32 Result ) { USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; EFI_USB_IO_PROTOCOL *UsbIo; EFI_USB_CORE_PROTOCOL *UsbCore; UINT8 EndpointAddr; UINT32 UsbResult; UINTN Mode; REPORT_GROUP *ReportGroup; UINTN Index; EFI_ABSOLUTE_POINTER_STATE AbsPtrState; UINTN BufferIn; UsbTouchPanelDevice = (USB_TOUCH_PANEL_DEV *) Context; UsbIo = UsbTouchPanelDevice->UsbIo; UsbCore = UsbTouchPanelDevice->UsbCore; if (UsbIo == NULL) return EFI_DEVICE_ERROR; // // Get current mode // UsbCore->GetMode(&Mode); // // Analyzes the Result and performs corresponding action. // if (Result != EFI_USB_NOERROR) { // // Some errors happen during the process // TouchPanelReportStatusCode ( UsbTouchPanelDevice, EFI_ERROR_CODE | EFI_ERROR_MINOR, (EFI_PERIPHERAL_TOUCH_PANEL | EFI_P_EC_INPUT_ERROR) ); if ((Result & EFI_USB_ERR_STALL) == EFI_USB_ERR_STALL) { EndpointAddr = UsbTouchPanelDevice->IntEndpointDescriptor.EndpointAddress; UsbCore->UsbClearEndpointHalt ( UsbIo, EndpointAddr, &UsbResult ); } UsbIo->UsbAsyncInterruptTransfer ( UsbIo, UsbTouchPanelDevice->IntEndpointDescriptor.EndpointAddress, FALSE, 0, 0, NULL, NULL ); // // Check is the device be detached // if (UsbCore->CheckDeviceDetached(UsbIo) == EFI_SUCCESS) { return EFI_DEVICE_ERROR; } UsbCore->InsertPeriodicTimer( USB_CORE_ONCE_TIMER, UsbTouchPanelRecoveryHandler, UsbTouchPanelDevice, EFI_USB_INTERRUPT_DELAY / 10000, &UsbTouchPanelDevice->RecoveryPollingHandle ); return EFI_DEVICE_ERROR; } if (DataLength == 0 || Data == NULL) { return EFI_SUCCESS; } if (Mode != USB_CORE_RUNTIME_MODE) { // // USB Device Class Definition for Human Interface Devices (HID) specification version 1.11 // page 36 states that a Report ID value of 0 is reserved. However, 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 (see page 18 of above // specification). // if (UsbTouchPanelDevice->ReportId == 0) { ReportGroup = &(UsbTouchPanelDevice->ReportFieldInfo.ReportGroup[0]); } else { ReportGroup = NULL; for (Index = 0; Index < UsbTouchPanelDevice->ReportFieldInfo.Total; Index++) { if ((UsbTouchPanelDevice->ReportFieldInfo.ReportGroup[Index].Id == (*(UINT8*)Data)) && (UsbTouchPanelDevice->ReportFieldInfo.ReportGroup[Index].DataValid)) { ReportGroup = &UsbTouchPanelDevice->ReportFieldInfo.ReportGroup[Index]; break; } } if (!ReportGroup) return EFI_NOT_FOUND; if (UsbTouchPanelDevice->ReportId != ReportGroup->Id) { // // Reset the Mode parameters due to report ID changed // UsbTouchPanelDevice->ReportId = ReportGroup->Id; // // Set limit by report descriptor // UsbTouchPanelDevice->Mode.AbsoluteMinX = ReportGroup->Data.TouchPanel.FieldX.Min; UsbTouchPanelDevice->Mode.AbsoluteMinY = ReportGroup->Data.TouchPanel.FieldY.Min; UsbTouchPanelDevice->Mode.AbsoluteMaxX = ReportGroup->Data.TouchPanel.FieldX.Max; UsbTouchPanelDevice->Mode.AbsoluteMaxY = ReportGroup->Data.TouchPanel.FieldY.Max; UsbTouchPanelDevice->Mode.Attributes = 0; if (ReportGroup->Data.TouchPanel.FieldAltActive.DataValid) { UsbTouchPanelDevice->Mode.Attributes |= EFI_ABSP_SupportsAltActive; } } } if (DataLength < ReportGroup->DataSize) { // // Partial packet data is not allowed // return EFI_NOT_FOUND; } if (ReportGroup->Data.TouchPanel.FieldContactCount.DataValid && GetReportFieldValue (Data, DataLength, &ReportGroup->Data.TouchPanel.FieldContactCount) == 0) { // // Dummy input data, keep the previous position but clear the button states // AbsPtrState.CurrentX = UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].CurrentX; AbsPtrState.CurrentY = UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].CurrentY; AbsPtrState.CurrentZ = 0; AbsPtrState.ActiveButtons = 0; } else { // // Got input data, renew the position and button states // AbsPtrState.CurrentX = (UINT32) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.TouchPanel.FieldX); AbsPtrState.CurrentY = (UINT32) GetReportFieldValue (Data, DataLength, &ReportGroup->Data.TouchPanel.FieldY); AbsPtrState.CurrentZ = 0; AbsPtrState.ActiveButtons = 0; if (GetReportFieldValue (Data, DataLength, &ReportGroup->Data.TouchPanel.FieldTouchActive)) { AbsPtrState.ActiveButtons |= EFI_ABSP_TouchActive; } if (GetReportFieldValue (Data, DataLength, &ReportGroup->Data.TouchPanel.FieldAltActive)) { AbsPtrState.ActiveButtons |= EFI_ABS_AltActive; } } UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].CurrentX = AbsPtrState.CurrentX; UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].CurrentY = AbsPtrState.CurrentY; UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].CurrentZ = AbsPtrState.CurrentZ; if ((UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].ActiveButtons != 0) || (AbsPtrState.ActiveButtons != 0)) { UsbTouchPanelDevice->StateChanged = TRUE; if (UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].ActiveButtons != AbsPtrState.ActiveButtons) { UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn].ActiveButtons = AbsPtrState.ActiveButtons; // // Advance the internal buffer only when button state changed to reduce the dragging phenomenon // BufferIn = UsbTouchPanelDevice->BufferIn + 1; if (BufferIn == STATE_BUFFER_SIZE) BufferIn = 0; if (BufferIn != UsbTouchPanelDevice->BufferOut) { // // Copy last state to new position for use of check procedure of last button state // CopyMem ( &UsbTouchPanelDevice->State[BufferIn], &UsbTouchPanelDevice->State[UsbTouchPanelDevice->BufferIn], sizeof (EFI_ABSOLUTE_POINTER_STATE) ); UsbTouchPanelDevice->BufferIn = BufferIn; } } } } return EFI_SUCCESS; } /** 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 GetTouchPanelState ( IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, OUT EFI_ABSOLUTE_POINTER_STATE *TouchPanelState ) { USB_TOUCH_PANEL_DEV *TouchPanelDev; if (TouchPanelState == NULL) { return EFI_DEVICE_ERROR; } TouchPanelDev = USB_TOUCH_PANEL_DEV_FROM_TOUCH_PANEL_PROTOCOL (This); if (!TouchPanelDev->StateChanged) { return EFI_NOT_READY; } CopyMem ( TouchPanelState, &TouchPanelDev->State[TouchPanelDev->BufferOut], sizeof (EFI_ABSOLUTE_POINTER_STATE) ); if (TouchPanelDev->BufferOut != TouchPanelDev->BufferIn) { TouchPanelDev->BufferOut ++; if (TouchPanelDev->BufferOut == STATE_BUFFER_SIZE) TouchPanelDev->BufferOut = 0; } if (TouchPanelDev->BufferOut == TouchPanelDev->BufferIn) { TouchPanelDev->StateChanged = FALSE; } 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 UsbTouchPanelReset ( IN EFI_ABSOLUTE_POINTER_PROTOCOL *This, IN BOOLEAN ExtendedVerification ) { USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; EFI_USB_IO_PROTOCOL *UsbIo; UsbTouchPanelDevice = USB_TOUCH_PANEL_DEV_FROM_TOUCH_PANEL_PROTOCOL (This); UsbIo = UsbTouchPanelDevice->UsbIo; TouchPanelReportStatusCode ( UsbTouchPanelDevice, EFI_PROGRESS_CODE, (EFI_PERIPHERAL_TOUCH_PANEL | EFI_P_PC_RESET) ); ZeroMem ( UsbTouchPanelDevice->State, sizeof (EFI_ABSOLUTE_POINTER_STATE) * STATE_BUFFER_SIZE ); UsbTouchPanelDevice->StateChanged = FALSE; UsbTouchPanelDevice->BufferIn = 0; UsbTouchPanelDevice->BufferOut = 0; return EFI_SUCCESS; } /** 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 UsbTouchPanelWaitForInput ( IN EFI_EVENT Event, IN VOID *Context ) { USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; UsbTouchPanelDevice = (USB_TOUCH_PANEL_DEV *) Context; // // Someone is waiting on the TouchPanel event, if there's // input from Touch Panel, signal the event // if (UsbTouchPanelDevice->StateChanged) { gBS->SignalEvent (Event); } } /** Timer handler for Delayed Recovery timer. @param Event The Delayed Recovery event. @param Context Points to the USB_KB_DEV instance. **/ STATIC VOID EFIAPI UsbTouchPanelRecoveryHandler ( IN UINTN Event, IN VOID *Context ) { USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice; EFI_USB_IO_PROTOCOL *UsbIo; UINT8 PacketSize; UsbTouchPanelDevice = (USB_TOUCH_PANEL_DEV *) Context; UsbTouchPanelDevice->RecoveryPollingHandle = NULL; UsbIo = UsbTouchPanelDevice->UsbIo; if (UsbIo == NULL) return; PacketSize = (UINT8)UsbTouchPanelDevice->IntEndpointDescriptor.MaxPacketSize; if (PacketSize < UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup->DataSize) { PacketSize = (UINT8)UsbTouchPanelDevice->ReportFieldInfo.BiggestInputReportGroup->DataSize; } UsbIo->UsbAsyncInterruptTransfer ( UsbIo, UsbTouchPanelDevice->IntEndpointDescriptor.EndpointAddress, TRUE, UsbTouchPanelDevice->IntEndpointDescriptor.Interval, PacketSize, OnTouchPanelInterruptComplete, UsbTouchPanelDevice ); } /** Report Status Code in Usb Bot Driver @param DevicePath Use this to get Device Path @param CodeType Status Code Type @param CodeValue Status Code Value **/ STATIC VOID TouchPanelReportStatusCode ( IN USB_TOUCH_PANEL_DEV *UsbTouchPanelDevice, IN EFI_STATUS_CODE_TYPE CodeType, IN EFI_STATUS_CODE_VALUE Value ) { UINTN Mode; BOOLEAN InSmm; UsbTouchPanelDevice->UsbCore->GetMode(&Mode); if (Mode != USB_CORE_RUNTIME_MODE) { // // Only report status code during POST and non-SMM mode // UsbTouchPanelDevice->UsbCore->IsInSmm(&InSmm); if (!InSmm) { ReportStatusCodeWithDevicePath ( CodeType, Value, UsbTouchPanelDevice->DevicePath ); } } }