Allow thermalHAL on Wahoo to look up thermistors by name.
Instead of hardcoding thermal zone numbers, make thermalHAL look up
thermistors by name. This is a backport of the way thermalHAL keeps
track of thermal zones in b1c1.
Bug: 75972023
Test: VtsHalThermalV1_0TargetTest passes
Test: VtsHalThermalV1_1TargetTest passes
Test: manual - flash device and check logcat for thermalHAL output.
Change-Id: I24194ea6dd0372b52f525ac288108ee9da30258a
diff --git a/thermal/Android.bp b/thermal/Android.bp
index be1e703..43c003e 100644
--- a/thermal/Android.bp
+++ b/thermal/Android.bp
@@ -5,6 +5,7 @@
vendor: true,
relative_install_path: "hw",
srcs: [
+ "sensors.cpp",
"Thermal.cpp",
"thermal-helper.cpp",
],
diff --git a/thermal/Thermal.cpp b/thermal/Thermal.cpp
index a9bd371..7b29b29 100644
--- a/thermal/Thermal.cpp
+++ b/thermal/Thermal.cpp
@@ -55,6 +55,8 @@
hidl_vec<Temperature> temperatures;
temperatures.resize(kTemperatureNum);
+ LOG(INFO) << "ThermalHAL enabled: " << enabled;
+
if (!enabled) {
status.code = ThermalStatusCode::FAILURE;
status.debugMessage = "Unsupported hardware";
@@ -62,10 +64,9 @@
return Void();
}
- ssize_t ret = fillTemperatures(&temperatures);
- if (ret < 0) {
+ if (fillTemperatures(&temperatures) != kTemperatureNum) {
status.code = ThermalStatusCode::FAILURE;
- status.debugMessage = strerror(-ret);
+ status.debugMessage = "Error reading thermal sensors.";
}
_hidl_cb(status, temperatures);
diff --git a/thermal/Thermal.h b/thermal/Thermal.h
index a62b9e3..d3ac2f9 100644
--- a/thermal/Thermal.h
+++ b/thermal/Thermal.h
@@ -54,7 +54,6 @@
// Methods from ::android::hardware::thermal::V1_1::IThermal follow.
Return<void> registerThermalCallback(
const sp<IThermalCallback>& callback) override;
-
private:
bool enabled;
};
diff --git a/thermal/sensors.cpp b/thermal/sensors.cpp
new file mode 100644
index 0000000..7df880b
--- /dev/null
+++ b/thermal/sensors.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
+#include "sensors.h"
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V1_1 {
+namespace implementation {
+
+std::string Sensors::getSensorPath(const std::string& sensor_name) {
+ if (sensor_name_to_data_map_.find(sensor_name) !=
+ sensor_name_to_data_map_.end()) {
+ return std::get<0>(sensor_name_to_data_map_.at(sensor_name));
+ }
+ return "";
+}
+
+bool Sensors::addSensor(
+ const std::string& sensor_name, const std::string& path,
+ const float throttling_threshold, const float shutdown_threshold,
+ const float vr_threshold, const TemperatureType& type) {
+ return sensor_name_to_data_map_.emplace(
+ sensor_name, std::make_tuple(
+ path, throttling_threshold, shutdown_threshold,
+ vr_threshold, type)).second;
+}
+
+bool Sensors::readSensorFile(
+ const std::string& sensor_name, std::string* data,
+ std::string* file_path) const {
+ std::string sensor_reading;
+ if (sensor_name_to_data_map_.find(sensor_name) ==
+ sensor_name_to_data_map_.end()) {
+ *data = "";
+ *file_path = "";
+ return false;
+ }
+
+ android::base::ReadFileToString(
+ std::get<0>(sensor_name_to_data_map_.at(sensor_name)), &sensor_reading);
+ // Strip the newline.
+ *data = ::android::base::Trim(sensor_reading);
+ *file_path = std::get<0>(sensor_name_to_data_map_.at(sensor_name));
+ return true;
+}
+
+bool Sensors::readTemperature(
+ const std::string& sensor_name, const float mult,
+ Temperature* out) const {
+ if (sensor_name_to_data_map_.find(sensor_name) ==
+ sensor_name_to_data_map_.end()) {
+ return false;
+ }
+
+ std::string sensor_reading;
+ std::string path;
+ readSensorFile(sensor_name, &sensor_reading, &path);
+
+ auto sensor = sensor_name_to_data_map_.at(sensor_name);
+ out->name = sensor_name;
+ out->currentValue = std::stoi(sensor_reading) * mult;
+ out->throttlingThreshold = std::get<1>(sensor);
+ out->shutdownThreshold = std::get<2>(sensor);
+ out->vrThrottlingThreshold = std::get<3>(sensor);
+ out->type = std::get<4>(sensor);
+
+ LOG(DEBUG) << android::base::StringPrintf(
+ "readTemperature: %s, %d, %s, %g, %g, %g, %g",
+ path.c_str(), out->type, out->name.c_str(), out->currentValue,
+ out->throttlingThreshold, out->shutdownThreshold,
+ out->vrThrottlingThreshold);
+ return true;
+}
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace thermal
+} // namespace hardware
+} // namespace android
diff --git a/thermal/sensors.h b/thermal/sensors.h
new file mode 100644
index 0000000..4924ac2
--- /dev/null
+++ b/thermal/sensors.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __SENSORS_H__
+#define __SENSORS_H__
+
+#include <string>
+#include <tuple>
+#include <unordered_map>
+
+#include <android/hardware/thermal/1.1/IThermal.h>
+
+namespace android {
+namespace hardware {
+namespace thermal {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::thermal::V1_0::Temperature;
+using ::android::hardware::thermal::V1_0::TemperatureType;
+
+class Sensors {
+ public:
+ Sensors() = default;
+ ~Sensors() = default;
+ Sensors(const Sensors&) = delete;
+ void operator=(const Sensors&) = delete;
+
+ std::string getSensorPath(const std::string& sensor_name);
+ // Returns true if add was successful, false otherwise.
+ bool addSensor(
+ const std::string& sensor_name, const std::string& path,
+ const float throttling_threshold, const float shutdown_threshold,
+ const float vr_threshold, const TemperatureType& type);
+ // If sensor is not found in the sensor names to path map, this will set
+ // data and file path to empty and return false. If the sensor is found,
+ // this function will fill in data and file_path accordingly then return
+ // true.
+ bool readSensorFile(
+ const std::string& sensor_name, std::string* data,
+ std::string* file_path) const;
+ bool readTemperature(
+ const std::string& sensor_name, const float mult,
+ Temperature* out) const;
+ size_t getNumSensors() const { return sensor_name_to_data_map_.size(); }
+
+ private:
+ // A map containing sensor names along with its thermal zone number, its
+ // thresholds, and its type. The tuple is formatted as such:
+ // <path, throttling threshold, shutdown threshold, vr threshold, type>
+ std::unordered_map<std::string, std::tuple<
+ std::string, float, float, float, TemperatureType>>
+ sensor_name_to_data_map_;
+};
+
+} // namespace implementation
+} // namespace V1_1
+} // namespace thermal
+} // namespace hardware
+} // namespace android
+
+#endif // __SENSORS_H__
+
diff --git a/thermal/thermal-helper.cpp b/thermal/thermal-helper.cpp
index 163f0fd..6e9cf17 100644
--- a/thermal/thermal-helper.cpp
+++ b/thermal/thermal-helper.cpp
@@ -20,11 +20,17 @@
#include <cmath>
#include <cstdlib>
#include <cstring>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+#include "sensors.h"
#include "thermal-helper.h"
namespace android {
@@ -33,14 +39,102 @@
namespace V1_1 {
namespace implementation {
+constexpr const char kThermalSensorsRoot[] = "/sys/class/thermal";
+constexpr char kThermalZoneDirSuffix[] = "thermal_zone";
+constexpr char kSensorTypeFileSuffix[] = "type";
+constexpr char kTemperatureFileSuffix[] = "temp";
+// This is a golden set of thermal sensor names, their types, and their
+// multiplier. Used when we read in sensor values. The tuple value stored is
+// formatted as such:
+// <temperature type, multiplier value for reading temp>
+const std::unordered_map<std::string, std::tuple<TemperatureType, float>>
+kValidThermalSensorsMap = {
+ {"tsens_tz_sensor1", {TemperatureType::CPU, 0.1}}, // CPU0
+ {"tsens_tz_sensor2", {TemperatureType::CPU, 0.1}}, // CPU1
+ {"tsens_tz_sensor4", {TemperatureType::CPU, 0.1}}, // CPU2
+ {"tsens_tz_sensor3", {TemperatureType::CPU, 0.1}}, // CPU3
+ {"tsens_tz_sensor7", {TemperatureType::CPU, 0.1}}, // CPU4
+ {"tsens_tz_sensor8", {TemperatureType::CPU, 0.1}}, // CPU5
+ {"tsens_tz_sensor9", {TemperatureType::CPU, 0.1}}, // CPU6
+ {"tsens_tz_sensor10", {TemperatureType::CPU, 0.1}}, // CPU7
+ // GPU thermal sensor.
+ {"tsens_tz_sensor13", {TemperatureType::GPU, 0.1}},
+ // Battery thermal sensor.
+ {"battery", {TemperatureType::BATTERY, 0.001}},
+ // Skin thermal sensors. We use back_therm for walleye. For taimen we use
+ // bd_therm and bd_therm2.
+ {"back_therm", {TemperatureType::SKIN, 1.}},
+ {"bd_therm", {TemperatureType::SKIN, 1.}},
+ {"bd_therm2", {TemperatureType::SKIN, 1.}},
+ // USBC thermal sensor.
+ {"usb_port_temp", {TemperatureType::UNKNOWN, 0.1}},
+};
+
+namespace {
+
using ::android::hardware::thermal::V1_0::TemperatureType;
-static unsigned int gSkinSensorNum;
static std::string gSkinSensorType;
-static unsigned int gTsensOffset;
static unsigned int gSkinThrottlingThreshold;
static unsigned int gSkinShutdownThreshold;
static unsigned int gVrThrottledBelowMin;
+Sensors gSensors;
+
+// A map containing hardcoded thresholds per sensor type. Its not const
+// because initThermal() will modify the skin sensor thresholds depending on the
+// hardware type. The tuple is formatted as follows:
+// <throttling threshold, shutdown threshold, vr threshold>
+std::unordered_map<TemperatureType, std::tuple<float, float, float>>
+gSensorTypeToThresholdsMap = {
+ {TemperatureType::CPU, {kCpuThrottlingThreshold, kCpuShutdownThreshold,
+ kCpuThrottlingThreshold}},
+ {TemperatureType::GPU, {NAN, NAN, NAN}},
+ {TemperatureType::BATTERY, {NAN, kBatteryShutdownThreshold, NAN}},
+ {TemperatureType::SKIN, {NAN, NAN, NAN}},
+ {TemperatureType::UNKNOWN, {NAN, NAN, NAN}}
+};
+
+bool initializeSensors() {
+ auto thermal_zone_dir = std::unique_ptr<DIR, int (*)(DIR*)>(
+ opendir(kThermalSensorsRoot), closedir);
+ struct dirent* dp;
+ size_t num_thermal_zones = 0;
+ while ((dp = readdir(thermal_zone_dir.get())) != nullptr) {
+ std::string dir_name(dp->d_name);
+ if (dir_name.find(kThermalZoneDirSuffix) != std::string::npos) {
+ ++num_thermal_zones;
+ }
+ }
+
+ for (size_t sensor_zone_num = 0; sensor_zone_num < num_thermal_zones;
+ ++sensor_zone_num) {
+ std::string path = android::base::StringPrintf("%s/%s%zu",
+ kThermalSensorsRoot,
+ kThermalZoneDirSuffix,
+ sensor_zone_num);
+ std::string sensor_name;
+ if (android::base::ReadFileToString(
+ path + "/" + kSensorTypeFileSuffix, &sensor_name)) {
+ sensor_name = android::base::Trim(sensor_name);
+ if (kValidThermalSensorsMap.find(sensor_name) !=
+ kValidThermalSensorsMap.end()) {
+ TemperatureType type = std::get<0>(
+ kValidThermalSensorsMap.at(sensor_name));
+ auto thresholds = gSensorTypeToThresholdsMap.at(type);
+ if (!gSensors.addSensor(
+ sensor_name, path + "/" + kTemperatureFileSuffix,
+ std::get<0>(thresholds), std::get<1>(thresholds),
+ std::get<2>(thresholds), type)) {
+ LOG(ERROR) << "Could not add " << sensor_name
+ << "to sensors map";
+ }
+ }
+ }
+ }
+ return (gSensors.getNumSensors() == kTemperatureNum);
+}
+
+} // namespace
/**
* Initialization constants based on platform
@@ -51,9 +145,6 @@
std::string hardware = android::base::GetProperty("ro.hardware", "");
if (hardware == "walleye") {
LOG(ERROR) << "Initialization on Walleye";
- gSkinSensorNum = kWalleyeSkinSensorNum;
- gSkinSensorType = kWalleyeSkinSensorType;
- gTsensOffset = kWalleyeTsensOffset;
gSkinThrottlingThreshold = kWalleyeSkinThrottlingThreshold;
gSkinShutdownThreshold = kWalleyeSkinShutdownThreshold;
gVrThrottledBelowMin = kWalleyeVrThrottledBelowMin;
@@ -61,17 +152,11 @@
std::string rev = android::base::GetProperty("ro.revision", "");
if (rev == "rev_a" || rev == "rev_b") {
LOG(ERROR) << "Initialization on Taimen pre revision C";
- gSkinSensorNum = kTaimenRabSkinSensorNum;
- gSkinSensorType = kTaimenRabSkinSensorType;
- gTsensOffset = kTaimenRabTsensOffset;
gSkinThrottlingThreshold = kTaimenRabSkinThrottlingThreshold;
gSkinShutdownThreshold = kTaimenRabSkinShutdownThreshold;
gVrThrottledBelowMin = kTaimenRabVrThrottledBelowMin;
} else {
LOG(ERROR) << "Initialization on Taimen revision C and later";
- gSkinSensorNum = kTaimenRcSkinSensorNum;
- gSkinSensorType = kTaimenRcSkinSensorType;
- gTsensOffset = kTaimenRcTsensOffset;
gSkinThrottlingThreshold = kTaimenRcSkinThrottlingThreshold;
gSkinShutdownThreshold = kTaimenRcSkinShutdownThreshold;
gVrThrottledBelowMin = kTaimenRcVrThrottledBelowMin;
@@ -80,139 +165,25 @@
LOG(ERROR) << "Unsupported hardware: " << hardware;
return false;
}
- return true;
+ gSensorTypeToThresholdsMap[TemperatureType::SKIN] =
+ std::make_tuple(gSkinThrottlingThreshold, gSkinShutdownThreshold,
+ gVrThrottledBelowMin);
+ return initializeSensors();
}
-/**
- * Reads device temperature.
- *
- * @param sensor_num Number of sensor file with temperature.
- * @param type Device temperature type.
- * @param name Device temperature name.
- * @param mult Multiplier used to translate temperature to Celsius.
- * @param throttling_threshold Throttling threshold for the temperature.
- * @param shutdown_threshold Shutdown threshold for the temperature.
- * @param out Pointer to temperature_t structure that will be filled with current
- * values.
- *
- * @return 0 on success or negative value -errno on error.
- */
-static ssize_t readTemperature(int sensor_num, TemperatureType type, const char *name, float mult,
- float throttling_threshold, float shutdown_threshold,
- float vr_throttling_threshold, Temperature *out) {
- FILE *file;
- char file_name[PATH_MAX];
- float temp;
-
- sprintf(file_name, kTemperatureFileFormat, sensor_num);
- file = fopen(file_name, "r");
- if (file == NULL) {
- PLOG(ERROR) << "readTemperature: failed to open file (" << file_name << ")";
- return -errno;
- }
- if (1 != fscanf(file, "%f", &temp)) {
- fclose(file);
- PLOG(ERROR) << "readTemperature: failed to read a float";
- return errno ? -errno : -EIO;
- }
-
- fclose(file);
-
- (*out).type = type;
- (*out).name = name;
- (*out).currentValue = temp * mult;
- (*out).throttlingThreshold = throttling_threshold;
- (*out).shutdownThreshold = shutdown_threshold;
- (*out).vrThrottlingThreshold = vr_throttling_threshold;
-
- LOG(DEBUG) << android::base::StringPrintf(
- "readTemperature: %d, %d, %s, %g, %g, %g, %g",
- sensor_num, type, name, temp * mult, throttling_threshold,
- shutdown_threshold, vr_throttling_threshold);
-
- return 0;
-}
-
-static ssize_t getCpuTemperatures(hidl_vec<Temperature> *temperatures) {
- size_t cpu;
-
- for (cpu = 0; cpu < kCpuNum; cpu++) {
- if (cpu >= temperatures->size()) {
- break;
- }
- // temperature in decidegrees Celsius.
- ssize_t result = readTemperature(kCpuTsensOffset[cpu] + gTsensOffset, TemperatureType::CPU, kCpuLabel[cpu],
- 0.1, kCpuThrottlingThreshold, kCpuShutdownThreshold, kCpuThrottlingThreshold,
- &(*temperatures)[cpu]);
- if (result != 0) {
- return result;
+ssize_t fillTemperatures(hidl_vec<Temperature>* temperatures) {
+ temperatures->resize(gSensors.getNumSensors());
+ ssize_t current_index = 0;
+ for (const auto& name_type_mult_pair : kValidThermalSensorsMap) {
+ Temperature temp;
+ if (gSensors.readTemperature(name_type_mult_pair.first,
+ std::get<1>(name_type_mult_pair.second),
+ &temp)) {
+ (*temperatures)[current_index] = temp;
+ ++current_index;
}
}
- return cpu;
-}
-
-ssize_t fillTemperatures(hidl_vec<Temperature> *temperatures) {
- ssize_t result = 0;
- size_t current_index = 0;
-
- if (temperatures == NULL || temperatures->size() < kTemperatureNum) {
- LOG(ERROR) << "fillTemperatures: incorrect buffer";
- return -EINVAL;
- }
-
- result = getCpuTemperatures(temperatures);
- if (result < 0) {
- return result;
- }
- current_index += result;
-
- // GPU temperature.
- if (current_index < temperatures->size()) {
- // temperature in decidegrees Celsius.
- result = readTemperature(gTsensOffset + kGpuTsensOffset, TemperatureType::GPU, kGpuLabel, 0.1,
- NAN, NAN, NAN, &(*temperatures)[current_index]);
- if (result < 0) {
- return result;
- }
- current_index++;
- }
-
- // Battery temperature.
- if (current_index < temperatures->size()) {
- // battery: temperature in millidegrees Celsius.
- result = readTemperature(kBatterySensorNum, TemperatureType::BATTERY, kBatteryLabel,
- 0.001, NAN, kBatteryShutdownThreshold, NAN,
- &(*temperatures)[current_index]);
- if (result < 0) {
- return result;
- }
- current_index++;
- }
-
- // Skin temperature.
- if (current_index < temperatures->size()) {
- // temperature in Celsius.
- result = readTemperature(gSkinSensorNum, TemperatureType::SKIN, kSkinLabel, 1.,
- gSkinThrottlingThreshold, gSkinShutdownThreshold, gVrThrottledBelowMin,
- &(*temperatures)[current_index]);
- if (result < 0) {
- return result;
- }
- current_index++;
- }
-
- // USB-C temperature.
- if (current_index < temperatures->size()) {
- // temperature in Celsius.
- result = readTemperature(
- kUsbcSensorNum, TemperatureType::UNKNOWN, kUsbcLabel, 0.1, NAN, NAN,
- NAN, &(*temperatures)[current_index]);
- if (result < 0) {
- return result;
- }
- current_index++;
- }
- return kTemperatureNum;
+ return current_index;
}
ssize_t fillCpuUsages(hidl_vec<CpuUsage> *cpuUsages) {
diff --git a/thermal/thermal-helper.h b/thermal/thermal-helper.h
index 46fc834..ef0b61f 100644
--- a/thermal/thermal-helper.h
+++ b/thermal/thermal-helper.h
@@ -40,45 +40,29 @@
using ::android::hardware::thermal::V1_0::CpuUsage;
using ::android::hardware::thermal::V1_0::Temperature;
+using ::android::hardware::thermal::V1_0::TemperatureType;
constexpr const char *kCpuUsageFile = "/proc/stat";
constexpr const char *kTemperatureFileFormat = "/sys/class/thermal/thermal_zone%d/temp";
constexpr const char *kCpuOnlineFileFormat = "/sys/devices/system/cpu/cpu%d/online";
// thermal-engine.conf
-constexpr unsigned int kWalleyeSkinSensorNum = 14;
-constexpr auto kWalleyeSkinSensorType = "back_therm";
-constexpr unsigned int kWalleyeTsensOffset = 16;
constexpr unsigned int kWalleyeSkinThrottlingThreshold = 40;
constexpr unsigned int kWalleyeSkinShutdownThreshold = 56;
constexpr unsigned int kWalleyeVrThrottledBelowMin = 52;
-constexpr unsigned int kTaimenRabSkinSensorNum = 13;
-constexpr auto kTaimenRabSkinSensorType = "bd_therm";
-constexpr unsigned int kTaimenRabTsensOffset = 14;
constexpr unsigned int kTaimenRabSkinThrottlingThreshold = 49;
constexpr unsigned int kTaimenRabSkinShutdownThreshold = 66;
constexpr unsigned int kTaimenRabVrThrottledBelowMin = 62;
-constexpr unsigned int kTaimenRcSkinSensorNum = 13;
-constexpr auto kTaimenRcSkinSensorType = "bd_therm2";
-constexpr unsigned int kTaimenRcTsensOffset = 14;
constexpr unsigned int kTaimenRcSkinThrottlingThreshold = 38;
constexpr unsigned int kTaimenRcSkinShutdownThreshold = 54;
constexpr unsigned int kTaimenRcVrThrottledBelowMin = 50;
-constexpr unsigned int kUsbcSensorNum = 6;
-constexpr unsigned int kBatterySensorNum = 5;
-// The gpu thermal sensor is tsens_tz_sensor13.
-constexpr unsigned int kGpuTsensOffset = 11;
constexpr unsigned int kCpuNum = 8;
-constexpr const char *kGpuLabel = "GPU";
-constexpr const char *kBatteryLabel = "battery";
-constexpr const char *kSkinLabel = "skin";
-constexpr const char *kUsbcLabel = "usbc";
-constexpr const char *kCpuLabel[kCpuNum] = {"CPU0", "CPU1", "CPU2", "CPU3", "CPU4", "CPU5", "CPU6", "CPU7"};
-constexpr int kCpuTsensOffset[kCpuNum] = {1, 2, 4, 3, 5, 6, 7, 8};
+constexpr const char *kCpuLabel[kCpuNum] = {
+ "CPU0", "CPU1", "CPU2", "CPU3", "CPU4", "CPU5", "CPU6", "CPU7"};
// Sum of kCpuNum + 4 for GPU, BATTERY, SKIN, and USB-C.
constexpr unsigned int kTemperatureNum = 4 + kCpuNum;