Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/lib/thermal: Add a thermal library

The thermal framework implements a netlink notification mechanism to
be used by the userspace to have a thermal configuration discovery,
trip point changes or violation, cooling device changes notifications,
etc...

This library provides a level of abstraction for the thermal netlink
notification allowing the userspace to connect to the notification
mechanism more easily. The library is callback oriented.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Tested-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Link: https://lore.kernel.org/r/20220420160933.347088-2-daniel.lezcano@linaro.org

+1348 -2
+1
MAINTAINERS
··· 19541 19541 F: include/linux/cpu_cooling.h 19542 19542 F: include/linux/thermal.h 19543 19543 F: include/uapi/linux/thermal.h 19544 + F: tools/lib/thermal/ 19544 19545 F: tools/thermal/ 19545 19546 19546 19547 THERMAL DRIVER FOR AMLOGIC SOCS
+12 -2
tools/Makefile
··· 31 31 @echo ' bootconfig - boot config tool' 32 32 @echo ' spi - spi tools' 33 33 @echo ' tmon - thermal monitoring and tuning tool' 34 + @echo ' thermal - thermal library' 34 35 @echo ' tracing - misc tracing tools' 35 36 @echo ' turbostat - Intel CPU idle stats and freq reporting tool' 36 37 @echo ' usb - USB testing tools' ··· 86 85 selftests: FORCE 87 86 $(call descend,testing/$@) 88 87 88 + thermal: FORCE 89 + $(call descend,lib/$@) 90 + 89 91 turbostat x86_energy_perf_policy intel-speed-select: FORCE 90 92 $(call descend,power/x86/$@) 91 93 ··· 105 101 perf selftests bootconfig spi turbostat usb \ 106 102 virtio vm bpf x86_energy_perf_policy \ 107 103 tmon freefall iio objtool kvm_stat wmi \ 108 - pci debugging tracing 104 + pci debugging tracing thermal 109 105 110 106 acpi_install: 111 107 $(call descend,power/$(@:_install=),install) ··· 118 114 119 115 selftests_install: 120 116 $(call descend,testing/$(@:_install=),install) 117 + 118 + thermal_install: 119 + $(call descend,lib/$(@:_install=),install) 121 120 122 121 turbostat_install x86_energy_perf_policy_install intel-speed-select_install: 123 122 $(call descend,power/x86/$(@:_install=),install) ··· 167 160 selftests_clean: 168 161 $(call descend,testing/$(@:_clean=),clean) 169 162 163 + thermal_clean: 164 + $(call descend,lib/thermal,clean) 165 + 170 166 turbostat_clean x86_energy_perf_policy_clean intel-speed-select_clean: 171 167 $(call descend,power/x86/$(@:_clean=),clean) 172 168 ··· 187 177 vm_clean bpf_clean iio_clean x86_energy_perf_policy_clean tmon_clean \ 188 178 freefall_clean build_clean libbpf_clean libsubcmd_clean \ 189 179 gpio_clean objtool_clean leds_clean wmi_clean pci_clean firmware_clean debugging_clean \ 190 - intel-speed-select_clean tracing_clean 180 + intel-speed-select_clean tracing_clean thermal_clean 191 181 192 182 .PHONY: FORCE
+2
tools/lib/thermal/.gitignore
··· 1 + libthermal.so* 2 + libthermal.pc
+5
tools/lib/thermal/Build
··· 1 + libthermal-y += commands.o 2 + libthermal-y += events.o 3 + libthermal-y += thermal_nl.o 4 + libthermal-y += sampling.o 5 + libthermal-y += thermal.o
+165
tools/lib/thermal/Makefile
··· 1 + # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 + # Most of this file is copied from tools/lib/perf/Makefile 3 + 4 + LIBTHERMAL_VERSION = 0 5 + LIBTHERMAL_PATCHLEVEL = 0 6 + LIBTHERMAL_EXTRAVERSION = 1 7 + 8 + MAKEFLAGS += --no-print-directory 9 + 10 + ifeq ($(srctree),) 11 + srctree := $(patsubst %/,%,$(dir $(CURDIR))) 12 + srctree := $(patsubst %/,%,$(dir $(srctree))) 13 + srctree := $(patsubst %/,%,$(dir $(srctree))) 14 + # $(info Determined 'srctree' to be $(srctree)) 15 + endif 16 + 17 + INSTALL = install 18 + 19 + # Use DESTDIR for installing into a different root directory. 20 + # This is useful for building a package. The program will be 21 + # installed in this directory as if it was the root directory. 22 + # Then the build tool can move it later. 23 + DESTDIR ?= 24 + DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' 25 + 26 + include $(srctree)/tools/scripts/Makefile.include 27 + include $(srctree)/tools/scripts/Makefile.arch 28 + 29 + ifeq ($(LP64), 1) 30 + libdir_relative = lib64 31 + else 32 + libdir_relative = lib 33 + endif 34 + 35 + prefix ?= 36 + libdir = $(prefix)/$(libdir_relative) 37 + 38 + # Shell quotes 39 + libdir_SQ = $(subst ','\'',$(libdir)) 40 + libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) 41 + 42 + ifeq ("$(origin V)", "command line") 43 + VERBOSE = $(V) 44 + endif 45 + ifndef VERBOSE 46 + VERBOSE = 0 47 + endif 48 + 49 + ifeq ($(VERBOSE),1) 50 + Q = 51 + else 52 + Q = @ 53 + endif 54 + 55 + # Set compile option CFLAGS 56 + ifdef EXTRA_CFLAGS 57 + CFLAGS := $(EXTRA_CFLAGS) 58 + else 59 + CFLAGS := -g -Wall 60 + endif 61 + 62 + INCLUDES = \ 63 + -I/usr/include/libnl3 \ 64 + -I$(srctree)/tools/lib/thermal/include \ 65 + -I$(srctree)/tools/lib/ \ 66 + -I$(srctree)/tools/include \ 67 + -I$(srctree)/tools/arch/$(SRCARCH)/include/ \ 68 + -I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \ 69 + -I$(srctree)/tools/include/uapi 70 + 71 + # Append required CFLAGS 72 + override CFLAGS += $(EXTRA_WARNINGS) 73 + override CFLAGS += -Werror -Wall 74 + override CFLAGS += -fPIC 75 + override CFLAGS += $(INCLUDES) 76 + override CFLAGS += -fvisibility=hidden 77 + override CFGLAS += -Wl,-L. 78 + override CFGLAS += -Wl,-lthermal 79 + 80 + all: 81 + 82 + export srctree OUTPUT CC LD CFLAGS V 83 + export DESTDIR DESTDIR_SQ 84 + 85 + include $(srctree)/tools/build/Makefile.include 86 + 87 + VERSION_SCRIPT := libthermal.map 88 + 89 + PATCHLEVEL = $(LIBTHERMAL_PATCHLEVEL) 90 + EXTRAVERSION = $(LIBTHERMAL_EXTRAVERSION) 91 + VERSION = $(LIBTHERMAL_VERSION).$(LIBTHERMAL_PATCHLEVEL).$(LIBTHERMAL_EXTRAVERSION) 92 + 93 + LIBTHERMAL_SO := $(OUTPUT)libthermal.so.$(VERSION) 94 + LIBTHERMAL_A := $(OUTPUT)libthermal.a 95 + LIBTHERMAL_IN := $(OUTPUT)libthermal-in.o 96 + LIBTHERMAL_PC := $(OUTPUT)libthermal.pc 97 + LIBTHERMAL_ALL := $(LIBTHERMAL_A) $(OUTPUT)libthermal.so* 98 + 99 + THERMAL_UAPI := include/uapi/linux/thermal.h 100 + 101 + $(THERMAL_UAPI): FORCE 102 + ln -sf $(srctree)/$@ $(srctree)/tools/$@ 103 + 104 + $(LIBTHERMAL_IN): FORCE 105 + $(Q)$(MAKE) $(build)=libthermal 106 + 107 + $(LIBTHERMAL_A): $(LIBTHERMAL_IN) 108 + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBTHERMAL_IN) 109 + 110 + $(LIBTHERMAL_SO): $(LIBTHERMAL_IN) 111 + $(QUIET_LINK)$(CC) --shared -Wl,-soname,libthermal.so \ 112 + -Wl,--version-script=$(VERSION_SCRIPT) $^ -o $@ 113 + @ln -sf $(@F) $(OUTPUT)libthermal.so 114 + @ln -sf $(@F) $(OUTPUT)libthermal.so.$(LIBTHERMAL_VERSION) 115 + 116 + 117 + libs: $(THERMAL_UAPI) $(LIBTHERMAL_A) $(LIBTHERMAL_SO) $(LIBTHERMAL_PC) 118 + 119 + all: fixdep 120 + $(Q)$(MAKE) libs 121 + 122 + clean: 123 + $(call QUIET_CLEAN, libthermal) $(RM) $(LIBTHERMAL_A) \ 124 + *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBTHERMAL_VERSION) .*.d .*.cmd LIBTHERMAL-CFLAGS $(LIBTHERMAL_PC) 125 + 126 + $(LIBTHERMAL_PC): 127 + $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \ 128 + -e "s|@LIBDIR@|$(libdir_SQ)|" \ 129 + -e "s|@VERSION@|$(VERSION)|" \ 130 + < libthermal.pc.template > $@ 131 + 132 + define do_install_mkdir 133 + if [ ! -d '$(DESTDIR_SQ)$1' ]; then \ 134 + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \ 135 + fi 136 + endef 137 + 138 + define do_install 139 + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ 140 + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ 141 + fi; \ 142 + $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2' 143 + endef 144 + 145 + install_lib: libs 146 + $(call QUIET_INSTALL, $(LIBTHERMAL_ALL)) \ 147 + $(call do_install_mkdir,$(libdir_SQ)); \ 148 + cp -fpR $(LIBTHERMAL_ALL) $(DESTDIR)$(libdir_SQ) 149 + 150 + install_headers: 151 + $(call QUIET_INSTALL, headers) \ 152 + $(call do_install,include/thermal.h,$(prefix)/include/thermal,644); \ 153 + 154 + install_pkgconfig: $(LIBTHERMAL_PC) 155 + $(call QUIET_INSTALL, $(LIBTHERMAL_PC)) \ 156 + $(call do_install,$(LIBTHERMAL_PC),$(libdir_SQ)/pkgconfig,644) 157 + 158 + install_doc: 159 + $(Q)$(MAKE) -C Documentation install-man install-html install-examples 160 + 161 + install: install_lib install_headers install_pkgconfig 162 + 163 + FORCE: 164 + 165 + .PHONY: all install clean FORCE
+349
tools/lib/thermal/commands.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1+ 2 + // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 + #define _GNU_SOURCE 4 + #include <errno.h> 5 + #include <stdio.h> 6 + #include <stdlib.h> 7 + #include <unistd.h> 8 + 9 + #include <thermal.h> 10 + #include "thermal_nl.h" 11 + 12 + static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = { 13 + /* Thermal zone */ 14 + [THERMAL_GENL_ATTR_TZ] = { .type = NLA_NESTED }, 15 + [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 }, 16 + [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 }, 17 + [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = NLA_NESTED }, 18 + [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 }, 19 + [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 }, 20 + [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 }, 21 + [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 }, 22 + [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 }, 23 + [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 }, 24 + [THERMAL_GENL_ATTR_TZ_NAME] = { .type = NLA_STRING }, 25 + 26 + /* Governor(s) */ 27 + [THERMAL_GENL_ATTR_TZ_GOV] = { .type = NLA_NESTED }, 28 + [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = NLA_STRING }, 29 + 30 + /* Cooling devices */ 31 + [THERMAL_GENL_ATTR_CDEV] = { .type = NLA_NESTED }, 32 + [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 }, 33 + [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 }, 34 + [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, 35 + [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING }, 36 + }; 37 + 38 + static int parse_tz_get(struct genl_info *info, struct thermal_zone **tz) 39 + { 40 + struct nlattr *attr; 41 + struct thermal_zone *__tz = NULL; 42 + size_t size = 0; 43 + int rem; 44 + 45 + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ], rem) { 46 + 47 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) { 48 + 49 + size++; 50 + 51 + __tz = realloc(__tz, sizeof(*__tz) * (size + 2)); 52 + if (!__tz) 53 + return THERMAL_ERROR; 54 + 55 + __tz[size - 1].id = nla_get_u32(attr); 56 + } 57 + 58 + 59 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) 60 + nla_strlcpy(__tz[size - 1].name, attr, 61 + THERMAL_NAME_LENGTH); 62 + } 63 + 64 + if (__tz) 65 + __tz[size].id = -1; 66 + 67 + *tz = __tz; 68 + 69 + return THERMAL_SUCCESS; 70 + } 71 + 72 + static int parse_cdev_get(struct genl_info *info, struct thermal_cdev **cdev) 73 + { 74 + struct nlattr *attr; 75 + struct thermal_cdev *__cdev = NULL; 76 + size_t size = 0; 77 + int rem; 78 + 79 + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_CDEV], rem) { 80 + 81 + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) { 82 + 83 + size++; 84 + 85 + __cdev = realloc(__cdev, sizeof(*__cdev) * (size + 2)); 86 + if (!__cdev) 87 + return THERMAL_ERROR; 88 + 89 + __cdev[size - 1].id = nla_get_u32(attr); 90 + } 91 + 92 + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) { 93 + nla_strlcpy(__cdev[size - 1].name, attr, 94 + THERMAL_NAME_LENGTH); 95 + } 96 + 97 + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_CUR_STATE) 98 + __cdev[size - 1].cur_state = nla_get_u32(attr); 99 + 100 + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_MAX_STATE) 101 + __cdev[size - 1].max_state = nla_get_u32(attr); 102 + } 103 + 104 + if (__cdev) 105 + __cdev[size].id = -1; 106 + 107 + *cdev = __cdev; 108 + 109 + return THERMAL_SUCCESS; 110 + } 111 + 112 + static int parse_tz_get_trip(struct genl_info *info, struct thermal_zone *tz) 113 + { 114 + struct nlattr *attr; 115 + struct thermal_trip *__tt = NULL; 116 + size_t size = 0; 117 + int rem; 118 + 119 + nla_for_each_nested(attr, info->attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) { 120 + 121 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) { 122 + 123 + size++; 124 + 125 + __tt = realloc(__tt, sizeof(*__tt) * (size + 2)); 126 + if (!__tt) 127 + return THERMAL_ERROR; 128 + 129 + __tt[size - 1].id = nla_get_u32(attr); 130 + } 131 + 132 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TYPE) 133 + __tt[size - 1].type = nla_get_u32(attr); 134 + 135 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_TEMP) 136 + __tt[size - 1].temp = nla_get_u32(attr); 137 + 138 + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_HYST) 139 + __tt[size - 1].hyst = nla_get_u32(attr); 140 + } 141 + 142 + if (__tt) 143 + __tt[size].id = -1; 144 + 145 + tz->trip = __tt; 146 + 147 + return THERMAL_SUCCESS; 148 + } 149 + 150 + static int parse_tz_get_temp(struct genl_info *info, struct thermal_zone *tz) 151 + { 152 + int id = -1; 153 + 154 + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) 155 + id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]); 156 + 157 + if (tz->id != id) 158 + return THERMAL_ERROR; 159 + 160 + if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]) 161 + tz->temp = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]); 162 + 163 + return THERMAL_SUCCESS; 164 + } 165 + 166 + static int parse_tz_get_gov(struct genl_info *info, struct thermal_zone *tz) 167 + { 168 + int id = -1; 169 + 170 + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) 171 + id = nla_get_u32(info->attrs[THERMAL_GENL_ATTR_TZ_ID]); 172 + 173 + if (tz->id != id) 174 + return THERMAL_ERROR; 175 + 176 + if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) { 177 + nla_strlcpy(tz->governor, 178 + info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME], 179 + THERMAL_NAME_LENGTH); 180 + } 181 + 182 + return THERMAL_SUCCESS; 183 + } 184 + 185 + static int handle_netlink(struct nl_cache_ops *unused, 186 + struct genl_cmd *cmd, 187 + struct genl_info *info, void *arg) 188 + { 189 + int ret; 190 + 191 + switch (cmd->c_id) { 192 + 193 + case THERMAL_GENL_CMD_TZ_GET_ID: 194 + ret = parse_tz_get(info, arg); 195 + break; 196 + 197 + case THERMAL_GENL_CMD_CDEV_GET: 198 + ret = parse_cdev_get(info, arg); 199 + break; 200 + 201 + case THERMAL_GENL_CMD_TZ_GET_TEMP: 202 + ret = parse_tz_get_temp(info, arg); 203 + break; 204 + 205 + case THERMAL_GENL_CMD_TZ_GET_TRIP: 206 + ret = parse_tz_get_trip(info, arg); 207 + break; 208 + 209 + case THERMAL_GENL_CMD_TZ_GET_GOV: 210 + ret = parse_tz_get_gov(info, arg); 211 + break; 212 + 213 + default: 214 + return THERMAL_ERROR; 215 + }; 216 + 217 + return ret; 218 + } 219 + 220 + static struct genl_cmd thermal_cmds[] = { 221 + { 222 + .c_id = THERMAL_GENL_CMD_TZ_GET_ID, 223 + .c_name = (char *)"List thermal zones", 224 + .c_msg_parser = handle_netlink, 225 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 226 + .c_attr_policy = thermal_genl_policy, 227 + }, 228 + { 229 + .c_id = THERMAL_GENL_CMD_TZ_GET_GOV, 230 + .c_name = (char *)"Get governor", 231 + .c_msg_parser = handle_netlink, 232 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 233 + .c_attr_policy = thermal_genl_policy, 234 + }, 235 + { 236 + .c_id = THERMAL_GENL_CMD_TZ_GET_TEMP, 237 + .c_name = (char *)"Get thermal zone temperature", 238 + .c_msg_parser = handle_netlink, 239 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 240 + .c_attr_policy = thermal_genl_policy, 241 + }, 242 + { 243 + .c_id = THERMAL_GENL_CMD_TZ_GET_TRIP, 244 + .c_name = (char *)"Get thermal zone trip points", 245 + .c_msg_parser = handle_netlink, 246 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 247 + .c_attr_policy = thermal_genl_policy, 248 + }, 249 + { 250 + .c_id = THERMAL_GENL_CMD_CDEV_GET, 251 + .c_name = (char *)"Get cooling devices", 252 + .c_msg_parser = handle_netlink, 253 + .c_maxattr = THERMAL_GENL_ATTR_MAX, 254 + .c_attr_policy = thermal_genl_policy, 255 + }, 256 + }; 257 + 258 + static struct genl_ops thermal_cmd_ops = { 259 + .o_name = (char *)"thermal", 260 + .o_cmds = thermal_cmds, 261 + .o_ncmds = ARRAY_SIZE(thermal_cmds), 262 + }; 263 + 264 + static thermal_error_t thermal_genl_auto(struct thermal_handler *th, int id, int cmd, 265 + int flags, void *arg) 266 + { 267 + struct nl_msg *msg; 268 + void *hdr; 269 + 270 + msg = nlmsg_alloc(); 271 + if (!msg) 272 + return THERMAL_ERROR; 273 + 274 + hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, thermal_cmd_ops.o_id, 275 + 0, flags, cmd, THERMAL_GENL_VERSION); 276 + if (!hdr) 277 + return THERMAL_ERROR; 278 + 279 + if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, id)) 280 + return THERMAL_ERROR; 281 + 282 + if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, genl_handle_msg, arg)) 283 + return THERMAL_ERROR; 284 + 285 + nlmsg_free(msg); 286 + 287 + return THERMAL_SUCCESS; 288 + } 289 + 290 + thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, struct thermal_zone **tz) 291 + { 292 + return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_TZ_GET_ID, 293 + NLM_F_DUMP | NLM_F_ACK, tz); 294 + } 295 + 296 + thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, struct thermal_cdev **tc) 297 + { 298 + return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, 299 + NLM_F_DUMP | NLM_F_ACK, tc); 300 + } 301 + 302 + thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, struct thermal_zone *tz) 303 + { 304 + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TRIP, 305 + 0, tz); 306 + } 307 + 308 + thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, struct thermal_zone *tz) 309 + { 310 + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); 311 + } 312 + 313 + thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, struct thermal_zone *tz) 314 + { 315 + return thermal_genl_auto(th, tz->id, THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); 316 + } 317 + 318 + thermal_error_t thermal_cmd_exit(struct thermal_handler *th) 319 + { 320 + if (genl_unregister_family(&thermal_cmd_ops)) 321 + return THERMAL_ERROR; 322 + 323 + nl_thermal_disconnect(th->sk_cmd, th->cb_cmd); 324 + 325 + return THERMAL_SUCCESS; 326 + } 327 + 328 + thermal_error_t thermal_cmd_init(struct thermal_handler *th) 329 + { 330 + int ret; 331 + int family; 332 + 333 + if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd)) 334 + return THERMAL_ERROR; 335 + 336 + ret = genl_register_family(&thermal_cmd_ops); 337 + if (ret) 338 + return THERMAL_ERROR; 339 + 340 + ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops); 341 + if (ret) 342 + return THERMAL_ERROR; 343 + 344 + family = genl_ctrl_resolve(th->sk_cmd, "nlctrl"); 345 + if (family != GENL_ID_CTRL) 346 + return THERMAL_ERROR; 347 + 348 + return THERMAL_SUCCESS; 349 + }
+164
tools/lib/thermal/events.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1+ 2 + // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 + #include <linux/netlink.h> 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <unistd.h> 7 + 8 + 9 + #include <thermal.h> 10 + #include "thermal_nl.h" 11 + 12 + /* 13 + * Optimization: fill this array to tell which event we do want to pay 14 + * attention to. That happens at init time with the ops 15 + * structure. Each ops will enable the event and the general handler 16 + * will be able to discard the event if there is not ops associated 17 + * with it. 18 + */ 19 + static int enabled_ops[__THERMAL_GENL_EVENT_MAX]; 20 + 21 + static int handle_thermal_event(struct nl_msg *n, void *arg) 22 + { 23 + struct nlmsghdr *nlh = nlmsg_hdr(n); 24 + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); 25 + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; 26 + struct thermal_handler_param *thp = arg; 27 + struct thermal_events_ops *ops = &thp->th->ops->events; 28 + 29 + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); 30 + 31 + arg = thp->arg; 32 + 33 + /* 34 + * This is an event we don't care of, bail out. 35 + */ 36 + if (!enabled_ops[genlhdr->cmd]) 37 + return THERMAL_SUCCESS; 38 + 39 + switch (genlhdr->cmd) { 40 + 41 + case THERMAL_GENL_EVENT_TZ_CREATE: 42 + return ops->tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]), 43 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); 44 + 45 + case THERMAL_GENL_EVENT_TZ_DELETE: 46 + return ops->tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); 47 + 48 + case THERMAL_GENL_EVENT_TZ_ENABLE: 49 + return ops->tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); 50 + 51 + case THERMAL_GENL_EVENT_TZ_DISABLE: 52 + return ops->tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); 53 + 54 + case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE: 55 + return ops->trip_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 56 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), 57 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), 58 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), 59 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); 60 + 61 + case THERMAL_GENL_EVENT_TZ_TRIP_ADD: 62 + return ops->trip_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 63 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), 64 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), 65 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), 66 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); 67 + 68 + case THERMAL_GENL_EVENT_TZ_TRIP_DELETE: 69 + return ops->trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 70 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), arg); 71 + 72 + case THERMAL_GENL_EVENT_TZ_TRIP_UP: 73 + return ops->trip_high(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 74 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), 75 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); 76 + 77 + case THERMAL_GENL_EVENT_TZ_TRIP_DOWN: 78 + return ops->trip_low(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 79 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), 80 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); 81 + 82 + case THERMAL_GENL_EVENT_CDEV_ADD: 83 + return ops->cdev_add(nla_get_string(attrs[THERMAL_GENL_ATTR_CDEV_NAME]), 84 + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), 85 + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]), arg); 86 + 87 + case THERMAL_GENL_EVENT_CDEV_DELETE: 88 + return ops->cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), arg); 89 + 90 + case THERMAL_GENL_EVENT_CDEV_STATE_UPDATE: 91 + return ops->cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), 92 + nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]), arg); 93 + 94 + case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: 95 + return ops->gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 96 + nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); 97 + default: 98 + return -1; 99 + } 100 + } 101 + 102 + static void thermal_events_ops_init(struct thermal_events_ops *ops) 103 + { 104 + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops->tz_create; 105 + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops->tz_delete; 106 + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops->tz_disable; 107 + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops->tz_enable; 108 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops->trip_high; 109 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops->trip_low; 110 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops->trip_change; 111 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops->trip_add; 112 + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops->trip_delete; 113 + enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops->cdev_add; 114 + enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops->cdev_delete; 115 + enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops->cdev_update; 116 + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops->gov_change; 117 + } 118 + 119 + thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg) 120 + { 121 + struct thermal_handler_param thp = { .th = th, .arg = arg }; 122 + 123 + if (!th) 124 + return THERMAL_ERROR; 125 + 126 + if (nl_cb_set(th->cb_event, NL_CB_VALID, NL_CB_CUSTOM, 127 + handle_thermal_event, &thp)) 128 + return THERMAL_ERROR; 129 + 130 + return nl_recvmsgs(th->sk_event, th->cb_event); 131 + } 132 + 133 + int thermal_events_fd(struct thermal_handler *th) 134 + { 135 + if (!th) 136 + return -1; 137 + 138 + return nl_socket_get_fd(th->sk_event); 139 + } 140 + 141 + thermal_error_t thermal_events_exit(struct thermal_handler *th) 142 + { 143 + if (nl_unsubscribe_thermal(th->sk_event, th->cb_event, 144 + THERMAL_GENL_EVENT_GROUP_NAME)) 145 + return THERMAL_ERROR; 146 + 147 + nl_thermal_disconnect(th->sk_event, th->cb_event); 148 + 149 + return THERMAL_SUCCESS; 150 + } 151 + 152 + thermal_error_t thermal_events_init(struct thermal_handler *th) 153 + { 154 + thermal_events_ops_init(&th->ops->events); 155 + 156 + if (nl_thermal_connect(&th->sk_event, &th->cb_event)) 157 + return THERMAL_ERROR; 158 + 159 + if (nl_subscribe_thermal(th->sk_event, th->cb_event, 160 + THERMAL_GENL_EVENT_GROUP_NAME)) 161 + return THERMAL_ERROR; 162 + 163 + return THERMAL_SUCCESS; 164 + }
+142
tools/lib/thermal/include/thermal.h
··· 1 + /* SPDX-License-Identifier: LGPL-2.1+ */ 2 + /* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */ 3 + #ifndef __LIBTHERMAL_H 4 + #define __LIBTHERMAL_H 5 + 6 + #include <linux/thermal.h> 7 + 8 + #ifndef LIBTHERMAL_API 9 + #define LIBTHERMAL_API __attribute__((visibility("default"))) 10 + #endif 11 + 12 + #ifdef __cplusplus 13 + extern "C" { 14 + #endif 15 + 16 + struct thermal_sampling_ops { 17 + int (*tz_temp)(int tz_id, int temp, void *arg); 18 + }; 19 + 20 + struct thermal_events_ops { 21 + int (*tz_create)(const char *name, int tz_id, void *arg); 22 + int (*tz_delete)(int tz_id, void *arg); 23 + int (*tz_enable)(int tz_id, void *arg); 24 + int (*tz_disable)(int tz_id, void *arg); 25 + int (*trip_high)(int tz_id, int trip_id, int temp, void *arg); 26 + int (*trip_low)(int tz_id, int trip_id, int temp, void *arg); 27 + int (*trip_add)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg); 28 + int (*trip_change)(int tz_id, int trip_id, int type, int temp, int hyst, void *arg); 29 + int (*trip_delete)(int tz_id, int trip_id, void *arg); 30 + int (*cdev_add)(const char *name, int cdev_id, int max_state, void *arg); 31 + int (*cdev_delete)(int cdev_id, void *arg); 32 + int (*cdev_update)(int cdev_id, int cur_state, void *arg); 33 + int (*gov_change)(int tz_id, const char *gov_name, void *arg); 34 + }; 35 + 36 + struct thermal_ops { 37 + struct thermal_sampling_ops sampling; 38 + struct thermal_events_ops events; 39 + }; 40 + 41 + struct thermal_trip { 42 + int id; 43 + int type; 44 + int temp; 45 + int hyst; 46 + }; 47 + 48 + struct thermal_zone { 49 + int id; 50 + int temp; 51 + char name[THERMAL_NAME_LENGTH]; 52 + char governor[THERMAL_NAME_LENGTH]; 53 + struct thermal_trip *trip; 54 + }; 55 + 56 + struct thermal_cdev { 57 + int id; 58 + char name[THERMAL_NAME_LENGTH]; 59 + int max_state; 60 + int min_state; 61 + int cur_state; 62 + }; 63 + 64 + typedef enum { 65 + THERMAL_ERROR = -1, 66 + THERMAL_SUCCESS = 0, 67 + } thermal_error_t; 68 + 69 + struct thermal_handler; 70 + 71 + typedef int (*cb_tz_t)(struct thermal_zone *, void *); 72 + 73 + typedef int (*cb_tt_t)(struct thermal_trip *, void *); 74 + 75 + typedef int (*cb_tc_t)(struct thermal_cdev *, void *); 76 + 77 + LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg); 78 + 79 + LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg); 80 + 81 + LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg); 82 + 83 + LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, 84 + const char *name); 85 + 86 + LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id); 87 + 88 + LIBTHERMAL_API struct thermal_zone *thermal_zone_discover(struct thermal_handler *th); 89 + 90 + LIBTHERMAL_API struct thermal_handler *thermal_init(struct thermal_ops *ops); 91 + 92 + LIBTHERMAL_API void thermal_exit(struct thermal_handler *th); 93 + 94 + /* 95 + * Netlink thermal events 96 + */ 97 + LIBTHERMAL_API thermal_error_t thermal_events_exit(struct thermal_handler *th); 98 + 99 + LIBTHERMAL_API thermal_error_t thermal_events_init(struct thermal_handler *th); 100 + 101 + LIBTHERMAL_API thermal_error_t thermal_events_handle(struct thermal_handler *th, void *arg); 102 + 103 + LIBTHERMAL_API int thermal_events_fd(struct thermal_handler *th); 104 + 105 + /* 106 + * Netlink thermal commands 107 + */ 108 + LIBTHERMAL_API thermal_error_t thermal_cmd_exit(struct thermal_handler *th); 109 + 110 + LIBTHERMAL_API thermal_error_t thermal_cmd_init(struct thermal_handler *th); 111 + 112 + LIBTHERMAL_API thermal_error_t thermal_cmd_get_tz(struct thermal_handler *th, 113 + struct thermal_zone **tz); 114 + 115 + LIBTHERMAL_API thermal_error_t thermal_cmd_get_cdev(struct thermal_handler *th, 116 + struct thermal_cdev **tc); 117 + 118 + LIBTHERMAL_API thermal_error_t thermal_cmd_get_trip(struct thermal_handler *th, 119 + struct thermal_zone *tz); 120 + 121 + LIBTHERMAL_API thermal_error_t thermal_cmd_get_governor(struct thermal_handler *th, 122 + struct thermal_zone *tz); 123 + 124 + LIBTHERMAL_API thermal_error_t thermal_cmd_get_temp(struct thermal_handler *th, 125 + struct thermal_zone *tz); 126 + 127 + /* 128 + * Netlink thermal samples 129 + */ 130 + LIBTHERMAL_API thermal_error_t thermal_sampling_exit(struct thermal_handler *th); 131 + 132 + LIBTHERMAL_API thermal_error_t thermal_sampling_init(struct thermal_handler *th); 133 + 134 + LIBTHERMAL_API thermal_error_t thermal_sampling_handle(struct thermal_handler *th, void *arg); 135 + 136 + LIBTHERMAL_API int thermal_sampling_fd(struct thermal_handler *th); 137 + 138 + #endif /* __LIBTHERMAL_H */ 139 + 140 + #ifdef __cplusplus 141 + } 142 + #endif
+25
tools/lib/thermal/libthermal.map
··· 1 + LIBTHERMAL_0.0.1 { 2 + global: 3 + thermal_init; 4 + for_each_thermal_zone; 5 + for_each_thermal_trip; 6 + for_each_thermal_cdev; 7 + thermal_zone_find_by_name; 8 + thermal_zone_find_by_id; 9 + thermal_zone_discover; 10 + thermal_init; 11 + thermal_events_init; 12 + thermal_events_handle; 13 + thermal_events_fd; 14 + thermal_cmd_init; 15 + thermal_cmd_get_tz; 16 + thermal_cmd_get_cdev; 17 + thermal_cmd_get_trip; 18 + thermal_cmd_get_governor; 19 + thermal_cmd_get_temp; 20 + thermal_sampling_init; 21 + thermal_sampling_handle; 22 + thermal_sampling_fd; 23 + local: 24 + *; 25 + };
+12
tools/lib/thermal/libthermal.pc.template
··· 1 + # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 + 3 + prefix=@PREFIX@ 4 + libdir=@LIBDIR@ 5 + includedir=${prefix}/include 6 + 7 + Name: libthermal 8 + Description: thermal library 9 + Requires: libnl-3.0 libnl-genl-3.0 10 + Version: @VERSION@ 11 + Libs: -L${libdir} -lnl-genl-3 -lnl-3 12 + Cflags: -I${includedir} -I{include}/libnl3
+75
tools/lib/thermal/sampling.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1+ 2 + // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 + #include <errno.h> 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <unistd.h> 7 + 8 + #include <thermal.h> 9 + #include "thermal_nl.h" 10 + 11 + static int handle_thermal_sample(struct nl_msg *n, void *arg) 12 + { 13 + struct nlmsghdr *nlh = nlmsg_hdr(n); 14 + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); 15 + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; 16 + struct thermal_handler_param *thp = arg; 17 + struct thermal_handler *th = thp->th; 18 + 19 + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); 20 + 21 + switch (genlhdr->cmd) { 22 + 23 + case THERMAL_GENL_SAMPLING_TEMP: 24 + return th->ops->sampling.tz_temp( 25 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), 26 + nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); 27 + default: 28 + return THERMAL_ERROR; 29 + } 30 + } 31 + 32 + thermal_error_t thermal_sampling_handle(struct thermal_handler *th, void *arg) 33 + { 34 + struct thermal_handler_param thp = { .th = th, .arg = arg }; 35 + 36 + if (!th) 37 + return THERMAL_ERROR; 38 + 39 + if (nl_cb_set(th->cb_sampling, NL_CB_VALID, NL_CB_CUSTOM, 40 + handle_thermal_sample, &thp)) 41 + return THERMAL_ERROR; 42 + 43 + return nl_recvmsgs(th->sk_sampling, th->cb_sampling); 44 + } 45 + 46 + int thermal_sampling_fd(struct thermal_handler *th) 47 + { 48 + if (!th) 49 + return -1; 50 + 51 + return nl_socket_get_fd(th->sk_sampling); 52 + } 53 + 54 + thermal_error_t thermal_sampling_exit(struct thermal_handler *th) 55 + { 56 + if (nl_unsubscribe_thermal(th->sk_sampling, th->cb_sampling, 57 + THERMAL_GENL_EVENT_GROUP_NAME)) 58 + return THERMAL_ERROR; 59 + 60 + nl_thermal_disconnect(th->sk_sampling, th->cb_sampling); 61 + 62 + return THERMAL_SUCCESS; 63 + } 64 + 65 + thermal_error_t thermal_sampling_init(struct thermal_handler *th) 66 + { 67 + if (nl_thermal_connect(&th->sk_sampling, &th->cb_sampling)) 68 + return THERMAL_ERROR; 69 + 70 + if (nl_subscribe_thermal(th->sk_sampling, th->cb_sampling, 71 + THERMAL_GENL_SAMPLING_GROUP_NAME)) 72 + return THERMAL_ERROR; 73 + 74 + return THERMAL_SUCCESS; 75 + }
+135
tools/lib/thermal/thermal.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1+ 2 + // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 + #include <stdio.h> 4 + #include <thermal.h> 5 + 6 + #include "thermal_nl.h" 7 + 8 + int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, void *arg) 9 + { 10 + int i, ret = 0; 11 + 12 + if (!cdev) 13 + return 0; 14 + 15 + for (i = 0; cdev[i].id != -1; i++) 16 + ret |= cb(&cdev[i], arg); 17 + 18 + return ret; 19 + } 20 + 21 + int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, void *arg) 22 + { 23 + int i, ret = 0; 24 + 25 + if (!tt) 26 + return 0; 27 + 28 + for (i = 0; tt[i].id != -1; i++) 29 + ret |= cb(&tt[i], arg); 30 + 31 + return ret; 32 + } 33 + 34 + int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, void *arg) 35 + { 36 + int i, ret = 0; 37 + 38 + if (!tz) 39 + return 0; 40 + 41 + for (i = 0; tz[i].id != -1; i++) 42 + ret |= cb(&tz[i], arg); 43 + 44 + return ret; 45 + } 46 + 47 + struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone *tz, 48 + const char *name) 49 + { 50 + int i; 51 + 52 + if (!tz || !name) 53 + return NULL; 54 + 55 + for (i = 0; tz[i].id != -1; i++) { 56 + if (!strcmp(tz[i].name, name)) 57 + return &tz[i]; 58 + } 59 + 60 + return NULL; 61 + } 62 + 63 + struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone *tz, int id) 64 + { 65 + int i; 66 + 67 + if (!tz || id < 0) 68 + return NULL; 69 + 70 + for (i = 0; tz[i].id != -1; i++) { 71 + if (tz[i].id == id) 72 + return &tz[i]; 73 + } 74 + 75 + return NULL; 76 + } 77 + 78 + static int __thermal_zone_discover(struct thermal_zone *tz, void *th) 79 + { 80 + if (thermal_cmd_get_trip(th, tz) < 0) 81 + return -1; 82 + 83 + if (thermal_cmd_get_governor(th, tz)) 84 + return -1; 85 + 86 + return 0; 87 + } 88 + 89 + struct thermal_zone *thermal_zone_discover(struct thermal_handler *th) 90 + { 91 + struct thermal_zone *tz; 92 + 93 + if (thermal_cmd_get_tz(th, &tz) < 0) 94 + return NULL; 95 + 96 + if (for_each_thermal_zone(tz, __thermal_zone_discover, th)) 97 + return NULL; 98 + 99 + return tz; 100 + } 101 + 102 + void thermal_exit(struct thermal_handler *th) 103 + { 104 + thermal_cmd_exit(th); 105 + thermal_events_exit(th); 106 + thermal_sampling_exit(th); 107 + 108 + free(th); 109 + } 110 + 111 + struct thermal_handler *thermal_init(struct thermal_ops *ops) 112 + { 113 + struct thermal_handler *th; 114 + 115 + th = malloc(sizeof(*th)); 116 + if (!th) 117 + return NULL; 118 + th->ops = ops; 119 + 120 + if (thermal_events_init(th)) 121 + goto out_free; 122 + 123 + if (thermal_sampling_init(th)) 124 + goto out_free; 125 + 126 + if (thermal_cmd_init(th)) 127 + goto out_free; 128 + 129 + return th; 130 + 131 + out_free: 132 + free(th); 133 + 134 + return NULL; 135 + }
+215
tools/lib/thermal/thermal_nl.c
··· 1 + // SPDX-License-Identifier: LGPL-2.1+ 2 + // Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> 3 + #include <errno.h> 4 + #include <stdio.h> 5 + #include <stdlib.h> 6 + #include <unistd.h> 7 + 8 + #include <thermal.h> 9 + #include "thermal_nl.h" 10 + 11 + struct handler_args { 12 + const char *group; 13 + int id; 14 + }; 15 + 16 + static __thread int err; 17 + static __thread int done; 18 + 19 + static int nl_seq_check_handler(struct nl_msg *msg, void *arg) 20 + { 21 + return NL_OK; 22 + } 23 + 24 + static int nl_error_handler(struct sockaddr_nl *nla, struct nlmsgerr *nl_err, 25 + void *arg) 26 + { 27 + int *ret = arg; 28 + 29 + if (ret) 30 + *ret = nl_err->error; 31 + 32 + return NL_STOP; 33 + } 34 + 35 + static int nl_finish_handler(struct nl_msg *msg, void *arg) 36 + { 37 + int *ret = arg; 38 + 39 + if (ret) 40 + *ret = 1; 41 + 42 + return NL_OK; 43 + } 44 + 45 + static int nl_ack_handler(struct nl_msg *msg, void *arg) 46 + { 47 + int *ret = arg; 48 + 49 + if (ret) 50 + *ret = 1; 51 + 52 + return NL_OK; 53 + } 54 + 55 + int nl_send_msg(struct nl_sock *sock, struct nl_cb *cb, struct nl_msg *msg, 56 + int (*rx_handler)(struct nl_msg *, void *), void *data) 57 + { 58 + if (!rx_handler) 59 + return THERMAL_ERROR; 60 + 61 + err = nl_send_auto_complete(sock, msg); 62 + if (err < 0) 63 + return err; 64 + 65 + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); 66 + 67 + err = done = 0; 68 + 69 + while (err == 0 && done == 0) 70 + nl_recvmsgs(sock, cb); 71 + 72 + return err; 73 + } 74 + 75 + static int nl_family_handler(struct nl_msg *msg, void *arg) 76 + { 77 + struct handler_args *grp = arg; 78 + struct nlattr *tb[CTRL_ATTR_MAX + 1]; 79 + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 80 + struct nlattr *mcgrp; 81 + int rem_mcgrp; 82 + 83 + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 84 + genlmsg_attrlen(gnlh, 0), NULL); 85 + 86 + if (!tb[CTRL_ATTR_MCAST_GROUPS]) 87 + return THERMAL_ERROR; 88 + 89 + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], rem_mcgrp) { 90 + 91 + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + 1]; 92 + 93 + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, 94 + nla_data(mcgrp), nla_len(mcgrp), NULL); 95 + 96 + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || 97 + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) 98 + continue; 99 + 100 + if (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), 101 + grp->group, 102 + nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) 103 + continue; 104 + 105 + grp->id = nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); 106 + 107 + break; 108 + } 109 + 110 + return THERMAL_SUCCESS; 111 + } 112 + 113 + static int nl_get_multicast_id(struct nl_sock *sock, struct nl_cb *cb, 114 + const char *family, const char *group) 115 + { 116 + struct nl_msg *msg; 117 + int ret = 0, ctrlid; 118 + struct handler_args grp = { 119 + .group = group, 120 + .id = -ENOENT, 121 + }; 122 + 123 + msg = nlmsg_alloc(); 124 + if (!msg) 125 + return THERMAL_ERROR; 126 + 127 + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); 128 + 129 + genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); 130 + 131 + nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family); 132 + 133 + ret = nl_send_msg(sock, cb, msg, nl_family_handler, &grp); 134 + if (ret) 135 + goto nla_put_failure; 136 + 137 + ret = grp.id; 138 + 139 + nla_put_failure: 140 + nlmsg_free(msg); 141 + return ret; 142 + } 143 + 144 + int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb) 145 + { 146 + struct nl_cb *cb; 147 + struct nl_sock *sock; 148 + 149 + cb = nl_cb_alloc(NL_CB_DEFAULT); 150 + if (!cb) 151 + return THERMAL_ERROR; 152 + 153 + sock = nl_socket_alloc(); 154 + if (!sock) 155 + goto out_cb_free; 156 + 157 + if (genl_connect(sock)) 158 + goto out_socket_free; 159 + 160 + if (nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err) || 161 + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, nl_finish_handler, &done) || 162 + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, &done) || 163 + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl_seq_check_handler, &done)) 164 + return THERMAL_ERROR; 165 + 166 + *nl_sock = sock; 167 + *nl_cb = cb; 168 + 169 + return THERMAL_SUCCESS; 170 + 171 + out_socket_free: 172 + nl_socket_free(sock); 173 + out_cb_free: 174 + nl_cb_put(cb); 175 + return THERMAL_ERROR; 176 + } 177 + 178 + void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb) 179 + { 180 + nl_close(nl_sock); 181 + nl_socket_free(nl_sock); 182 + nl_cb_put(nl_cb); 183 + } 184 + 185 + int nl_unsubscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, 186 + const char *group) 187 + { 188 + int mcid; 189 + 190 + mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME, 191 + group); 192 + if (mcid < 0) 193 + return THERMAL_ERROR; 194 + 195 + if (nl_socket_drop_membership(nl_sock, mcid)) 196 + return THERMAL_ERROR; 197 + 198 + return THERMAL_SUCCESS; 199 + } 200 + 201 + int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, 202 + const char *group) 203 + { 204 + int mcid; 205 + 206 + mcid = nl_get_multicast_id(nl_sock, nl_cb, THERMAL_GENL_FAMILY_NAME, 207 + group); 208 + if (mcid < 0) 209 + return THERMAL_ERROR; 210 + 211 + if (nl_socket_add_membership(nl_sock, mcid)) 212 + return THERMAL_ERROR; 213 + 214 + return THERMAL_SUCCESS; 215 + }
+46
tools/lib/thermal/thermal_nl.h
··· 1 + /* SPDX-License-Identifier: LGPL-2.1+ */ 2 + /* Copyright (C) 2022, Linaro Ltd - Daniel Lezcano <daniel.lezcano@linaro.org> */ 3 + #ifndef __THERMAL_H 4 + #define __THERMAL_H 5 + 6 + #include <netlink/netlink.h> 7 + #include <netlink/genl/genl.h> 8 + #include <netlink/genl/mngt.h> 9 + #include <netlink/genl/ctrl.h> 10 + 11 + struct thermal_handler { 12 + int done; 13 + int error; 14 + struct thermal_ops *ops; 15 + struct nl_msg *msg; 16 + struct nl_sock *sk_event; 17 + struct nl_sock *sk_sampling; 18 + struct nl_sock *sk_cmd; 19 + struct nl_cb *cb_cmd; 20 + struct nl_cb *cb_event; 21 + struct nl_cb *cb_sampling; 22 + }; 23 + 24 + struct thermal_handler_param { 25 + struct thermal_handler *th; 26 + void *arg; 27 + }; 28 + 29 + /* 30 + * Low level netlink 31 + */ 32 + extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, 33 + const char *group); 34 + 35 + extern int nl_unsubscribe_thermal(struct nl_sock *nl_sock, struct nl_cb *nl_cb, 36 + const char *group); 37 + 38 + extern int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb **nl_cb); 39 + 40 + extern void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb *nl_cb); 41 + 42 + extern int nl_send_msg(struct nl_sock *sock, struct nl_cb *nl_cb, struct nl_msg *msg, 43 + int (*rx_handler)(struct nl_msg *, void *), 44 + void *data); 45 + 46 + #endif /* __THERMAL_H */