Messages in this thread | | | Subject | Re: [PATCH] tools/lib/thermal: Add a thermal library | From | srinivas pandruvada <> | Date | Fri, 04 Feb 2022 11:32:05 -0800 |
| |
On Fri, 2022-02-04 at 18:11 +0100, Rafael J. Wysocki wrote: > CC Rui and Srinivas > > On Fri, Feb 4, 2022 at 5:15 PM Daniel Lezcano > <daniel.lezcano@linaro.org> wrote: > > > > 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. > > > > As it is the very first iteration, the API may be subject to > > changes. For this reason, the documentation will be provided after > > those are stabilized. > > So shouldn't this be an RFC? > > Also, I would prefer documentation to be provided or at least some > intended usage examples to be given. > > > Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> > > --- > > tools/Makefile | 14 +- > > tools/include/uapi/linux/thermal.h | 91 ++++++ This is duplicating the file at include/uapi/linux. I know this is done for other tools also. In my use I am copying and using symbolic link.
BTW I have potential usage of netlink targeted for next release in linux tools.
https://github.com/spandruvada/linux-kernel/blob/intel-sst/tools/power/x86/intel-speed-select/hfi-events.c
I can replace with libthermal calls once ready. I will check the code below next week.
Thanks, Srinivas
> > tools/lib/thermal/.gitignore | 2 + > > tools/lib/thermal/Build | 5 + > > tools/lib/thermal/Makefile | 162 +++++++++++ > > tools/lib/thermal/commands.c | 338 > > +++++++++++++++++++++++ > > tools/lib/thermal/events.c | 152 ++++++++++ > > tools/lib/thermal/include/thermal.h | 128 +++++++++ > > tools/lib/thermal/libthermal.map | 25 ++ > > tools/lib/thermal/libthermal.pc.template | 12 + > > tools/lib/thermal/sampling.c | 63 +++++ > > tools/lib/thermal/thermal.c | 116 ++++++++ > > tools/lib/thermal/thermal_nl.c | 201 ++++++++++++++ > > tools/lib/thermal/thermal_nl.h | 42 +++ > > 14 files changed, 1349 insertions(+), 2 deletions(-) > > create mode 100644 tools/include/uapi/linux/thermal.h > > create mode 100644 tools/lib/thermal/.gitignore > > create mode 100644 tools/lib/thermal/Build > > create mode 100644 tools/lib/thermal/Makefile > > create mode 100644 tools/lib/thermal/commands.c > > create mode 100644 tools/lib/thermal/events.c > > create mode 100644 tools/lib/thermal/include/thermal.h > > create mode 100644 tools/lib/thermal/libthermal.map > > create mode 100644 tools/lib/thermal/libthermal.pc.template > > create mode 100644 tools/lib/thermal/sampling.c > > create mode 100644 tools/lib/thermal/thermal.c > > create mode 100644 tools/lib/thermal/thermal_nl.c > > create mode 100644 tools/lib/thermal/thermal_nl.h > > > > diff --git a/tools/Makefile b/tools/Makefile > > index db2f7b8ebed5..c253cbd27c06 100644 > > --- a/tools/Makefile > > +++ b/tools/Makefile > > @@ -31,6 +31,7 @@ help: > > @echo ' bootconfig - boot config tool' > > @echo ' spi - spi tools' > > @echo ' tmon - thermal monitoring and > > tuning tool' > > + @echo ' thermal - thermal library' > > @echo ' tracing - misc tracing tools' > > @echo ' turbostat - Intel CPU idle stats and > > freq reporting tool' > > @echo ' usb - USB testing tools' > > @@ -85,6 +86,9 @@ perf: FORCE > > selftests: FORCE > > $(call descend,testing/$@) > > > > +thermal: FORCE > > + $(call descend,lib/$@) > > + > > turbostat x86_energy_perf_policy intel-speed-select: FORCE > > $(call descend,power/x86/$@) > > > > @@ -101,7 +105,7 @@ all: acpi cgroup counter cpupower gpio hv > > firewire \ > > perf selftests bootconfig spi turbostat usb \ > > virtio vm bpf x86_energy_perf_policy \ > > tmon freefall iio objtool kvm_stat wmi \ > > - pci debugging tracing > > + pci debugging tracing thermal > > > > acpi_install: > > $(call descend,power/$(@:_install=),install) > > @@ -115,6 +119,9 @@ cgroup_install counter_install firewire_install > > gpio_install hv_install iio_inst > > selftests_install: > > $(call descend,testing/$(@:_install=),install) > > > > +thermal_install: > > + $(call descend,lib/$(@:_install=),install) > > + > > turbostat_install x86_energy_perf_policy_install intel-speed- > > select_install: > > $(call descend,power/x86/$(@:_install=),install) > > > > @@ -160,6 +167,9 @@ perf_clean: > > selftests_clean: > > $(call descend,testing/$(@:_clean=),clean) > > > > +thermal_clean: > > + $(call descend,lib/thermal,clean) > > + > > turbostat_clean x86_energy_perf_policy_clean intel-speed- > > select_clean: > > $(call descend,power/x86/$(@:_clean=),clean) > > > > @@ -177,6 +187,6 @@ clean: acpi_clean cgroup_clean counter_clean > > cpupower_clean hv_clean firewire_cl > > vm_clean bpf_clean iio_clean > > x86_energy_perf_policy_clean tmon_clean \ > > freefall_clean build_clean libbpf_clean > > libsubcmd_clean \ > > gpio_clean objtool_clean leds_clean wmi_clean > > pci_clean firmware_clean debugging_clean \ > > - intel-speed-select_clean tracing_clean > > + intel-speed-select_clean tracing_clean > > thermal_clean > > > > .PHONY: FORCE > > diff --git a/tools/include/uapi/linux/thermal.h > > b/tools/include/uapi/linux/thermal.h > > new file mode 100644 > > index 000000000000..9aa2fedfa309 > > --- /dev/null > > +++ b/tools/include/uapi/linux/thermal.h > > @@ -0,0 +1,91 @@ > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > > +#ifndef _UAPI_LINUX_THERMAL_H > > +#define _UAPI_LINUX_THERMAL_H > > + > > +#define THERMAL_NAME_LENGTH 20 > > + > > +enum thermal_device_mode { > > + THERMAL_DEVICE_DISABLED = 0, > > + THERMAL_DEVICE_ENABLED, > > +}; > > + > > +enum thermal_trip_type { > > + THERMAL_TRIP_ACTIVE = 0, > > + THERMAL_TRIP_PASSIVE, > > + THERMAL_TRIP_HOT, > > + THERMAL_TRIP_CRITICAL, > > +}; > > + > > +/* Adding event notification support elements */ > > +#define THERMAL_GENL_FAMILY_NAME "thermal" > > +#define THERMAL_GENL_VERSION 0x01 > > +#define THERMAL_GENL_SAMPLING_GROUP_NAME "sampling" > > +#define THERMAL_GENL_EVENT_GROUP_NAME "event" > > + > > +/* Attributes of thermal_genl_family */ > > +enum thermal_genl_attr { > > + THERMAL_GENL_ATTR_UNSPEC, > > + THERMAL_GENL_ATTR_TZ, > > + THERMAL_GENL_ATTR_TZ_ID, > > + THERMAL_GENL_ATTR_TZ_TEMP, > > + THERMAL_GENL_ATTR_TZ_TRIP, > > + THERMAL_GENL_ATTR_TZ_TRIP_ID, > > + THERMAL_GENL_ATTR_TZ_TRIP_TYPE, > > + THERMAL_GENL_ATTR_TZ_TRIP_TEMP, > > + THERMAL_GENL_ATTR_TZ_TRIP_HYST, > > + THERMAL_GENL_ATTR_TZ_MODE, > > + THERMAL_GENL_ATTR_TZ_NAME, > > + THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT, > > + THERMAL_GENL_ATTR_TZ_GOV, > > + THERMAL_GENL_ATTR_TZ_GOV_NAME, > > + THERMAL_GENL_ATTR_CDEV, > > + THERMAL_GENL_ATTR_CDEV_ID, > > + THERMAL_GENL_ATTR_CDEV_CUR_STATE, > > + THERMAL_GENL_ATTR_CDEV_MAX_STATE, > > + THERMAL_GENL_ATTR_CDEV_NAME, > > + THERMAL_GENL_ATTR_GOV_NAME, > > + > > + __THERMAL_GENL_ATTR_MAX, > > +}; > > +#define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) > > + > > +enum thermal_genl_sampling { > > + THERMAL_GENL_SAMPLING_TEMP, > > + __THERMAL_GENL_SAMPLING_MAX, > > +}; > > +#define THERMAL_GENL_SAMPLING_MAX (__THERMAL_GENL_SAMPLING_MAX - > > 1) > > + > > +/* Events of thermal_genl_family */ > > +enum thermal_genl_event { > > + THERMAL_GENL_EVENT_UNSPEC, > > + THERMAL_GENL_EVENT_TZ_CREATE, /* Thermal zone > > creation */ > > + THERMAL_GENL_EVENT_TZ_DELETE, /* Thermal zone > > deletion */ > > + THERMAL_GENL_EVENT_TZ_DISABLE, /* Thermal zone > > disabled */ > > + THERMAL_GENL_EVENT_TZ_ENABLE, /* Thermal zone > > enabled */ > > + THERMAL_GENL_EVENT_TZ_TRIP_UP, /* Trip point > > crossed the way up */ > > + THERMAL_GENL_EVENT_TZ_TRIP_DOWN, /* Trip point > > crossed the way down */ > > + THERMAL_GENL_EVENT_TZ_TRIP_CHANGE, /* Trip point > > changed */ > > + THERMAL_GENL_EVENT_TZ_TRIP_ADD, /* Trip point added > > */ > > + THERMAL_GENL_EVENT_TZ_TRIP_DELETE, /* Trip point > > deleted */ > > + THERMAL_GENL_EVENT_CDEV_ADD, /* Cdev bound to > > the thermal zone */ > > + THERMAL_GENL_EVENT_CDEV_DELETE, /* Cdev unbound */ > > + THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state > > updated */ > > + THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy > > changed */ > > + __THERMAL_GENL_EVENT_MAX, > > +}; > > +#define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) > > + > > +/* Commands supported by the thermal_genl_family */ > > +enum thermal_genl_cmd { > > + THERMAL_GENL_CMD_UNSPEC, > > + THERMAL_GENL_CMD_TZ_GET_ID, /* List of thermal zones id > > */ > > + THERMAL_GENL_CMD_TZ_GET_TRIP, /* List of thermal trips */ > > + THERMAL_GENL_CMD_TZ_GET_TEMP, /* Get the thermal zone > > temperature */ > > + THERMAL_GENL_CMD_TZ_GET_GOV, /* Get the thermal zone > > governor */ > > + THERMAL_GENL_CMD_TZ_GET_MODE, /* Get the thermal zone > > mode */ > > + THERMAL_GENL_CMD_CDEV_GET, /* List of cdev id */ > > + __THERMAL_GENL_CMD_MAX, > > +}; > > +#define THERMAL_GENL_CMD_MAX (__THERMAL_GENL_CMD_MAX - 1) > > + > > +#endif /* _UAPI_LINUX_THERMAL_H */ > > diff --git a/tools/lib/thermal/.gitignore > > b/tools/lib/thermal/.gitignore > > new file mode 100644 > > index 000000000000..5d2aeda80fea > > --- /dev/null > > +++ b/tools/lib/thermal/.gitignore > > @@ -0,0 +1,2 @@ > > +libthermal.so* > > +libthermal.pc > > diff --git a/tools/lib/thermal/Build b/tools/lib/thermal/Build > > new file mode 100644 > > index 000000000000..4a892d9e24f9 > > --- /dev/null > > +++ b/tools/lib/thermal/Build > > @@ -0,0 +1,5 @@ > > +libthermal-y += commands.o > > +libthermal-y += events.o > > +libthermal-y += thermal_nl.o > > +libthermal-y += sampling.o > > +libthermal-y += thermal.o > > diff --git a/tools/lib/thermal/Makefile > > b/tools/lib/thermal/Makefile > > new file mode 100644 > > index 000000000000..1a08fd6a28d8 > > --- /dev/null > > +++ b/tools/lib/thermal/Makefile > > @@ -0,0 +1,162 @@ > > +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) > > +# Most of this file is copied from tools/lib/perf/Makefile > > + > > +LIBTHERMAL_VERSION = 0 > > +LIBTHERMAL_PATCHLEVEL = 0 > > +LIBTHERMAL_EXTRAVERSION = 1 > > + > > +MAKEFLAGS += --no-print-directory > > + > > +ifeq ($(srctree),) > > +srctree := $(patsubst %/,%,$(dir $(CURDIR))) > > +srctree := $(patsubst %/,%,$(dir $(srctree))) > > +srctree := $(patsubst %/,%,$(dir $(srctree))) > > +# $(info Determined 'srctree' to be $(srctree)) > > +endif > > + > > +INSTALL = install > > + > > +# Use DESTDIR for installing into a different root directory. > > +# This is useful for building a package. The program will be > > +# installed in this directory as if it was the root directory. > > +# Then the build tool can move it later. > > +DESTDIR ?= > > +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' > > + > > +include $(srctree)/tools/scripts/Makefile.include > > +include $(srctree)/tools/scripts/Makefile.arch > > + > > +ifeq ($(LP64), 1) > > + libdir_relative = lib64 > > +else > > + libdir_relative = lib > > +endif > > + > > +prefix ?= > > +libdir = $(prefix)/$(libdir_relative) > > + > > +# Shell quotes > > +libdir_SQ = $(subst ','\'',$(libdir)) > > +libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) > > + > > +ifeq ("$(origin V)", "command line") > > + VERBOSE = $(V) > > +endif > > +ifndef VERBOSE > > + VERBOSE = 0 > > +endif > > + > > +ifeq ($(VERBOSE),1) > > + Q = > > +else > > + Q = @ > > +endif > > + > > +# Set compile option CFLAGS > > +ifdef EXTRA_CFLAGS > > + CFLAGS := $(EXTRA_CFLAGS) > > +else > > + CFLAGS := -g -Wall > > +endif > > + > > +INCLUDES = \ > > +-I/usr/include/libnl3 \ > > +-I$(srctree)/tools/lib/thermal/include \ > > +-I$(srctree)/tools/lib/ \ > > +-I$(srctree)/tools/include \ > > +-I$(srctree)/tools/arch/$(SRCARCH)/include/ \ > > +-I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \ > > +-I$(srctree)/tools/include/uapi > > + > > +# Append required CFLAGS > > +override CFLAGS += $(EXTRA_WARNINGS) > > +override CFLAGS += -Werror -Wall > > +override CFLAGS += -fPIC > > +override CFLAGS += $(INCLUDES) > > +override CFLAGS += -fvisibility=hidden > > +override CFGLAS += -Wl,-L. > > +override CFGLAS += -Wl,-lthermal > > + > > +all: > > + > > +export srctree OUTPUT CC LD CFLAGS V > > +export DESTDIR DESTDIR_SQ > > + > > +include $(srctree)/tools/build/Makefile.include > > + > > +VERSION_SCRIPT := libthermal.map > > + > > +PATCHLEVEL = $(LIBTHERMAL_PATCHLEVEL) > > +EXTRAVERSION = $(LIBTHERMAL_EXTRAVERSION) > > +VERSION = > > $(LIBTHERMAL_VERSION).$(LIBTHERMAL_PATCHLEVEL).$(LIBTHERMAL_EXTRAVE > > RSION) > > + > > +LIBTHERMAL_SO := $(OUTPUT)libthermal.so.$(VERSION) > > +LIBTHERMAL_A := $(OUTPUT)libthermal.a > > +LIBTHERMAL_IN := $(OUTPUT)libthermal-in.o > > +LIBTHERMAL_PC := $(OUTPUT)libthermal.pc > > + > > +LIBTHERMAL_ALL := $(LIBTHERMAL_A) $(OUTPUT)libthermal.so* > > + > > +$(LIBTHERMAL_IN): FORCE > > + $(Q)$(MAKE) $(build)=libthermal > > + > > +$(LIBTHERMAL_A): $(LIBTHERMAL_IN) > > + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBTHERMAL_IN) > > + > > +$(LIBTHERMAL_SO): $(LIBTHERMAL_IN) > > + $(QUIET_LINK)$(CC) --shared -Wl,-soname,libthermal.so \ > > + -Wl,--version- > > script=$(VERSION_SCRIPT) $^ -o $@ > > + @ln -sf $(@F) $(OUTPUT)libthermal.so > > + @ln -sf $(@F) $(OUTPUT)libthermal.so.$(LIBTHERMAL_VERSION) > > + > > + > > +libs: $(LIBTHERMAL_A) $(LIBTHERMAL_SO) $(LIBTHERMAL_PC) > > + > > +all: fixdep > > + $(Q)$(MAKE) libs > > + > > +clean: > > + $(call QUIET_CLEAN, libthermal) $(RM) $(LIBTHERMAL_A) \ > > + *.o *~ *.a *.so *.so.$(VERSION) > > *.so.$(LIBTHERMAL_VERSION) .*.d .*.cmd LIBTHERMAL-CFLAGS > > $(LIBTHERMAL_PC) > > + > > +$(LIBTHERMAL_PC): > > + $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \ > > + -e "s|@LIBDIR@|$(libdir_SQ)|" \ > > + -e "s|@VERSION@|$(VERSION)|" \ > > + < libthermal.pc.template > $@ > > + > > +define do_install_mkdir > > + if [ ! -d '$(DESTDIR_SQ)$1' ]; then \ > > + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \ > > + fi > > +endef > > + > > +define do_install > > + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ > > + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ > > + fi; \ > > + $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2' > > +endef > > + > > +install_lib: libs > > + $(call QUIET_INSTALL, $(LIBTHERMAL_ALL)) \ > > + $(call do_install_mkdir,$(libdir_SQ)); \ > > + cp -fpR $(LIBTHERMAL_ALL) $(DESTDIR)$(libdir_SQ) > > + > > +install_headers: > > + $(call QUIET_INSTALL, headers) \ > > + $(call > > do_install,include/thermal.h,$(prefix)/include/thermal,644); \ > > + > > +install_pkgconfig: $(LIBTHERMAL_PC) > > + $(call QUIET_INSTALL, $(LIBTHERMAL_PC)) \ > > + $(call > > do_install,$(LIBTHERMAL_PC),$(libdir_SQ)/pkgconfig,644) > > + > > +install_doc: > > + $(Q)$(MAKE) -C Documentation install-man install-html > > install-examples > > + > > +#install: install_lib install_headers install_pkgconfig > > install_doc > > +install: install_lib install_headers install_pkgconfig > > + > > +FORCE: > > + > > +.PHONY: all install clean FORCE > > diff --git a/tools/lib/thermal/commands.c > > b/tools/lib/thermal/commands.c > > new file mode 100644 > > index 000000000000..ad63a2c929ee > > --- /dev/null > > +++ b/tools/lib/thermal/commands.c > > @@ -0,0 +1,338 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#define _GNU_SOURCE > > +#include <errno.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <unistd.h> > > + > > +#include <thermal.h> > > +#include "thermal_nl.h" > > + > > +static struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX > > + 1] = { > > + /* Thermal zone */ > > + [THERMAL_GENL_ATTR_TZ] = { .type = > > NLA_NESTED }, > > + [THERMAL_GENL_ATTR_TZ_ID] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_TEMP] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_TRIP] = { .type = > > NLA_NESTED }, > > + [THERMAL_GENL_ATTR_TZ_TRIP_ID] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_TRIP_TEMP] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_TRIP_TYPE] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_TRIP_HYST] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_MODE] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_CDEV_WEIGHT] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_TZ_NAME] = { .type = > > NLA_STRING }, > > + > > + /* Governor(s) */ > > + [THERMAL_GENL_ATTR_TZ_GOV] = { .type = > > NLA_NESTED }, > > + [THERMAL_GENL_ATTR_TZ_GOV_NAME] = { .type = > > NLA_STRING }, > > + > > + /* Cooling devices */ > > + [THERMAL_GENL_ATTR_CDEV] = { .type = > > NLA_NESTED }, > > + [THERMAL_GENL_ATTR_CDEV_ID] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_CDEV_CUR_STATE] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 > > }, > > + [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = > > NLA_STRING }, > > +}; > > + > > +static int parse_tz_get(struct genl_info *info, struct > > thermal_zone **tz) > > +{ > > + struct nlattr *attr; > > + struct thermal_zone *__tz = NULL; > > + size_t size = 0; > > + int rem; > > + > > + nla_for_each_nested(attr, info- > > >attrs[THERMAL_GENL_ATTR_TZ], rem) { > > + > > + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_ID) { > > + > > + size++; > > + > > + __tz = realloc(__tz, sizeof(*__tz) * (size > > + 2)); > > + if (!__tz) > > + return -1; > > + > > + __tz[size - 1].id = nla_get_u32(attr); > > + } > > + > > + > > + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_NAME) > > + nla_strlcpy(__tz[size - 1].name, attr, > > + THERMAL_NAME_LENGTH); > > + } > > + > > + /* > > + * We end the array of thermal zones > > + */ > > + __tz[size].id = -1; > > + > > + *tz = __tz; > > + > > + return 0; > > +} > > + > > +static int parse_cdev_get(struct genl_info *info, struct > > thermal_cdev **cdev) > > +{ > > + struct nlattr *attr; > > + struct thermal_cdev *__cdev = NULL; > > + size_t size = 0; > > + int rem; > > + > > + nla_for_each_nested(attr, info- > > >attrs[THERMAL_GENL_ATTR_CDEV], rem) { > > + > > + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_ID) { > > + > > + size++; > > + > > + __cdev = realloc(__cdev, sizeof(*__cdev) * > > (size + 2)); > > + if (!__cdev) > > + return -1; > > + > > + __cdev[size - 1].id = nla_get_u32(attr); > > + } > > + > > + if (nla_type(attr) == THERMAL_GENL_ATTR_CDEV_NAME) > > { > > + nla_strlcpy(__cdev[size - 1].name, attr, > > + THERMAL_NAME_LENGTH); > > + } > > + > > + if (nla_type(attr) == > > THERMAL_GENL_ATTR_CDEV_CUR_STATE) { > > + __cdev[size - 1].cur_state = > > nla_get_u32(attr); > > + } > > + > > + if (nla_type(attr) == > > THERMAL_GENL_ATTR_CDEV_MAX_STATE) { > > + __cdev[size - 1].max_state = > > nla_get_u32(attr); > > + } > > + } > > + > > + __cdev[size].id = -1; > > + > > + *cdev = __cdev; > > + > > + return 0; > > +} > > + > > +static int parse_tz_get_trip(struct genl_info *info, struct > > thermal_zone *tz) > > +{ > > + struct nlattr *attr; > > + struct thermal_trip *__tt = NULL; > > + size_t size = 0; > > + int rem; > > + > > + nla_for_each_nested(attr, info- > > >attrs[THERMAL_GENL_ATTR_TZ_TRIP], rem) { > > + > > + if (nla_type(attr) == THERMAL_GENL_ATTR_TZ_TRIP_ID) > > { > > + > > + size++; > > + > > + __tt = realloc(__tt, sizeof(*__tt) * (size > > + 2)); > > + if (!__tt) > > + return -1; > > + > > + __tt[size - 1].id = nla_get_u32(attr); > > + } > > + > > + if (nla_type(attr) == > > THERMAL_GENL_ATTR_TZ_TRIP_TYPE) > > + __tt[size - 1].type = nla_get_u32(attr); > > + > > + if (nla_type(attr) == > > THERMAL_GENL_ATTR_TZ_TRIP_TEMP) > > + __tt[size - 1].temp = nla_get_u32(attr); > > + > > + if (nla_type(attr) == > > THERMAL_GENL_ATTR_TZ_TRIP_HYST) > > + __tt[size - 1].hyst = nla_get_u32(attr); > > + } > > + > > + __tt[size].id = -1; > > + > > + tz->trip = __tt; > > + > > + return 0; > > +} > > + > > +static int parse_tz_get_temp(struct genl_info *info, struct > > thermal_zone *tz) > > +{ > > + int id = -1; > > + > > + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) > > + id = nla_get_u32(info- > > >attrs[THERMAL_GENL_ATTR_TZ_ID]); > > + > > + if (tz->id != id) > > + return -1; > > + > > + if (info->attrs[THERMAL_GENL_ATTR_TZ_TEMP]) > > + tz->temp = nla_get_u32(info- > > >attrs[THERMAL_GENL_ATTR_TZ_TEMP]); > > + > > + return 0; > > +} > > + > > +static int parse_tz_get_gov(struct genl_info *info, struct > > thermal_zone *tz) > > +{ > > + int id = -1; > > + > > + if (info->attrs[THERMAL_GENL_ATTR_TZ_ID]) > > + id = nla_get_u32(info- > > >attrs[THERMAL_GENL_ATTR_TZ_ID]); > > + > > + if (tz->id != id) > > + return -1; > > + > > + if (info->attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME]) { > > + nla_strlcpy(tz->governor, > > + info- > > >attrs[THERMAL_GENL_ATTR_TZ_GOV_NAME], > > + THERMAL_NAME_LENGTH); > > + } > > + > > + return 0; > > +} > > + > > +static int handle_netlink(struct nl_cache_ops *unused, > > + struct genl_cmd *cmd, > > + struct genl_info *info, void *arg) > > +{ > > + switch (cmd->c_id) { > > + > > + case THERMAL_GENL_CMD_TZ_GET_ID: > > + parse_tz_get(info, arg); > > + break; > > + > > + case THERMAL_GENL_CMD_CDEV_GET: > > + parse_cdev_get(info, arg); > > + break; > > + > > + case THERMAL_GENL_CMD_TZ_GET_TEMP: > > + parse_tz_get_temp(info, arg); > > + break; > > + > > + case THERMAL_GENL_CMD_TZ_GET_TRIP: > > + parse_tz_get_trip(info, arg); > > + break; > > + > > + case THERMAL_GENL_CMD_TZ_GET_GOV: > > + parse_tz_get_gov(info, arg); > > + break; > > + > > + default: > > + return -1; > > + }; > > + > > + return 0; > > +} > > + > > +static struct genl_cmd thermal_cmds[] = { > > + { > > + .c_id = THERMAL_GENL_CMD_TZ_GET_ID, > > + .c_name = (char *)"List thermal zones", > > + .c_msg_parser = handle_netlink, > > + .c_maxattr = THERMAL_GENL_ATTR_MAX, > > + .c_attr_policy = thermal_genl_policy, > > + }, > > + { > > + .c_id = THERMAL_GENL_CMD_TZ_GET_GOV, > > + .c_name = (char *)"Get governor", > > + .c_msg_parser = handle_netlink, > > + .c_maxattr = THERMAL_GENL_ATTR_MAX, > > + .c_attr_policy = thermal_genl_policy, > > + }, > > + { > > + .c_id = THERMAL_GENL_CMD_TZ_GET_TEMP, > > + .c_name = (char *)"Get thermal zone > > temperature", > > + .c_msg_parser = handle_netlink, > > + .c_maxattr = THERMAL_GENL_ATTR_MAX, > > + .c_attr_policy = thermal_genl_policy, > > + }, > > + { > > + .c_id = THERMAL_GENL_CMD_TZ_GET_TRIP, > > + .c_name = (char *)"Get thermal zone trip > > points", > > + .c_msg_parser = handle_netlink, > > + .c_maxattr = THERMAL_GENL_ATTR_MAX, > > + .c_attr_policy = thermal_genl_policy, > > + }, > > + { > > + .c_id = THERMAL_GENL_CMD_CDEV_GET, > > + .c_name = (char *)"Get cooling devices", > > + .c_msg_parser = handle_netlink, > > + .c_maxattr = THERMAL_GENL_ATTR_MAX, > > + .c_attr_policy = thermal_genl_policy, > > + }, > > +}; > > + > > +static struct genl_ops thermal_cmd_ops = { > > + .o_name = (char *)"thermal", > > + .o_cmds = thermal_cmds, > > + .o_ncmds = ARRAY_SIZE(thermal_cmds), > > +}; > > + > > +static int thermal_genl_auto(struct thermal_handler *th, int id, > > int cmd, > > + int flags, void *arg) > > +{ > > + struct nl_msg *msg; > > + void *hdr; > > + > > + msg = nlmsg_alloc(); > > + if (!msg) > > + return -1; > > + > > + hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, > > thermal_cmd_ops.o_id, > > + 0, flags, cmd, THERMAL_GENL_VERSION); > > + if (!hdr) > > + return -1; > > + > > + if (id >= 0 && nla_put_u32(msg, THERMAL_GENL_ATTR_TZ_ID, > > id)) > > + return -1; > > + > > + if (nl_send_msg(th->sk_cmd, th->cb_cmd, msg, > > genl_handle_msg, arg)) > > + return -1; > > + > > + nlmsg_free(msg); > > + > > + return 0; > > +} > > + > > +int thermal_cmd_get_tz(struct thermal_handler *th, struct > > thermal_zone **tz) > > +{ > > + return thermal_genl_auto(th, -1, > > THERMAL_GENL_CMD_TZ_GET_ID, > > + NLM_F_DUMP | NLM_F_ACK, tz); > > +} > > + > > +int thermal_cmd_get_cdev(struct thermal_handler *th, struct > > thermal_cdev **tc) > > +{ > > + return thermal_genl_auto(th, -1, THERMAL_GENL_CMD_CDEV_GET, > > + NLM_F_DUMP | NLM_F_ACK, tc); > > +} > > + > > +int thermal_cmd_get_trip(struct thermal_handler *th, struct > > thermal_zone *tz) > > +{ > > + return thermal_genl_auto(th, tz->id, > > THERMAL_GENL_CMD_TZ_GET_TRIP, > > + 0, tz); > > +} > > + > > +int thermal_cmd_get_governor(struct thermal_handler *th, struct > > thermal_zone *tz) > > +{ > > + return thermal_genl_auto(th, tz->id, > > THERMAL_GENL_CMD_TZ_GET_GOV, 0, tz); > > +} > > + > > +int thermal_cmd_get_temp(struct thermal_handler *th, struct > > thermal_zone *tz) > > +{ > > + return thermal_genl_auto(th, tz->id, > > THERMAL_GENL_CMD_TZ_GET_TEMP, 0, tz); > > +} > > + > > +int thermal_cmd_init(struct thermal_handler *th) > > +{ > > + int ret; > > + int family; > > + > > + if (nl_thermal_connect(&th->sk_cmd, &th->cb_cmd)) > > + return -1; > > + > > + ret = genl_register_family(&thermal_cmd_ops); > > + if (ret) > > + return -1; > > + > > + ret = genl_ops_resolve(th->sk_cmd, &thermal_cmd_ops); > > + if (ret) > > + return -1; > > + > > + family = genl_ctrl_resolve(th->sk_cmd, "nlctrl"); > > + if (family != GENL_ID_CTRL) > > + return -1; > > + > > + return 0; > > +} > > diff --git a/tools/lib/thermal/events.c > > b/tools/lib/thermal/events.c > > new file mode 100644 > > index 000000000000..67803c7038d6 > > --- /dev/null > > +++ b/tools/lib/thermal/events.c > > @@ -0,0 +1,152 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#include <linux/netlink.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <unistd.h> > > + > > + > > +#include <thermal.h> > > +#include "thermal_nl.h" > > + > > +/* > > + * Optimization: fill this array to tell which event we do want to > > pay > > + * attention to. That happens at init time with the ops > > + * structure. Each ops will enable the event and the general > > handler > > + * will be able to discard the event if there is not ops > > associated > > + * with it. > > + */ > > +static int enabled_ops[__THERMAL_GENL_EVENT_MAX]; > > + > > +static int handle_thermal_event(struct nl_msg *n, void *arg) > > +{ > > + struct nlmsghdr *nlh = nlmsg_hdr(n); > > + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); > > + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; > > + struct thermal_handler_param *thp = arg; > > + struct thermal_events_ops *ops = &thp->th->ops->events; > > + > > + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); > > + > > + arg = thp->arg; > > + > > + /* > > + * This is an event we don't care of, bail out. > > + */ > > + if (!enabled_ops[genlhdr->cmd]) > > + return 0; > > + > > + switch (genlhdr->cmd) { > > + > > + case THERMAL_GENL_EVENT_TZ_CREATE: > > + return ops- > > >tz_create(nla_get_string(attrs[THERMAL_GENL_ATTR_TZ_NAME]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_DELETE: > > + return ops- > > >tz_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_ENABLE: > > + return ops- > > >tz_enable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_DISABLE: > > + return ops- > > >tz_disable(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_TRIP_CHANGE: > > + return ops- > > >trip_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_TRIP_ADD: > > + return ops- > > >trip_add(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TYPE]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_TEMP]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_HYST]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_TRIP_DELETE: > > + return ops- > > >trip_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_TRIP_UP: > > + return ops- > > >trip_high(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_TRIP_DOWN: > > + return ops- > > >trip_low(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TRIP_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); > > + > > + case THERMAL_GENL_EVENT_CDEV_ADD: > > + return ops- > > >cdev_add(nla_get_string(attrs[THERMAL_GENL_ATTR_CDEV_NAME]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_MAX_STATE]), arg); > > + > > + case THERMAL_GENL_EVENT_CDEV_DELETE: > > + return ops- > > >cdev_delete(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), arg); > > + > > + case THERMAL_GENL_EVENT_CDEV_STATE_UPDATE: > > + return ops- > > >cdev_update(nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_CDEV_CUR_STATE]), arg); > > + > > + case THERMAL_GENL_EVENT_TZ_GOV_CHANGE: > > + return ops- > > >gov_change(nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_string(attrs[THERMAL_GENL_ATTR_GOV_NAME]), arg); > > + default: > > + return -1; > > + } > > +} > > + > > +static void thermal_events_ops_init(struct thermal_events_ops > > *ops) > > +{ > > + enabled_ops[THERMAL_GENL_EVENT_TZ_CREATE] = !!ops- > > >tz_create; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_DELETE] = !!ops- > > >tz_delete; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_DISABLE] = !!ops- > > >tz_disable; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_ENABLE] = !!ops- > > >tz_enable; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_UP] = !!ops- > > >trip_high; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DOWN] = !!ops- > > >trip_low; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_CHANGE] = !!ops- > > >trip_change; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_ADD] = !!ops- > > >trip_add; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_TRIP_DELETE] = !!ops- > > >trip_delete; > > + enabled_ops[THERMAL_GENL_EVENT_CDEV_ADD] = !!ops- > > >cdev_add; > > + enabled_ops[THERMAL_GENL_EVENT_CDEV_DELETE] = !!ops- > > >cdev_delete; > > + enabled_ops[THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = !!ops- > > >cdev_update; > > + enabled_ops[THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = !!ops- > > >gov_change; > > +} > > + > > +int thermal_events_handle(struct thermal_handler *th, void *arg) > > +{ > > + struct thermal_handler_param thp = { .th = th, .arg = arg > > }; > > + > > + if (!th) > > + return -1; > > + > > + if (nl_cb_set(th->cb_event, NL_CB_VALID, NL_CB_CUSTOM, > > + handle_thermal_event, &thp)) > > + return -1; > > + > > + return nl_recvmsgs(th->sk_event, th->cb_event); > > +} > > + > > +int thermal_events_fd(struct thermal_handler *th) > > +{ > > + if (!th) > > + return -1; > > + > > + return nl_socket_get_fd(th->sk_event); > > +} > > + > > +int thermal_events_init(struct thermal_handler *th) > > +{ > > + thermal_events_ops_init(&th->ops->events); > > + > > + if (nl_thermal_connect(&th->sk_event, &th->cb_event)) > > + return -1; > > + > > + if (nl_subscribe_thermal(th->sk_event, th->cb_event, > > + THERMAL_GENL_EVENT_GROUP_NAME)) > > + return -1; > > + > > + return 0; > > +} > > diff --git a/tools/lib/thermal/include/thermal.h > > b/tools/lib/thermal/include/thermal.h > > new file mode 100644 > > index 000000000000..e1453d063915 > > --- /dev/null > > +++ b/tools/lib/thermal/include/thermal.h > > @@ -0,0 +1,128 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#ifndef __LIBTHERMAL_H > > +#define __LIBTHERMAL_H > > + > > +#include <linux/thermal.h> > > + > > +#ifndef LIBTHERMAL_API > > +#define LIBTHERMAL_API __attribute__((visibility("default"))) > > +#endif > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif > > + > > +struct thermal_sampling_ops { > > + int (*tz_temp)(int tz_id, int temp, void *arg); > > +}; > > + > > +struct thermal_events_ops { > > + int (*tz_create)(const char *name, int tz_id, void *arg); > > + int (*tz_delete)(int tz_id, void *arg); > > + int (*tz_enable)(int tz_id, void *arg); > > + int (*tz_disable)(int tz_id, void *arg); > > + int (*trip_high)(int tz_id, int trip_id, int temp, void > > *arg); > > + int (*trip_low)(int tz_id, int trip_id, int temp, void > > *arg); > > + int (*trip_add)(int tz_id, int trip_id, int type, int temp, > > int hyst, void *arg); > > + int (*trip_change)(int tz_id, int trip_id, int type, int > > temp, int hyst, void *arg); > > + int (*trip_delete)(int tz_id, int trip_id, void *arg); > > + int (*cdev_add)(const char *name, int cdev_id, int > > max_state, void *arg); > > + int (*cdev_delete)(int cdev_id, void *arg); > > + int (*cdev_update)(int cdev_id, int cur_state, void *arg); > > + int (*gov_change)(int tz_id, const char *gov_name, void > > *arg); > > +}; > > + > > +struct thermal_ops { > > + struct thermal_sampling_ops sampling; > > + struct thermal_events_ops events; > > +}; > > + > > +struct thermal_trip { > > + int id; > > + int type; > > + int temp; > > + int hyst; > > +}; > > + > > +struct thermal_zone { > > + int id; > > + int temp; > > + char name[THERMAL_NAME_LENGTH]; > > + char governor[THERMAL_NAME_LENGTH]; > > + struct thermal_trip *trip; > > +}; > > + > > +struct thermal_cdev { > > + int id; > > + char name[THERMAL_NAME_LENGTH]; > > + int max_state; > > + int min_state; > > + int cur_state; > > +}; > > + > > +struct thermal_handler; > > + > > +typedef int (*cb_tz_t)(struct thermal_zone *, void *); > > + > > +typedef int (*cb_tt_t)(struct thermal_trip *, void *); > > + > > +typedef int (*cb_tc_t)(struct thermal_cdev *, void *); > > + > > +LIBTHERMAL_API int for_each_thermal_zone(struct thermal_zone *tz, > > cb_tz_t cb, void *arg); > > + > > +LIBTHERMAL_API int for_each_thermal_trip(struct thermal_trip *tt, > > cb_tt_t cb, void *arg); > > + > > +LIBTHERMAL_API int for_each_thermal_cdev(struct thermal_cdev > > *cdev, cb_tc_t cb, void *arg); > > + > > +LIBTHERMAL_API struct thermal_zone > > *thermal_zone_find_by_name(struct thermal_zone *tz, > > + const > > char *name); > > + > > +LIBTHERMAL_API struct thermal_zone *thermal_zone_find_by_id(struct > > thermal_zone *tz, int id); > > + > > +LIBTHERMAL_API struct thermal_zone *thermal_zone_discover(struct > > thermal_handler *th); > > + > > +LIBTHERMAL_API struct thermal_handler *thermal_init(struct > > thermal_ops *ops); > > + > > +/* > > + * Netlink thermal events > > + */ > > +LIBTHERMAL_API int thermal_events_init(struct thermal_handler > > *th); > > + > > +LIBTHERMAL_API int thermal_events_handle(struct thermal_handler > > *th, void *arg); > > + > > +LIBTHERMAL_API int thermal_events_fd(struct thermal_handler *th); > > + > > +/* > > + * Netlink thermal commands > > + */ > > +LIBTHERMAL_API int thermal_cmd_init(struct thermal_handler *th); > > + > > +LIBTHERMAL_API int thermal_cmd_get_tz(struct thermal_handler *th, > > + struct thermal_zone **tz); > > + > > +LIBTHERMAL_API int thermal_cmd_get_cdev(struct thermal_handler > > *th, > > + struct thermal_cdev **tc); > > + > > +LIBTHERMAL_API int thermal_cmd_get_trip(struct thermal_handler > > *th, > > + struct thermal_zone *tz); > > + > > +LIBTHERMAL_API int thermal_cmd_get_governor(struct thermal_handler > > *th, > > + struct thermal_zone > > *tz); > > + > > +LIBTHERMAL_API int thermal_cmd_get_temp(struct thermal_handler > > *th, > > + struct thermal_zone *tz); > > + > > +/* > > + * Netlink thermal samples > > + */ > > +LIBTHERMAL_API int thermal_sampling_init(struct thermal_handler > > *th); > > + > > +LIBTHERMAL_API int thermal_sampling_handle(struct thermal_handler > > *th, void *arg); > > + > > +LIBTHERMAL_API int thermal_sampling_fd(struct thermal_handler > > *th); > > + > > +#endif /* __LIBTHERMAL_H */ > > + > > +#ifdef __cplusplus > > +} > > +#endif > > diff --git a/tools/lib/thermal/libthermal.map > > b/tools/lib/thermal/libthermal.map > > new file mode 100644 > > index 000000000000..d5e77738c7a4 > > --- /dev/null > > +++ b/tools/lib/thermal/libthermal.map > > @@ -0,0 +1,25 @@ > > +LIBTHERMAL_0.0.1 { > > + global: > > + thermal_init; > > + for_each_thermal_zone; > > + for_each_thermal_trip; > > + for_each_thermal_cdev; > > + thermal_zone_find_by_name; > > + thermal_zone_find_by_id; > > + thermal_zone_discover; > > + thermal_init; > > + thermal_events_init; > > + thermal_events_handle; > > + thermal_events_fd; > > + thermal_cmd_init; > > + thermal_cmd_get_tz; > > + thermal_cmd_get_cdev; > > + thermal_cmd_get_trip; > > + thermal_cmd_get_governor; > > + thermal_cmd_get_temp; > > + thermal_sampling_init; > > + thermal_sampling_handle; > > + thermal_sampling_fd; > > +local: > > + *; > > +}; > > diff --git a/tools/lib/thermal/libthermal.pc.template > > b/tools/lib/thermal/libthermal.pc.template > > new file mode 100644 > > index 000000000000..6f3769731b59 > > --- /dev/null > > +++ b/tools/lib/thermal/libthermal.pc.template > > @@ -0,0 +1,12 @@ > > +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) > > + > > +prefix=@PREFIX@ > > +libdir=@LIBDIR@ > > +includedir=${prefix}/include > > + > > +Name: libthermal > > +Description: thermal library > > +Requires: libnl-3.0 libnl-genl-3.0 > > +Version: @VERSION@ > > +Libs: -L${libdir} -lnl-genl-3 -lnl-3 > > +Cflags: -I${includedir} -I{include}/libnl3 > > diff --git a/tools/lib/thermal/sampling.c > > b/tools/lib/thermal/sampling.c > > new file mode 100644 > > index 000000000000..c2247fd23c0c > > --- /dev/null > > +++ b/tools/lib/thermal/sampling.c > > @@ -0,0 +1,63 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#include <errno.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <unistd.h> > > + > > +#include <thermal.h> > > +#include "thermal_nl.h" > > + > > +static int handle_thermal_sample(struct nl_msg *n, void *arg) > > +{ > > + struct nlmsghdr *nlh = nlmsg_hdr(n); > > + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); > > + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; > > + struct thermal_handler_param *thp = arg; > > + struct thermal_handler *th = thp->th; > > + > > + genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); > > + > > + switch (genlhdr->cmd) { > > + > > + case THERMAL_GENL_SAMPLING_TEMP: > > + return th->ops->sampling.tz_temp( > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_ID]), > > + > > nla_get_u32(attrs[THERMAL_GENL_ATTR_TZ_TEMP]), arg); > > + default: > > + return -1; > > + } > > +} > > + > > +int thermal_sampling_handle(struct thermal_handler *th, void *arg) > > +{ > > + struct thermal_handler_param thp = { .th = th, .arg = arg > > }; > > + > > + if (!th) > > + return -1; > > + > > + if (nl_cb_set(th->cb_sampling, NL_CB_VALID, NL_CB_CUSTOM, > > + handle_thermal_sample, &thp)) > > + return -1; > > + > > + return nl_recvmsgs(th->sk_sampling, th->cb_sampling); > > +} > > + > > +int thermal_sampling_fd(struct thermal_handler *th) > > +{ > > + if (!th) > > + return -1; > > + > > + return nl_socket_get_fd(th->sk_sampling); > > +} > > + > > +int thermal_sampling_init(struct thermal_handler *th) > > +{ > > + if (nl_thermal_connect(&th->sk_sampling, &th->cb_sampling)) > > + return -1; > > + > > + if (nl_subscribe_thermal(th->sk_sampling, th->cb_sampling, > > + THERMAL_GENL_SAMPLING_GROUP_NAME)) > > + return -1; > > + > > + return 0; > > +} > > diff --git a/tools/lib/thermal/thermal.c > > b/tools/lib/thermal/thermal.c > > new file mode 100644 > > index 000000000000..d8ad2e60dfbc > > --- /dev/null > > +++ b/tools/lib/thermal/thermal.c > > @@ -0,0 +1,116 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#include <stdio.h> > > +#include <thermal.h> > > + > > +#include "thermal_nl.h" > > + > > +int for_each_thermal_cdev(struct thermal_cdev *cdev, cb_tc_t cb, > > void *arg) > > +{ > > + int i, ret = 0; > > + > > + for (i = 0; cdev[i].id != -1; i++) > > + ret |= cb(&cdev[i], arg); > > + > > + return ret; > > +} > > + > > +int for_each_thermal_trip(struct thermal_trip *tt, cb_tt_t cb, > > void *arg) > > +{ > > + int i, ret = 0; > > + > > + for (i = 0; tt[i].id != -1; i++) > > + ret |= cb(&tt[i], arg); > > + > > + return ret; > > +} > > + > > +int for_each_thermal_zone(struct thermal_zone *tz, cb_tz_t cb, > > void *arg) > > +{ > > + int i, ret = 0; > > + > > + for (i = 0; tz[i].id != -1; i++) > > + ret |= cb(&tz[i], arg); > > + > > + return ret; > > +} > > + > > +struct thermal_zone *thermal_zone_find_by_name(struct thermal_zone > > *tz, > > + const char *name) > > +{ > > + int i; > > + > > + if (!name) > > + return NULL; > > + > > + for (i = 0; tz[i].id != -1; i++) { > > + if (!strcmp(tz[i].name, name)) > > + return &tz[i]; > > + } > > + > > + return NULL; > > +} > > + > > +struct thermal_zone *thermal_zone_find_by_id(struct thermal_zone > > *tz, int id) > > +{ > > + int i; > > + > > + if (id < 0) > > + return NULL; > > + > > + for (i = 0; tz[i].id != -1; i++) { > > + if (tz[i].id == id) > > + return &tz[i]; > > + } > > + > > + return NULL; > > +} > > + > > +static int __thermal_zone_discover(struct thermal_zone *tz, void > > *th) > > +{ > > + if (thermal_cmd_get_trip(th, tz) < 0) > > + return -1; > > + > > + if (thermal_cmd_get_governor(th, tz)) > > + return -1; > > + > > + return 0; > > +} > > + > > +struct thermal_zone *thermal_zone_discover(struct thermal_handler > > *th) > > +{ > > + struct thermal_zone *tz; > > + > > + if (thermal_cmd_get_tz(th, &tz) < 0) > > + return NULL; > > + > > + if (for_each_thermal_zone(tz, __thermal_zone_discover, th)) > > + return NULL; > > + > > + return tz; > > +} > > + > > +struct thermal_handler *thermal_init(struct thermal_ops *ops) > > +{ > > + struct thermal_handler *th; > > + > > + th = malloc(sizeof(*th)); > > + if (!th) > > + return NULL; > > + th->ops = ops; > > + > > + if (thermal_events_init(th)) > > + goto out_free; > > + > > + if (thermal_sampling_init(th)) > > + goto out_free; > > + > > + if (thermal_cmd_init(th)) > > + goto out_free; > > + > > + return th; > > + > > +out_free: > > + free(th); > > + > > + return NULL; > > +} > > diff --git a/tools/lib/thermal/thermal_nl.c > > b/tools/lib/thermal/thermal_nl.c > > new file mode 100644 > > index 000000000000..893ab0e1b12e > > --- /dev/null > > +++ b/tools/lib/thermal/thermal_nl.c > > @@ -0,0 +1,201 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#include <errno.h> > > +#include <stdio.h> > > +#include <stdlib.h> > > +#include <unistd.h> > > + > > +#include <sys/epoll.h> > > + > > +#include <thermal.h> > > + > > +#include "thermal_nl.h" > > + > > +struct handler_args { > > + const char *group; > > + int id; > > +}; > > + > > +static __thread int err; > > +static __thread int done; > > + > > +static int nl_seq_check_handler(struct nl_msg *msg, void *arg) > > +{ > > + return NL_OK; > > +} > > + > > +static int nl_error_handler(struct sockaddr_nl *nla, struct > > nlmsgerr *nl_err, > > + void *arg) > > +{ > > + int *ret = arg; > > + > > + if (ret) > > + *ret = nl_err->error; > > + > > + return NL_STOP; > > +} > > + > > +static int nl_finish_handler(struct nl_msg *msg, void *arg) > > +{ > > + int *ret = arg; > > + > > + if (ret) > > + *ret = 1; > > + > > + return NL_OK; > > +} > > + > > +static int nl_ack_handler(struct nl_msg *msg, void *arg) > > +{ > > + int *ret = arg; > > + > > + if (ret) > > + *ret = 1; > > + > > + return NL_OK; > > +} > > + > > +int nl_send_msg(struct nl_sock *sock, struct nl_cb *cb, struct > > nl_msg *msg, > > + int (*rx_handler)(struct nl_msg *, void *), void > > *data) > > +{ > > + if (!rx_handler) > > + return -1; > > + > > + err = nl_send_auto_complete(sock, msg); > > + if (err < 0) > > + return err; > > + > > + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, rx_handler, data); > > + > > + err = done = 0; > > + > > + while (err == 0 && done == 0) > > + nl_recvmsgs(sock, cb); > > + > > + return err; > > +} > > + > > +static int nl_family_handler(struct nl_msg *msg, void *arg) > > +{ > > + struct handler_args *grp = arg; > > + struct nlattr *tb[CTRL_ATTR_MAX + 1]; > > + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); > > + struct nlattr *mcgrp; > > + int rem_mcgrp; > > + > > + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), > > + genlmsg_attrlen(gnlh, 0), NULL); > > + > > + if (!tb[CTRL_ATTR_MCAST_GROUPS]) > > + return -1; > > + > > + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], > > rem_mcgrp) { > > + > > + struct nlattr *tb_mcgrp[CTRL_ATTR_MCAST_GRP_MAX + > > 1]; > > + > > + nla_parse(tb_mcgrp, CTRL_ATTR_MCAST_GRP_MAX, > > + nla_data(mcgrp), nla_len(mcgrp), NULL); > > + > > + if (!tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME] || > > + !tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]) > > + continue; > > + > > + if > > (strncmp(nla_data(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]), > > + grp->group, > > + > > nla_len(tb_mcgrp[CTRL_ATTR_MCAST_GRP_NAME]))) > > + continue; > > + > > + grp->id = > > nla_get_u32(tb_mcgrp[CTRL_ATTR_MCAST_GRP_ID]); > > + > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static int nl_get_multicast_id(struct nl_sock *sock, struct nl_cb > > *cb, > > + const char *family, const char > > *group) > > +{ > > + struct nl_msg *msg; > > + int ret = 0, ctrlid; > > + struct handler_args grp = { > > + .group = group, > > + .id = -ENOENT, > > + }; > > + > > + msg = nlmsg_alloc(); > > + if (!msg) > > + return -ENOMEM; > > + > > + ctrlid = genl_ctrl_resolve(sock, "nlctrl"); > > + > > + genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, > > 0); > > + > > + nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family); > > + > > + ret = nl_send_msg(sock, cb, msg, nl_family_handler, &grp); > > + if (ret) > > + goto nla_put_failure; > > + > > + ret = grp.id; > > + > > +nla_put_failure: > > + nlmsg_free(msg); > > + return ret; > > +} > > + > > +int nl_thermal_connect(struct nl_sock **nl_sock, struct nl_cb > > **nl_cb) > > +{ > > + struct nl_cb *cb; > > + struct nl_sock *sock; > > + > > + cb = nl_cb_alloc(NL_CB_DEFAULT); > > + if (!cb) > > + return -1; > > + > > + sock = nl_socket_alloc(); > > + if (!sock) > > + goto out_cb_free; > > + > > + if (genl_connect(sock)) > > + goto out_socket_free; > > + > > + if (nl_cb_err(cb, NL_CB_CUSTOM, nl_error_handler, &err) || > > + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, > > nl_finish_handler, &done) || > > + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, nl_ack_handler, > > &done) || > > + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, > > nl_seq_check_handler, &done)) > > + return -1; > > + > > + *nl_sock = sock; > > + *nl_cb = cb; > > + > > + return 0; > > + > > +out_socket_free: > > + nl_socket_free(sock); > > +out_cb_free: > > + nl_cb_put(cb); > > + return -1; > > +} > > + > > +void nl_thermal_disconnect(struct nl_sock *nl_sock, struct nl_cb > > *nl_cb) > > +{ > > + nl_close(nl_sock); > > + nl_socket_free(nl_sock); > > + nl_cb_put(nl_cb); > > +} > > + > > +int nl_subscribe_thermal(struct nl_sock *nl_sock, struct nl_cb > > *nl_cb, > > + const char *group) > > +{ > > + int mcid; > > + > > + mcid = nl_get_multicast_id(nl_sock, nl_cb, > > THERMAL_GENL_FAMILY_NAME, > > + group); > > + if (mcid < 0) > > + return -1; > > + > > + if (nl_socket_add_membership(nl_sock, mcid)) > > + return -1; > > + > > + return 0; > > +} > > diff --git a/tools/lib/thermal/thermal_nl.h > > b/tools/lib/thermal/thermal_nl.h > > new file mode 100644 > > index 000000000000..b1593c29c8bb > > --- /dev/null > > +++ b/tools/lib/thermal/thermal_nl.h > > @@ -0,0 +1,42 @@ > > +/* SPDX-License-Identifier: LGPL-2.1+ */ > > +#ifndef __THERMAL_H > > +#define __THERMAL_H > > + > > +#include <netlink/netlink.h> > > +#include <netlink/genl/genl.h> > > +#include <netlink/genl/mngt.h> > > +#include <netlink/genl/ctrl.h> > > + > > +struct thermal_handler { > > + int done; > > + int error; > > + struct thermal_ops *ops; > > + struct nl_msg *msg; > > + struct nl_sock *sk_event; > > + struct nl_sock *sk_sampling; > > + struct nl_sock *sk_cmd; > > + struct nl_cb *cb_cmd; > > + struct nl_cb *cb_event; > > + struct nl_cb *cb_sampling; > > +}; > > + > > +struct thermal_handler_param { > > + struct thermal_handler *th; > > + void *arg; > > +}; > > + > > +/* > > + * Low level netlink > > + */ > > +extern int nl_subscribe_thermal(struct nl_sock *nl_sock, struct > > nl_cb *nl_cb, > > + const char *group); > > + > > +extern int nl_thermal_connect(struct nl_sock **nl_sock, struct > > nl_cb **nl_cb); > > + > > +extern void nl_thermal_disconnect(struct nl_sock *nl_sock, struct > > nl_cb *nl_cb); > > + > > +extern int nl_send_msg(struct nl_sock *sock, struct nl_cb *nl_cb, > > struct nl_msg *msg, > > + int (*rx_handler)(struct nl_msg *, void *), > > + void *data); > > + > > +#endif /* __THERMAL_H */ > > -- > > 2.25.1 > >
| |