├── .gitattributes ├── crosecbus ├── crosecbus.h ├── resource.h ├── gpiowrapper.h ├── comm-host.h ├── crosecbus.rc ├── userspaceQueue.h ├── trace.h ├── crosecbus.inf ├── driver.h ├── comm-mec_lpc.c ├── gpio.c ├── crosecbus.vcxproj ├── userspaceQueue.c ├── comm-lpc.c ├── crosecbus.c └── ec_commands.h ├── LICENSE.txt ├── README.md ├── crosecbus.sln ├── .gitignore └── crosecbus Package └── crosecbus Package.vcxproj /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /crosecbus/crosecbus.h: -------------------------------------------------------------------------------- 1 | #ifndef __CROS_EC_REGS_H__ 2 | #define __CROS_EC_REGS_H__ 3 | 4 | 5 | #endif /* __CROS_EC_REGS_H__ */ -------------------------------------------------------------------------------- /crosecbus/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by crosecbus.rc 4 | 5 | // Next default values for new objects 6 | // 7 | #ifdef APSTUDIO_INVOKED 8 | #ifndef APSTUDIO_READONLY_SYMBOLS 9 | #define _APS_NEXT_RESOURCE_VALUE 101 10 | #define _APS_NEXT_COMMAND_VALUE 40001 11 | #define _APS_NEXT_CONTROL_VALUE 1001 12 | #define _APS_NEXT_SYMED_VALUE 101 13 | #endif 14 | #endif 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 CoolStar 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /crosecbus/gpiowrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define DEFAULT_SPB_BUFFER_SIZE 64 7 | #define RESHUB_USE_HELPER_ROUTINES 8 | 9 | // 10 | // SPB (I2C) context 11 | // 12 | 13 | typedef struct _GPIO_CONTEXT 14 | { 15 | WDFIOTARGET GpioIoTarget; 16 | LARGE_INTEGER GpioResHubId; 17 | WDFWAITLOCK GpioLock; 18 | } GPIO_CONTEXT; 19 | 20 | VOID 21 | GpioTargetDeinitialize( 22 | IN WDFDEVICE FxDevice, 23 | IN GPIO_CONTEXT *GpioContext 24 | ); 25 | 26 | NTSTATUS 27 | GpioTargetInitialize( 28 | IN WDFDEVICE FxDevice, 29 | IN GPIO_CONTEXT *GpioContext 30 | ); 31 | 32 | NTSTATUS 33 | GpioReadDataSynchronously( 34 | _In_ GPIO_CONTEXT *GpioContext, 35 | _In_reads_bytes_(Length) PVOID Data, 36 | _In_ ULONG Length 37 | ); -------------------------------------------------------------------------------- /crosecbus/comm-host.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMM_HOST_H__ 2 | #define __COMM_HOST_H__ 3 | 4 | #include "ec_commands.h" 5 | 6 | /* ec_command return value for non-success result from EC */ 7 | #define EECRESULT 1000 8 | 9 | typedef struct lpc_driver_ops { 10 | int(*read)(unsigned int offset, unsigned int length, UINT8* dest); 11 | int(*write)(unsigned int offset, unsigned int length, const UINT8* dest); 12 | } lpc_driver_ops; 13 | 14 | extern UINT32 ec_max_outsize, ec_max_insize; 15 | 16 | extern lpc_driver_ops ec_lpc_ops; 17 | 18 | extern int (*ec_command_proto)(UINT16 command, UINT8 version, 19 | const void* outdata, int outsize, /* to EC */ 20 | void* indata, int insize); /* from EC */ 21 | 22 | /** 23 | * Return the content of the EC information area mapped as "memory". 24 | * The offsets are defined by the EC_MEMMAP_ constants. Returns the number 25 | * of bytes read, or negative on error. Specifying bytes=0 will read a 26 | * string (always including the trailing '\0'). 27 | */ 28 | extern int (*ec_readmem)(int offset, int bytes, void* dest); 29 | 30 | #endif -------------------------------------------------------------------------------- /crosecbus/crosecbus.rc: -------------------------------------------------------------------------------- 1 | /*++ 2 | 3 | Copyright (c) Microsoft Corporation All Rights Reserved 4 | 5 | Module Name: 6 | 7 | crosecbus.rc 8 | 9 | Abstract: 10 | 11 | --*/ 12 | 13 | #include 14 | 15 | #define VER_FILETYPE VFT_DRV 16 | #define VER_FILESUBTYPE VFT2_DRV_SYSTEM 17 | #define VER_FILEDESCRIPTION_STR "Chromebook EC Bus" 18 | #define VER_INTERNALNAME_STR "crosecbus.sys" 19 | #define VER_ORIGINALFILENAME_STR "crosecbus.sys" 20 | 21 | #define VER_LEGALCOPYRIGHT_YEARS "2023" 22 | #define VER_LEGALCOPYRIGHT_STR "Copyright (C) " VER_LEGALCOPYRIGHT_YEARS " CoolStar." 23 | 24 | #define VER_FILEVERSION 2,0,0,0 25 | #define VER_PRODUCTVERSION_STR "2.0.0.0" 26 | #define VER_PRODUCTVERSION 2,0,0,0 27 | #define LVER_PRODUCTVERSION_STR L"2.0.0.0" 28 | 29 | #define VER_FILEFLAGSMASK (VS_FF_DEBUG | VS_FF_PRERELEASE) 30 | #ifdef DEBUG 31 | #define VER_FILEFLAGS (VS_FF_DEBUG) 32 | #else 33 | #define VER_FILEFLAGS (0) 34 | #endif 35 | 36 | #define VER_FILEOS VOS_NT_WINDOWS32 37 | 38 | #define VER_COMPANYNAME_STR "CoolStar" 39 | #define VER_PRODUCTNAME_STR "Chromebook EC Bus" 40 | 41 | #include "common.ver" -------------------------------------------------------------------------------- /crosecbus/userspaceQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | NTSTATUS CrosECQueueInitialize(_In_ WDFDEVICE Device); 4 | 5 | DEFINE_GUID(GUID_DEVINTERFACE_CrosEC, 0xd66bb4f8, 0x0a7a, 0x4f89, 0x90, 0x33, 0x79, 0x8a, 0xff, 0xa4, 0xf5, 0x38); 6 | // {d66bb4f8-0a7a-4f89-9033-798affa4f538} 7 | 8 | #define FILE_DEVICE_CROS_EMBEDDED_CONTROLLER 0x80EC 9 | 10 | #define IOCTL_CROSEC_XCMD \ 11 | CTL_CODE(FILE_DEVICE_CROS_EMBEDDED_CONTROLLER, 0x801, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA) 12 | #define IOCTL_CROSEC_RDMEM CTL_CODE(FILE_DEVICE_CROS_EMBEDDED_CONTROLLER, 0x802, METHOD_BUFFERED, FILE_READ_DATA) 13 | 14 | #define CROSEC_CMD_MAX_REQUEST 0x100 15 | #define CROSEC_CMD_MAX_RESPONSE 0x100 16 | #define CROSEC_MEMMAP_SIZE 0xFF 17 | 18 | #define CROSEC_STATUS_IN_PROGRESS ((NTSTATUS)0xE0EC0001) // EC Command in progress 19 | #define CROSEC_STATUS_UNAVAILABLE ((NTSTATUS)0xE0EC0002) // EC not available 20 | 21 | // Events 22 | EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL CrosECEvtIoDeviceControl; 23 | EVT_WDF_IO_QUEUE_IO_STOP CrosECEvtIoStop; 24 | 25 | typedef struct _CROSEC_READMEM { 26 | ULONG offset; 27 | ULONG bytes; 28 | UCHAR buffer[CROSEC_MEMMAP_SIZE]; 29 | } *PCROSEC_READMEM, CROSEC_READMEM; -------------------------------------------------------------------------------- /crosecbus/trace.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifndef _TRACE_H_ 4 | #define _TRACE_H_ 5 | 6 | extern "C" 7 | { 8 | // 9 | // Tracing Definitions: 10 | // 11 | // Control GUID: 12 | // {73e3b785-f5fb-423e-94a9-56627fea9053} 13 | // 14 | 15 | #define WPP_CONTROL_GUIDS \ 16 | WPP_DEFINE_CONTROL_GUID( \ 17 | SpbTestToolTraceGuid, \ 18 | (73e3b785,f5fb,423e,94a9,56627fea9053), \ 19 | WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \ 20 | WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \ 21 | WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \ 22 | ) 23 | } 24 | 25 | #define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags) 26 | #define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level) 27 | 28 | #define Trace CyapaPrint 29 | #define FuncEntry 30 | #define FuncExit 31 | #define WPP_INIT_TRACING 32 | #define WPP_CLEANUP 33 | #define TRACE_FLAG_SPBAPI 0 34 | #define TRACE_FLAG_WDFLOADING 0 35 | 36 | // begin_wpp config 37 | // FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 38 | // FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS); 39 | // USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry"); 40 | // USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--"); 41 | // end_wpp 42 | 43 | #endif _TRACE_H_ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Chrome EC Bus Driver 2 | 3 | * NOTE: This driver does NOT expose services to userspace directly 4 | * To use this driver, either ACPI must expose child devices, or you must modify this driver to enumerate child devices for other drivers to attach to 5 | 6 | Known ACPI IDs: 7 | * ACPI\GOOG0004: Chrome EC Bus (This driver) 8 | * ACPI\GOOG0002: Chrome EC Keyboard Backlight (Technically could be controlled via ACPI, but crosecbus is more reliable) - https://github.com/coolstar/croskblight 9 | * ACPI\GOOG0003: Chrome EC PD Notify (used for USB-C) 10 | * ACPI\GOOG0006: Chrome EC Sensor Hub - https://github.com/coolstar/crossensors 11 | * ACPI\GOOG0007: Chrome EC Vivaldi Keyboard Settings - https://github.com/coolstar/crosecvivaldi 12 | * ACPI\GOOG000A: Chrome EC Keyboard - https://github.com/coolstar/croskeyboard4 13 | * ACPI\GOOG000B: Pixel Slate Base (also used for Sensor Hub) - https://github.com/coolstar/crossensors 14 | * ACPI\GOOG0012: Chrome EC I2C Passthrough - https://github.com/coolstar/croseci2c 15 | * ACPI\GOOG0013: Chrome EC Audio Codec (Usually DMIC over I2S) - https://github.com/coolstar/croseccodec 16 | * ACPI\GOOG0014: Chrome EC USB-C 17 | * ACPI\GOOG0015: Chrome EC Trackpoint 18 | * ACPI\GOOG0016: Chrome OS GPIOs 19 | 20 | IDs not covered by crosec: 21 | * ACPI\GOOG000C: Wilco EC (not this driver) 22 | * ACPI\GOOG000D: Wilco EC Event (unused in Windows) 23 | * ACPI\GOOG000E: Wilco EC UCSI (covered by in-box UCSI driver) 24 | 25 | Protocols Implemented: 26 | * LPC v2 27 | * LPC v3 (Most Chromebooks) 28 | * MEC LPC (Braswell/Skylake Chromebooks, Framework laptop) 29 | 30 | Note: Framework laptop does not implement GOOG0004 ACPI device. Override DSDT/SSDT with testsigning or with OpenCore to add it. (See https://github.com/coreboot/coreboot/blob/master/src/ec/google/chromeec/acpi/cros_ec.asl for an example) 31 | 32 | Tested on HP Chromebook 14b (Ryzen 3 3250C) 33 | -------------------------------------------------------------------------------- /crosecbus/crosecbus.inf: -------------------------------------------------------------------------------- 1 | ;/*++ 2 | ; 3 | ;Copyright (c) CoolStar. All rights reserved. 4 | ; 5 | ;Module Name: 6 | ; crosecbus.inf 7 | ; 8 | ;Abstract: 9 | ; INF file for installing the CR50 I2C Driver 10 | ; 11 | ; 12 | ;--*/ 13 | 14 | [Version] 15 | Signature = "$WINDOWS NT$" 16 | Class = System 17 | ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} 18 | Provider = CoolStar 19 | DriverVer = 12/16/2021,1.0.0 20 | CatalogFile = crosecbus.cat 21 | PnpLockdown = 1 22 | 23 | [DestinationDirs] 24 | DefaultDestDir = 12 25 | 26 | ; ================= Class section ===================== 27 | 28 | [SourceDisksNames] 29 | 1 = %DiskId1%,,,"" 30 | 31 | [SourceDisksFiles] 32 | crosecbus.sys = 1,, 33 | 34 | ;***************************************** 35 | ; CrosEcBus Install Section 36 | ;***************************************** 37 | 38 | [Manufacturer] 39 | %StdMfg%=Standard,NT$ARCH$ 40 | 41 | ; Decorated model section take precedence over undecorated 42 | ; ones on XP and later. 43 | [Standard.NT$ARCH$] 44 | %CrosEcBus.DeviceDesc%=CrosEcBus_Device, ACPI\GOOG0004 45 | 46 | [CrosEcBus_Device.NT] 47 | CopyFiles=Drivers_Dir 48 | 49 | [CrosEcBus_Device.NT.HW] 50 | AddReg=CrosEcBus_AddReg 51 | 52 | [Drivers_Dir] 53 | crosecbus.sys 54 | 55 | [CrosEcBus_AddReg] 56 | ; Set to 1 to connect the first interrupt resource found, 0 to leave disconnected 57 | HKR,Settings,"ConnectInterrupt",0x00010001,0 58 | 59 | ;-------------- Service installation 60 | [CrosEcBus_Device.NT.Services] 61 | AddService = CrosEcBus,%SPSVCINST_ASSOCSERVICE%, CrosEcBus_Service_Inst 62 | 63 | ; -------------- CrosEcBus driver install sections 64 | [CrosEcBus_Service_Inst] 65 | DisplayName = %CrosEcBus.SVCDESC% 66 | ServiceType = 1 ; SERVICE_KERNEL_DRIVER 67 | StartType = 3 ; SERVICE_DEMAND_START 68 | ErrorControl = 1 ; SERVICE_ERROR_NORMAL 69 | ServiceBinary = %12%\crosecbus.sys 70 | LoadOrderGroup = Base 71 | 72 | [Strings] 73 | SPSVCINST_ASSOCSERVICE= 0x00000002 74 | StdMfg = "CoolStar" 75 | DiskId1 = "Chrome EC Installation Disk #1" 76 | CrosEcBus.DeviceDesc = "Chromebook EC Bus" 77 | CrosEcBus.SVCDESC = "Chrome EC Service" 78 | -------------------------------------------------------------------------------- /crosecbus.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31829.152 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crosecbus", "crosecbus\crosecbus.vcxproj", "{B3E71397-9BE4-492B-AAED-4D056E59CB1F}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crosecbus Package", "crosecbus Package\crosecbus Package.vcxproj", "{EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F} = {B3E71397-9BE4-492B-AAED-4D056E59CB1F} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Win32 = Debug|Win32 16 | Debug|x64 = Debug|x64 17 | Release|Win32 = Release|Win32 18 | Release|x64 = Release|x64 19 | EndGlobalSection 20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 21 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|Win32.ActiveCfg = Debug|Win32 22 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|Win32.Build.0 = Debug|Win32 23 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|Win32.Deploy.0 = Debug|Win32 24 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|x64.ActiveCfg = Debug|x64 25 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|x64.Build.0 = Debug|x64 26 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Debug|x64.Deploy.0 = Debug|x64 27 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|Win32.ActiveCfg = Release|Win32 28 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|Win32.Build.0 = Release|Win32 29 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|Win32.Deploy.0 = Release|Win32 30 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|x64.ActiveCfg = Release|x64 31 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|x64.Build.0 = Release|x64 32 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F}.Release|x64.Deploy.0 = Release|x64 33 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|Win32.ActiveCfg = Debug|Win32 34 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|Win32.Build.0 = Debug|Win32 35 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|Win32.Deploy.0 = Debug|Win32 36 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|x64.ActiveCfg = Debug|x64 37 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|x64.Build.0 = Debug|x64 38 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Debug|x64.Deploy.0 = Debug|x64 39 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|Win32.ActiveCfg = Release|Win32 40 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|Win32.Build.0 = Release|Win32 41 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|Win32.Deploy.0 = Release|Win32 42 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|x64.ActiveCfg = Release|x64 43 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|x64.Build.0 = Release|x64 44 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B}.Release|x64.Deploy.0 = Release|x64 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {86D249D6-FF1E-41F4-AA9B-3813428D6C1A} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /crosecbus/driver.h: -------------------------------------------------------------------------------- 1 | #if !defined(_CROSECBUS_H_) 2 | #define _CROSECBUS_H_ 3 | 4 | #pragma warning(disable:4200) // suppress nameless struct/union warning 5 | #pragma warning(disable:4201) // suppress nameless struct/union warning 6 | #pragma warning(disable:4214) // suppress bit field types other than int warning 7 | #include 8 | #include 9 | 10 | #pragma warning(default:4200) 11 | #pragma warning(default:4201) 12 | #pragma warning(default:4214) 13 | #include 14 | #include 15 | 16 | #pragma warning(disable:4201) // suppress nameless struct/union warning 17 | #pragma warning(disable:4214) // suppress bit field types other than int warning 18 | #include 19 | 20 | #include 21 | 22 | #include "gpiowrapper.h" 23 | 24 | // 25 | // String definitions 26 | // 27 | 28 | #define DRIVERNAME "crosecbus.sys: " 29 | 30 | #define CROSECBUS_POOL_TAG (ULONG) 'CREC' 31 | #define CROSECBUS_HARDWARE_IDS L"CoolStar\\GOOG0004\0\0" 32 | #define CROSECBUS_HARDWARE_IDS_LENGTH sizeof(CROSECBUS_HARDWARE_IDS) 33 | 34 | #define NTDEVICE_NAME_STRING L"\\Device\\CrosEC" 35 | #define SYMBOLIC_NAME_STRING L"\\DosDevices\\GOOG0004" 36 | 37 | #define true 1 38 | #define false 0 39 | 40 | typedef struct _CROSEC_COMMAND { 41 | UINT32 Version; 42 | UINT32 Command; 43 | UINT32 OutSize; 44 | UINT32 InSize; 45 | UINT32 Result; 46 | #pragma warning(disable:4200) 47 | UINT8 Data[]; 48 | } CROSEC_COMMAND, *PCROSEC_COMMAND; 49 | 50 | typedef 51 | NTSTATUS 52 | (*PCROSEC_CMD_XFER_STATUS)( 53 | IN PVOID Context, 54 | OUT PCROSEC_COMMAND Msg 55 | ); 56 | 57 | typedef 58 | BOOLEAN 59 | (*PCROSEC_CHECK_FEATURES)( 60 | IN PVOID Context, 61 | IN INT Feature 62 | ); 63 | 64 | typedef 65 | INT 66 | (*PCROSEC_READ_MEM) ( 67 | IN PVOID Context, 68 | IN INT offset, 69 | IN INT bytes, 70 | OUT PVOID dest 71 | ); 72 | 73 | DEFINE_GUID(GUID_CROSEC_INTERFACE_STANDARD, 74 | 0xd7062676, 0xe3a4, 0x11ec, 0xa6, 0xc4, 0x24, 0x4b, 0xfe, 0x99, 0x46, 0xd0); 75 | 76 | DEFINE_GUID(GUID_CROSEC_INTERFACE_STANDARD_V2, 77 | 0xad8649fa, 0x7c71, 0x11ed, 0xb6, 0x3c, 0x00, 0x15, 0x5d, 0xa4, 0x49, 0xad); 78 | 79 | typedef enum { 80 | CSVivaldiRequestUpdateButton = 0x101 81 | } CSVivaldiRequest; 82 | 83 | #include 84 | typedef struct CSVivaldiSettingsArg { 85 | UINT32 argSz; 86 | CSVivaldiRequest settingsRequest; 87 | union args { 88 | struct { 89 | UINT8 button; 90 | } button; 91 | } args; 92 | } CSVivaldiSettingsArg, * PCSVivaldiSettingsArg; 93 | #include 94 | 95 | // 96 | // Interface for getting and setting power level etc., 97 | // 98 | typedef struct _CROSEC_INTERFACE_STANDARD { 99 | INTERFACE InterfaceHeader; 100 | PCROSEC_CMD_XFER_STATUS CmdXferStatus; 101 | PCROSEC_CHECK_FEATURES CheckFeatures; 102 | } CROSEC_INTERFACE_STANDARD, * PCROSEC_INTERFACE_STANDARD; 103 | 104 | typedef struct _CROSEC_INTERFACE_STANDARD_V2 { 105 | INTERFACE InterfaceHeader; 106 | PCROSEC_CMD_XFER_STATUS CmdXferStatus; 107 | PCROSEC_CHECK_FEATURES CheckFeatures; 108 | PCROSEC_READ_MEM ReadEcMem; 109 | } CROSEC_INTERFACE_STANDARD_V2, * PCROSEC_INTERFACE_STANDARD_V2; 110 | 111 | typedef struct _CROSECBUS_CONTEXT 112 | { 113 | 114 | // 115 | // Handle back to the WDFDEVICE 116 | // 117 | 118 | WDFDEVICE FxDevice; 119 | 120 | UINT32 EcFeatures[2]; 121 | 122 | LONG64 KernelAccessesWaiting; 123 | WDFWAITLOCK EcLock; 124 | 125 | BOOLEAN FoundSyncGPIO; 126 | GPIO_CONTEXT SyncGpioContext; 127 | LONG SyncGpioWorkItemActive; 128 | WDFTIMER SyncGpioTimer; 129 | WDFWORKITEM SyncGpioWorkItem; 130 | PCALLBACK_OBJECT CSButtonsCallback; 131 | 132 | //S0IX Notify 133 | ACPI_INTERFACE_STANDARD2 S0ixNotifyAcpiInterface; 134 | BOOLEAN isInS0ix; 135 | BOOLEAN hostSleepV1; 136 | 137 | } CROSECBUS_CONTEXT, *PCROSECBUS_CONTEXT; 138 | 139 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CROSECBUS_CONTEXT, GetDeviceContext) 140 | 141 | // 142 | // Function definitions 143 | // 144 | 145 | DRIVER_INITIALIZE DriverEntry; 146 | 147 | EVT_WDF_DRIVER_UNLOAD CrosEcBusDriverUnload; 148 | 149 | EVT_WDF_DRIVER_DEVICE_ADD CrosEcBusEvtDeviceAdd; 150 | 151 | EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL CrosEcBusEvtInternalDeviceControl; 152 | 153 | // 154 | // Helper macros 155 | // 156 | 157 | #define DEBUG_LEVEL_ERROR 1 158 | #define DEBUG_LEVEL_INFO 2 159 | #define DEBUG_LEVEL_VERBOSE 3 160 | 161 | #define DBG_INIT 1 162 | #define DBG_PNP 2 163 | #define DBG_IOCTL 4 164 | 165 | #if 0 166 | #define CrosEcBusPrint(dbglevel, dbgcatagory, fmt, ...) { \ 167 | if (CrosEcBusDebugLevel >= dbglevel && \ 168 | (CrosEcBusDebugCatagories && dbgcatagory)) \ 169 | { \ 170 | DbgPrint(DRIVERNAME); \ 171 | DbgPrint(fmt, __VA_ARGS__); \ 172 | } \ 173 | } 174 | #else 175 | #define CrosEcBusPrint(dbglevel, fmt, ...) { \ 176 | } 177 | #endif 178 | #endif -------------------------------------------------------------------------------- /crosecbus/comm-mec_lpc.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "comm-host.h" 3 | 4 | static __inline void outb(unsigned char __val, unsigned int __port) { 5 | WRITE_PORT_UCHAR((PUCHAR)__port, __val); 6 | } 7 | 8 | static __inline void outw(unsigned short __val, unsigned int __port) { 9 | WRITE_PORT_USHORT((PUSHORT)__port, __val); 10 | } 11 | 12 | static __inline unsigned char inb(unsigned int __port) { 13 | return READ_PORT_UCHAR((PUCHAR)__port); 14 | } 15 | 16 | static __inline unsigned short inw(unsigned int __port) { 17 | return READ_PORT_USHORT((PUSHORT)__port); 18 | } 19 | 20 | 21 | static ULONG CrosEcBusDebugLevel = 100; 22 | static ULONG CrosEcBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 23 | 24 | FAST_MUTEX MecAccessMutex; 25 | 26 | int wait_for_ec(int status_addr, int timeout_usec); 27 | 28 | // Thanks @DHowett! 29 | 30 | typedef enum _ec_xfer_direction { EC_MEC_WRITE, EC_MEC_READ } ec_xfer_direction; 31 | 32 | enum cros_ec_lpc_mec_emi_access_mode { 33 | /* 8-bit access */ 34 | MEC_EC_BYTE_ACCESS = 0x0, 35 | /* 16-bit access */ 36 | MEC_EC_WORD_ACCESS = 0x1, 37 | /* 32-bit access */ 38 | MEC_EC_LONG_ACCESS = 0x2, 39 | /* 40 | * 32-bit access, read or write of MEC_EMI_EC_DATA_B3 causes the 41 | * EC data register to be incremented. 42 | */ 43 | MEC_EC_LONG_ACCESS_AUTOINCREMENT = 0x3, 44 | }; 45 | 46 | /* EMI registers are relative to base */ 47 | #define MEC_EMI_HOST_TO_EC(MEC_EMI_BASE) ((MEC_EMI_BASE) + 0) 48 | #define MEC_EMI_EC_TO_HOST(MEC_EMI_BASE) ((MEC_EMI_BASE) + 1) 49 | #define MEC_EMI_EC_ADDRESS_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 2) 50 | #define MEC_EMI_EC_ADDRESS_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 3) 51 | #define MEC_EMI_EC_DATA_B0(MEC_EMI_BASE) ((MEC_EMI_BASE) + 4) 52 | #define MEC_EMI_EC_DATA_B1(MEC_EMI_BASE) ((MEC_EMI_BASE) + 5) 53 | #define MEC_EMI_EC_DATA_B2(MEC_EMI_BASE) ((MEC_EMI_BASE) + 6) 54 | #define MEC_EMI_EC_DATA_B3(MEC_EMI_BASE) ((MEC_EMI_BASE) + 7) 55 | 56 | UINT16 mec_emi_base = 0, mec_emi_end = 0; 57 | 58 | static void ec_mec_emi_write_access(UINT16 address, enum cros_ec_lpc_mec_emi_access_mode access_type) { 59 | outw((address & 0xFFFC) | (UINT16)access_type, MEC_EMI_EC_ADDRESS_B0(mec_emi_base)); 60 | } 61 | 62 | static int ec_mec_xfer(ec_xfer_direction direction, UINT16 address, 63 | UINT8* data, UINT16 size) 64 | { 65 | if (mec_emi_base == 0 || mec_emi_end == 0) 66 | return 0; 67 | 68 | ExAcquireFastMutex(&MecAccessMutex); 69 | 70 | /* 71 | * There's a cleverer way to do this, but it's somewhat less clear what's happening. 72 | * I prefer clarity over cleverness. :) 73 | */ 74 | int pos = 0; 75 | UINT16 temp[2]; 76 | if (address % 4 > 0) { 77 | ec_mec_emi_write_access(address, MEC_EC_BYTE_ACCESS); 78 | /* Unaligned start address */ 79 | for (int i = address % 4; i < 4; ++i) { 80 | UINT8* storage = &data[pos++]; 81 | if (direction == EC_MEC_WRITE) 82 | outb(*storage, MEC_EMI_EC_DATA_B0(mec_emi_base) + i); 83 | else if (direction == EC_MEC_READ) 84 | *storage = inb(MEC_EMI_EC_DATA_B0(mec_emi_base) + i); 85 | } 86 | address = (address + 4) & 0xFFFC; 87 | } 88 | 89 | if (size - pos >= 4) { 90 | ec_mec_emi_write_access(address, MEC_EC_LONG_ACCESS_AUTOINCREMENT); 91 | while (size - pos >= 4) { 92 | if (direction == EC_MEC_WRITE) { 93 | memcpy(temp, &data[pos], sizeof(temp)); 94 | outw(temp[0], MEC_EMI_EC_DATA_B0(mec_emi_base)); 95 | outw(temp[1], MEC_EMI_EC_DATA_B2(mec_emi_base)); 96 | } 97 | else if (direction == EC_MEC_READ) { 98 | temp[0] = inw(MEC_EMI_EC_DATA_B0(mec_emi_base)); 99 | temp[1] = inw(MEC_EMI_EC_DATA_B2(mec_emi_base)); 100 | memcpy(&data[pos], temp, sizeof(temp)); 101 | } 102 | 103 | pos += 4; 104 | address += 4; 105 | } 106 | } 107 | 108 | if (size - pos > 0) { 109 | ec_mec_emi_write_access(address, MEC_EC_BYTE_ACCESS); 110 | for (int i = 0; i < (size - pos); ++i) { 111 | UINT8* storage = &data[pos + i]; 112 | if (direction == EC_MEC_WRITE) 113 | outb(*storage, MEC_EMI_EC_DATA_B0(mec_emi_base) + i); 114 | else if (direction == EC_MEC_READ) 115 | *storage = inb(MEC_EMI_EC_DATA_B0(mec_emi_base) + i); 116 | } 117 | } 118 | 119 | ExReleaseFastMutex(&MecAccessMutex); 120 | 121 | return 0; 122 | } 123 | 124 | static int cros_ec_lpc_mec_in_range(unsigned int offset, unsigned int length) { 125 | if (length == 0) 126 | return -1; 127 | 128 | if (mec_emi_base == 0 || mec_emi_end == 0) 129 | return -1; 130 | 131 | if (offset >= mec_emi_base && offset < mec_emi_end) { 132 | if (offset + length - 1 >= mec_emi_end) 133 | return -1; 134 | return 1; 135 | } 136 | 137 | if (offset + length > mec_emi_base && offset < mec_emi_end) 138 | return -1; 139 | 140 | return 0; 141 | } 142 | 143 | int ec_lpc_read_bytes(unsigned int offset, unsigned int length, UINT8* dest); 144 | int ec_lpc_write_bytes(unsigned int offset, unsigned int length, const UINT8* msg); 145 | 146 | static int ec_mec_lpc_read_bytes(unsigned int offset, unsigned int length, UINT8* dest) { 147 | int in_range = cros_ec_lpc_mec_in_range(offset, length); 148 | if (!in_range) { 149 | return ec_lpc_read_bytes(offset, length, dest); 150 | } 151 | 152 | int sum = 0; 153 | unsigned int i; 154 | ec_mec_xfer(EC_MEC_READ, (UINT16)offset - EC_HOST_CMD_REGION0, dest, (UINT16)length); 155 | for (i = 0; i < length; ++i) { 156 | sum += dest[i]; 157 | } 158 | 159 | /* Return checksum of all bytes read */ 160 | return sum; 161 | } 162 | 163 | static int ec_mec_lpc_write_bytes(unsigned int offset, unsigned int length, const UINT8* msg) { 164 | int in_range = cros_ec_lpc_mec_in_range(offset, length); 165 | if (!in_range) { 166 | return ec_lpc_write_bytes(offset, length, msg); 167 | } 168 | 169 | int sum = 0; 170 | unsigned int i; 171 | ec_mec_xfer(EC_MEC_WRITE, (UINT16)offset - EC_HOST_CMD_REGION0, (UINT8 *)msg, (UINT16)length); 172 | for (i = 0; i < length; ++i) { 173 | sum += msg[i]; 174 | } 175 | 176 | /* Return checksum of all bytes written */ 177 | return sum; 178 | } 179 | 180 | NTSTATUS comm_init_lpc_mec(void) 181 | { 182 | /* This function assumes some setup was done by comm_init_lpc. */ 183 | 184 | ExInitializeFastMutex(&MecAccessMutex); 185 | 186 | mec_emi_base = EC_HOST_CMD_REGION0; 187 | mec_emi_end = EC_LPC_ADDR_MEMMAP + EC_MEMMAP_SIZE; 188 | 189 | ec_lpc_ops.read = ec_mec_lpc_read_bytes; 190 | ec_lpc_ops.write = ec_mec_lpc_write_bytes; 191 | 192 | return STATUS_SUCCESS; 193 | } 194 | -------------------------------------------------------------------------------- /crosecbus/gpio.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "gpiowrapper.h" 3 | #include 4 | #include 5 | 6 | static ULONG CrosEcBusDebugLevel = 100; 7 | static ULONG CrosEcBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 8 | 9 | NTSTATUS 10 | GpioReadDataSynchronously( 11 | _In_ GPIO_CONTEXT *GpioContext, 12 | _In_reads_bytes_(Length) PVOID Data, 13 | _In_ ULONG Length 14 | ) 15 | /*++ 16 | 17 | Routine Description: 18 | 19 | This helper routine abstracts creating and sending an I/O 20 | request (I2C Read) to the Spb I/O target. 21 | 22 | Arguments: 23 | 24 | SpbContext - Pointer to the current device context 25 | Data - A buffer to receive the data at at the above address 26 | Length - The amount of data to be read from the above address 27 | 28 | Return Value: 29 | 30 | NTSTATUS Status indicating success or failure 31 | 32 | --*/ 33 | { 34 | WDF_OBJECT_ATTRIBUTES Attributes; 35 | WDFREQUEST IoctlRequest; 36 | WDFMEMORY WdfMemory; 37 | WDF_OBJECT_ATTRIBUTES RequestAttributes; 38 | WDF_REQUEST_SEND_OPTIONS SendOptions; 39 | NTSTATUS status; 40 | 41 | WdfWaitLockAcquire(GpioContext->GpioLock, NULL); 42 | 43 | status = STATUS_INVALID_PARAMETER; 44 | 45 | WDF_OBJECT_ATTRIBUTES_INIT(&RequestAttributes); 46 | status = WdfRequestCreate(&RequestAttributes, GpioContext->GpioIoTarget, &IoctlRequest); 47 | if (!NT_SUCCESS(status)) { 48 | CrosEcBusPrint( 49 | DEBUG_LEVEL_ERROR, 50 | DBG_IOCTL, 51 | "Error creating request - %x\n", 52 | status); 53 | goto exit; 54 | } 55 | 56 | WDF_OBJECT_ATTRIBUTES_INIT(&Attributes); 57 | Attributes.ParentObject = IoctlRequest; 58 | status = WdfMemoryCreatePreallocated(&Attributes, Data, Length, &WdfMemory); 59 | if (!NT_SUCCESS(status)) { 60 | CrosEcBusPrint( 61 | DEBUG_LEVEL_ERROR, 62 | DBG_IOCTL, 63 | "Error making preallocated memory - %x\n", 64 | status); 65 | goto exit; 66 | } 67 | 68 | status = WdfIoTargetFormatRequestForIoctl(GpioContext->GpioIoTarget, 69 | IoctlRequest, 70 | IOCTL_GPIO_READ_PINS, 71 | NULL, 72 | 0, 73 | WdfMemory, 74 | 0); 75 | if (!NT_SUCCESS(status)) 76 | { 77 | CrosEcBusPrint( 78 | DEBUG_LEVEL_ERROR, 79 | DBG_IOCTL, 80 | "Error formatting request for ioctl - %x\n", 81 | status); 82 | goto exit; 83 | } 84 | 85 | WDF_REQUEST_SEND_OPTIONS_INIT(&SendOptions, 86 | WDF_REQUEST_SEND_OPTION_SYNCHRONOUS); 87 | 88 | if (!WdfRequestSend(IoctlRequest, GpioContext->GpioIoTarget, &SendOptions)) { 89 | status = WdfRequestGetStatus(IoctlRequest); 90 | } 91 | 92 | if (!NT_SUCCESS(status)) 93 | { 94 | CrosEcBusPrint( 95 | DEBUG_LEVEL_ERROR, 96 | DBG_IOCTL, 97 | "Error reading from Gpio - %x\n", 98 | status); 99 | goto exit; 100 | } 101 | exit: 102 | if (IoctlRequest != NULL) { 103 | WdfObjectDelete(IoctlRequest); 104 | } 105 | 106 | WdfWaitLockRelease(GpioContext->GpioLock); 107 | 108 | return status; 109 | } 110 | 111 | VOID 112 | GpioTargetDeinitialize( 113 | IN WDFDEVICE FxDevice, 114 | IN GPIO_CONTEXT *GpioContext 115 | ) 116 | /*++ 117 | 118 | Routine Description: 119 | 120 | This helper routine is used to free any members added to the SPB_CONTEXT, 121 | note the SPB I/O target is parented to the device and will be 122 | closed and free'd when the device is removed. 123 | 124 | Arguments: 125 | 126 | FxDevice - Handle to the framework device object 127 | SpbContext - Pointer to the current device context 128 | 129 | Return Value: 130 | 131 | NTSTATUS Status indicating success or failure 132 | 133 | --*/ 134 | { 135 | UNREFERENCED_PARAMETER(FxDevice); 136 | UNREFERENCED_PARAMETER(GpioContext); 137 | 138 | // 139 | // Free any SPB_CONTEXT allocations here 140 | // 141 | if (GpioContext->GpioLock != NULL) 142 | { 143 | WdfObjectDelete(GpioContext->GpioLock); 144 | } 145 | } 146 | 147 | NTSTATUS 148 | GpioTargetInitialize( 149 | IN WDFDEVICE FxDevice, 150 | IN GPIO_CONTEXT *GpioContext 151 | ) 152 | /*++ 153 | 154 | Routine Description: 155 | 156 | This helper routine opens the Spb I/O target and 157 | initializes a request object used for the lifetime 158 | of communication between this driver and Spb. 159 | 160 | Arguments: 161 | 162 | FxDevice - Handle to the framework device object 163 | SpbContext - Pointer to the current device context 164 | 165 | Return Value: 166 | 167 | NTSTATUS Status indicating success or failure 168 | 169 | --*/ 170 | { 171 | WDF_OBJECT_ATTRIBUTES objectAttributes; 172 | WDF_IO_TARGET_OPEN_PARAMS openParams; 173 | UNICODE_STRING gpioDeviceName; 174 | WCHAR gpioDeviceNameBuffer[RESOURCE_HUB_PATH_SIZE]; 175 | NTSTATUS status; 176 | 177 | WDF_OBJECT_ATTRIBUTES_INIT(&objectAttributes); 178 | objectAttributes.ParentObject = FxDevice; 179 | 180 | status = WdfIoTargetCreate( 181 | FxDevice, 182 | &objectAttributes, 183 | &GpioContext->GpioIoTarget); 184 | 185 | if (!NT_SUCCESS(status)) 186 | { 187 | CrosEcBusPrint( 188 | DEBUG_LEVEL_ERROR, 189 | DBG_IOCTL, 190 | "Error creating IoTarget object - %!STATUS!", 191 | status); 192 | 193 | WdfObjectDelete(GpioContext->GpioIoTarget); 194 | goto exit; 195 | } 196 | 197 | RtlInitEmptyUnicodeString( 198 | &gpioDeviceName, 199 | gpioDeviceNameBuffer, 200 | sizeof(gpioDeviceNameBuffer)); 201 | 202 | status = RESOURCE_HUB_CREATE_PATH_FROM_ID( 203 | &gpioDeviceName, 204 | GpioContext->GpioResHubId.LowPart, 205 | GpioContext->GpioResHubId.HighPart); 206 | 207 | if (!NT_SUCCESS(status)) 208 | { 209 | CrosEcBusPrint( 210 | DEBUG_LEVEL_ERROR, 211 | DBG_IOCTL, 212 | "Error creating GPIO resource hub path string - %!STATUS!", 213 | status); 214 | goto exit; 215 | } 216 | 217 | WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( 218 | &openParams, 219 | &gpioDeviceName, 220 | (FILE_GENERIC_READ)); 221 | 222 | status = WdfIoTargetOpen(GpioContext->GpioIoTarget, &openParams); 223 | 224 | if (!NT_SUCCESS(status)) 225 | { 226 | CrosEcBusPrint( 227 | DEBUG_LEVEL_ERROR, 228 | DBG_IOCTL, 229 | "Error opening GPIO target for communication - %!STATUS!", 230 | status); 231 | goto exit; 232 | } 233 | 234 | // 235 | // Allocate a waitlock to guard access to the default buffers 236 | // 237 | status = WdfWaitLockCreate( 238 | WDF_NO_OBJECT_ATTRIBUTES, 239 | &GpioContext->GpioLock); 240 | 241 | if (!NT_SUCCESS(status)) 242 | { 243 | CrosEcBusPrint( 244 | DEBUG_LEVEL_ERROR, 245 | DBG_IOCTL, 246 | "Error creating Spb Waitlock - %!STATUS!", 247 | status); 248 | goto exit; 249 | } 250 | 251 | exit: 252 | 253 | if (!NT_SUCCESS(status)) 254 | { 255 | GpioTargetDeinitialize(FxDevice, GpioContext); 256 | } 257 | 258 | return status; 259 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015/2017 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # Visual Studio 2017 auto generated files 34 | Generated\ Files/ 35 | 36 | # MSTest test Results 37 | [Tt]est[Rr]esult*/ 38 | [Bb]uild[Ll]og.* 39 | 40 | # NUNIT 41 | *.VisualState.xml 42 | TestResult.xml 43 | 44 | # Build Results of an ATL Project 45 | [Dd]ebugPS/ 46 | [Rr]eleasePS/ 47 | dlldata.c 48 | 49 | # Benchmark Results 50 | BenchmarkDotNet.Artifacts/ 51 | 52 | # .NET Core 53 | project.lock.json 54 | project.fragment.lock.json 55 | artifacts/ 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_h.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *_wpftmp.csproj 81 | *.log 82 | *.vspscc 83 | *.vssscc 84 | .builds 85 | *.pidb 86 | *.svclog 87 | *.scc 88 | 89 | # Chutzpah Test files 90 | _Chutzpah* 91 | 92 | # Visual C++ cache files 93 | ipch/ 94 | *.aps 95 | *.ncb 96 | *.opendb 97 | *.opensdf 98 | *.sdf 99 | *.cachefile 100 | *.VC.db 101 | *.VC.VC.opendb 102 | 103 | # Visual Studio profiler 104 | *.psess 105 | *.vsp 106 | *.vspx 107 | *.sap 108 | 109 | # Visual Studio Trace Files 110 | *.e2e 111 | 112 | # TFS 2012 Local Workspace 113 | $tf/ 114 | 115 | # Guidance Automation Toolkit 116 | *.gpState 117 | 118 | # ReSharper is a .NET coding add-in 119 | _ReSharper*/ 120 | *.[Rr]e[Ss]harper 121 | *.DotSettings.user 122 | 123 | # JustCode is a .NET coding add-in 124 | .JustCode 125 | 126 | # TeamCity is a build add-in 127 | _TeamCity* 128 | 129 | # DotCover is a Code Coverage Tool 130 | *.dotCover 131 | 132 | # AxoCover is a Code Coverage Tool 133 | .axoCover/* 134 | !.axoCover/settings.json 135 | 136 | # Visual Studio code coverage results 137 | *.coverage 138 | *.coveragexml 139 | 140 | # NCrunch 141 | _NCrunch_* 142 | .*crunch*.local.xml 143 | nCrunchTemp_* 144 | 145 | # MightyMoose 146 | *.mm.* 147 | AutoTest.Net/ 148 | 149 | # Web workbench (sass) 150 | .sass-cache/ 151 | 152 | # Installshield output folder 153 | [Ee]xpress/ 154 | 155 | # DocProject is a documentation generator add-in 156 | DocProject/buildhelp/ 157 | DocProject/Help/*.HxT 158 | DocProject/Help/*.HxC 159 | DocProject/Help/*.hhc 160 | DocProject/Help/*.hhk 161 | DocProject/Help/*.hhp 162 | DocProject/Help/Html2 163 | DocProject/Help/html 164 | 165 | # Click-Once directory 166 | publish/ 167 | 168 | # Publish Web Output 169 | *.[Pp]ublish.xml 170 | *.azurePubxml 171 | # Note: Comment the next line if you want to checkin your web deploy settings, 172 | # but database connection strings (with potential passwords) will be unencrypted 173 | *.pubxml 174 | *.publishproj 175 | 176 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 177 | # checkin your Azure Web App publish settings, but sensitive information contained 178 | # in these scripts will be unencrypted 179 | PublishScripts/ 180 | 181 | # NuGet Packages 182 | *.nupkg 183 | # The packages folder can be ignored because of Package Restore 184 | **/[Pp]ackages/* 185 | # except build/, which is used as an MSBuild target. 186 | !**/[Pp]ackages/build/ 187 | # Uncomment if necessary however generally it will be regenerated when needed 188 | #!**/[Pp]ackages/repositories.config 189 | # NuGet v3's project.json files produces more ignorable files 190 | *.nuget.props 191 | *.nuget.targets 192 | 193 | # Microsoft Azure Build Output 194 | csx/ 195 | *.build.csdef 196 | 197 | # Microsoft Azure Emulator 198 | ecf/ 199 | rcf/ 200 | 201 | # Windows Store app package directories and files 202 | AppPackages/ 203 | BundleArtifacts/ 204 | Package.StoreAssociation.xml 205 | _pkginfo.txt 206 | *.appx 207 | 208 | # Visual Studio cache files 209 | # files ending in .cache can be ignored 210 | *.[Cc]ache 211 | # but keep track of directories ending in .cache 212 | !*.[Cc]ache/ 213 | 214 | # Others 215 | ClientBin/ 216 | ~$* 217 | *~ 218 | *.dbmdl 219 | *.dbproj.schemaview 220 | *.jfm 221 | *.pfx 222 | *.publishsettings 223 | orleans.codegen.cs 224 | 225 | # Including strong name files can present a security risk 226 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 227 | #*.snk 228 | 229 | # Since there are multiple workflows, uncomment next line to ignore bower_components 230 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 231 | #bower_components/ 232 | 233 | # RIA/Silverlight projects 234 | Generated_Code/ 235 | 236 | # Backup & report files from converting an old project file 237 | # to a newer Visual Studio version. Backup files are not needed, 238 | # because we have git ;-) 239 | _UpgradeReport_Files/ 240 | Backup*/ 241 | UpgradeLog*.XML 242 | UpgradeLog*.htm 243 | ServiceFabricBackup/ 244 | *.rptproj.bak 245 | 246 | # SQL Server files 247 | *.mdf 248 | *.ldf 249 | *.ndf 250 | 251 | # Business Intelligence projects 252 | *.rdl.data 253 | *.bim.layout 254 | *.bim_*.settings 255 | *.rptproj.rsuser 256 | 257 | # Microsoft Fakes 258 | FakesAssemblies/ 259 | 260 | # GhostDoc plugin setting file 261 | *.GhostDoc.xml 262 | 263 | # Node.js Tools for Visual Studio 264 | .ntvs_analysis.dat 265 | node_modules/ 266 | 267 | # Visual Studio 6 build log 268 | *.plg 269 | 270 | # Visual Studio 6 workspace options file 271 | *.opt 272 | 273 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 274 | *.vbw 275 | 276 | # Visual Studio LightSwitch build output 277 | **/*.HTMLClient/GeneratedArtifacts 278 | **/*.DesktopClient/GeneratedArtifacts 279 | **/*.DesktopClient/ModelManifest.xml 280 | **/*.Server/GeneratedArtifacts 281 | **/*.Server/ModelManifest.xml 282 | _Pvt_Extensions 283 | 284 | # Paket dependency manager 285 | .paket/paket.exe 286 | paket-files/ 287 | 288 | # FAKE - F# Make 289 | .fake/ 290 | 291 | # JetBrains Rider 292 | .idea/ 293 | *.sln.iml 294 | 295 | # CodeRush personal settings 296 | .cr/personal 297 | 298 | # Python Tools for Visual Studio (PTVS) 299 | __pycache__/ 300 | *.pyc 301 | 302 | # Cake - Uncomment if you are using it 303 | # tools/** 304 | # !tools/packages.config 305 | 306 | # Tabs Studio 307 | *.tss 308 | 309 | # Telerik's JustMock configuration file 310 | *.jmconfig 311 | 312 | # BizTalk build output 313 | *.btp.cs 314 | *.btm.cs 315 | *.odx.cs 316 | *.xsd.cs 317 | 318 | # OpenCover UI analysis results 319 | OpenCover/ 320 | 321 | # Azure Stream Analytics local run output 322 | ASALocalRun/ 323 | 324 | # MSBuild Binary and Structured Log 325 | *.binlog 326 | 327 | # NVidia Nsight GPU debugger configuration file 328 | *.nvuser 329 | 330 | # MFractors (Xamarin productivity tool) working folder 331 | .mfractor/ 332 | 333 | # Local History for Visual Studio 334 | .localhistory/ 335 | -------------------------------------------------------------------------------- /crosecbus Package/crosecbus Package.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {EA676041-89D8-4ACF-A48B-F11CA9F5DD8B} 23 | {4605da2c-74a5-4865-98e1-152ef136825f} 24 | v4.5 25 | 11.0 26 | Win8.1 Debug 27 | Win32 28 | crosecbus_Package 29 | $(LatestTargetPlatformVersion) 30 | crosecbus Package 31 | 32 | 33 | 34 | Windows10 35 | true 36 | WindowsKernelModeDriver10.0 37 | Utility 38 | Package 39 | true 40 | 41 | 42 | Windows10 43 | false 44 | WindowsKernelModeDriver10.0 45 | Utility 46 | Package 47 | true 48 | 49 | 50 | Windows10 51 | true 52 | WindowsKernelModeDriver10.0 53 | Utility 54 | Package 55 | true 56 | 57 | 58 | Windows10 59 | false 60 | WindowsKernelModeDriver10.0 61 | Utility 62 | Package 63 | true 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | DbgengKernelDebugger 75 | False 76 | True 77 | 78 | 79 | 80 | False 81 | False 82 | True 83 | 84 | 133563 85 | 86 | 87 | DbgengKernelDebugger 88 | False 89 | True 90 | 91 | 92 | 93 | False 94 | False 95 | True 96 | 97 | 133563 98 | 99 | 100 | DbgengKernelDebugger 101 | False 102 | True 103 | 104 | 105 | 106 | False 107 | False 108 | True 109 | 110 | 133563 111 | 112 | 113 | DbgengKernelDebugger 114 | False 115 | True 116 | 117 | 118 | 119 | False 120 | False 121 | True 122 | 123 | 133563 124 | 125 | 126 | 127 | SHA256 128 | 129 | 130 | 131 | 132 | SHA256 133 | 134 | 135 | 136 | 137 | SHA256 138 | 139 | 140 | 141 | 142 | SHA256 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | {b3e71397-9be4-492b-aaed-4d056e59cb1f} 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /crosecbus/crosecbus.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | {B3E71397-9BE4-492B-AAED-4D056E59CB1F} 23 | {1bc93793-694f-48fe-9372-81e2b05556fd} 24 | v4.5 25 | 11.0 26 | Win8.1 Debug 27 | Win32 28 | crosecbus 29 | $(LatestTargetPlatformVersion) 30 | crosecbus 31 | 32 | 33 | 34 | Windows10 35 | true 36 | WindowsKernelModeDriver10.0 37 | Driver 38 | KMDF 39 | 40 | 41 | Windows10 42 | false 43 | WindowsKernelModeDriver10.0 44 | Driver 45 | KMDF 46 | 47 | 48 | Windows10 49 | true 50 | WindowsKernelModeDriver10.0 51 | Driver 52 | KMDF 53 | 54 | 55 | Windows10 56 | false 57 | WindowsKernelModeDriver10.0 58 | Driver 59 | KMDF 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | DbgengKernelDebugger 71 | 72 | 73 | DbgengKernelDebugger 74 | 75 | 76 | DbgengKernelDebugger 77 | 78 | 79 | DbgengKernelDebugger 80 | 81 | 82 | 83 | true 84 | trace.h 85 | true 86 | false 87 | 88 | 89 | 2.0.0 90 | 91 | 92 | SHA256 93 | 94 | 95 | 96 | 97 | true 98 | trace.h 99 | true 100 | false 101 | 102 | 103 | 2.0.0 104 | 105 | 106 | SHA256 107 | 108 | 109 | 110 | 111 | true 112 | trace.h 113 | true 114 | false 115 | 116 | 117 | 2.0.0 118 | 119 | 120 | SHA256 121 | 122 | 123 | 124 | 125 | true 126 | trace.h 127 | true 128 | false 129 | 130 | 131 | 2.0.0 132 | 133 | 134 | SHA256 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /crosecbus/userspaceQueue.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "userspaceQueue.h" 3 | #include "ec_commands.h" 4 | #include "comm-host.h" 5 | 6 | static ULONG CrosEcBusDebugLevel = 100; 7 | static ULONG CrosEcBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 8 | 9 | //Mostly based on DHowett's FrameworkWindowsUtils to preserve compatibility with his driver 10 | 11 | #define FAILED_NTSTATUS(status) (((NTSTATUS)(status)) < 0) 12 | #define NT_RETURN_IF_NTSTATUS_FAILED(status) \ 13 | do { \ 14 | const NTSTATUS __statusRet = (status); \ 15 | if(FAILED_NTSTATUS(__statusRet)) { \ 16 | return __statusRet; \ 17 | } \ 18 | } while((void)0, 0) 19 | #define NT_RETURN_IF(status, condition) \ 20 | do { \ 21 | const NTSTATUS __statusRet = (status); \ 22 | if((condition)) { \ 23 | return __statusRet; \ 24 | } \ 25 | } while((void)0, 0) 26 | 27 | #define EC_CMD_USB_PD_FW_UPDATE 0x0110 28 | #define EC_CMD_FLASH_WRITE 0x0012 29 | #define EC_CMD_FLASH_ERASE 0x0013 30 | #define EC_CMD_FLASH_PROTECT 0x0015 31 | 32 | static NTSTATUS sCrosECErrorCodeMapping[] = { 33 | [EC_RES_SUCCESS] = STATUS_SUCCESS, 34 | [EC_RES_INVALID_COMMAND] = STATUS_INVALID_PARAMETER, 35 | [EC_RES_ERROR] = STATUS_UNSUCCESSFUL, 36 | [EC_RES_INVALID_PARAM] = STATUS_INVALID_PARAMETER, 37 | [EC_RES_ACCESS_DENIED] = STATUS_ACCESS_DENIED, 38 | [EC_RES_INVALID_RESPONSE] = STATUS_DATA_ERROR, 39 | [EC_RES_INVALID_VERSION] = STATUS_DATA_ERROR, 40 | [EC_RES_INVALID_CHECKSUM] = STATUS_CRC_ERROR, 41 | [EC_RES_IN_PROGRESS] = CROSEC_STATUS_IN_PROGRESS, 42 | [EC_RES_UNAVAILABLE] = CROSEC_STATUS_UNAVAILABLE, 43 | [EC_RES_TIMEOUT] = STATUS_IO_TIMEOUT, 44 | [EC_RES_OVERFLOW] = STATUS_BUFFER_OVERFLOW, 45 | [EC_RES_INVALID_HEADER] = STATUS_DATA_ERROR, 46 | [EC_RES_REQUEST_TRUNCATED] = STATUS_BUFFER_TOO_SMALL, 47 | [EC_RES_RESPONSE_TOO_BIG] = STATUS_BUFFER_OVERFLOW, 48 | [EC_RES_BUS_ERROR] = STATUS_UNSUCCESSFUL, 49 | [EC_RES_BUSY] = STATUS_DEVICE_BUSY, 50 | [EC_RES_INVALID_HEADER_VERSION] = STATUS_DATA_ERROR, 51 | [EC_RES_INVALID_HEADER_CRC] = STATUS_CRC_ERROR, 52 | [EC_RES_INVALID_DATA_CRC] = STATUS_CRC_ERROR, 53 | [EC_RES_DUP_UNAVAILABLE] = STATUS_UNSUCCESSFUL, 54 | }; 55 | 56 | NTSTATUS CrosECQueueInitialize(_In_ WDFDEVICE Device) { 57 | WDFQUEUE queue; 58 | NTSTATUS status; 59 | WDF_IO_QUEUE_CONFIG queueConfig; 60 | 61 | PAGED_CODE(); 62 | 63 | // 64 | // Configure a default queue so that requests that are not 65 | // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto 66 | // other queues get dispatched here. 67 | // 68 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel); 69 | 70 | queueConfig.EvtIoDeviceControl = CrosECEvtIoDeviceControl; 71 | queueConfig.EvtIoStop = CrosECEvtIoStop; 72 | 73 | status = WdfIoQueueCreate(Device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue); 74 | 75 | if (!NT_SUCCESS(status)) { 76 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "WdfIoQueueCreate failed %!STATUS!", status); 77 | return status; 78 | } 79 | 80 | return status; 81 | } 82 | 83 | NTSTATUS CrosECIoctlXCmd(_In_ PCROSECBUS_CONTEXT pDevice, _In_ WDFREQUEST Request) { 84 | PCROSEC_COMMAND cmd; 85 | size_t cmdLen; 86 | NT_RETURN_IF_NTSTATUS_FAILED(WdfRequestRetrieveInputBuffer(Request, sizeof(cmd), (PVOID*)&cmd, &cmdLen)); 87 | 88 | PCROSEC_COMMAND outCmd; 89 | size_t outLen; 90 | NT_RETURN_IF_NTSTATUS_FAILED(WdfRequestRetrieveOutputBuffer(Request, sizeof(*cmd), &outCmd, &outLen)); 91 | NT_ANALYSIS_ASSUME(outLen >= sizeof(*cmd)); 92 | 93 | // User tried to send/receive too much data 94 | NT_RETURN_IF(STATUS_BUFFER_OVERFLOW, cmdLen > (sizeof(CROSEC_COMMAND) + ec_max_insize)); 95 | NT_RETURN_IF(STATUS_BUFFER_OVERFLOW, outLen > (sizeof(CROSEC_COMMAND) + ec_max_outsize)); 96 | // User tried to send/receive more bytes than they offered in storage 97 | NT_RETURN_IF(STATUS_BUFFER_TOO_SMALL, cmdLen < (sizeof(CROSEC_COMMAND) + cmd->OutSize)); 98 | NT_RETURN_IF(STATUS_BUFFER_TOO_SMALL, outLen < (sizeof(CROSEC_COMMAND) + cmd->InSize)); 99 | 100 | // I know this seems overprotective, and that I am wielding too much power over you, 101 | // but I don't think that the Windows driver should let you erase your EC flash. 102 | // Since the device grants access to all administrators, that would put you one 103 | // bad apple away from bricking your machine. Sorry. 104 | 105 | //DHowett wrote the above, but I (CoolStar) agree ^^. If you need to update your EC, you should let coreboot update the RW portion 106 | 107 | NT_RETURN_IF(STATUS_ACCESS_DENIED, cmd->Command == EC_CMD_FLASH_ERASE || cmd->Command == EC_CMD_FLASH_PROTECT || 108 | cmd->Command == EC_CMD_FLASH_WRITE || 109 | cmd->Command == EC_CMD_USB_PD_FW_UPDATE); 110 | 111 | int tries = 5; //Wait our turn if kernel driver wants to access first 112 | while (tries > 0) { 113 | LONG64 accesses = InterlockedCompareExchange64(&pDevice->KernelAccessesWaiting, 0, 0); 114 | if (accesses == 0) //No kernel driver access. We're good to go. 115 | break; 116 | 117 | LARGE_INTEGER Interval; 118 | Interval.QuadPart = -10 * 500; //Wait 500 microseconds before checking again to hopefully let kernel complete 119 | KeDelayExecutionThread(KernelMode, TRUE, &Interval); 120 | tries--; 121 | } 122 | if (tries <= 0) { 123 | return STATUS_RETRY; //Userland should retry later. 124 | } 125 | 126 | WdfWaitLockAcquire(pDevice->EcLock, NULL); 127 | 128 | RtlCopyMemory(outCmd, cmd, sizeof(*cmd)); //Copy header 129 | 130 | int res = ec_command_proto((UINT16)cmd->Command, (UINT8)cmd->Version, outCmd->Data, cmd->OutSize, 131 | cmd->Data, cmd->InSize); 132 | 133 | WdfWaitLockRelease(pDevice->EcLock); 134 | 135 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL, 136 | "%!FUNC! Request 0x%p Command %u Version %u OutSize %u Result %d", Request, cmd->Command, 137 | cmd->Version, cmd->OutSize, res); 138 | 139 | if (res < -EECRESULT) { 140 | // Propagate a response code from the EC as res (EC result codes are positive) 141 | cmd->Result = (-res) - EECRESULT; 142 | res = 0; // tell the client we received nothing 143 | } 144 | else if (res < 0) { 145 | // Transform the protocol failure into an NTSTATUS and return early. 146 | NT_RETURN_IF(STATUS_FAIL_CHECK, -res > EC_RES_DUP_UNAVAILABLE); 147 | return sCrosECErrorCodeMapping[-res]; 148 | } 149 | else { 150 | cmd->Result = 0; // 0 = SUCCESS 151 | } 152 | 153 | int requiredReplySize = sizeof(CROSEC_COMMAND) + res; 154 | if (requiredReplySize > outLen) { 155 | return STATUS_BUFFER_TOO_SMALL; 156 | } 157 | 158 | WdfRequestSetInformation(Request, requiredReplySize); 159 | return STATUS_SUCCESS; 160 | } 161 | 162 | NTSTATUS CrosECIoctlReadMem(_In_ WDFREQUEST Request) { 163 | PCROSEC_READMEM rq, rs; 164 | NT_RETURN_IF_NTSTATUS_FAILED(WdfRequestRetrieveInputBuffer(Request, sizeof(*rq), (PVOID*)&rq, NULL)); 165 | NT_RETURN_IF_NTSTATUS_FAILED(WdfRequestRetrieveOutputBuffer(Request, sizeof(*rs), (PVOID*)&rs, NULL)); 166 | 167 | NT_RETURN_IF(STATUS_INVALID_ADDRESS, (rq->offset + rq->bytes) > CROSEC_MEMMAP_SIZE); 168 | 169 | int res = ec_readmem(rq->offset, rq->bytes, rs->buffer); 170 | 171 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%!FUNC! Request 0x%p Offset 0x%x Buffer %d Result %d", 172 | Request, rq->offset, rq->bytes, res); 173 | 174 | NT_RETURN_IF(STATUS_UNSUCCESSFUL, res < 0); 175 | 176 | rs->offset = rq->offset; 177 | rs->bytes = res; 178 | 179 | WdfRequestSetInformation(Request, sizeof(*rs)); 180 | return STATUS_SUCCESS; 181 | } 182 | 183 | VOID CrosECEvtIoDeviceControl(_In_ WDFQUEUE Queue, 184 | _In_ WDFREQUEST Request, 185 | _In_ size_t OutputBufferLength, 186 | _In_ size_t InputBufferLength, 187 | _In_ ULONG IoControlCode) { 188 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 189 | "%!FUNC! Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d", 190 | Queue, Request, (int)OutputBufferLength, (int)InputBufferLength, IoControlCode); 191 | UNREFERENCED_PARAMETER(InputBufferLength); 192 | UNREFERENCED_PARAMETER(OutputBufferLength); 193 | 194 | WDFDEVICE device = WdfIoQueueGetDevice(Queue); 195 | PCROSECBUS_CONTEXT deviceContext = GetDeviceContext(device); 196 | NTSTATUS Status = STATUS_INVALID_PARAMETER; 197 | 198 | switch (IoControlCode) { 199 | case IOCTL_CROSEC_XCMD: { 200 | Status = CrosECIoctlXCmd(deviceContext, Request); 201 | break; 202 | } 203 | case IOCTL_CROSEC_RDMEM: { 204 | Status = CrosECIoctlReadMem(Request); 205 | break; 206 | } 207 | } 208 | 209 | WdfRequestComplete(Request, Status); 210 | 211 | return; 212 | } 213 | 214 | VOID CrosECEvtIoStop(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request, _In_ ULONG ActionFlags) { 215 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, "%!FUNC! Queue 0x%p, Request 0x%p ActionFlags %d", Queue, 216 | Request, ActionFlags); 217 | 218 | UNREFERENCED_PARAMETER(Queue); 219 | UNREFERENCED_PARAMETER(Request); 220 | UNREFERENCED_PARAMETER(ActionFlags); 221 | 222 | // 223 | // In most cases, the EvtIoStop callback function completes, cancels, or postpones 224 | // further processing of the I/O request. 225 | // 226 | // Typically, the driver uses the following rules: 227 | // 228 | // - If the driver owns the I/O request, it calls WdfRequestUnmarkCancelable 229 | // (if the request is cancelable) and either calls WdfRequestStopAcknowledge 230 | // with a Requeue value of TRUE, or it calls WdfRequestComplete with a 231 | // completion status value of STATUS_SUCCESS or STATUS_CANCELLED. 232 | // 233 | // Before it can call these methods safely, the driver must make sure that 234 | // its implementation of EvtIoStop has exclusive access to the request. 235 | // 236 | // In order to do that, the driver must synchronize access to the request 237 | // to prevent other threads from manipulating the request concurrently. 238 | // The synchronization method you choose will depend on your driver's design. 239 | // 240 | // For example, if the request is held in a shared context, the EvtIoStop callback 241 | // might acquire an internal driver lock, take the request from the shared context, 242 | // and then release the lock. At this point, the EvtIoStop callback owns the request 243 | // and can safely complete or requeue the request. 244 | // 245 | // - If the driver has forwarded the I/O request to an I/O target, it either calls 246 | // WdfRequestCancelSentRequest to attempt to cancel the request, or it postpones 247 | // further processing of the request and calls WdfRequestStopAcknowledge with 248 | // a Requeue value of FALSE. 249 | // 250 | // A driver might choose to take no action in EvtIoStop for requests that are 251 | // guaranteed to complete in a small amount of time. 252 | // 253 | // In this case, the framework waits until the specified request is complete 254 | // before moving the device (or system) to a lower power state or removing the device. 255 | // Potentially, this inaction can prevent a system from entering its hibernation state 256 | // or another low system power state. In extreme cases, it can cause the system 257 | // to crash with bugcheck code 9F. 258 | // 259 | 260 | return; 261 | } -------------------------------------------------------------------------------- /crosecbus/comm-lpc.c: -------------------------------------------------------------------------------- 1 | #include "driver.h" 2 | #include "comm-host.h" 3 | 4 | static __inline void outb(unsigned char __val, unsigned int __port) { 5 | WRITE_PORT_UCHAR((PUCHAR)__port, __val); 6 | } 7 | 8 | static __inline void outw(unsigned short __val, unsigned int __port) { 9 | WRITE_PORT_USHORT((PUSHORT)__port, __val); 10 | } 11 | 12 | static __inline unsigned char inb(unsigned int __port) { 13 | return READ_PORT_UCHAR((PUCHAR)__port); 14 | } 15 | 16 | static __inline unsigned short inw(unsigned int __port) { 17 | return READ_PORT_USHORT((PUSHORT)__port); 18 | } 19 | 20 | 21 | #define INITIAL_UDELAY 5 /* 5 us */ 22 | #define MAXIMUM_UDELAY 10000 /* 10 ms */ 23 | 24 | static ULONG CrosEcBusDebugLevel = 100; 25 | static ULONG CrosEcBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 26 | 27 | UINT32 ec_max_outsize, ec_max_insize; 28 | 29 | lpc_driver_ops ec_lpc_ops = {0}; 30 | 31 | int (*ec_command_proto)(UINT16 command, UINT8 version, 32 | const void* outdata, int outsize, 33 | void* indata, int insize); 34 | int (*ec_readmem)(int offset, int bytes, void* dest); 35 | 36 | /* 37 | * Wait for the EC to be unbusy. Returns 0 if unbusy, non-zero if 38 | * timeout. 39 | */ 40 | int wait_for_ec(int status_addr, int timeout_usec) 41 | { 42 | LARGE_INTEGER StartTime; 43 | KeQuerySystemTimePrecise(&StartTime); 44 | 45 | /* 46 | * Delay first, in case we just sent out a command but the EC 47 | * hasn't raised the busy flag. However, I think this doesn't 48 | * happen since the LPC commands are executed in order and the 49 | * busy flag is set by hardware. Minor issue in any case, 50 | * since the initial delay is very short. 51 | */ 52 | 53 | LARGE_INTEGER WaitInterval; 54 | WaitInterval.QuadPart = -10 * 200; 55 | KeDelayExecutionThread(KernelMode, false, &WaitInterval); 56 | 57 | while (true) { 58 | LARGE_INTEGER CurrentTime; 59 | KeQuerySystemTimePrecise(&CurrentTime); 60 | 61 | if (CurrentTime.QuadPart > StartTime.QuadPart + 10 * (LONGLONG)timeout_usec) 62 | break; 63 | 64 | if (!(inb(status_addr) & EC_LPC_STATUS_BUSY_MASK)) 65 | return 0; 66 | 67 | WaitInterval.QuadPart = -10 * 100; 68 | KeDelayExecutionThread(KernelMode, false, &WaitInterval); 69 | } 70 | return -1; /* Timeout */ 71 | } 72 | 73 | static int ec_command_lpc(UINT16 command, UINT8 version, 74 | const void* outdata, int outsize, 75 | void* indata, int insize) 76 | { 77 | struct ec_lpc_host_args args; 78 | const UINT8* d; 79 | UINT8* dout; 80 | int csum; 81 | int i; 82 | 83 | /* Fill in args */ 84 | args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 85 | args.command_version = version; 86 | args.data_size = (UINT8)outsize; 87 | 88 | /* Initialize checksum */ 89 | csum = command + args.flags + args.command_version + args.data_size; 90 | 91 | /* Write data and update checksum */ 92 | for (i = 0, d = (UINT8*)outdata; i < outsize; i++, d++) { 93 | outb(*d, EC_LPC_ADDR_HOST_PARAM + i); 94 | csum += *d; 95 | } 96 | 97 | /* Finalize checksum and write args */ 98 | args.checksum = (UINT8)csum; 99 | for (i = 0, d = (const UINT8*)&args; i < sizeof(args); i++, d++) 100 | outb(*d, EC_LPC_ADDR_HOST_ARGS + i); 101 | 102 | outb((UINT8)command, EC_LPC_ADDR_HOST_CMD); 103 | 104 | if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) { 105 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 106 | "Timeout waiting for EC response\n"); 107 | return -EC_RES_ERROR; 108 | } 109 | 110 | /* Check result */ 111 | i = inb(EC_LPC_ADDR_HOST_DATA); 112 | if (i) { 113 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 114 | "EC returned error result code %d\n", i); 115 | return -EECRESULT - i; 116 | } 117 | 118 | /* Read back args */ 119 | for (i = 0, dout = (UINT8*)&args; i < sizeof(args); i++, dout++) 120 | *dout = inb(EC_LPC_ADDR_HOST_ARGS + i); 121 | 122 | /* 123 | * If EC didn't modify args flags, then somehow we sent a new-style 124 | * command to an old EC, which means it would have read its params 125 | * from the wrong place. 126 | */ 127 | if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { 128 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 129 | "EC protocol mismatch\n"); 130 | return -EC_RES_INVALID_RESPONSE; 131 | } 132 | 133 | if (args.data_size > insize) { 134 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 135 | "EC returned too much data\n"); 136 | return -EC_RES_INVALID_RESPONSE; 137 | } 138 | 139 | /* Start calculating response checksum */ 140 | csum = command + args.flags + args.command_version + args.data_size; 141 | 142 | /* Read response and update checksum */ 143 | for (i = 0, dout = (UINT8*)indata; i < args.data_size; 144 | i++, dout++) { 145 | *dout = inb(EC_LPC_ADDR_HOST_PARAM + i); 146 | csum += *dout; 147 | } 148 | 149 | /* Verify checksum */ 150 | if (args.checksum != (UINT8)csum) { 151 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 152 | "EC response has invalid checksum\n"); 153 | return -EC_RES_INVALID_CHECKSUM; 154 | } 155 | 156 | /* Return actual amount of data received */ 157 | return args.data_size; 158 | } 159 | 160 | int ec_lpc_read_bytes(unsigned int offset, unsigned int length, UINT8* dest) { 161 | int sum = 0; 162 | unsigned int i; 163 | for (i = 0; i < length; ++i) { 164 | dest[i] = inb(offset + i); 165 | sum += dest[i]; 166 | } 167 | 168 | /* Return checksum of all bytes read */ 169 | return sum; 170 | } 171 | 172 | int ec_lpc_write_bytes(unsigned int offset, unsigned int length, const UINT8* msg) { 173 | int sum = 0; 174 | unsigned int i; 175 | for (i = 0; i < length; ++i) { 176 | outb(msg[i], offset + i); 177 | sum += msg[i]; 178 | } 179 | 180 | /* Return checksum of all bytes written */ 181 | return sum; 182 | } 183 | 184 | static int ec_command_lpc_3(UINT16 command, UINT8 version, 185 | const void* outdata, int outsize, 186 | void* indata, int insize) 187 | { 188 | struct ec_host_request rq; 189 | struct ec_host_response rs; 190 | const UINT8* d; 191 | UINT8* dout; 192 | int csum = 0; 193 | int i; 194 | 195 | /* Fail if output size is too big */ 196 | if (outsize + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) 197 | return -EC_RES_REQUEST_TRUNCATED; 198 | 199 | /* Fill in request packet */ 200 | /* TODO(crosbug.com/p/23825): This should be common to all protocols */ 201 | rq.struct_version = EC_HOST_REQUEST_VERSION; 202 | rq.checksum = 0; 203 | rq.command = command; 204 | rq.command_version = version; 205 | rq.reserved = 0; 206 | rq.data_len = (UINT16)outsize; 207 | 208 | /* Copy data and update checksum */ 209 | ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET + sizeof(rq), outsize, outdata); 210 | for (i = 0, d = (const UINT8*)outdata; i < outsize; i++, d++) { 211 | csum += *d; 212 | } 213 | 214 | /* Finish checksum */ 215 | for (i = 0, d = (const UINT8*)&rq; i < sizeof(rq); i++, d++) 216 | csum += *d; 217 | 218 | /* Write checksum field so the entire packet sums to 0 */ 219 | rq.checksum = (UINT8)(-csum); 220 | 221 | /* Copy header */ 222 | ec_lpc_ops.write(EC_LPC_ADDR_HOST_PACKET, sizeof(rq), (const UINT8 *)&rq); 223 | 224 | /* Start the command */ 225 | outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); 226 | 227 | if (wait_for_ec(EC_LPC_ADDR_HOST_CMD, 1000000)) { 228 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 229 | "Timeout waiting for EC response\n"); 230 | return -EC_RES_ERROR; 231 | } 232 | 233 | /* Check result */ 234 | i = inb(EC_LPC_ADDR_HOST_DATA); 235 | if (i) { 236 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 237 | "EC returned error result code %d\n", i); 238 | return -EECRESULT - i; 239 | } 240 | 241 | /* Read back response header and start checksum */ 242 | ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET, sizeof(rs), (UINT8*)&rs); 243 | csum = 0; 244 | for (i = 0, dout = (UINT8*)&rs; i < sizeof(rs); i++, dout++) { 245 | csum += *dout; 246 | } 247 | 248 | if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { 249 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 250 | "EC response version mismatch\n"); 251 | return -EC_RES_INVALID_RESPONSE; 252 | } 253 | 254 | if (rs.reserved) { 255 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 256 | "EC response reserved != 0\n"); 257 | return -EC_RES_INVALID_RESPONSE; 258 | } 259 | 260 | if (rs.data_len > insize) { 261 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 262 | "EC returned too much data\n"); 263 | return -EC_RES_RESPONSE_TOO_BIG; 264 | } 265 | 266 | /* Read back data and update checksum */ 267 | ec_lpc_ops.read(EC_LPC_ADDR_HOST_PACKET + sizeof(rs), rs.data_len, indata); 268 | for (i = 0, dout = (UINT8*)indata; i < rs.data_len; i++, dout++) { 269 | csum += *dout; 270 | } 271 | 272 | /* Verify checksum */ 273 | if ((UINT8)csum) { 274 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_IOCTL, 275 | "EC response has invalid checksum\n"); 276 | return -EC_RES_INVALID_CHECKSUM; 277 | } 278 | 279 | /* Return actual amount of data received */ 280 | return rs.data_len; 281 | } 282 | 283 | static int ec_readmem_lpc(int offset, int bytes, void* dest) 284 | { 285 | int i = offset; 286 | UINT8* s = (UINT8*)(dest); 287 | int cnt = 0; 288 | 289 | if (offset >= EC_MEMMAP_SIZE - bytes) 290 | return -1; 291 | 292 | if (bytes) { /* fixed length */ 293 | ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, bytes, dest); 294 | cnt = bytes; 295 | } 296 | else { /* string */ 297 | for (; i < EC_MEMMAP_SIZE; i++, s++) { 298 | ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + i, 1, s); 299 | cnt++; 300 | if (!*s) 301 | break; 302 | } 303 | } 304 | 305 | return cnt; 306 | } 307 | 308 | int comm_init_lpc_mec(void); 309 | 310 | NTSTATUS comm_init_lpc(void) 311 | { 312 | int i; 313 | int byte = 0xff; 314 | 315 | /* 316 | * Test if the I/O port has been configured for Chromium EC LPC 317 | * interface. Chromium EC guarantees that at least one status bit will 318 | * be 0, so if the command and data bytes are both 0xff, very likely 319 | * that Chromium EC is not present. See crosbug.com/p/10963. 320 | */ 321 | byte &= inb(EC_LPC_ADDR_HOST_CMD); 322 | byte &= inb(EC_LPC_ADDR_HOST_DATA); 323 | if (byte == 0xff) { 324 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, 325 | "Port 0x%x,0x%x are both 0xFF.\n", 326 | EC_LPC_ADDR_HOST_CMD, EC_LPC_ADDR_HOST_DATA); 327 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, 328 | "Very likely this board doesn't have a Chromium EC.\n"); 329 | return STATUS_CONNECTION_INVALID; 330 | } 331 | 332 | /* All EC's supports reading mapped memory directly. */ 333 | ec_readmem = ec_readmem_lpc; 334 | 335 | UINT8 signature[2]; 336 | 337 | /* Check for a MEC first. */ 338 | if (comm_init_lpc_mec && NT_SUCCESS(comm_init_lpc_mec())) { 339 | ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, signature); 340 | if (signature[0] == 'E' && signature[1] == 'C') { 341 | ec_max_outsize = EC_LPC_HOST_PACKET_SIZE - 342 | sizeof(struct ec_host_request); 343 | ec_max_insize = EC_LPC_HOST_PACKET_SIZE - 344 | sizeof(struct ec_host_response); 345 | 346 | //All MEC EC's are Protocol V3 347 | ec_command_proto = ec_command_lpc_3; 348 | 349 | DbgPrint("MEC EC\n"); 350 | return STATUS_SUCCESS; 351 | } 352 | } 353 | 354 | /* 355 | * Test if LPC command args are supported. 356 | * 357 | * The cheapest way to do this is by looking for the memory-mapped 358 | * flag. This is faster than sending a new-style 'hello' command and 359 | * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag 360 | * in args when it responds. 361 | */ 362 | 363 | ec_lpc_ops.read = ec_lpc_read_bytes; 364 | ec_lpc_ops.write = ec_lpc_write_bytes; 365 | ec_lpc_ops.read(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, signature); 366 | if (signature[0] != 'E' || signature[1] != 'C') { 367 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, 368 | "Missing Chromium EC memory map.\n"); 369 | return STATUS_NO_MEMORY; 370 | } 371 | 372 | /* Check which command version we'll use */ 373 | i = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS); 374 | 375 | if (i & EC_HOST_CMD_FLAG_VERSION_3) { 376 | /* Protocol version 3 */ 377 | ec_command_proto = ec_command_lpc_3; 378 | ec_max_outsize = EC_LPC_HOST_PACKET_SIZE - 379 | sizeof(struct ec_host_request); 380 | ec_max_insize = EC_LPC_HOST_PACKET_SIZE - 381 | sizeof(struct ec_host_response); 382 | 383 | DbgPrint("Ver 3\n"); 384 | 385 | } 386 | else if (i & EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED) { 387 | /* Protocol version 2 */ 388 | ec_command_proto = ec_command_lpc; 389 | ec_max_outsize = ec_max_insize = EC_PROTO2_MAX_PARAM_SIZE; 390 | 391 | DbgPrint("Ver 2\n"); 392 | } 393 | else { 394 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, 395 | "EC doesn't support protocols we need.\n"); 396 | return STATUS_INVALID_DEVICE_STATE; 397 | } 398 | return STATUS_SUCCESS; 399 | } 400 | -------------------------------------------------------------------------------- /crosecbus/crosecbus.c: -------------------------------------------------------------------------------- 1 | #define DESCRIPTOR_DEF 2 | #include "driver.h" 3 | #pragma warning(disable:4005) 4 | #pragma warning(disable:4083) 5 | #include 6 | #include "comm-host.h" 7 | #include "userspaceQueue.h" 8 | 9 | #define bool int 10 | #define MS_IN_US 1000 11 | 12 | void CrosEcBusSyncTimer(_In_ WDFTIMER hTimer); 13 | 14 | VOID 15 | CrosEcBusS0ixNotifyCallback( 16 | PCROSECBUS_CONTEXT pDevice, 17 | ULONG NotifyCode); 18 | 19 | static ULONG CrosEcBusDebugLevel = 100; 20 | static ULONG CrosEcBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; 21 | 22 | NTSTATUS comm_init_lpc(void); 23 | 24 | NTSTATUS 25 | DriverEntry( 26 | __in PDRIVER_OBJECT DriverObject, 27 | __in PUNICODE_STRING RegistryPath 28 | ) 29 | { 30 | NTSTATUS status = STATUS_SUCCESS; 31 | WDF_DRIVER_CONFIG config; 32 | WDF_OBJECT_ATTRIBUTES attributes; 33 | 34 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, 35 | "Driver Entry\n"); 36 | 37 | WDF_DRIVER_CONFIG_INIT(&config, CrosEcBusEvtDeviceAdd); 38 | 39 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 40 | 41 | // 42 | // Create a framework driver object to represent our driver. 43 | // 44 | 45 | status = WdfDriverCreate(DriverObject, 46 | RegistryPath, 47 | &attributes, 48 | &config, 49 | WDF_NO_HANDLE 50 | ); 51 | 52 | if (!NT_SUCCESS(status)) 53 | { 54 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, 55 | "WdfDriverCreate failed with status 0x%x\n", status); 56 | } 57 | 58 | return status; 59 | } 60 | 61 | static NTSTATUS CrosEcCmdXferStatus( 62 | IN PCROSECBUS_CONTEXT pDevice, 63 | OUT PCROSEC_COMMAND Msg 64 | ) 65 | { 66 | if (!Msg) { 67 | return STATUS_INVALID_PARAMETER; 68 | } 69 | 70 | if (ec_command_proto) { 71 | if (Msg->InSize > ec_max_insize) { 72 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL, "Clamping message receive buffer\n"); 73 | Msg->InSize = ec_max_insize; 74 | } 75 | 76 | if (Msg->OutSize > ec_max_outsize) { 77 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL, "request of size %u is too big (max: %u)\n", Msg->OutSize, ec_max_outsize); 78 | return STATUS_INVALID_PARAMETER_3; 79 | } 80 | 81 | InterlockedIncrement64(&pDevice->KernelAccessesWaiting); 82 | WdfWaitLockAcquire(pDevice->EcLock, NULL); 83 | 84 | int cmdstatus = ec_command_proto((UINT16)Msg->Command, (UINT8)Msg->Version, Msg->Data, Msg->OutSize, Msg->Data, Msg->InSize); 85 | 86 | InterlockedDecrement64(&pDevice->KernelAccessesWaiting); 87 | WdfWaitLockRelease(pDevice->EcLock); 88 | 89 | if (cmdstatus >= 0) { 90 | return STATUS_SUCCESS; 91 | } 92 | else { 93 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL, "EC Returned Error: %d\n", cmdstatus); 94 | return STATUS_INTERNAL_ERROR; 95 | } 96 | } 97 | else { 98 | return STATUS_NOINTERFACE; 99 | } 100 | } 101 | 102 | static BOOLEAN CrosEcCheckFeatures( 103 | IN PCROSECBUS_CONTEXT pDevice, 104 | IN INT Feature 105 | ) { 106 | if (pDevice->EcFeatures[0] == -1 && pDevice->EcFeatures[1] == -1) 107 | return false; //Failed to get features 108 | 109 | return !!(pDevice->EcFeatures[Feature / 32] & EC_FEATURE_MASK_0(Feature)); 110 | } 111 | 112 | static INT CrosEcReadMem( 113 | IN PCROSECBUS_CONTEXT pDevice, 114 | IN INT offset, 115 | IN INT bytes, 116 | OUT PVOID dest 117 | ) 118 | { 119 | UNREFERENCED_PARAMETER(pDevice); 120 | return ec_readmem(offset, bytes, dest); 121 | } 122 | 123 | NTSTATUS 124 | OnPrepareHardware( 125 | _In_ WDFDEVICE FxDevice, 126 | _In_ WDFCMRESLIST FxResourcesRaw, 127 | _In_ WDFCMRESLIST FxResourcesTranslated 128 | ) 129 | /*++ 130 | 131 | Routine Description: 132 | 133 | This routine caches the SPB resource connection ID. 134 | 135 | Arguments: 136 | 137 | FxDevice - a handle to the framework device object 138 | FxResourcesRaw - list of translated hardware resources that 139 | the PnP manager has assigned to the device 140 | FxResourcesTranslated - list of raw hardware resources that 141 | the PnP manager has assigned to the device 142 | 143 | Return Value: 144 | 145 | Status 146 | 147 | --*/ 148 | { 149 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(FxDevice); 150 | NTSTATUS status = STATUS_INSUFFICIENT_RESOURCES; 151 | 152 | UNREFERENCED_PARAMETER(FxResourcesRaw); 153 | 154 | pDevice->FoundSyncGPIO = FALSE; 155 | 156 | // 157 | // Parse the peripheral's resources. 158 | // 159 | 160 | ULONG resourceCount = WdfCmResourceListGetCount(FxResourcesTranslated); 161 | 162 | for (ULONG i = 0; i < resourceCount; i++) 163 | { 164 | PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; 165 | UCHAR Class; 166 | UCHAR Type; 167 | 168 | pDescriptor = WdfCmResourceListGetDescriptor( 169 | FxResourcesTranslated, i); 170 | 171 | switch (pDescriptor->Type) 172 | { 173 | case CmResourceTypeConnection: 174 | // 175 | // Look for I2C or SPI resource and save connection ID. 176 | // 177 | Class = pDescriptor->u.Connection.Class; 178 | Type = pDescriptor->u.Connection.Type; 179 | if (Class == CM_RESOURCE_CONNECTION_CLASS_GPIO && Type == CM_RESOURCE_CONNECTION_TYPE_GPIO_IO) { 180 | pDevice->SyncGpioContext.GpioResHubId.LowPart = pDescriptor->u.Connection.IdLowPart; 181 | pDevice->SyncGpioContext.GpioResHubId.HighPart = pDescriptor->u.Connection.IdHighPart; 182 | pDevice->FoundSyncGPIO = TRUE; 183 | } 184 | break; 185 | default: 186 | // 187 | // Ignoring all other resource types. 188 | // 189 | break; 190 | } 191 | } 192 | 193 | if (pDevice->FoundSyncGPIO) { 194 | DbgPrint("Found Sync GPIO! Increasing EC polling rate!\n"); 195 | 196 | status = GpioTargetInitialize(pDevice->FxDevice, &pDevice->SyncGpioContext); 197 | if (!NT_SUCCESS(status)) { 198 | return status; 199 | } 200 | } 201 | 202 | WDF_OBJECT_ATTRIBUTES attributes; 203 | WDF_TIMER_CONFIG timerConfig; 204 | WDFTIMER hTimer; 205 | 206 | WDF_TIMER_CONFIG_INIT_PERIODIC(&timerConfig, CrosEcBusSyncTimer, pDevice->FoundSyncGPIO ? 10 : 100); 207 | timerConfig.TolerableDelay = TolerableDelayUnlimited; //Don't wake from S0ix 208 | 209 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 210 | attributes.ParentObject = pDevice->FxDevice; 211 | status = WdfTimerCreate(&timerConfig, &attributes, &hTimer); 212 | pDevice->SyncGpioTimer = hTimer; 213 | if (!NT_SUCCESS(status)) 214 | { 215 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, "(%!FUNC!) WdfTimerCreate failed status:%!STATUS!\n", status); 216 | return status; 217 | } 218 | 219 | status = WdfWaitLockCreate( 220 | WDF_NO_OBJECT_ATTRIBUTES, 221 | &pDevice->EcLock); 222 | if (!NT_SUCCESS(status)) 223 | { 224 | CrosEcBusPrint( 225 | DEBUG_LEVEL_ERROR, 226 | DBG_IOCTL, 227 | "Error creating Waitlock - %x\n", 228 | status); 229 | return status; 230 | } 231 | 232 | status = comm_init_lpc(); 233 | if (!NT_SUCCESS(status)) { 234 | return status; 235 | } 236 | 237 | struct ec_response_get_version r = { 0 }; 238 | int rv = ec_command_proto(EC_CMD_GET_VERSION, 0, NULL, 0, &r, sizeof(struct ec_response_get_version)); 239 | if (rv >= 0) { 240 | /* Ensure versions are null-terminated before we print them */ 241 | r.version_string_ro[sizeof(r.version_string_ro) - 1] = '\0'; 242 | r.version_string_rw[sizeof(r.version_string_rw) - 1] = '\0'; 243 | 244 | DbgPrint("EC RO Version: %s\n", r.version_string_ro); 245 | DbgPrint("EC RW Version: %s\n", r.version_string_rw); 246 | } 247 | else { 248 | DbgPrint("Error: Could not get version\n"); 249 | return STATUS_DEVICE_CONFIGURATION_ERROR; 250 | } 251 | 252 | struct ec_response_get_features f = { 0 }; 253 | 254 | rv = ec_command_proto(EC_CMD_GET_FEATURES, 0, NULL, 0, &f, sizeof(struct ec_response_get_features)); 255 | if (rv >= 0) { 256 | pDevice->EcFeatures[0] = f.flags[0]; 257 | pDevice->EcFeatures[1] = f.flags[1]; 258 | 259 | DbgPrint("EC Features: %08x %08x\n", pDevice->EcFeatures[0], pDevice->EcFeatures[1]); 260 | } 261 | else { 262 | DbgPrint("Warning: Couldn't get device features\n"); 263 | pDevice->EcFeatures[0] = (UINT32)-1; 264 | pDevice->EcFeatures[1] = (UINT32)-1; 265 | } 266 | 267 | pDevice->hostSleepV1 = FALSE; 268 | 269 | NTSTATUS acpiNotifyStatus = WdfFdoQueryForInterface(FxDevice, 270 | &GUID_ACPI_INTERFACE_STANDARD2, 271 | (PINTERFACE)&pDevice->S0ixNotifyAcpiInterface, 272 | sizeof(ACPI_INTERFACE_STANDARD2), 273 | 1, 274 | NULL); 275 | 276 | if (NT_SUCCESS(acpiNotifyStatus)) { 277 | struct ec_params_get_cmd_versions_v1 req_v1 = { 0 }; 278 | struct ec_response_get_cmd_versions resp = { 0 }; 279 | req_v1.cmd = EC_CMD_HOST_SLEEP_EVENT; 280 | rv = ec_command_proto(EC_CMD_GET_CMD_VERSIONS, 1, &req_v1, sizeof(req_v1), &resp, sizeof(resp)); 281 | if (rv >= 0) { 282 | pDevice->hostSleepV1 = (resp.version_mask & EC_VER_MASK(1)) != 0; 283 | } 284 | 285 | acpiNotifyStatus = pDevice->S0ixNotifyAcpiInterface.RegisterForDeviceNotifications( 286 | pDevice->S0ixNotifyAcpiInterface.Context, 287 | (PDEVICE_NOTIFY_CALLBACK2)CrosEcBusS0ixNotifyCallback, 288 | pDevice); 289 | if (!NT_SUCCESS(acpiNotifyStatus)) { 290 | DbgPrint("Warning: Failed to register notifications on ACPI device\n"); 291 | } 292 | } 293 | else { 294 | DbgPrint("Warning: Failed to get ACPI device\n"); 295 | } 296 | 297 | return status; 298 | } 299 | 300 | NTSTATUS 301 | OnSelfManagedIoInit( 302 | _In_ 303 | WDFDEVICE FxDevice 304 | ) { 305 | PCROSECBUS_CONTEXT pDevice; 306 | pDevice = GetDeviceContext(FxDevice); 307 | 308 | NTSTATUS status = STATUS_SUCCESS; 309 | 310 | // CS Keyboard Callback 311 | 312 | UNICODE_STRING CSKeyboardSettingsCallbackAPI; 313 | RtlInitUnicodeString(&CSKeyboardSettingsCallbackAPI, L"\\CallBack\\CsKeyboardSettingsCallbackAPI"); 314 | 315 | 316 | OBJECT_ATTRIBUTES attributes; 317 | InitializeObjectAttributes(&attributes, 318 | &CSKeyboardSettingsCallbackAPI, 319 | OBJ_KERNEL_HANDLE | OBJ_OPENIF | OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, 320 | NULL, 321 | NULL 322 | ); 323 | status = ExCreateCallback(&pDevice->CSButtonsCallback, &attributes, TRUE, TRUE); 324 | if (!NT_SUCCESS(status)) { 325 | 326 | return status; 327 | } 328 | 329 | return status; 330 | } 331 | 332 | NTSTATUS 333 | OnReleaseHardware( 334 | _In_ WDFDEVICE FxDevice, 335 | _In_ WDFCMRESLIST FxResourcesTranslated 336 | ) 337 | /*++ 338 | 339 | Routine Description: 340 | 341 | Arguments: 342 | 343 | FxDevice - a handle to the framework device object 344 | FxResourcesTranslated - list of raw hardware resources that 345 | the PnP manager has assigned to the device 346 | 347 | Return Value: 348 | 349 | Status 350 | 351 | --*/ 352 | { 353 | NTSTATUS status = STATUS_SUCCESS; 354 | 355 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(FxDevice); 356 | UNREFERENCED_PARAMETER(FxResourcesTranslated); 357 | 358 | if (pDevice->FoundSyncGPIO) { 359 | GpioTargetDeinitialize(pDevice->FxDevice, &pDevice->SyncGpioContext); 360 | } 361 | 362 | if (pDevice->S0ixNotifyAcpiInterface.Context) { //Used for S0ix notifications 363 | pDevice->S0ixNotifyAcpiInterface.UnregisterForDeviceNotifications(pDevice->S0ixNotifyAcpiInterface.Context); 364 | } 365 | 366 | if (pDevice->EcLock != NULL) 367 | { 368 | WdfObjectDelete(pDevice->EcLock); 369 | } 370 | 371 | if (pDevice->CSButtonsCallback) { 372 | ObfDereferenceObject(pDevice->CSButtonsCallback); 373 | pDevice->CSButtonsCallback = NULL; 374 | } 375 | 376 | return status; 377 | } 378 | 379 | static NTSTATUS send_ec_command( 380 | _In_ PCROSECBUS_CONTEXT pDevice, 381 | UINT32 cmd, 382 | UINT32 version, 383 | UINT8* out, 384 | size_t outSize, 385 | UINT8* in, 386 | size_t inSize) 387 | { 388 | PCROSEC_COMMAND msg = (PCROSEC_COMMAND)ExAllocatePoolWithTag(NonPagedPool, sizeof(CROSEC_COMMAND) + max(outSize, inSize), CROSECBUS_POOL_TAG); 389 | if (!msg) { 390 | return STATUS_NO_MEMORY; 391 | } 392 | msg->Version = version; 393 | msg->Command = cmd; 394 | msg->OutSize = (UINT32)outSize; 395 | msg->InSize = (UINT32)inSize; 396 | 397 | if (outSize) 398 | memcpy(msg->Data, out, outSize); 399 | 400 | NTSTATUS status = CrosEcCmdXferStatus(pDevice, msg); 401 | if (!NT_SUCCESS(status)) { 402 | goto exit; 403 | } 404 | 405 | if (in && inSize) { 406 | memcpy(in, msg->Data, inSize); 407 | } 408 | 409 | exit: 410 | ExFreePoolWithTag(msg, CROSECBUS_POOL_TAG); 411 | return status; 412 | } 413 | 414 | NTSTATUS 415 | OnD0Entry( 416 | _In_ WDFDEVICE FxDevice, 417 | _In_ WDF_POWER_DEVICE_STATE FxPreviousState 418 | ) 419 | /*++ 420 | 421 | Routine Description: 422 | 423 | This routine allocates objects needed by the driver. 424 | 425 | Arguments: 426 | 427 | FxDevice - a handle to the framework device object 428 | FxPreviousState - previous power state 429 | 430 | Return Value: 431 | 432 | Status 433 | 434 | --*/ 435 | { 436 | UNREFERENCED_PARAMETER(FxPreviousState); 437 | NTSTATUS status = STATUS_SUCCESS; 438 | 439 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(FxDevice); 440 | 441 | struct ec_params_motion_sense params = { 0 }; 442 | struct ec_response_motion_sense resp; 443 | 444 | params.cmd = MOTIONSENSE_CMD_FIFO_INT_ENABLE; 445 | params.fifo_int_enable.enable = 0; 446 | 447 | send_ec_command(pDevice, EC_CMD_MOTION_SENSE_CMD, 1, (UINT8*)¶ms, sizeof(params), (UINT8*)&resp, sizeof(resp)); //Ignore response as device may not have sensors 448 | 449 | WdfTimerStart(pDevice->SyncGpioTimer, WDF_REL_TIMEOUT_IN_MS(pDevice->FoundSyncGPIO ? 10 : 100)); 450 | 451 | DbgPrint("D0 Entry\n"); 452 | 453 | return status; 454 | } 455 | 456 | NTSTATUS 457 | OnD0Exit( 458 | _In_ WDFDEVICE FxDevice, 459 | _In_ WDF_POWER_DEVICE_STATE FxTargetState 460 | ) 461 | /*++ 462 | 463 | Routine Description: 464 | 465 | This routine destroys objects needed by the driver. 466 | 467 | Arguments: 468 | 469 | FxDevice - a handle to the framework device object 470 | FxTargetState - target power state 471 | 472 | Return Value: 473 | 474 | Status 475 | 476 | --*/ 477 | { 478 | UNREFERENCED_PARAMETER(FxTargetState); 479 | 480 | NTSTATUS status = STATUS_SUCCESS; 481 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(FxDevice); 482 | 483 | if (pDevice->FoundSyncGPIO) { 484 | WdfTimerStop(pDevice->SyncGpioTimer, TRUE); 485 | WdfWorkItemFlush(pDevice->SyncGpioWorkItem); 486 | } 487 | 488 | return status; 489 | } 490 | 491 | NTSTATUS CrosEcBusSleepEvent( 492 | PCROSECBUS_CONTEXT pDevice, 493 | UINT8 sleepEvent 494 | ) { 495 | if (!pDevice->hostSleepV1) { 496 | DbgPrint("Warning: EC does not support S0ix!\n"); 497 | return STATUS_NOT_SUPPORTED; 498 | } 499 | 500 | struct ec_params_host_sleep_event_v1 req1 = { 0 }; 501 | struct ec_response_host_sleep_event_v1 resp1 = { 0 }; 502 | 503 | req1.sleep_event = sleepEvent; 504 | req1.suspend_params.sleep_timeout_ms = EC_HOST_SLEEP_TIMEOUT_DEFAULT; 505 | 506 | return send_ec_command(pDevice, EC_CMD_HOST_SLEEP_EVENT, 1, (UINT8 *)&req1, sizeof(req1), (UINT8 *)&resp1, sizeof(resp1)); 507 | } 508 | 509 | VOID 510 | CrosEcBusSyncWorkItem( 511 | IN WDFWORKITEM WorkItem 512 | ) 513 | { 514 | WDFDEVICE Device = (WDFDEVICE)WdfWorkItemGetParentObject(WorkItem); 515 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(Device); 516 | 517 | if (InterlockedExchange(&pDevice->SyncGpioWorkItemActive, 1) == 1) { 518 | return; 519 | } 520 | 521 | if (pDevice->FoundSyncGPIO) { 522 | UINT8 gpioData = 1; 523 | if (!NT_SUCCESS(GpioReadDataSynchronously(&pDevice->SyncGpioContext, &gpioData, sizeof(gpioData)))) { 524 | goto out; 525 | } 526 | 527 | if (gpioData != 0) { 528 | goto out; 529 | } 530 | } 531 | 532 | const uint32_t mkbp_mask = 533 | EC_HOST_EVENT_MASK(EC_HOST_EVENT_MKBP); 534 | struct ec_response_host_event_mask r; 535 | 536 | NTSTATUS status = send_ec_command(pDevice, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, (UINT8*)&r, sizeof(r)); 537 | if (!NT_SUCCESS(status)) { 538 | goto out; 539 | } 540 | 541 | if (r.mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) { 542 | goto out; 543 | } 544 | 545 | if (r.mask & mkbp_mask) { 546 | struct ec_params_host_event_mask p; 547 | p.mask = mkbp_mask; 548 | 549 | status = send_ec_command(pDevice, EC_CMD_HOST_EVENT_CLEAR_B, 0, (UINT8 *)&p, sizeof(p), NULL, 0); 550 | if (!NT_SUCCESS(status)) { 551 | goto out; 552 | } 553 | 554 | struct ec_response_get_next_event event = { 0 }; 555 | status = send_ec_command(pDevice, EC_CMD_GET_NEXT_EVENT, 0, NULL, 0, (UINT8 *)&event, sizeof(event)); 556 | if (!NT_SUCCESS(status)) { 557 | goto out; 558 | } 559 | 560 | if (event.event_type == EC_MKBP_EVENT_BUTTON) { 561 | if (pDevice->CSButtonsCallback) { 562 | CSVivaldiSettingsArg newArg; 563 | RtlZeroMemory(&newArg, sizeof(CSVivaldiSettingsArg)); 564 | newArg.argSz = sizeof(CSVivaldiSettingsArg); 565 | newArg.settingsRequest = CSVivaldiRequestUpdateButton; 566 | newArg.args.button.button = (UINT8)event.data.buttons; 567 | ExNotifyCallback(pDevice->CSButtonsCallback, &newArg, NULL); 568 | } 569 | } 570 | } 571 | out: 572 | InterlockedExchange(&pDevice->SyncGpioWorkItemActive, 0); 573 | return; 574 | } 575 | 576 | void CrosEcBusSyncTimer(_In_ WDFTIMER hTimer) { 577 | WDFDEVICE Device = (WDFDEVICE)WdfTimerGetParentObject(hTimer); 578 | PCROSECBUS_CONTEXT pDevice = GetDeviceContext(Device); 579 | 580 | WdfWorkItemEnqueue(pDevice->SyncGpioWorkItem); 581 | } 582 | 583 | VOID 584 | CrosEcBusS0ixNotifyCallback( 585 | PCROSECBUS_CONTEXT pDevice, 586 | ULONG NotifyCode) { 587 | if (NotifyCode == 2 && pDevice->isInS0ix) { 588 | if (NT_SUCCESS(CrosEcBusSleepEvent(pDevice, HOST_SLEEP_EVENT_S0IX_RESUME))) { 589 | pDevice->isInS0ix = FALSE; 590 | } 591 | } 592 | else if (NotifyCode == 1 && !pDevice->isInS0ix) { 593 | if (NT_SUCCESS(CrosEcBusSleepEvent(pDevice, HOST_SLEEP_EVENT_S0IX_SUSPEND))) { 594 | pDevice->isInS0ix = TRUE; 595 | } 596 | } 597 | } 598 | 599 | NTSTATUS 600 | CrosEcBusEvtDeviceAdd( 601 | IN WDFDRIVER Driver, 602 | IN PWDFDEVICE_INIT DeviceInit 603 | ) 604 | { 605 | NTSTATUS status = STATUS_SUCCESS; 606 | WDF_OBJECT_ATTRIBUTES attributes; 607 | WDFDEVICE device; 608 | PCROSECBUS_CONTEXT devContext; 609 | WDF_QUERY_INTERFACE_CONFIG qiConfig; 610 | 611 | UNREFERENCED_PARAMETER(Driver); 612 | 613 | PAGED_CODE(); 614 | 615 | CrosEcBusPrint(DEBUG_LEVEL_INFO, DBG_PNP, 616 | "CrosEcBusEvtDeviceAdd called\n"); 617 | 618 | { 619 | WDF_PNPPOWER_EVENT_CALLBACKS pnpCallbacks; 620 | WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpCallbacks); 621 | 622 | pnpCallbacks.EvtDevicePrepareHardware = OnPrepareHardware; 623 | pnpCallbacks.EvtDeviceReleaseHardware = OnReleaseHardware; 624 | pnpCallbacks.EvtDeviceSelfManagedIoInit = OnSelfManagedIoInit; 625 | pnpCallbacks.EvtDeviceD0Entry = OnD0Entry; 626 | pnpCallbacks.EvtDeviceD0Exit = OnD0Exit; 627 | 628 | WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpCallbacks); 629 | } 630 | 631 | { 632 | DECLARE_CONST_UNICODE_STRING(Name, NTDEVICE_NAME_STRING); 633 | status = WdfDeviceInitAssignName(DeviceInit, 634 | &Name 635 | ); 636 | if (!NT_SUCCESS(status)) { 637 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 638 | "WdfDeviceInitAssignName failed 0x%x\n", status); 639 | return status; 640 | } 641 | } 642 | 643 | // 644 | // Setup the device context 645 | // 646 | 647 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, CROSECBUS_CONTEXT); 648 | 649 | // Set DeviceType 650 | WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_CONTROLLER); 651 | 652 | // 653 | // Create a framework device object.This call will in turn create 654 | // a WDM device object, attach to the lower stack, and set the 655 | // appropriate flags and attributes. 656 | // 657 | 658 | status = WdfDeviceCreate(&DeviceInit, &attributes, &device); 659 | 660 | if (!NT_SUCCESS(status)) 661 | { 662 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 663 | "WdfDeviceCreate failed with status code 0x%x\n", status); 664 | 665 | return status; 666 | } 667 | 668 | { 669 | WDF_DEVICE_STATE deviceState; 670 | WDF_DEVICE_STATE_INIT(&deviceState); 671 | 672 | deviceState.NotDisableable = WdfFalse; 673 | WdfDeviceSetDeviceState(device, &deviceState); 674 | } 675 | 676 | status = CrosECQueueInitialize(device); 677 | if (!NT_SUCCESS(status)) { 678 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 679 | "CrosECQueueInitialize failed 0x%x\n", status); 680 | return status; 681 | } 682 | 683 | devContext = GetDeviceContext(device); 684 | 685 | DECLARE_CONST_UNICODE_STRING(dosDeviceName, SYMBOLIC_NAME_STRING); 686 | 687 | status = WdfDeviceCreateSymbolicLink(device, 688 | &dosDeviceName 689 | ); 690 | if (!NT_SUCCESS(status)) { 691 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 692 | "WdfDeviceCreateSymbolicLink failed 0x%x\n", status); 693 | return status; 694 | } 695 | 696 | WDF_WORKITEM_CONFIG workitemConfig; 697 | 698 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 699 | WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, CROSECBUS_CONTEXT); 700 | attributes.ParentObject = device; 701 | WDF_WORKITEM_CONFIG_INIT(&workitemConfig, CrosEcBusSyncWorkItem); 702 | 703 | WdfWorkItemCreate(&workitemConfig, 704 | &attributes, 705 | &devContext->SyncGpioWorkItem); 706 | 707 | { // V1 708 | CROSEC_INTERFACE_STANDARD CrosEcInterface; 709 | RtlZeroMemory(&CrosEcInterface, sizeof(CrosEcInterface)); 710 | 711 | CrosEcInterface.InterfaceHeader.Size = sizeof(CrosEcInterface); 712 | CrosEcInterface.InterfaceHeader.Version = 1; 713 | CrosEcInterface.InterfaceHeader.Context = (PVOID)devContext; 714 | 715 | // 716 | // Let the framework handle reference counting. 717 | // 718 | CrosEcInterface.InterfaceHeader.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; 719 | CrosEcInterface.InterfaceHeader.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; 720 | 721 | CrosEcInterface.CheckFeatures = CrosEcCheckFeatures; 722 | CrosEcInterface.CmdXferStatus = CrosEcCmdXferStatus; 723 | 724 | WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig, 725 | (PINTERFACE)&CrosEcInterface, 726 | &GUID_CROSEC_INTERFACE_STANDARD, 727 | NULL); 728 | 729 | status = WdfDeviceAddQueryInterface(device, &qiConfig); 730 | if (!NT_SUCCESS(status)) { 731 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 732 | "WdfDeviceAddQueryInterface failed 0x%x\n", status); 733 | 734 | return status; 735 | } 736 | } 737 | 738 | { // V2 739 | CROSEC_INTERFACE_STANDARD_V2 CrosEcInterface; 740 | RtlZeroMemory(&CrosEcInterface, sizeof(CrosEcInterface)); 741 | 742 | CrosEcInterface.InterfaceHeader.Size = sizeof(CrosEcInterface); 743 | CrosEcInterface.InterfaceHeader.Version = 2; 744 | CrosEcInterface.InterfaceHeader.Context = (PVOID)devContext; 745 | 746 | // 747 | // Let the framework handle reference counting. 748 | // 749 | CrosEcInterface.InterfaceHeader.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; 750 | CrosEcInterface.InterfaceHeader.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; 751 | 752 | CrosEcInterface.CheckFeatures = CrosEcCheckFeatures; 753 | CrosEcInterface.CmdXferStatus = CrosEcCmdXferStatus; 754 | CrosEcInterface.ReadEcMem = CrosEcReadMem; 755 | 756 | WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig, 757 | (PINTERFACE)&CrosEcInterface, 758 | &GUID_CROSEC_INTERFACE_STANDARD_V2, 759 | NULL); 760 | 761 | status = WdfDeviceAddQueryInterface(device, &qiConfig); 762 | if (!NT_SUCCESS(status)) { 763 | CrosEcBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, 764 | "WdfDeviceAddQueryInterface failed 0x%x\n", status); 765 | 766 | return status; 767 | } 768 | } 769 | 770 | devContext->KernelAccessesWaiting = 0; 771 | devContext->FxDevice = device; 772 | 773 | return status; 774 | } 775 | -------------------------------------------------------------------------------- /crosecbus/ec_commands.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. 2 | * Use of this source code is governed by a BSD-style license that can be 3 | * found in the LICENSE file. 4 | */ 5 | 6 | /* Host communication command constants for Chrome EC */ 7 | 8 | #ifndef __CROS_EC_COMMANDS_H 9 | #define __CROS_EC_COMMANDS_H 10 | 11 | #define BIT(nr) (1UL << (nr)) 12 | 13 | /* 14 | * Current version of this protocol 15 | * 16 | * TODO(crosbug.com/p/11223): This is effectively useless; protocol is 17 | * determined in other ways. Remove this once the kernel code no longer 18 | * depends on it. 19 | */ 20 | #define EC_PROTO_VERSION 0x00000002 21 | 22 | /* Command version mask */ 23 | #define EC_VER_MASK(version) (1UL << (version)) 24 | 25 | /* I/O addresses for ACPI commands */ 26 | #define EC_LPC_ADDR_ACPI_DATA 0x62 27 | #define EC_LPC_ADDR_ACPI_CMD 0x66 28 | 29 | /* I/O addresses for host command */ 30 | #define EC_LPC_ADDR_HOST_DATA 0x200 31 | #define EC_LPC_ADDR_HOST_CMD 0x204 32 | 33 | /* I/O addresses for host command args and params */ 34 | /* Protocol version 2 */ 35 | #define EC_LPC_ADDR_HOST_ARGS 0x800 /* And 0x801, 0x802, 0x803 */ 36 | #define EC_LPC_ADDR_HOST_PARAM 0x804 /* For version 2 params; size is 37 | * EC_PROTO2_MAX_PARAM_SIZE */ 38 | /* Protocol version 3 */ 39 | #define EC_LPC_ADDR_HOST_PACKET 0x800 /* Offset of version 3 packet */ 40 | #define EC_LPC_HOST_PACKET_SIZE 0x100 /* Max size of version 3 packet */ 41 | 42 | /* The actual block is 0x800-0x8ff, but some BIOSes think it's 0x880-0x8ff 43 | * and they tell the kernel that so we have to think of it as two parts. */ 44 | #define EC_HOST_CMD_REGION0 0x800 45 | #define EC_HOST_CMD_REGION1 0x880 46 | #define EC_HOST_CMD_REGION_SIZE 0x80 47 | 48 | /* EC command register bit functions */ 49 | #define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ 50 | #define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ 51 | #define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ 52 | #define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ 53 | #define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ 54 | #define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ 55 | #define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ 56 | 57 | #define EC_LPC_ADDR_MEMMAP 0x900 58 | #define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ 59 | #define EC_MEMMAP_TEXT_MAX 8 /* Size of a string in the memory map */ 60 | 61 | /* The offset address of each type of data in mapped memory. */ 62 | #define EC_MEMMAP_TEMP_SENSOR 0x00 /* Temp sensors 0x00 - 0x0f */ 63 | #define EC_MEMMAP_FAN 0x10 /* Fan speeds 0x10 - 0x17 */ 64 | #define EC_MEMMAP_TEMP_SENSOR_B 0x18 /* More temp sensors 0x18 - 0x1f */ 65 | #define EC_MEMMAP_ID 0x20 /* 0x20 == 'E', 0x21 == 'C' */ 66 | #define EC_MEMMAP_ID_VERSION 0x22 /* Version of data in 0x20 - 0x2f */ 67 | #define EC_MEMMAP_BATTERY_VERSION 0x24 /* Version of data in 0x40 - 0x7f */ 68 | #define EC_MEMMAP_HOST_CMD_FLAGS 0x27 /* Host cmd interface flags (8 bits) */ 69 | /* Unused 0x28 - 0x2f */ 70 | #define EC_MEMMAP_SWITCHES 0x30 /* 8 bits */ 71 | /* Reserve 0x38 - 0x3f for additional host event-related stuff */ 72 | /* Battery values are all 32 bits */ 73 | #define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ 74 | #define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ 75 | #define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ 76 | #define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ 77 | #define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ 78 | #define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ 79 | #define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ 80 | #define EC_MEMMAP_BATT_CCNT 0x5c /* Battery Cycle Count */ 81 | /* Strings are all 8 bytes (EC_MEMMAP_TEXT_MAX) */ 82 | #define EC_MEMMAP_BATT_MFGR 0x60 /* Battery Manufacturer String */ 83 | #define EC_MEMMAP_BATT_MODEL 0x68 /* Battery Model Number String */ 84 | #define EC_MEMMAP_BATT_SERIAL 0x70 /* Battery Serial Number String */ 85 | #define EC_MEMMAP_BATT_TYPE 0x78 /* Battery Type String */ 86 | /* Unused 0xa6 - 0xdf */ 87 | 88 | /* 89 | * ACPI is unable to access memory mapped data at or above this offset due to 90 | * limitations of the ACPI protocol. Do not place data in the range 0xe0 - 0xfe 91 | * which might be needed by ACPI. 92 | */ 93 | #define EC_MEMMAP_NO_ACPI 0xe0 94 | 95 | /* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ 96 | #define EC_BATT_FLAG_AC_PRESENT 0x01 97 | #define EC_BATT_FLAG_BATT_PRESENT 0x02 98 | #define EC_BATT_FLAG_DISCHARGING 0x04 99 | #define EC_BATT_FLAG_CHARGING 0x08 100 | #define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 101 | 102 | /* Switch flags at EC_MEMMAP_SWITCHES */ 103 | #define EC_SWITCH_LID_OPEN 0x01 104 | #define EC_SWITCH_POWER_BUTTON_PRESSED 0x02 105 | #define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 106 | /* Was recovery requested via keyboard; now unused. */ 107 | #define EC_SWITCH_IGNORE1 0x08 108 | /* Recovery requested via dedicated signal (from servo board) */ 109 | #define EC_SWITCH_DEDICATED_RECOVERY 0x10 110 | /* Was fake developer mode switch; now unused. Remove in next refactor. */ 111 | #define EC_SWITCH_IGNORE0 0x20 112 | 113 | /* Host command interface flags */ 114 | /* Host command interface supports LPC args (LPC interface only) */ 115 | #define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED 0x01 116 | /* Host command interface supports version 3 protocol */ 117 | #define EC_HOST_CMD_FLAG_VERSION_3 0x02 118 | 119 | /*****************************************************************************/ 120 | /* 121 | * ACPI commands 122 | * 123 | * These are valid ONLY on the ACPI command/data port. 124 | */ 125 | 126 | /* 127 | * ACPI Read Embedded Controller 128 | * 129 | * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). 130 | * 131 | * Use the following sequence: 132 | * 133 | * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD 134 | * - Wait for EC_LPC_CMDR_PENDING bit to clear 135 | * - Write address to EC_LPC_ADDR_ACPI_DATA 136 | * - Wait for EC_LPC_CMDR_DATA bit to set 137 | * - Read value from EC_LPC_ADDR_ACPI_DATA 138 | */ 139 | #define EC_CMD_ACPI_READ 0x80 140 | 141 | /* 142 | * ACPI Write Embedded Controller 143 | * 144 | * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). 145 | * 146 | * Use the following sequence: 147 | * 148 | * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD 149 | * - Wait for EC_LPC_CMDR_PENDING bit to clear 150 | * - Write address to EC_LPC_ADDR_ACPI_DATA 151 | * - Wait for EC_LPC_CMDR_PENDING bit to clear 152 | * - Write value to EC_LPC_ADDR_ACPI_DATA 153 | */ 154 | #define EC_CMD_ACPI_WRITE 0x81 155 | 156 | /* 157 | * ACPI Burst Enable Embedded Controller 158 | * 159 | * This enables burst mode on the EC to allow the host to issue several 160 | * commands back-to-back. While in this mode, writes to mapped multi-byte 161 | * data are locked out to ensure data consistency. 162 | */ 163 | #define EC_CMD_ACPI_BURST_ENABLE 0x82 164 | 165 | /* 166 | * ACPI Burst Disable Embedded Controller 167 | * 168 | * This disables burst mode on the EC and stops preventing EC writes to mapped 169 | * multi-byte data. 170 | */ 171 | #define EC_CMD_ACPI_BURST_DISABLE 0x83 172 | 173 | /* 174 | * ACPI Query Embedded Controller 175 | * 176 | * This clears the lowest-order bit in the currently pending host events, and 177 | * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, 178 | * event 0x80000000 = 32), or 0 if no event was pending. 179 | */ 180 | #define EC_CMD_ACPI_QUERY_EVENT 0x84 181 | 182 | /* Valid addresses in ACPI memory space, for read/write commands */ 183 | 184 | /* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ 185 | #define EC_ACPI_MEM_VERSION 0x00 186 | /* 187 | * Test location; writing value here updates test compliment byte to (0xff - 188 | * value). 189 | */ 190 | #define EC_ACPI_MEM_TEST 0x01 191 | /* Test compliment; writes here are ignored. */ 192 | #define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 193 | 194 | /* 195 | * ACPI addresses 0x20 - 0xff map to EC_MEMMAP offset 0x00 - 0xdf. This data 196 | * is read-only from the AP. Added in EC_ACPI_MEM_VERSION 2. 197 | */ 198 | #define EC_ACPI_MEM_MAPPED_BEGIN 0x20 199 | #define EC_ACPI_MEM_MAPPED_SIZE 0xe0 200 | 201 | /* Current version of ACPI memory address space */ 202 | #define EC_ACPI_MEM_VERSION_CURRENT 2 203 | 204 | 205 | /* 206 | * This header file is used in coreboot both in C and ACPI code. The ACPI code 207 | * is pre-processed to handle constants but the ASL compiler is unable to 208 | * handle actual C code so keep it separate. 209 | */ 210 | #ifndef __ACPI__ 211 | 212 | /* LPC command status byte masks */ 213 | /* EC has written a byte in the data register and host hasn't read it yet */ 214 | #define EC_LPC_STATUS_TO_HOST 0x01 215 | /* Host has written a command/data byte and the EC hasn't read it yet */ 216 | #define EC_LPC_STATUS_FROM_HOST 0x02 217 | /* EC is processing a command */ 218 | #define EC_LPC_STATUS_PROCESSING 0x04 219 | /* Last write to EC was a command, not data */ 220 | #define EC_LPC_STATUS_LAST_CMD 0x08 221 | /* EC is in burst mode */ 222 | #define EC_LPC_STATUS_BURST_MODE 0x10 223 | /* SCI event is pending (requesting SCI query) */ 224 | #define EC_LPC_STATUS_SCI_PENDING 0x20 225 | /* SMI event is pending (requesting SMI query) */ 226 | #define EC_LPC_STATUS_SMI_PENDING 0x40 227 | /* (reserved) */ 228 | #define EC_LPC_STATUS_RESERVED 0x80 229 | 230 | /* 231 | * EC is busy. This covers both the EC processing a command, and the host has 232 | * written a new command but the EC hasn't picked it up yet. 233 | */ 234 | #define EC_LPC_STATUS_BUSY_MASK \ 235 | (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) 236 | 237 | /* Host command response codes */ 238 | enum ec_status { 239 | EC_RES_SUCCESS = 0, 240 | EC_RES_INVALID_COMMAND = 1, 241 | EC_RES_ERROR = 2, 242 | EC_RES_INVALID_PARAM = 3, 243 | EC_RES_ACCESS_DENIED = 4, 244 | EC_RES_INVALID_RESPONSE = 5, 245 | EC_RES_INVALID_VERSION = 6, 246 | EC_RES_INVALID_CHECKSUM = 7, 247 | EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ 248 | EC_RES_UNAVAILABLE = 9, /* No response available */ 249 | EC_RES_TIMEOUT = 10, /* We got a timeout */ 250 | EC_RES_OVERFLOW = 11, /* Table / data overflow */ 251 | EC_RES_INVALID_HEADER = 12, /* Header contains invalid data */ 252 | EC_RES_REQUEST_TRUNCATED = 13, /* Didn't get the entire request */ 253 | EC_RES_RESPONSE_TOO_BIG = 14, /* Response was too big to handle */ 254 | EC_RES_BUS_ERROR = 15, /* Communications bus error */ 255 | EC_RES_BUSY = 16, /* Up but too busy. Should retry */ 256 | EC_RES_INVALID_HEADER_VERSION = 17, /* Header version invalid */ 257 | EC_RES_INVALID_HEADER_CRC = 18, /* Header CRC invalid */ 258 | EC_RES_INVALID_DATA_CRC = 19, /* Data CRC invalid */ 259 | EC_RES_DUP_UNAVAILABLE = 20 /* Can't resend response */ 260 | }; 261 | 262 | /* 263 | * Host event codes. Note these are 1-based, not 0-based, because ACPI query 264 | * EC command uses code 0 to mean "no event pending". We explicitly specify 265 | * each value in the enum listing so they won't change if we delete/insert an 266 | * item or rearrange the list (it needs to be stable across platforms, not 267 | * just within a single compiled instance). 268 | */ 269 | enum host_event_code { 270 | EC_HOST_EVENT_LID_CLOSED = 1, 271 | EC_HOST_EVENT_LID_OPEN = 2, 272 | EC_HOST_EVENT_POWER_BUTTON = 3, 273 | EC_HOST_EVENT_AC_CONNECTED = 4, 274 | EC_HOST_EVENT_AC_DISCONNECTED = 5, 275 | EC_HOST_EVENT_BATTERY_LOW = 6, 276 | EC_HOST_EVENT_BATTERY_CRITICAL = 7, 277 | EC_HOST_EVENT_BATTERY = 8, 278 | EC_HOST_EVENT_THERMAL_THRESHOLD = 9, 279 | EC_HOST_EVENT_THERMAL_OVERLOAD = 10, 280 | EC_HOST_EVENT_THERMAL = 11, 281 | EC_HOST_EVENT_USB_CHARGER = 12, 282 | EC_HOST_EVENT_KEY_PRESSED = 13, 283 | /* 284 | * EC has finished initializing the host interface. The host can check 285 | * for this event following sending a EC_CMD_REBOOT_EC command to 286 | * determine when the EC is ready to accept subsequent commands. 287 | */ 288 | EC_HOST_EVENT_INTERFACE_READY = 14, 289 | /* Keyboard recovery combo has been pressed */ 290 | EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, 291 | 292 | /* Shutdown due to thermal overload */ 293 | EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, 294 | /* Shutdown due to battery level too low */ 295 | EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, 296 | 297 | /* Suggest that the AP throttle itself */ 298 | EC_HOST_EVENT_THROTTLE_START = 18, 299 | /* Suggest that the AP resume normal speed */ 300 | EC_HOST_EVENT_THROTTLE_STOP = 19, 301 | 302 | /* Hang detect logic detected a hang and host event timeout expired */ 303 | EC_HOST_EVENT_HANG_DETECT = 20, 304 | /* Hang detect logic detected a hang and warm rebooted the AP */ 305 | EC_HOST_EVENT_HANG_REBOOT = 21, 306 | 307 | /* PD MCU triggering host event */ 308 | EC_HOST_EVENT_PD_MCU = 22, 309 | 310 | /* Battery Status flags have changed */ 311 | EC_HOST_EVENT_BATTERY_STATUS = 23, 312 | 313 | /* EC encountered a panic, triggering a reset */ 314 | EC_HOST_EVENT_PANIC = 24, 315 | 316 | /* Keyboard fastboot combo has been pressed */ 317 | EC_HOST_EVENT_KEYBOARD_FASTBOOT = 25, 318 | 319 | /* EC RTC event occurred */ 320 | EC_HOST_EVENT_RTC = 26, 321 | 322 | /* Emulate MKBP event */ 323 | EC_HOST_EVENT_MKBP = 27, 324 | 325 | /* EC desires to change state of host-controlled USB mux */ 326 | EC_HOST_EVENT_USB_MUX = 28, 327 | 328 | /* 329 | * The device has changed "modes". This can be one of the following: 330 | * 331 | * - TABLET/LAPTOP mode 332 | * - detachable base attach/detach event 333 | * - on body/off body transition event 334 | */ 335 | EC_HOST_EVENT_MODE_CHANGE = 29, 336 | 337 | /* Keyboard recovery combo with hardware reinitialization */ 338 | EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT = 30, 339 | 340 | /* WoV */ 341 | EC_HOST_EVENT_WOV = 31, 342 | 343 | /* 344 | * The high bit of the event mask is not used as a host event code. If 345 | * it reads back as set, then the entire event mask should be 346 | * considered invalid by the host. This can happen when reading the 347 | * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is 348 | * not initialized on the EC, or improperly configured on the host. 349 | */ 350 | EC_HOST_EVENT_INVALID = 32 351 | }; 352 | /* Host event mask */ 353 | #define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) 354 | 355 | #include 356 | 357 | /* Arguments at EC_LPC_ADDR_HOST_ARGS */ 358 | struct ec_lpc_host_args { 359 | UINT8 flags; 360 | UINT8 command_version; 361 | UINT8 data_size; 362 | /* 363 | * Checksum; sum of command + flags + command_version + data_size + 364 | * all params/response data bytes. 365 | */ 366 | UINT8 checksum; 367 | }; 368 | 369 | #include 370 | 371 | /* Flags for ec_lpc_host_args.flags */ 372 | /* 373 | * Args are from host. Data area at EC_LPC_ADDR_HOST_PARAM contains command 374 | * params. 375 | * 376 | * If EC gets a command and this flag is not set, this is an old-style command. 377 | * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with 378 | * unknown length. EC must respond with an old-style response (that is, 379 | * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). 380 | */ 381 | #define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 382 | /* 383 | * Args are from EC. Data area at EC_LPC_ADDR_HOST_PARAM contains response. 384 | * 385 | * If EC responds to a command and this flag is not set, this is an old-style 386 | * response. Command version is 0 and response data from EC is at 387 | * EC_LPC_ADDR_OLD_PARAM with unknown length. 388 | */ 389 | #define EC_HOST_ARGS_FLAG_TO_HOST 0x02 390 | 391 | 392 | /* Parameter length was limited by the LPC interface */ 393 | #define EC_PROTO2_MAX_PARAM_SIZE 0xfc 394 | 395 | /* Maximum request and response packet sizes for protocol version 2 */ 396 | #define EC_PROTO2_MAX_REQUEST_SIZE (EC_PROTO2_REQUEST_OVERHEAD + \ 397 | EC_PROTO2_MAX_PARAM_SIZE) 398 | #define EC_PROTO2_MAX_RESPONSE_SIZE (EC_PROTO2_RESPONSE_OVERHEAD + \ 399 | EC_PROTO2_MAX_PARAM_SIZE) 400 | 401 | /*****************************************************************************/ 402 | 403 | /* 404 | * Value written to legacy command port / prefix byte to indicate protocol 405 | * 3+ structs are being used. Usage is bus-dependent. 406 | */ 407 | #define EC_COMMAND_PROTOCOL_3 0xda 408 | 409 | #define EC_HOST_REQUEST_VERSION 3 410 | 411 | #include 412 | 413 | /* Version 3 request from host */ 414 | struct ec_host_request { 415 | /* Struct version (=3) 416 | * 417 | * EC will return EC_RES_INVALID_HEADER if it receives a header with a 418 | * version it doesn't know how to parse. 419 | */ 420 | UINT8 struct_version; 421 | 422 | /* 423 | * Checksum of request and data; sum of all bytes including checksum 424 | * should total to 0. 425 | */ 426 | UINT8 checksum; 427 | 428 | /* Command code */ 429 | UINT16 command; 430 | 431 | /* Command version */ 432 | UINT8 command_version; 433 | 434 | /* Unused byte in current protocol version; set to 0 */ 435 | UINT8 reserved; 436 | 437 | /* Length of data which follows this header */ 438 | UINT16 data_len; 439 | }; 440 | 441 | #define EC_HOST_RESPONSE_VERSION 3 442 | 443 | /* Version 3 response from EC */ 444 | struct ec_host_response { 445 | /* Struct version (=3) */ 446 | UINT8 struct_version; 447 | 448 | /* 449 | * Checksum of response and data; sum of all bytes including checksum 450 | * should total to 0. 451 | */ 452 | UINT8 checksum; 453 | 454 | /* Result code (EC_RES_*) */ 455 | UINT16 result; 456 | 457 | /* Length of data which follows this header */ 458 | UINT16 data_len; 459 | 460 | /* Unused bytes in current protocol version; set to 0 */ 461 | UINT16 reserved; 462 | }; 463 | 464 | #include 465 | 466 | /*****************************************************************************/ 467 | /* 468 | * Notes on commands: 469 | * 470 | * Each command is an 16-bit command value. Commands which take params or 471 | * return response data specify structs for that data. If no struct is 472 | * specified, the command does not input or output data, respectively. 473 | * Parameter/response length is implicit in the structs. Some underlying 474 | * communication protocols (I2C, SPI) may add length or checksum headers, but 475 | * those are implementation-dependent and not defined here. 476 | */ 477 | 478 | /*****************************************************************************/ 479 | /* General / test commands */ 480 | 481 | /* 482 | * Get protocol version, used to deal with non-backward compatible protocol 483 | * changes. 484 | */ 485 | #define EC_CMD_PROTO_VERSION 0x00 486 | 487 | #include 488 | 489 | struct ec_response_proto_version { 490 | UINT32 version; 491 | }; 492 | 493 | /* 494 | * Hello. This is a simple command to test the EC is responsive to 495 | * commands. 496 | */ 497 | #define EC_CMD_HELLO 0x01 498 | 499 | struct ec_params_hello { 500 | UINT32 in_data; /* Pass anything here */ 501 | }; 502 | 503 | struct ec_response_hello { 504 | UINT32 out_data; /* Output will be in_data + 0x01020304 */ 505 | }; 506 | 507 | #include 508 | 509 | /* Get version number */ 510 | #define EC_CMD_GET_VERSION 0x02 511 | 512 | enum ec_current_image { 513 | EC_IMAGE_UNKNOWN = 0, 514 | EC_IMAGE_RO, 515 | EC_IMAGE_RW, 516 | EC_IMAGE_RW_A = EC_IMAGE_RW, 517 | EC_IMAGE_RO_B, 518 | EC_IMAGE_RW_B 519 | }; 520 | 521 | #include 522 | 523 | struct ec_response_get_version { 524 | /* Null-terminated version strings for RO, RW */ 525 | char version_string_ro[32]; 526 | char version_string_rw[32]; 527 | char reserved[32]; /* Was previously RW-B string */ 528 | UINT32 current_image; /* One of ec_current_image */ 529 | }; 530 | 531 | #include 532 | 533 | /* 534 | * Read memory-mapped data. 535 | * 536 | * This is an alternate interface to memory-mapped data for bus protocols 537 | * which don't support direct-mapped memory - I2C, SPI, etc. 538 | * 539 | * Response is params.size bytes of data. 540 | */ 541 | #define EC_CMD_READ_MEMMAP 0x07 542 | 543 | #include 544 | 545 | struct ec_params_read_memmap { 546 | UINT8 offset; /* Offset in memmap (EC_MEMMAP_*) */ 547 | UINT8 size; /* Size to read in bytes */ 548 | }; 549 | 550 | #include 551 | 552 | /* Read versions supported for a command */ 553 | #define EC_CMD_GET_CMD_VERSIONS 0x0008 554 | 555 | #include 556 | /** 557 | * struct ec_params_get_cmd_versions - Parameters for the get command versions. 558 | * @cmd: Command to check. 559 | */ 560 | struct ec_params_get_cmd_versions { 561 | UINT8 cmd; 562 | }; 563 | 564 | /** 565 | * struct ec_params_get_cmd_versions_v1 - Parameters for the get command 566 | * versions (v1) 567 | * @cmd: Command to check. 568 | */ 569 | struct ec_params_get_cmd_versions_v1 { 570 | UINT16 cmd; 571 | }; 572 | 573 | /** 574 | * struct ec_response_get_cmd_version - Response to the get command versions. 575 | * @version_mask: Mask of supported versions; use EC_VER_MASK() to compare with 576 | * a desired version. 577 | */ 578 | struct ec_response_get_cmd_versions { 579 | UINT32 version_mask; 580 | }; 581 | #include 582 | 583 | /* Get protocol information */ 584 | #define EC_CMD_GET_PROTOCOL_INFO 0x0b 585 | 586 | /* Flags for ec_response_get_protocol_info.flags */ 587 | /* EC_RES_IN_PROGRESS may be returned if a command is slow */ 588 | #define EC_PROTOCOL_INFO_IN_PROGRESS_SUPPORTED (1 << 0) 589 | 590 | #include 591 | 592 | struct ec_response_get_protocol_info { 593 | /* Fields which exist if at least protocol version 3 supported */ 594 | 595 | /* Bitmask of protocol versions supported (1 << n means version n)*/ 596 | UINT32 protocol_versions; 597 | 598 | /* Maximum request packet size, in bytes */ 599 | UINT16 max_request_packet_size; 600 | 601 | /* Maximum response packet size, in bytes */ 602 | UINT16 max_response_packet_size; 603 | 604 | /* Flags; see EC_PROTOCOL_INFO_* */ 605 | UINT32 flags; 606 | }; 607 | 608 | #include 609 | 610 | #endif /* !__ACPI__ */ 611 | /*****************************************************************************/ 612 | /* 613 | * Passthru commands 614 | * 615 | * Some platforms have sub-processors chained to each other. For example. 616 | * 617 | * AP <--> EC <--> PD MCU 618 | * 619 | * The top 2 bits of the command number are used to indicate which device the 620 | * command is intended for. Device 0 is always the device receiving the 621 | * command; other device mapping is board-specific. 622 | * 623 | * When a device receives a command to be passed to a sub-processor, it passes 624 | * it on with the device number set back to 0. This allows the sub-processor 625 | * to remain blissfully unaware of whether the command originated on the next 626 | * device up the chain, or was passed through from the AP. 627 | * 628 | * In the above example, if the AP wants to send command 0x0002 to the PD MCU, 629 | * AP sends command 0x4002 to the EC 630 | * EC sends command 0x0002 to the PD MCU 631 | * EC forwards PD MCU response back to the AP 632 | */ 633 | 634 | /* Offset and max command number for sub-device n */ 635 | #define EC_CMD_PASSTHRU_OFFSET(n) (0x4000 * (n)) 636 | #define EC_CMD_PASSTHRU_MAX(n) (EC_CMD_PASSTHRU_OFFSET(n) + 0x3fff) 637 | 638 | /*****************************************************************************/ 639 | /* 640 | * Deprecated constants. These constants have been renamed for clarity. The 641 | * meaning and size has not changed. Programs that use the old names should 642 | * switch to the new names soon, as the old names may not be carried forward 643 | * forever. 644 | */ 645 | #define EC_HOST_PARAM_SIZE EC_PROTO2_MAX_PARAM_SIZE 646 | #define EC_LPC_ADDR_OLD_PARAM EC_HOST_CMD_REGION1 647 | #define EC_OLD_PARAM_SIZE EC_HOST_CMD_REGION_SIZE 648 | 649 | /*****************************************************************************/ 650 | /* PWM commands */ 651 | 652 | /* Get fan target RPM */ 653 | #define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 654 | 655 | #include 656 | 657 | struct ec_response_pwm_get_fan_rpm { 658 | UINT32 rpm; 659 | }; 660 | 661 | #include 662 | 663 | /* Set target fan RPM */ 664 | #define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 665 | 666 | #include 667 | 668 | /* Version 0 of input params */ 669 | struct ec_params_pwm_set_fan_target_rpm_v0 { 670 | UINT32 rpm; 671 | }; 672 | 673 | #include 674 | 675 | #include 676 | 677 | /* Version 1 of input params */ 678 | struct ec_params_pwm_set_fan_target_rpm_v1 { 679 | UINT32 rpm; 680 | UINT8 fan_idx; 681 | }; 682 | 683 | /* Get keyboard backlight */ 684 | #define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 685 | struct ec_response_pwm_get_keyboard_backlight { 686 | UINT8 percent; 687 | UINT8 enabled; 688 | }; 689 | 690 | /* Set keyboard backlight */ 691 | #define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 692 | struct ec_params_pwm_set_keyboard_backlight { 693 | UINT8 percent; 694 | }; 695 | 696 | #include 697 | 698 | /* Set target fan PWM duty cycle */ 699 | #define EC_CMD_PWM_SET_FAN_DUTY 0x24 700 | 701 | #include 702 | 703 | /* Version 0 of input params */ 704 | struct ec_params_pwm_set_fan_duty_v0 { 705 | UINT32 percent; 706 | }; 707 | 708 | /* Version 1 of input params */ 709 | struct ec_params_pwm_set_fan_duty_v1 { 710 | UINT32 percent; 711 | UINT8 fan_idx; 712 | }; 713 | 714 | /*****************************************************************************/ 715 | /* 716 | * Motion sense commands. We'll make separate structs for sub-commands with 717 | * different input args, so that we know how much to expect. 718 | */ 719 | #define EC_CMD_MOTION_SENSE_CMD 0x002B 720 | 721 | /* Motion sense commands */ 722 | enum motionsense_command { 723 | /* 724 | * Dump command returns all motion sensor data including motion sense 725 | * module flags and individual sensor flags. 726 | */ 727 | MOTIONSENSE_CMD_DUMP = 0, 728 | 729 | /* 730 | * Info command returns data describing the details of a given sensor, 731 | * including enum motionsensor_type, enum motionsensor_location, and 732 | * enum motionsensor_chip. 733 | */ 734 | MOTIONSENSE_CMD_INFO = 1, 735 | 736 | /* 737 | * EC Rate command is a setter/getter command for the EC sampling rate 738 | * in milliseconds. 739 | * It is per sensor, the EC run sample task at the minimum of all 740 | * sensors EC_RATE. 741 | * For sensors without hardware FIFO, EC_RATE should be equals to 1/ODR 742 | * to collect all the sensor samples. 743 | * For sensor with hardware FIFO, EC_RATE is used as the maximal delay 744 | * to process of all motion sensors in milliseconds. 745 | */ 746 | MOTIONSENSE_CMD_EC_RATE = 2, 747 | 748 | /* 749 | * Sensor ODR command is a setter/getter command for the output data 750 | * rate of a specific motion sensor in millihertz. 751 | */ 752 | MOTIONSENSE_CMD_SENSOR_ODR = 3, 753 | 754 | /* 755 | * Sensor range command is a setter/getter command for the range of 756 | * a specified motion sensor in +/-G's or +/- deg/s. 757 | */ 758 | MOTIONSENSE_CMD_SENSOR_RANGE = 4, 759 | 760 | /* 761 | * Setter/getter command for the keyboard wake angle. When the lid 762 | * angle is greater than this value, keyboard wake is disabled in S3, 763 | * and when the lid angle goes less than this value, keyboard wake is 764 | * enabled. Note, the lid angle measurement is an approximate, 765 | * un-calibrated value, hence the wake angle isn't exact. 766 | */ 767 | MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5, 768 | 769 | /* 770 | * Returns a single sensor data. 771 | */ 772 | MOTIONSENSE_CMD_DATA = 6, 773 | 774 | /* 775 | * Return sensor fifo info. 776 | */ 777 | MOTIONSENSE_CMD_FIFO_INFO = 7, 778 | 779 | /* 780 | * Insert a flush element in the fifo and return sensor fifo info. 781 | * The host can use that element to synchronize its operation. 782 | */ 783 | MOTIONSENSE_CMD_FIFO_FLUSH = 8, 784 | 785 | /* 786 | * Return a portion of the fifo. 787 | */ 788 | MOTIONSENSE_CMD_FIFO_READ = 9, 789 | 790 | /* 791 | * Perform low level calibration. 792 | * On sensors that support it, ask to do offset calibration. 793 | */ 794 | MOTIONSENSE_CMD_PERFORM_CALIB = 10, 795 | 796 | /* 797 | * Sensor Offset command is a setter/getter command for the offset 798 | * used for factory calibration. 799 | * The offsets can be calculated by the host, or via 800 | * PERFORM_CALIB command. 801 | */ 802 | MOTIONSENSE_CMD_SENSOR_OFFSET = 11, 803 | 804 | /* 805 | * List available activities for a MOTION sensor. 806 | * Indicates if they are enabled or disabled. 807 | */ 808 | MOTIONSENSE_CMD_LIST_ACTIVITIES = 12, 809 | 810 | /* 811 | * Activity management 812 | * Enable/Disable activity recognition. 813 | */ 814 | MOTIONSENSE_CMD_SET_ACTIVITY = 13, 815 | 816 | /* 817 | * Lid Angle 818 | */ 819 | MOTIONSENSE_CMD_LID_ANGLE = 14, 820 | 821 | /* 822 | * Allow the FIFO to trigger interrupt via MKBP events. 823 | * By default the FIFO does not send interrupt to process the FIFO 824 | * until the AP is ready or it is coming from a wakeup sensor. 825 | */ 826 | MOTIONSENSE_CMD_FIFO_INT_ENABLE = 15, 827 | 828 | /* 829 | * Spoof the readings of the sensors. The spoofed readings can be set 830 | * to arbitrary values, or will lock to the last read actual values. 831 | */ 832 | MOTIONSENSE_CMD_SPOOF = 16, 833 | 834 | /* Set lid angle for tablet mode detection. */ 835 | MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE = 17, 836 | 837 | /* 838 | * Sensor Scale command is a setter/getter command for the calibration 839 | * scale. 840 | */ 841 | MOTIONSENSE_CMD_SENSOR_SCALE = 18, 842 | 843 | /* 844 | * Read the current online calibration values (if available). 845 | */ 846 | MOTIONSENSE_CMD_ONLINE_CALIB_READ = 19, 847 | 848 | /* 849 | * Activity management 850 | * Retrieve current status of given activity. 851 | */ 852 | MOTIONSENSE_CMD_GET_ACTIVITY = 20, 853 | 854 | /* Number of motionsense sub-commands. */ 855 | MOTIONSENSE_NUM_CMDS, 856 | }; 857 | 858 | /* List of motion sensor types. */ 859 | enum motionsensor_type { 860 | MOTIONSENSE_TYPE_ACCEL = 0, 861 | MOTIONSENSE_TYPE_GYRO = 1, 862 | MOTIONSENSE_TYPE_MAG = 2, 863 | MOTIONSENSE_TYPE_PROX = 3, 864 | MOTIONSENSE_TYPE_LIGHT = 4, 865 | MOTIONSENSE_TYPE_ACTIVITY = 5, 866 | MOTIONSENSE_TYPE_BARO = 6, 867 | MOTIONSENSE_TYPE_SYNC = 7, 868 | MOTIONSENSE_TYPE_LIGHT_RGB = 8, 869 | MOTIONSENSE_TYPE_MAX, 870 | }; 871 | 872 | /* List of motion sensor locations. */ 873 | enum motionsensor_location { 874 | MOTIONSENSE_LOC_BASE = 0, 875 | MOTIONSENSE_LOC_LID = 1, 876 | MOTIONSENSE_LOC_CAMERA = 2, 877 | MOTIONSENSE_LOC_MAX, 878 | }; 879 | 880 | /* List of motion sensor chips. */ 881 | enum motionsensor_chip { 882 | MOTIONSENSE_CHIP_KXCJ9 = 0, 883 | MOTIONSENSE_CHIP_LSM6DS0 = 1, 884 | MOTIONSENSE_CHIP_BMI160 = 2, 885 | MOTIONSENSE_CHIP_SI1141 = 3, 886 | MOTIONSENSE_CHIP_SI1142 = 4, 887 | MOTIONSENSE_CHIP_SI1143 = 5, 888 | MOTIONSENSE_CHIP_KX022 = 6, 889 | MOTIONSENSE_CHIP_L3GD20H = 7, 890 | MOTIONSENSE_CHIP_BMA255 = 8, 891 | MOTIONSENSE_CHIP_BMP280 = 9, 892 | MOTIONSENSE_CHIP_OPT3001 = 10, 893 | MOTIONSENSE_CHIP_BH1730 = 11, 894 | MOTIONSENSE_CHIP_GPIO = 12, 895 | MOTIONSENSE_CHIP_LIS2DH = 13, 896 | MOTIONSENSE_CHIP_LSM6DSM = 14, 897 | MOTIONSENSE_CHIP_LIS2DE = 15, 898 | MOTIONSENSE_CHIP_LIS2MDL = 16, 899 | MOTIONSENSE_CHIP_LSM6DS3 = 17, 900 | MOTIONSENSE_CHIP_LSM6DSO = 18, 901 | MOTIONSENSE_CHIP_LNG2DM = 19, 902 | MOTIONSENSE_CHIP_TCS3400 = 20, 903 | MOTIONSENSE_CHIP_LIS2DW12 = 21, 904 | MOTIONSENSE_CHIP_LIS2DWL = 22, 905 | MOTIONSENSE_CHIP_LIS2DS = 23, 906 | MOTIONSENSE_CHIP_BMI260 = 24, 907 | MOTIONSENSE_CHIP_ICM426XX = 25, 908 | MOTIONSENSE_CHIP_ICM42607 = 26, 909 | MOTIONSENSE_CHIP_BMA422 = 27, 910 | MOTIONSENSE_CHIP_BMI323 = 28, 911 | MOTIONSENSE_CHIP_BMI220 = 29, 912 | MOTIONSENSE_CHIP_CM32183 = 30, 913 | MOTIONSENSE_CHIP_MAX, 914 | }; 915 | 916 | /* List of orientation positions */ 917 | enum motionsensor_orientation { 918 | MOTIONSENSE_ORIENTATION_LANDSCAPE = 0, 919 | MOTIONSENSE_ORIENTATION_PORTRAIT = 1, 920 | MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_PORTRAIT = 2, 921 | MOTIONSENSE_ORIENTATION_UPSIDE_DOWN_LANDSCAPE = 3, 922 | MOTIONSENSE_ORIENTATION_UNKNOWN = 4, 923 | }; 924 | 925 | #include 926 | struct ec_response_activity_data { 927 | UINT8 activity; /* motionsensor_activity */ 928 | UINT8 state; 929 | }; 930 | 931 | struct ec_response_motion_sensor_data { 932 | /* Flags for each sensor. */ 933 | UINT8 flags; 934 | /* Sensor number the data comes from. */ 935 | UINT8 sensor_num; 936 | /* Each sensor is up to 3-axis. */ 937 | union { 938 | INT16 data[3]; 939 | /* for sensors using unsigned data */ 940 | UINT16 udata[3]; 941 | struct { 942 | UINT16 reserved; 943 | UINT32 timestamp; 944 | }; 945 | struct { 946 | struct ec_response_activity_data activity_data; 947 | INT16 add_info[2]; 948 | }; 949 | }; 950 | }; 951 | #include 952 | 953 | /* Response to AP reporting calibration data for a given sensor. */ 954 | struct ec_response_online_calibration_data { 955 | /** The calibration values. */ 956 | INT16 data[3]; 957 | }; 958 | 959 | #include 960 | /* Note: used in ec_response_get_next_data */ 961 | struct ec_response_motion_sense_fifo_info { 962 | /* Size of the fifo */ 963 | UINT16 size; 964 | /* Amount of space used in the fifo */ 965 | UINT16 count; 966 | /* Timestamp recorded in us. 967 | * aka accurate timestamp when host event was triggered. 968 | */ 969 | UINT32 timestamp; 970 | /* Total amount of vector lost */ 971 | UINT16 total_lost; 972 | /* Lost events since the last fifo_info, per sensors */ 973 | UINT16 lost[0]; 974 | }; 975 | 976 | struct ec_response_motion_sense_fifo_data { 977 | UINT32 number_data; 978 | struct ec_response_motion_sensor_data data[0]; 979 | }; 980 | #include 981 | 982 | /* List supported activity recognition */ 983 | enum motionsensor_activity { 984 | MOTIONSENSE_ACTIVITY_RESERVED = 0, 985 | MOTIONSENSE_ACTIVITY_SIG_MOTION = 1, 986 | MOTIONSENSE_ACTIVITY_DOUBLE_TAP = 2, 987 | MOTIONSENSE_ACTIVITY_ORIENTATION = 3, 988 | MOTIONSENSE_ACTIVITY_BODY_DETECTION = 4, 989 | }; 990 | 991 | #include 992 | struct ec_motion_sense_activity { 993 | UINT8 sensor_num; 994 | UINT8 activity; /* one of enum motionsensor_activity */ 995 | UINT8 enable; /* 1: enable, 0: disable */ 996 | UINT8 reserved; 997 | UINT16 parameters[3]; /* activity dependent parameters */ 998 | }; 999 | #include 1000 | 1001 | /* Module flag masks used for the dump sub-command. */ 1002 | #define MOTIONSENSE_MODULE_FLAG_ACTIVE BIT(0) 1003 | 1004 | /* Sensor flag masks used for the dump sub-command. */ 1005 | #define MOTIONSENSE_SENSOR_FLAG_PRESENT BIT(0) 1006 | 1007 | /* 1008 | * Flush entry for synchronization. 1009 | * data contains time stamp 1010 | */ 1011 | #define MOTIONSENSE_SENSOR_FLAG_FLUSH BIT(0) 1012 | #define MOTIONSENSE_SENSOR_FLAG_TIMESTAMP BIT(1) 1013 | #define MOTIONSENSE_SENSOR_FLAG_WAKEUP BIT(2) 1014 | #define MOTIONSENSE_SENSOR_FLAG_TABLET_MODE BIT(3) 1015 | #define MOTIONSENSE_SENSOR_FLAG_ODR BIT(4) 1016 | 1017 | #define MOTIONSENSE_SENSOR_FLAG_BYPASS_FIFO BIT(7) 1018 | 1019 | /* 1020 | * Send this value for the data element to only perform a read. If you 1021 | * send any other value, the EC will interpret it as data to set and will 1022 | * return the actual value set. 1023 | */ 1024 | #define EC_MOTION_SENSE_NO_VALUE -1 1025 | 1026 | #define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000 1027 | 1028 | /* MOTIONSENSE_CMD_SENSOR_OFFSET subcommand flag */ 1029 | /* Set Calibration information */ 1030 | #define MOTION_SENSE_SET_OFFSET BIT(0) 1031 | 1032 | /* Default Scale value, factor 1. */ 1033 | #define MOTION_SENSE_DEFAULT_SCALE BIT(15) 1034 | 1035 | #define LID_ANGLE_UNRELIABLE 500 1036 | 1037 | enum motionsense_spoof_mode { 1038 | /* Disable spoof mode. */ 1039 | MOTIONSENSE_SPOOF_MODE_DISABLE = 0, 1040 | 1041 | /* Enable spoof mode, but use provided component values. */ 1042 | MOTIONSENSE_SPOOF_MODE_CUSTOM, 1043 | 1044 | /* Enable spoof mode, but use the current sensor values. */ 1045 | MOTIONSENSE_SPOOF_MODE_LOCK_CURRENT, 1046 | 1047 | /* Query the current spoof mode status for the sensor. */ 1048 | MOTIONSENSE_SPOOF_MODE_QUERY, 1049 | }; 1050 | 1051 | #include 1052 | struct ec_params_motion_sense { 1053 | UINT8 cmd; 1054 | union { 1055 | /* Used for MOTIONSENSE_CMD_DUMP. */ 1056 | struct { 1057 | /* 1058 | * Maximal number of sensor the host is expecting. 1059 | * 0 means the host is only interested in the number 1060 | * of sensors controlled by the EC. 1061 | */ 1062 | UINT8 max_sensor_count; 1063 | } dump; 1064 | 1065 | /* 1066 | * Used for MOTIONSENSE_CMD_KB_WAKE_ANGLE. 1067 | */ 1068 | struct { 1069 | /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. 1070 | * kb_wake_angle: angle to wakup AP. 1071 | */ 1072 | INT16 data; 1073 | } kb_wake_angle; 1074 | 1075 | /* 1076 | * Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA 1077 | */ 1078 | struct { 1079 | UINT8 sensor_num; 1080 | } info, info_3, info_4, data, fifo_flush, list_activities; 1081 | 1082 | /* 1083 | * Used for MOTIONSENSE_CMD_PERFORM_CALIB: 1084 | * Allow entering/exiting the calibration mode. 1085 | */ 1086 | struct { 1087 | UINT8 sensor_num; 1088 | UINT8 enable; 1089 | } perform_calib; 1090 | 1091 | /* 1092 | * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR 1093 | * and MOTIONSENSE_CMD_SENSOR_RANGE. 1094 | */ 1095 | struct { 1096 | UINT8 sensor_num; 1097 | 1098 | /* Rounding flag, true for round-up, false for down. */ 1099 | UINT8 roundup; 1100 | 1101 | UINT16 reserved; 1102 | 1103 | /* Data to set or EC_MOTION_SENSE_NO_VALUE to read. */ 1104 | INT32 data; 1105 | } ec_rate, sensor_odr, sensor_range; 1106 | 1107 | /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */ 1108 | struct { 1109 | UINT8 sensor_num; 1110 | 1111 | /* 1112 | * bit 0: If set (MOTION_SENSE_SET_OFFSET), set 1113 | * the calibration information in the EC. 1114 | * If unset, just retrieve calibration information. 1115 | */ 1116 | UINT16 flags; 1117 | 1118 | /* 1119 | * Temperature at calibration, in units of 0.01 C 1120 | * 0x8000: invalid / unknown. 1121 | * 0x0: 0C 1122 | * 0x7fff: +327.67C 1123 | */ 1124 | INT16 temp; 1125 | 1126 | /* 1127 | * Offset for calibration. 1128 | * Unit: 1129 | * Accelerometer: 1/1024 g 1130 | * Gyro: 1/1024 deg/s 1131 | * Compass: 1/16 uT 1132 | */ 1133 | INT16 offset[3]; 1134 | } sensor_offset; 1135 | 1136 | /* Used for MOTIONSENSE_CMD_SENSOR_SCALE */ 1137 | struct { 1138 | UINT8 sensor_num; 1139 | 1140 | /* 1141 | * bit 0: If set (MOTION_SENSE_SET_OFFSET), set 1142 | * the calibration information in the EC. 1143 | * If unset, just retrieve calibration information. 1144 | */ 1145 | UINT16 flags; 1146 | 1147 | /* 1148 | * Temperature at calibration, in units of 0.01 C 1149 | * 0x8000: invalid / unknown. 1150 | * 0x0: 0C 1151 | * 0x7fff: +327.67C 1152 | */ 1153 | INT16 temp; 1154 | 1155 | /* 1156 | * Scale for calibration: 1157 | * By default scale is 1, it is encoded on 16bits: 1158 | * 1 = BIT(15) 1159 | * ~2 = 0xFFFF 1160 | * ~0 = 0. 1161 | */ 1162 | UINT16 scale[3]; 1163 | } sensor_scale; 1164 | 1165 | /* Used for MOTIONSENSE_CMD_FIFO_INFO */ 1166 | /* (no params) */ 1167 | 1168 | /* Used for MOTIONSENSE_CMD_FIFO_READ */ 1169 | struct { 1170 | /* 1171 | * Number of expected vector to return. 1172 | * EC may return less or 0 if none available. 1173 | */ 1174 | UINT32 max_data_vector; 1175 | } fifo_read; 1176 | 1177 | /* Used for MOTIONSENSE_CMD_SET_ACTIVITY */ 1178 | struct ec_motion_sense_activity set_activity; 1179 | 1180 | /* Used for MOTIONSENSE_CMD_LID_ANGLE */ 1181 | /* (no params) */ 1182 | 1183 | /* Used for MOTIONSENSE_CMD_FIFO_INT_ENABLE */ 1184 | struct { 1185 | /* 1186 | * 1: enable, 0 disable fifo, 1187 | * EC_MOTION_SENSE_NO_VALUE return value. 1188 | */ 1189 | INT8 enable; 1190 | } fifo_int_enable; 1191 | 1192 | /* Used for MOTIONSENSE_CMD_SPOOF */ 1193 | struct { 1194 | UINT8 sensor_id; 1195 | 1196 | /* See enum motionsense_spoof_mode. */ 1197 | UINT8 spoof_enable; 1198 | 1199 | /* Ignored, used for alignment. */ 1200 | UINT8 reserved; 1201 | 1202 | union { 1203 | /* Individual component values to spoof. */ 1204 | INT16 components[3]; 1205 | 1206 | /* Used when spoofing an activity */ 1207 | struct { 1208 | /* enum motionsensor_activity */ 1209 | UINT8 activity_num; 1210 | 1211 | /* spoof activity state */ 1212 | UINT8 activity_state; 1213 | }; 1214 | }; 1215 | } spoof; 1216 | 1217 | /* Used for MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ 1218 | struct { 1219 | /* 1220 | * Lid angle threshold for switching between tablet and 1221 | * clamshell mode. 1222 | */ 1223 | INT16 lid_angle; 1224 | 1225 | /* 1226 | * Hysteresis degree to prevent fluctuations between 1227 | * clamshell and tablet mode if lid angle keeps 1228 | * changing around the threshold. Lid motion driver will 1229 | * use lid_angle + hys_degree to trigger tablet mode and 1230 | * lid_angle - hys_degree to trigger clamshell mode. 1231 | */ 1232 | INT16 hys_degree; 1233 | } tablet_mode_threshold; 1234 | 1235 | /* 1236 | * Used for MOTIONSENSE_CMD_ONLINE_CALIB_READ: 1237 | * Allow reading a single sensor's online calibration value. 1238 | */ 1239 | struct { 1240 | UINT8 sensor_num; 1241 | } online_calib_read; 1242 | 1243 | /* 1244 | * Used for MOTIONSENSE_CMD_GET_ACTIVITY. 1245 | */ 1246 | struct { 1247 | UINT8 sensor_num; 1248 | UINT8 activity; /* enum motionsensor_activity */ 1249 | } get_activity; 1250 | }; 1251 | }; 1252 | #include 1253 | 1254 | enum motion_sense_cmd_info_flags { 1255 | /* The sensor supports online calibration */ 1256 | MOTION_SENSE_CMD_INFO_FLAG_ONLINE_CALIB = BIT(0), 1257 | }; 1258 | 1259 | #include 1260 | struct ec_response_motion_sense { 1261 | union { 1262 | /* Used for MOTIONSENSE_CMD_DUMP */ 1263 | struct { 1264 | /* Flags representing the motion sensor module. */ 1265 | UINT8 module_flags; 1266 | 1267 | /* Number of sensors managed directly by the EC. */ 1268 | UINT8 sensor_count; 1269 | 1270 | /* 1271 | * Sensor data is truncated if response_max is too small 1272 | * for holding all the data. 1273 | */ 1274 | struct ec_response_motion_sensor_data sensor[0]; 1275 | } dump; 1276 | 1277 | /* Used for MOTIONSENSE_CMD_INFO. */ 1278 | struct { 1279 | /* Should be element of enum motionsensor_type. */ 1280 | UINT8 type; 1281 | 1282 | /* Should be element of enum motionsensor_location. */ 1283 | UINT8 location; 1284 | 1285 | /* Should be element of enum motionsensor_chip. */ 1286 | UINT8 chip; 1287 | } info; 1288 | 1289 | /* Used for MOTIONSENSE_CMD_INFO version 3 */ 1290 | struct { 1291 | /* Should be element of enum motionsensor_type. */ 1292 | UINT8 type; 1293 | 1294 | /* Should be element of enum motionsensor_location. */ 1295 | UINT8 location; 1296 | 1297 | /* Should be element of enum motionsensor_chip. */ 1298 | UINT8 chip; 1299 | 1300 | /* Minimum sensor sampling frequency */ 1301 | UINT32 min_frequency; 1302 | 1303 | /* Maximum sensor sampling frequency */ 1304 | UINT32 max_frequency; 1305 | 1306 | /* Max number of sensor events that could be in fifo */ 1307 | UINT32 fifo_max_event_count; 1308 | } info_3; 1309 | 1310 | /* Used for MOTIONSENSE_CMD_INFO version 4 */ 1311 | struct { 1312 | /* Should be element of enum motionsensor_type. */ 1313 | UINT8 type; 1314 | 1315 | /* Should be element of enum motionsensor_location. */ 1316 | UINT8 location; 1317 | 1318 | /* Should be element of enum motionsensor_chip. */ 1319 | UINT8 chip; 1320 | 1321 | /* Minimum sensor sampling frequency */ 1322 | UINT32 min_frequency; 1323 | 1324 | /* Maximum sensor sampling frequency */ 1325 | UINT32 max_frequency; 1326 | 1327 | /* Max number of sensor events that could be in fifo */ 1328 | UINT32 fifo_max_event_count; 1329 | 1330 | /* 1331 | * Should be elements of 1332 | * enum motion_sense_cmd_info_flags 1333 | */ 1334 | UINT32 flags; 1335 | } info_4; 1336 | 1337 | /* Used for MOTIONSENSE_CMD_DATA */ 1338 | struct ec_response_motion_sensor_data data; 1339 | 1340 | /* 1341 | * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR, 1342 | * MOTIONSENSE_CMD_SENSOR_RANGE, 1343 | * MOTIONSENSE_CMD_KB_WAKE_ANGLE, 1344 | * MOTIONSENSE_CMD_FIFO_INT_ENABLE and 1345 | * MOTIONSENSE_CMD_SPOOF. 1346 | */ 1347 | struct { 1348 | /* Current value of the parameter queried. */ 1349 | INT32 ret; 1350 | } ec_rate, sensor_odr, sensor_range, kb_wake_angle, 1351 | fifo_int_enable, spoof; 1352 | 1353 | /* 1354 | * Used for MOTIONSENSE_CMD_SENSOR_OFFSET, 1355 | * PERFORM_CALIB. 1356 | */ 1357 | struct { 1358 | INT16 temp; 1359 | INT16 offset[3]; 1360 | } sensor_offset, perform_calib; 1361 | 1362 | /* Used for MOTIONSENSE_CMD_SENSOR_SCALE */ 1363 | struct { 1364 | INT16 temp; 1365 | UINT16 scale[3]; 1366 | } sensor_scale; 1367 | 1368 | struct ec_response_motion_sense_fifo_info fifo_info, fifo_flush; 1369 | 1370 | struct ec_response_motion_sense_fifo_data fifo_read; 1371 | 1372 | struct ec_response_online_calibration_data online_calib_read; 1373 | 1374 | struct { 1375 | UINT16 reserved; 1376 | UINT32 enabled; 1377 | UINT32 disabled; 1378 | } list_activities; 1379 | 1380 | /* No params for set activity */ 1381 | 1382 | /* Used for MOTIONSENSE_CMD_LID_ANGLE */ 1383 | struct { 1384 | /* 1385 | * Angle between 0 and 360 degree if available, 1386 | * LID_ANGLE_UNRELIABLE otherwise. 1387 | */ 1388 | UINT16 value; 1389 | } lid_angle; 1390 | 1391 | /* Used for MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. */ 1392 | struct { 1393 | /* 1394 | * Lid angle threshold for switching between tablet and 1395 | * clamshell mode. 1396 | */ 1397 | UINT16 lid_angle; 1398 | 1399 | /* Hysteresis degree. */ 1400 | UINT16 hys_degree; 1401 | } tablet_mode_threshold; 1402 | 1403 | /* USED for MOTIONSENSE_CMD_GET_ACTIVITY. */ 1404 | struct { 1405 | UINT8 state; 1406 | } get_activity; 1407 | }; 1408 | }; 1409 | #include 1410 | 1411 | /*****************************************************************************/ 1412 | 1413 | #include 1414 | 1415 | /*****************************************************************************/ 1416 | /* Hibernate/Deep Sleep Commands */ 1417 | 1418 | /* Set the delay before going into hibernation. */ 1419 | #define EC_CMD_HIBERNATION_DELAY 0x00A8 1420 | 1421 | #include 1422 | 1423 | struct ec_params_hibernation_delay { 1424 | /* 1425 | * Seconds to wait in G3 before hibernate. Pass in 0 to read the 1426 | * current settings without changing them. 1427 | */ 1428 | UINT32 seconds; 1429 | }; 1430 | 1431 | struct ec_response_hibernation_delay { 1432 | /* 1433 | * The current time in seconds in which the system has been in the G3 1434 | * state. This value is reset if the EC transitions out of G3. 1435 | */ 1436 | UINT32 time_g3; 1437 | 1438 | /* 1439 | * The current time remaining in seconds until the EC should hibernate. 1440 | * This value is also reset if the EC transitions out of G3. 1441 | */ 1442 | UINT32 time_remaining; 1443 | 1444 | /* 1445 | * The current time in seconds that the EC should wait in G3 before 1446 | * hibernating. 1447 | */ 1448 | UINT32 hibernate_delay; 1449 | }; 1450 | 1451 | #include 1452 | 1453 | /* Inform the EC when entering a sleep state */ 1454 | #define EC_CMD_HOST_SLEEP_EVENT 0x00A9 1455 | 1456 | enum host_sleep_event { 1457 | HOST_SLEEP_EVENT_S3_SUSPEND = 1, 1458 | HOST_SLEEP_EVENT_S3_RESUME = 2, 1459 | HOST_SLEEP_EVENT_S0IX_SUSPEND = 3, 1460 | HOST_SLEEP_EVENT_S0IX_RESUME = 4, 1461 | /* S3 suspend with additional enabled wake sources */ 1462 | HOST_SLEEP_EVENT_S3_WAKEABLE_SUSPEND = 5, 1463 | }; 1464 | 1465 | #include 1466 | 1467 | struct ec_params_host_sleep_event { 1468 | UINT8 sleep_event; 1469 | }; 1470 | 1471 | #include 1472 | 1473 | /* 1474 | * Use a default timeout value (CONFIG_SLEEP_TIMEOUT_MS) for detecting sleep 1475 | * transition failures 1476 | */ 1477 | #define EC_HOST_SLEEP_TIMEOUT_DEFAULT 0 1478 | 1479 | /* Disable timeout detection for this sleep transition */ 1480 | #define EC_HOST_SLEEP_TIMEOUT_INFINITE 0xFFFF 1481 | 1482 | #include 1483 | 1484 | struct ec_params_host_sleep_event_v1 { 1485 | /* The type of sleep being entered or exited. */ 1486 | UINT8 sleep_event; 1487 | 1488 | /* Padding */ 1489 | UINT8 reserved; 1490 | union { 1491 | /* Parameters that apply for suspend messages. */ 1492 | struct { 1493 | /* 1494 | * The timeout in milliseconds between when this message 1495 | * is received and when the EC will declare sleep 1496 | * transition failure if the sleep signal is not 1497 | * asserted. 1498 | */ 1499 | UINT16 sleep_timeout_ms; 1500 | } suspend_params; 1501 | 1502 | /* No parameters for non-suspend messages. */ 1503 | }; 1504 | }; 1505 | 1506 | #include 1507 | 1508 | /* A timeout occurred when this bit is set */ 1509 | #define EC_HOST_RESUME_SLEEP_TIMEOUT 0x80000000 1510 | 1511 | /* 1512 | * The mask defining which bits correspond to the number of sleep transitions, 1513 | * as well as the maximum number of suspend line transitions that will be 1514 | * reported back to the host. 1515 | */ 1516 | #define EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK 0x7FFFFFFF 1517 | 1518 | #include 1519 | 1520 | struct ec_response_host_sleep_event_v1 { 1521 | union { 1522 | /* Response fields that apply for resume messages. */ 1523 | struct { 1524 | /* 1525 | * The number of sleep power signal transitions that 1526 | * occurred since the suspend message. The high bit 1527 | * indicates a timeout occurred. 1528 | */ 1529 | UINT32 sleep_transitions; 1530 | } resume_response; 1531 | 1532 | /* No response fields for non-resume messages. */ 1533 | }; 1534 | }; 1535 | 1536 | #include 1537 | 1538 | /*****************************************************************************/ 1539 | /* List the features supported by the firmware */ 1540 | #define EC_CMD_GET_FEATURES 0x000D 1541 | 1542 | /* Supported features */ 1543 | enum ec_feature_code { 1544 | /* 1545 | * This image contains a limited set of features. Another image 1546 | * in RW partition may support more features. 1547 | */ 1548 | EC_FEATURE_LIMITED = 0, 1549 | /* 1550 | * Commands for probing/reading/writing/erasing the flash in the 1551 | * EC are present. 1552 | */ 1553 | EC_FEATURE_FLASH = 1, 1554 | /* 1555 | * Can control the fan speed directly. 1556 | */ 1557 | EC_FEATURE_PWM_FAN = 2, 1558 | /* 1559 | * Can control the intensity of the keyboard backlight. 1560 | */ 1561 | EC_FEATURE_PWM_KEYB = 3, 1562 | /* 1563 | * Support Google lightbar, introduced on Pixel. 1564 | */ 1565 | EC_FEATURE_LIGHTBAR = 4, 1566 | /* Control of LEDs */ 1567 | EC_FEATURE_LED = 5, 1568 | /* Exposes an interface to control gyro and sensors. 1569 | * The host goes through the EC to access these sensors. 1570 | * In addition, the EC may provide composite sensors, like lid angle. 1571 | */ 1572 | EC_FEATURE_MOTION_SENSE = 6, 1573 | /* The keyboard is controlled by the EC */ 1574 | EC_FEATURE_KEYB = 7, 1575 | /* The AP can use part of the EC flash as persistent storage. */ 1576 | EC_FEATURE_PSTORE = 8, 1577 | /* The EC monitors BIOS port 80h, and can return POST codes. */ 1578 | EC_FEATURE_PORT80 = 9, 1579 | /* 1580 | * Thermal management: include TMP specific commands. 1581 | * Higher level than direct fan control. 1582 | */ 1583 | EC_FEATURE_THERMAL = 10, 1584 | /* Can switch the screen backlight on/off */ 1585 | EC_FEATURE_BKLIGHT_SWITCH = 11, 1586 | /* Can switch the wifi module on/off */ 1587 | EC_FEATURE_WIFI_SWITCH = 12, 1588 | /* Monitor host events, through for example SMI or SCI */ 1589 | EC_FEATURE_HOST_EVENTS = 13, 1590 | /* The EC exposes GPIO commands to control/monitor connected devices. */ 1591 | EC_FEATURE_GPIO = 14, 1592 | /* The EC can send i2c messages to downstream devices. */ 1593 | EC_FEATURE_I2C = 15, 1594 | /* Command to control charger are included */ 1595 | EC_FEATURE_CHARGER = 16, 1596 | /* Simple battery support. */ 1597 | EC_FEATURE_BATTERY = 17, 1598 | /* 1599 | * Support Smart battery protocol 1600 | * (Common Smart Battery System Interface Specification) 1601 | */ 1602 | EC_FEATURE_SMART_BATTERY = 18, 1603 | /* EC can detect when the host hangs. */ 1604 | EC_FEATURE_HANG_DETECT = 19, 1605 | /* Report power information, for pit only */ 1606 | EC_FEATURE_PMU = 20, 1607 | /* Another Cros EC device is present downstream of this one */ 1608 | EC_FEATURE_SUB_MCU = 21, 1609 | /* Support USB Power delivery (PD) commands */ 1610 | EC_FEATURE_USB_PD = 22, 1611 | /* Control USB multiplexer, for audio through USB port for instance. */ 1612 | EC_FEATURE_USB_MUX = 23, 1613 | /* Motion Sensor code has an internal software FIFO */ 1614 | EC_FEATURE_MOTION_SENSE_FIFO = 24, 1615 | /* Support temporary secure vstore */ 1616 | EC_FEATURE_VSTORE = 25, 1617 | /* EC decides on USB-C SS mux state, muxes configured by host */ 1618 | EC_FEATURE_USBC_SS_MUX_VIRTUAL = 26, 1619 | /* EC has RTC feature that can be controlled by host commands */ 1620 | EC_FEATURE_RTC = 27, 1621 | /* The MCU exposes a Fingerprint sensor */ 1622 | EC_FEATURE_FINGERPRINT = 28, 1623 | /* The MCU exposes a Touchpad */ 1624 | EC_FEATURE_TOUCHPAD = 29, 1625 | /* The MCU has RWSIG task enabled */ 1626 | EC_FEATURE_RWSIG = 30, 1627 | /* EC has device events support */ 1628 | EC_FEATURE_DEVICE_EVENT = 31, 1629 | /* EC supports the unified wake masks for LPC/eSPI systems */ 1630 | EC_FEATURE_UNIFIED_WAKE_MASKS = 32, 1631 | /* EC supports 64-bit host events */ 1632 | EC_FEATURE_HOST_EVENT64 = 33, 1633 | /* EC runs code in RAM (not in place, a.k.a. XIP) */ 1634 | EC_FEATURE_EXEC_IN_RAM = 34, 1635 | /* EC supports CEC commands */ 1636 | EC_FEATURE_CEC = 35, 1637 | /* EC supports tight sensor timestamping. */ 1638 | EC_FEATURE_MOTION_SENSE_TIGHT_TIMESTAMPS = 36, 1639 | /* 1640 | * EC supports tablet mode detection aligned to Chrome and allows 1641 | * setting of threshold by host command using 1642 | * MOTIONSENSE_CMD_TABLET_MODE_LID_ANGLE. 1643 | */ 1644 | EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS = 37, 1645 | /* The MCU is a System Companion Processor (SCP). */ 1646 | EC_FEATURE_SCP = 39, 1647 | /* The MCU is an Integrated Sensor Hub */ 1648 | EC_FEATURE_ISH = 40, 1649 | /* New TCPMv2 TYPEC_ prefaced commands supported */ 1650 | EC_FEATURE_TYPEC_CMD = 41, 1651 | /* 1652 | * The EC will wait for direction from the AP to enter Type-C alternate 1653 | * modes or USB4. 1654 | */ 1655 | EC_FEATURE_TYPEC_REQUIRE_AP_MODE_ENTRY = 42, 1656 | /* 1657 | * The EC will wait for an acknowledge from the AP after setting the 1658 | * mux. 1659 | */ 1660 | EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK = 43 1661 | }; 1662 | 1663 | #define BIT(nr) (1UL << (nr)) 1664 | 1665 | #define EC_FEATURE_MASK_0(event_code) BIT(event_code % 32) 1666 | #define EC_FEATURE_MASK_1(event_code) BIT(event_code - 32) 1667 | 1668 | #include 1669 | 1670 | struct ec_response_get_features { 1671 | UINT32 flags[2]; 1672 | }; 1673 | 1674 | #include 1675 | 1676 | /*****************************************************************************/ 1677 | /* MKBP - Matrix KeyBoard Protocol */ 1678 | 1679 | /* 1680 | * Read key state 1681 | * 1682 | * Returns raw data for keyboard cols; see ec_response_mkbp_info.cols for 1683 | * expected response size. 1684 | * 1685 | * NOTE: This has been superseded by EC_CMD_MKBP_GET_NEXT_EVENT. If you wish 1686 | * to obtain the instantaneous state, use EC_CMD_MKBP_INFO with the type 1687 | * EC_MKBP_INFO_CURRENT and event EC_MKBP_EVENT_KEY_MATRIX. 1688 | */ 1689 | #define EC_CMD_MKBP_STATE 0x0060 1690 | 1691 | /* 1692 | * Provide information about various MKBP things. See enum ec_mkbp_info_type. 1693 | */ 1694 | #define EC_CMD_MKBP_INFO 0x0061 1695 | 1696 | #include 1697 | 1698 | struct ec_response_mkbp_info { 1699 | UINT32 rows; 1700 | UINT32 cols; 1701 | /* Formerly "switches", which was 0. */ 1702 | UINT8 reserved; 1703 | }; 1704 | 1705 | struct ec_params_mkbp_info { 1706 | UINT8 info_type; 1707 | UINT8 event_type; 1708 | }; 1709 | 1710 | #include 1711 | 1712 | enum ec_mkbp_info_type { 1713 | /* 1714 | * Info about the keyboard matrix: number of rows and columns. 1715 | * 1716 | * Returns struct ec_response_mkbp_info. 1717 | */ 1718 | EC_MKBP_INFO_KBD = 0, 1719 | 1720 | /* 1721 | * For buttons and switches, info about which specifically are 1722 | * supported. event_type must be set to one of the values in enum 1723 | * ec_mkbp_event. 1724 | * 1725 | * For EC_MKBP_EVENT_BUTTON and EC_MKBP_EVENT_SWITCH, returns a 4 byte 1726 | * bitmask indicating which buttons or switches are present. See the 1727 | * bit inidices below. 1728 | */ 1729 | EC_MKBP_INFO_SUPPORTED = 1, 1730 | 1731 | /* 1732 | * Instantaneous state of buttons and switches. 1733 | * 1734 | * event_type must be set to one of the values in enum ec_mkbp_event. 1735 | * 1736 | * For EC_MKBP_EVENT_KEY_MATRIX, returns uint8_t key_matrix[13] 1737 | * indicating the current state of the keyboard matrix. 1738 | * 1739 | * For EC_MKBP_EVENT_HOST_EVENT, return uint32_t host_event, the raw 1740 | * event state. 1741 | * 1742 | * For EC_MKBP_EVENT_BUTTON, returns uint32_t buttons, indicating the 1743 | * state of supported buttons. 1744 | * 1745 | * For EC_MKBP_EVENT_SWITCH, returns uint32_t switches, indicating the 1746 | * state of supported switches. 1747 | */ 1748 | EC_MKBP_INFO_CURRENT = 2, 1749 | }; 1750 | 1751 | /* Simulate key press */ 1752 | #define EC_CMD_MKBP_SIMULATE_KEY 0x0062 1753 | 1754 | #include 1755 | struct ec_params_mkbp_simulate_key { 1756 | UINT8 col; 1757 | UINT8 row; 1758 | UINT8 pressed; 1759 | }; 1760 | #include 1761 | 1762 | #define EC_CMD_GET_KEYBOARD_ID 0x0063 1763 | 1764 | #include 1765 | struct ec_response_keyboard_id { 1766 | UINT32 keyboard_id; 1767 | }; 1768 | #include 1769 | 1770 | enum keyboard_id { 1771 | KEYBOARD_ID_UNSUPPORTED = 0, 1772 | KEYBOARD_ID_UNREADABLE = 0xffffffff, 1773 | }; 1774 | 1775 | /* Configure keyboard scanning */ 1776 | #define EC_CMD_MKBP_SET_CONFIG 0x0064 1777 | #define EC_CMD_MKBP_GET_CONFIG 0x0065 1778 | 1779 | /* flags */ 1780 | enum mkbp_config_flags { 1781 | EC_MKBP_FLAGS_ENABLE = 1, /* Enable keyboard scanning */ 1782 | }; 1783 | 1784 | enum mkbp_config_valid { 1785 | EC_MKBP_VALID_SCAN_PERIOD = BIT(0), 1786 | EC_MKBP_VALID_POLL_TIMEOUT = BIT(1), 1787 | EC_MKBP_VALID_MIN_POST_SCAN_DELAY = BIT(3), 1788 | EC_MKBP_VALID_OUTPUT_SETTLE = BIT(4), 1789 | EC_MKBP_VALID_DEBOUNCE_DOWN = BIT(5), 1790 | EC_MKBP_VALID_DEBOUNCE_UP = BIT(6), 1791 | EC_MKBP_VALID_FIFO_MAX_DEPTH = BIT(7), 1792 | }; 1793 | 1794 | #include 1795 | /* 1796 | * Configuration for our key scanning algorithm. 1797 | * 1798 | * Note that this is used as a sub-structure of 1799 | * ec_{params/response}_mkbp_get_config. 1800 | */ 1801 | struct ec_mkbp_config { 1802 | UINT32 valid_mask; /* valid fields */ 1803 | UINT8 flags; /* some flags (enum mkbp_config_flags) */ 1804 | UINT8 valid_flags; /* which flags are valid */ 1805 | UINT16 scan_period_us; /* period between start of scans */ 1806 | /* revert to interrupt mode after no activity for this long */ 1807 | UINT32 poll_timeout_us; 1808 | /* 1809 | * minimum post-scan relax time. Once we finish a scan we check 1810 | * the time until we are due to start the next one. If this time is 1811 | * shorter this field, we use this instead. 1812 | */ 1813 | UINT16 min_post_scan_delay_us; 1814 | /* delay between setting up output and waiting for it to settle */ 1815 | UINT16 output_settle_us; 1816 | UINT16 debounce_down_us; /* time for debounce on key down */ 1817 | UINT16 debounce_up_us; /* time for debounce on key up */ 1818 | /* maximum depth to allow for fifo (0 = no keyscan output) */ 1819 | UINT8 fifo_max_depth; 1820 | }; 1821 | 1822 | struct ec_params_mkbp_set_config { 1823 | struct ec_mkbp_config config; 1824 | }; 1825 | 1826 | struct ec_response_mkbp_get_config { 1827 | struct ec_mkbp_config config; 1828 | }; 1829 | #include 1830 | 1831 | /* Run the key scan emulation */ 1832 | #define EC_CMD_KEYSCAN_SEQ_CTRL 0x0066 1833 | 1834 | enum ec_keyscan_seq_cmd { 1835 | EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ 1836 | EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */ 1837 | EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */ 1838 | EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */ 1839 | EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */ 1840 | }; 1841 | 1842 | enum ec_collect_flags { 1843 | /* 1844 | * Indicates this scan was processed by the EC. Due to timing, some 1845 | * scans may be skipped. 1846 | */ 1847 | EC_KEYSCAN_SEQ_FLAG_DONE = BIT(0), 1848 | }; 1849 | 1850 | #include 1851 | struct ec_collect_item { 1852 | UINT8 flags; /* some flags (enum ec_collect_flags) */ 1853 | }; 1854 | 1855 | struct ec_params_keyscan_seq_ctrl { 1856 | UINT8 cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ 1857 | union { 1858 | struct { 1859 | UINT8 active; /* still active */ 1860 | UINT8 num_items; /* number of items */ 1861 | /* Current item being presented */ 1862 | UINT8 cur_item; 1863 | } status; 1864 | struct { 1865 | /* 1866 | * Absolute time for this scan, measured from the 1867 | * start of the sequence. 1868 | */ 1869 | UINT32 time_us; 1870 | UINT8 scan[0]; /* keyscan data */ 1871 | } add; 1872 | struct { 1873 | UINT8 start_item; /* First item to return */ 1874 | UINT8 num_items; /* Number of items to return */ 1875 | } collect; 1876 | }; 1877 | }; 1878 | 1879 | struct ec_result_keyscan_seq_ctrl { 1880 | union { 1881 | struct { 1882 | UINT8 num_items; /* Number of items */ 1883 | /* Data for each item */ 1884 | struct ec_collect_item item[0]; 1885 | } collect; 1886 | }; 1887 | }; 1888 | #include 1889 | 1890 | /* 1891 | * Get the next pending MKBP event. 1892 | * 1893 | * Returns EC_RES_UNAVAILABLE if there is no event pending. 1894 | */ 1895 | #define EC_CMD_GET_NEXT_EVENT 0x0067 1896 | 1897 | #define EC_MKBP_HAS_MORE_EVENTS_SHIFT 7 1898 | 1899 | /* 1900 | * We use the most significant bit of the event type to indicate to the host 1901 | * that the EC has more MKBP events available to provide. 1902 | */ 1903 | #define EC_MKBP_HAS_MORE_EVENTS BIT(EC_MKBP_HAS_MORE_EVENTS_SHIFT) 1904 | 1905 | /* The mask to apply to get the raw event type */ 1906 | #define EC_MKBP_EVENT_TYPE_MASK (BIT(EC_MKBP_HAS_MORE_EVENTS_SHIFT) - 1) 1907 | 1908 | enum ec_mkbp_event { 1909 | /* Keyboard matrix changed. The event data is the new matrix state. */ 1910 | EC_MKBP_EVENT_KEY_MATRIX = 0, 1911 | 1912 | /* New host event. The event data is 4 bytes of host event flags. */ 1913 | EC_MKBP_EVENT_HOST_EVENT = 1, 1914 | 1915 | /* New Sensor FIFO data. The event data is fifo_info structure. */ 1916 | EC_MKBP_EVENT_SENSOR_FIFO = 2, 1917 | 1918 | /* The state of the non-matrixed buttons have changed. */ 1919 | EC_MKBP_EVENT_BUTTON = 3, 1920 | 1921 | /* The state of the switches have changed. */ 1922 | EC_MKBP_EVENT_SWITCH = 4, 1923 | 1924 | /* New Fingerprint sensor event, the event data is fp_events bitmap. */ 1925 | EC_MKBP_EVENT_FINGERPRINT = 5, 1926 | 1927 | /* 1928 | * Sysrq event: send emulated sysrq. The event data is sysrq, 1929 | * corresponding to the key to be pressed. 1930 | */ 1931 | EC_MKBP_EVENT_SYSRQ = 6, 1932 | 1933 | /* 1934 | * New 64-bit host event. 1935 | * The event data is 8 bytes of host event flags. 1936 | */ 1937 | EC_MKBP_EVENT_HOST_EVENT64 = 7, 1938 | 1939 | /* Notify the AP that something happened on CEC */ 1940 | EC_MKBP_EVENT_CEC_EVENT = 8, 1941 | 1942 | /* Send an incoming CEC message to the AP */ 1943 | EC_MKBP_EVENT_CEC_MESSAGE = 9, 1944 | 1945 | /* We have entered DisplayPort Alternate Mode on a Type-C port. */ 1946 | EC_MKBP_EVENT_DP_ALT_MODE_ENTERED = 10, 1947 | 1948 | /* New online calibration values are available. */ 1949 | EC_MKBP_EVENT_ONLINE_CALIBRATION = 11, 1950 | 1951 | /* Peripheral device charger event */ 1952 | EC_MKBP_EVENT_PCHG = 12, 1953 | 1954 | /* Number of MKBP events */ 1955 | EC_MKBP_EVENT_COUNT, 1956 | }; 1957 | 1958 | /* clang-format off */ 1959 | #define EC_MKBP_EVENT_TEXT \ 1960 | { \ 1961 | [EC_MKBP_EVENT_KEY_MATRIX] = "KEY_MATRIX", \ 1962 | [EC_MKBP_EVENT_HOST_EVENT] = "HOST_EVENT", \ 1963 | [EC_MKBP_EVENT_SENSOR_FIFO] = "SENSOR_FIFO", \ 1964 | [EC_MKBP_EVENT_BUTTON] = "BUTTON", \ 1965 | [EC_MKBP_EVENT_SWITCH] = "SWITCH", \ 1966 | [EC_MKBP_EVENT_FINGERPRINT] = "FINGERPRINT", \ 1967 | [EC_MKBP_EVENT_SYSRQ] = "SYSRQ", \ 1968 | [EC_MKBP_EVENT_HOST_EVENT64] = "HOST_EVENT64", \ 1969 | [EC_MKBP_EVENT_CEC_EVENT] = "CEC_EVENT", \ 1970 | [EC_MKBP_EVENT_CEC_MESSAGE] = "CEC_MESSAGE", \ 1971 | [EC_MKBP_EVENT_DP_ALT_MODE_ENTERED] = "DP_ALT_MODE_ENTERED", \ 1972 | [EC_MKBP_EVENT_ONLINE_CALIBRATION] = "ONLINE_CALIBRATION", \ 1973 | [EC_MKBP_EVENT_PCHG] = "PCHG", \ 1974 | } 1975 | /* clang-format on */ 1976 | 1977 | #include 1978 | union ec_response_get_next_data { 1979 | UINT8 key_matrix[13]; 1980 | 1981 | /* Unaligned */ 1982 | UINT32 host_event; 1983 | UINT64 host_event64; 1984 | 1985 | struct { 1986 | /* For aligning the fifo_info */ 1987 | UINT8 reserved[3]; 1988 | struct ec_response_motion_sense_fifo_info info; 1989 | } sensor_fifo; 1990 | 1991 | UINT32 buttons; 1992 | 1993 | UINT32 switches; 1994 | 1995 | UINT32 fp_events; 1996 | 1997 | UINT32 sysrq; 1998 | 1999 | /* CEC events from enum mkbp_cec_event */ 2000 | UINT32 cec_events; 2001 | }; 2002 | 2003 | union ec_response_get_next_data_v1 { 2004 | UINT8 key_matrix[16]; 2005 | 2006 | /* Unaligned */ 2007 | UINT32 host_event; 2008 | UINT64 host_event64; 2009 | 2010 | struct { 2011 | /* For aligning the fifo_info */ 2012 | UINT8 reserved[3]; 2013 | struct ec_response_motion_sense_fifo_info info; 2014 | } sensor_fifo; 2015 | 2016 | UINT32 buttons; 2017 | 2018 | UINT32 switches; 2019 | 2020 | UINT32 fp_events; 2021 | 2022 | UINT32 sysrq; 2023 | 2024 | /* CEC events from enum mkbp_cec_event */ 2025 | UINT32 cec_events; 2026 | 2027 | UINT8 cec_message[16]; 2028 | }; 2029 | 2030 | struct ec_response_get_next_event { 2031 | UINT8 event_type; 2032 | /* Followed by event data if any */ 2033 | union ec_response_get_next_data data; 2034 | }; 2035 | 2036 | struct ec_response_get_next_event_v1 { 2037 | UINT8 event_type; 2038 | /* Followed by event data if any */ 2039 | union ec_response_get_next_data_v1 data; 2040 | }; 2041 | #include 2042 | 2043 | /* Bit indices for buttons and switches.*/ 2044 | /* Buttons */ 2045 | #define EC_MKBP_POWER_BUTTON 0 2046 | #define EC_MKBP_VOL_UP 1 2047 | #define EC_MKBP_VOL_DOWN 2 2048 | #define EC_MKBP_RECOVERY 3 2049 | 2050 | /* Switches */ 2051 | #define EC_MKBP_LID_OPEN 0 2052 | #define EC_MKBP_TABLET_MODE 1 2053 | #define EC_MKBP_BASE_ATTACHED 2 2054 | #define EC_MKBP_FRONT_PROXIMITY 3 2055 | 2056 | /* Run keyboard factory test scanning */ 2057 | #define EC_CMD_KEYBOARD_FACTORY_TEST 0x0068 2058 | 2059 | #include 2060 | 2061 | struct ec_response_keyboard_factory_test { 2062 | UINT16 shorted; /* Keyboard pins are shorted */ 2063 | }; 2064 | 2065 | #include 2066 | 2067 | #define EC_CMD_MKBP_WAKE_MASK 0x0069 2068 | enum ec_mkbp_event_mask_action { 2069 | /* Retrieve the value of a wake mask. */ 2070 | GET_WAKE_MASK = 0, 2071 | 2072 | /* Set the value of a wake mask. */ 2073 | SET_WAKE_MASK, 2074 | }; 2075 | 2076 | enum ec_mkbp_mask_type { 2077 | /* 2078 | * These are host events sent via MKBP. 2079 | * 2080 | * Some examples are: 2081 | * EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN) 2082 | * EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEY_PRESSED) 2083 | * 2084 | * The only things that should be in this mask are: 2085 | * EC_HOST_EVENT_MASK(EC_HOST_EVENT_*) 2086 | */ 2087 | EC_MKBP_HOST_EVENT_WAKE_MASK = 0, 2088 | 2089 | /* 2090 | * These are MKBP events. Some examples are: 2091 | * 2092 | * EC_MKBP_EVENT_KEY_MATRIX 2093 | * EC_MKBP_EVENT_SWITCH 2094 | * 2095 | * The only things that should be in this mask are EC_MKBP_EVENT_*. 2096 | */ 2097 | EC_MKBP_EVENT_WAKE_MASK, 2098 | }; 2099 | 2100 | struct ec_params_mkbp_event_wake_mask { 2101 | /* One of enum ec_mkbp_event_mask_action */ 2102 | UINT8 action; 2103 | 2104 | /* 2105 | * Which MKBP mask are you interested in acting upon? This is one of 2106 | * ec_mkbp_mask_type. 2107 | */ 2108 | UINT8 mask_type; 2109 | 2110 | /* If setting a new wake mask, this contains the mask to set. */ 2111 | UINT32 new_wake_mask; 2112 | }; 2113 | 2114 | struct ec_response_mkbp_event_wake_mask { 2115 | UINT32 wake_mask; 2116 | }; 2117 | 2118 | /*****************************************************************************/ 2119 | 2120 | /* 2121 | * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI 2122 | * commands accidentally sent to the wrong interface. See the ACPI section 2123 | * below. 2124 | */ 2125 | 2126 | /*****************************************************************************/ 2127 | /* Host event commands */ 2128 | 2129 | /* Obsolete. New implementation should use EC_CMD_HOST_EVENT instead */ 2130 | /* 2131 | * Host event mask params and response structures, shared by all of the host 2132 | * event commands below. 2133 | */ 2134 | 2135 | #include 2136 | 2137 | struct ec_params_host_event_mask { 2138 | UINT32 mask; 2139 | }; 2140 | 2141 | struct ec_response_host_event_mask { 2142 | UINT32 mask; 2143 | }; 2144 | 2145 | #include 2146 | 2147 | /* These all use ec_response_host_event_mask */ 2148 | #define EC_CMD_HOST_EVENT_GET_B 0x0087 2149 | #define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x0088 2150 | #define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x0089 2151 | #define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x008D 2152 | 2153 | /* These all use ec_params_host_event_mask */ 2154 | #define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x008A 2155 | #define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x008B 2156 | #define EC_CMD_HOST_EVENT_CLEAR 0x008C 2157 | #define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x008E 2158 | #define EC_CMD_HOST_EVENT_CLEAR_B 0x008F 2159 | 2160 | /* 2161 | * Unified host event programming interface - Should be used by newer versions 2162 | * of BIOS/OS to program host events and masks 2163 | * 2164 | * EC returns: 2165 | * - EC_RES_INVALID_PARAM: Action or mask type is unknown. 2166 | * - EC_RES_ACCESS_DENIED: Action is prohibited for specified mask type. 2167 | */ 2168 | 2169 | #include 2170 | 2171 | struct ec_params_host_event { 2172 | /* Action requested by host - one of enum ec_host_event_action. */ 2173 | UINT8 action; 2174 | 2175 | /* 2176 | * Mask type that the host requested the action on - one of 2177 | * enum ec_host_event_mask_type. 2178 | */ 2179 | UINT8 mask_type; 2180 | 2181 | /* Set to 0, ignore on read */ 2182 | UINT16 reserved; 2183 | 2184 | /* Value to be used in case of set operations. */ 2185 | UINT64 value; 2186 | }; 2187 | 2188 | /* 2189 | * Response structure returned by EC_CMD_HOST_EVENT. 2190 | * Update the value on a GET request. Set to 0 on GET/CLEAR 2191 | */ 2192 | 2193 | struct ec_response_host_event { 2194 | /* Mask value in case of get operation */ 2195 | UINT64 value; 2196 | }; 2197 | 2198 | #include 2199 | 2200 | enum ec_host_event_action { 2201 | /* 2202 | * params.value is ignored. Value of mask_type populated 2203 | * in response.value 2204 | */ 2205 | EC_HOST_EVENT_GET, 2206 | 2207 | /* Bits in params.value are set */ 2208 | EC_HOST_EVENT_SET, 2209 | 2210 | /* Bits in params.value are cleared */ 2211 | EC_HOST_EVENT_CLEAR, 2212 | }; 2213 | 2214 | enum ec_host_event_mask_type { 2215 | 2216 | /* Main host event copy */ 2217 | EC_HOST_EVENT_MAIN, 2218 | 2219 | /* Copy B of host events */ 2220 | EC_HOST_EVENT_B, 2221 | 2222 | /* SCI Mask */ 2223 | EC_HOST_EVENT_SCI_MASK, 2224 | 2225 | /* SMI Mask */ 2226 | EC_HOST_EVENT_SMI_MASK, 2227 | 2228 | /* Mask of events that should be always reported in hostevents */ 2229 | EC_HOST_EVENT_ALWAYS_REPORT_MASK, 2230 | 2231 | /* Active wake mask */ 2232 | EC_HOST_EVENT_ACTIVE_WAKE_MASK, 2233 | 2234 | /* Lazy wake mask for S0ix */ 2235 | EC_HOST_EVENT_LAZY_WAKE_MASK_S0IX, 2236 | 2237 | /* Lazy wake mask for S3 */ 2238 | EC_HOST_EVENT_LAZY_WAKE_MASK_S3, 2239 | 2240 | /* Lazy wake mask for S5 */ 2241 | EC_HOST_EVENT_LAZY_WAKE_MASK_S5, 2242 | }; 2243 | 2244 | #define EC_CMD_HOST_EVENT 0x00A4 2245 | 2246 | #endif /* __CROS_EC_COMMANDS_H */ 2247 | --------------------------------------------------------------------------------