Files
wehub-resource-sync 1d1286fadb
Build / Build and test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:18:38 +08:00

178 lines
5.4 KiB
Swift

import Foundation
import IOKit
final class SystemStatusSMCReader {
private enum Selector: UInt8 {
case kernelIndex = 2
case readBytes = 5
case readKeyInfo = 9
}
private struct KeyData {
typealias Bytes = (
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8,
UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8
)
struct Version {
var major: UInt8 = 0
var minor: UInt8 = 0
var build: UInt8 = 0
var reserved: UInt8 = 0
var release: UInt16 = 0
}
struct LimitData {
var version: UInt16 = 0
var length: UInt16 = 0
var cpuLimit: UInt32 = 0
var gpuLimit: UInt32 = 0
var memoryLimit: UInt32 = 0
}
struct KeyInfo {
var dataSize: IOByteCount32 = 0
var dataType: UInt32 = 0
var dataAttributes: UInt8 = 0
}
var key: UInt32 = 0
var version = Version()
var limitData = LimitData()
var keyInfo = KeyInfo()
var padding: UInt16 = 0
var result: UInt8 = 0
var status: UInt8 = 0
var data8: UInt8 = 0
var data32: UInt32 = 0
var bytes: Bytes = (
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
)
}
private struct Value {
let key: String
var dataSize: UInt32 = 0
var dataType = ""
var bytes = [UInt8](repeating: 0, count: 32)
}
private var connection: io_connect_t = 0
init?() {
let service = IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("AppleSMC"))
guard service != 0 else {
return nil
}
defer { IOObjectRelease(service) }
guard IOServiceOpen(service, mach_task_self_, 0, &connection) == kIOReturnSuccess, connection != 0 else {
return nil
}
}
deinit {
if connection != 0 {
IOServiceClose(connection)
}
}
func value(for key: String) -> Double? {
guard key.count == 4 else {
return nil
}
var value = Value(key: key)
guard read(&value) == kIOReturnSuccess, value.dataSize > 0 else {
return nil
}
guard value.bytes.contains(where: { $0 != 0 }) else {
return nil
}
switch value.dataType {
case "ui8 ":
return Double(value.bytes[0])
case "ui16":
return Double(UInt16(value.bytes[0]) << 8 | UInt16(value.bytes[1]))
case "ui32":
return Double(
UInt32(value.bytes[0]) << 24 |
UInt32(value.bytes[1]) << 16 |
UInt32(value.bytes[2]) << 8 |
UInt32(value.bytes[3])
)
case "sp78":
let rawValue = Double(Int(Int8(bitPattern: value.bytes[0])) * 256 + Int(value.bytes[1]))
return rawValue / 256
case "flt ":
var floatValue: Float = 0
withUnsafeMutableBytes(of: &floatValue) { pointer in
pointer.copyBytes(from: value.bytes.prefix(4))
}
return Double(floatValue)
case "fpe2":
return Double((Int(value.bytes[0]) << 6) + (Int(value.bytes[1]) >> 2))
default:
return nil
}
}
private func read(_ value: inout Value) -> kern_return_t {
var input = KeyData()
var output = KeyData()
input.key = Self.fourCharCode(value.key)
input.data8 = Selector.readKeyInfo.rawValue
var result = call(Selector.kernelIndex.rawValue, input: &input, output: &output)
guard result == kIOReturnSuccess else {
return result
}
value.dataSize = UInt32(output.keyInfo.dataSize)
value.dataType = Self.string(from: output.keyInfo.dataType)
input.keyInfo.dataSize = output.keyInfo.dataSize
input.data8 = Selector.readBytes.rawValue
result = call(Selector.kernelIndex.rawValue, input: &input, output: &output)
guard result == kIOReturnSuccess else {
return result
}
withUnsafeBytes(of: output.bytes) { rawBuffer in
let count = min(Int(value.dataSize), rawBuffer.count, value.bytes.count)
value.bytes.replaceSubrange(0..<count, with: rawBuffer.prefix(count))
}
return kIOReturnSuccess
}
private func call(_ selector: UInt8, input: inout KeyData, output: inout KeyData) -> kern_return_t {
let inputSize = MemoryLayout<KeyData>.stride
var outputSize = MemoryLayout<KeyData>.stride
return IOConnectCallStructMethod(connection, UInt32(selector), &input, inputSize, &output, &outputSize)
}
private static func fourCharCode(_ value: String) -> UInt32 {
value.utf8.reduce(UInt32(0)) { result, character in
result << 8 | UInt32(character)
}
}
private static func string(from value: UInt32) -> String {
let bytes = [
UInt8((value >> 24) & 0xff),
UInt8((value >> 16) & 0xff),
UInt8((value >> 8) & 0xff),
UInt8(value & 0xff)
]
return String(bytes: bytes, encoding: .ascii) ?? ""
}
}