LenovoLegionToolkit
372 строки · 15.6 Кб
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Threading.Tasks;
5using LenovoLegionToolkit.Lib.Extensions;
6using LenovoLegionToolkit.Lib.Settings;
7using LenovoLegionToolkit.Lib.SoftwareDisabler;
8using LenovoLegionToolkit.Lib.System.Management;
9using LenovoLegionToolkit.Lib.Utils;
10
11namespace LenovoLegionToolkit.Lib.Controllers.GodMode;
12
13public class GodModeControllerV2 : AbstractGodModeController
14{
15public GodModeControllerV2(GodModeSettings settings, VantageDisabler vantageDisabler, LegionZoneDisabler legionZoneDisabler) : base(settings, vantageDisabler, legionZoneDisabler) { }
16
17public override Task<bool> NeedsVantageDisabledAsync() => Task.FromResult(true);
18public override Task<bool> NeedsLegionZoneDisabledAsync() => Task.FromResult(true);
19
20public override async Task ApplyStateAsync()
21{
22if (await VantageDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled)
23{
24if (Log.Instance.IsTraceEnabled)
25Log.Instance.Trace($"Can't correctly apply state when Vantage is running.");
26return;
27}
28
29if (await LegionZoneDisabler.GetStatusAsync().ConfigureAwait(false) == SoftwareStatus.Enabled)
30{
31if (Log.Instance.IsTraceEnabled)
32Log.Instance.Trace($"Can't correctly apply state when Legion Zone is running.");
33return;
34}
35
36if (Log.Instance.IsTraceEnabled)
37Log.Instance.Trace($"Applying state...");
38
39var (presetId, preset) = await GetActivePresetAsync().ConfigureAwait(false);
40
41var settings = new Dictionary<CapabilityID, StepperValue?>
42{
43{ CapabilityID.CPULongTermPowerLimit, preset.CPULongTermPowerLimit },
44{ CapabilityID.CPUShortTermPowerLimit, preset.CPUShortTermPowerLimit },
45{ CapabilityID.CPUPeakPowerLimit, preset.CPUPeakPowerLimit },
46{ CapabilityID.CPUCrossLoadingPowerLimit, preset.CPUCrossLoadingPowerLimit },
47{ CapabilityID.CPUPL1Tau, preset.CPUPL1Tau },
48{ CapabilityID.APUsPPTPowerLimit, preset.APUsPPTPowerLimit },
49{ CapabilityID.CPUTemperatureLimit, preset.CPUTemperatureLimit },
50{ CapabilityID.GPUPowerBoost, preset.GPUPowerBoost },
51{ CapabilityID.GPUConfigurableTGP, preset.GPUConfigurableTGP },
52{ CapabilityID.GPUTemperatureLimit, preset.GPUTemperatureLimit },
53{ CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, preset.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline },
54{ CapabilityID.GPUToCPUDynamicBoost, preset.GPUToCPUDynamicBoost },
55};
56
57var fanTable = preset.FanTable ?? await GetDefaultFanTableAsync().ConfigureAwait(false);
58var fanFullSpeed = preset.FanFullSpeed ?? false;
59
60foreach (var (id, value) in settings)
61{
62if (!value.HasValue)
63continue;
64
65try
66{
67if (Log.Instance.IsTraceEnabled)
68Log.Instance.Trace($"Applying {id}: {value}...");
69
70await SetValueAsync(id, value.Value).ConfigureAwait(false);
71}
72catch (Exception ex)
73{
74if (Log.Instance.IsTraceEnabled)
75Log.Instance.Trace($"Failed to apply {id}. [value={value}]", ex);
76throw;
77}
78}
79
80if (fanFullSpeed)
81{
82try
83{
84if (Log.Instance.IsTraceEnabled)
85Log.Instance.Trace($"Applying Fan Full Speed {fanFullSpeed}...");
86
87await SetFanFullSpeedAsync(fanFullSpeed).ConfigureAwait(false);
88}
89catch (Exception ex)
90{
91if (Log.Instance.IsTraceEnabled)
92Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex);
93throw;
94}
95}
96else
97{
98try
99{
100if (Log.Instance.IsTraceEnabled)
101Log.Instance.Trace($"Making sure Fan Full Speed is false...");
102
103await SetFanFullSpeedAsync(false).ConfigureAwait(false);
104}
105catch (Exception ex)
106{
107if (Log.Instance.IsTraceEnabled)
108Log.Instance.Trace($"Apply failed. [setting=fanFullSpeed]", ex);
109throw;
110}
111
112try
113{
114if (Log.Instance.IsTraceEnabled)
115Log.Instance.Trace($"Applying Fan Table {fanTable}...");
116
117if (!await IsValidFanTableAsync(fanTable).ConfigureAwait(false))
118{
119if (Log.Instance.IsTraceEnabled)
120Log.Instance.Trace($"Fan table invalid, replacing with default...");
121
122fanTable = await GetDefaultFanTableAsync().ConfigureAwait(false);
123}
124
125await SetFanTable(fanTable).ConfigureAwait(false);
126}
127catch (Exception ex)
128{
129if (Log.Instance.IsTraceEnabled)
130Log.Instance.Trace($"Apply failed. [setting=fanTable]", ex);
131throw;
132}
133}
134
135RaisePresetChanged(presetId);
136
137if (Log.Instance.IsTraceEnabled)
138Log.Instance.Trace($"State applied. [name={preset.Name}, id={presetId}]");
139}
140
141public override Task<FanTable> GetMinimumFanTableAsync()
142{
143var fanTable = new FanTable(new ushort[] { 1, 1, 1, 1, 1, 1, 1, 1, 3, 5 });
144return Task.FromResult(fanTable);
145}
146
147public override async Task<Dictionary<PowerModeState, GodModeDefaults>> GetDefaultsInOtherPowerModesAsync()
148{
149try
150{
151if (Log.Instance.IsTraceEnabled)
152Log.Instance.Trace($"Getting defaults in other power modes...");
153
154var result = new Dictionary<PowerModeState, GodModeDefaults>();
155
156var allCapabilityData = await WMI.LenovoCapabilityData01.ReadAsync().ConfigureAwait(false);
157allCapabilityData = allCapabilityData.ToArray();
158
159foreach (var powerMode in new[] { PowerModeState.Quiet, PowerModeState.Balance, PowerModeState.Performance })
160{
161var defaults = new GodModeDefaults
162{
163CPULongTermPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPULongTermPowerLimit, powerMode),
164CPUShortTermPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUShortTermPowerLimit, powerMode),
165CPUPeakPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUPeakPowerLimit, powerMode),
166CPUCrossLoadingPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUCrossLoadingPowerLimit, powerMode),
167CPUPL1Tau = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUPL1Tau, powerMode),
168APUsPPTPowerLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.APUsPPTPowerLimit, powerMode),
169CPUTemperatureLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.CPUTemperatureLimit, powerMode),
170GPUPowerBoost = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUPowerBoost, powerMode),
171GPUConfigurableTGP = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUConfigurableTGP, powerMode),
172GPUTemperatureLimit = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUTemperatureLimit, powerMode),
173GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline, powerMode),
174GPUToCPUDynamicBoost = GetDefaultCapabilityIdValueInPowerMode(allCapabilityData, CapabilityID.GPUToCPUDynamicBoost, powerMode),
175FanTable = await GetDefaultFanTableAsync().ConfigureAwait(false),
176FanFullSpeed = false
177};
178
179result[powerMode] = defaults;
180}
181
182if (Log.Instance.IsTraceEnabled)
183{
184Log.Instance.Trace($"Defaults in other power modes retrieved:");
185foreach (var (powerMode, defaults) in result)
186Log.Instance.Trace($" - {powerMode}: {defaults}");
187}
188
189return result;
190}
191catch (Exception ex)
192{
193if (Log.Instance.IsTraceEnabled)
194Log.Instance.Trace($"Failed to get defaults in other power modes.", ex);
195
196return new Dictionary<PowerModeState, GodModeDefaults>();
197}
198}
199
200public override Task RestoreDefaultsInOtherPowerModeAsync(PowerModeState _) => Task.CompletedTask;
201
202protected override async Task<GodModePreset> GetDefaultStateAsync()
203{
204var allCapabilityData = await WMI.LenovoCapabilityData01.ReadAsync().ConfigureAwait(false);
205allCapabilityData = allCapabilityData.ToArray();
206
207var capabilityData = allCapabilityData
208.Where(d => Enum.IsDefined(d.Id))
209.ToArray();
210
211var allDiscreteData = await WMI.LenovoDiscreteData.ReadAsync().ConfigureAwait(false);
212allDiscreteData = allDiscreteData.ToArray();
213
214var discreteData = allDiscreteData
215.Where(d => Enum.IsDefined(d.Id))
216.GroupBy(d => d.Id, d => d.Value, (id, values) => (id, values))
217.ToDictionary(d => d.id, d => d.values.ToArray());
218
219var stepperValues = new Dictionary<CapabilityID, StepperValue>();
220
221foreach (var c in capabilityData)
222{
223var value = await GetValueAsync(c.Id).OrNullIfException().ConfigureAwait(false) ?? c.DefaultValue;
224var steps = discreteData.GetValueOrDefault(c.Id) ?? Array.Empty<int>();
225
226if (c.Step == 0 && steps.Length < 1)
227continue;
228
229if (Log.Instance.IsTraceEnabled)
230Log.Instance.Trace($"Creating StepperValue {c.Id}... [idRaw={(int)c.Id:X}, defaultValue={c.DefaultValue}, min={c.Min}, max={c.Max}, step={c.Step}, steps={string.Join(", ", steps)}]");
231
232var stepperValue = new StepperValue(value, c.Min, c.Max, c.Step, steps, c.DefaultValue);
233stepperValues[c.Id] = stepperValue;
234}
235
236var fanTableData = await GetFanTableDataAsync().ConfigureAwait(false);
237
238var preset = new GodModePreset
239{
240Name = "Default",
241CPULongTermPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPULongTermPowerLimit),
242CPUShortTermPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUShortTermPowerLimit),
243CPUPeakPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUPeakPowerLimit),
244CPUCrossLoadingPowerLimit = stepperValues.GetValueOrNull(CapabilityID.CPUCrossLoadingPowerLimit),
245CPUPL1Tau = stepperValues.GetValueOrNull(CapabilityID.CPUPL1Tau),
246APUsPPTPowerLimit = stepperValues.GetValueOrNull(CapabilityID.APUsPPTPowerLimit),
247CPUTemperatureLimit = stepperValues.GetValueOrNull(CapabilityID.CPUTemperatureLimit),
248GPUPowerBoost = stepperValues.GetValueOrNull(CapabilityID.GPUPowerBoost),
249GPUConfigurableTGP = stepperValues.GetValueOrNull(CapabilityID.GPUConfigurableTGP),
250GPUTemperatureLimit = stepperValues.GetValueOrNull(CapabilityID.GPUTemperatureLimit),
251GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = stepperValues.GetValueOrNull(CapabilityID.GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline),
252GPUToCPUDynamicBoost = stepperValues.GetValueOrNull(CapabilityID.GPUToCPUDynamicBoost),
253FanTableInfo = fanTableData is null ? null : new FanTableInfo(fanTableData, await GetDefaultFanTableAsync().ConfigureAwait(false)),
254FanFullSpeed = await GetFanFullSpeedAsync().ConfigureAwait(false),
255MinValueOffset = 0,
256MaxValueOffset = 0
257};
258
259if (Log.Instance.IsTraceEnabled)
260Log.Instance.Trace($"Default state retrieved: {preset}");
261
262return preset;
263}
264
265private static CapabilityID AdjustCapabilityIdForPowerMode(CapabilityID id, PowerModeState powerMode)
266{
267var idRaw = (uint)id & 0xFFFF00FF;
268var powerModeRaw = ((uint)powerMode + 1) << 8;
269return (CapabilityID)(idRaw + powerModeRaw);
270}
271
272private static int? GetDefaultCapabilityIdValueInPowerMode(IEnumerable<RangeCapability> capabilities, CapabilityID id, PowerModeState powerMode)
273{
274var adjustedId = AdjustCapabilityIdForPowerMode(id, powerMode);
275var value = capabilities
276.Where(c => c.Id == adjustedId)
277.Select(c => c.DefaultValue)
278.DefaultIfEmpty(-1)
279.First();
280return value < 0 ? null : value;
281}
282
283#region Get/Set Value
284
285private static Task<int> GetValueAsync(CapabilityID id)
286{
287var idRaw = (uint)id & 0xFFFF00FF;
288return WMI.LenovoOtherMethod.GetFeatureValueAsync(idRaw);
289}
290
291private static Task SetValueAsync(CapabilityID id, StepperValue value)
292{
293var idRaw = (uint)id & 0xFFFF00FF;
294return WMI.LenovoOtherMethod.SetFeatureValueAsync(idRaw, value.Value);
295}
296
297#endregion
298
299#region Fan Table
300
301private static async Task<FanTableData[]?> GetFanTableDataAsync(PowerModeState powerModeState = PowerModeState.GodMode)
302{
303if (Log.Instance.IsTraceEnabled)
304Log.Instance.Trace($"Reading fan table data...");
305
306var data = await WMI.LenovoFanTableData.ReadAsync().ConfigureAwait(false);
307
308var fanTableData = data
309.Where(d => d.mode == (int)powerModeState + 1)
310.Select(d => new FanTableData
311{
312Type = (d.fanId, d.sensorId) switch
313{
314(1, 4) => FanTableType.CPU,
315(1, 1) => FanTableType.CPUSensor,
316(2, 5) => FanTableType.GPU,
317_ => FanTableType.Unknown,
318},
319FanId = d.fanId,
320SensorId = d.sensorId,
321FanSpeeds = d.fanTableData,
322Temps = d.sensorTableData
323})
324.ToArray();
325
326if (fanTableData.Length != 3)
327{
328if (Log.Instance.IsTraceEnabled)
329Log.Instance.Trace($"Bad fan table length: {string.Join(", ", fanTableData)}");
330
331return null;
332}
333
334if (fanTableData.Count(ftd => ftd.FanSpeeds.Length == 10) != 3)
335{
336if (Log.Instance.IsTraceEnabled)
337Log.Instance.Trace($"Bad fan table fan speeds length: {string.Join(", ", fanTableData)}");
338
339return null;
340}
341
342if (fanTableData.Count(ftd => ftd.Temps.Length == 10) != 3)
343{
344if (Log.Instance.IsTraceEnabled)
345Log.Instance.Trace($"Bad fan table temps length: {string.Join(", ", fanTableData)}");
346
347return null;
348}
349
350if (Log.Instance.IsTraceEnabled)
351Log.Instance.Trace($"Fan table data: {string.Join(", ", fanTableData)}");
352
353return fanTableData;
354}
355
356private static Task SetFanTable(FanTable fanTable) => WMI.LenovoFanMethod.FanSetTableAsync(fanTable.GetBytes());
357
358#endregion
359
360#region Fan Full Speed
361
362private static async Task<bool> GetFanFullSpeedAsync()
363{
364var value = await WMI.LenovoOtherMethod.GetFeatureValueAsync(CapabilityID.FanFullSpeed).ConfigureAwait(false);
365return value != 0;
366}
367
368private static Task SetFanFullSpeedAsync(bool enabled) => WMI.LenovoOtherMethod.SetFeatureValueAsync(CapabilityID.FanFullSpeed, enabled ? 1 : 0);
369
370#endregion
371
372}
373