From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05BD0C433F5 for ; Fri, 4 Feb 2022 19:32:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232521AbiBDTcH (ORCPT ); Fri, 4 Feb 2022 14:32:07 -0500 Received: from mga07.intel.com ([134.134.136.100]:57254 "EHLO mga07.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233300AbiBDTcG (ORCPT ); Fri, 4 Feb 2022 14:32:06 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1644003126; x=1675539126; h=message-id:subject:from:to:cc:date:in-reply-to: references:mime-version:content-transfer-encoding; bh=hXxBpn3whwxCRqkLRcJ6tlAOuvN4omyav1mttaHCJKo=; b=mgsCTbcQduX/zAUBu7DHySutJy8gDCM9+a4pdh4VG+nuKvYMQqc+rEZD 30LlhMkBan2VE17qEdGaekf2GrwvhjHV4xkbnyVZNXWWK1vnBe6QI6tD5 qoaAPjpaBTyeHrXnVaYX7ioDVN7HkBC87dF4J/p/2qRMdl8n/C//akhx5 kx1X/B0a+O9qhKCx82KfxkoZbF0vK+75jostpULCzgXTf0xa4AJJi53K1 5IuvjT8GzTphAWmFr6AHblNI8Pd4tD9+IzV977/JVjVuypG/Zh0JVf7tG odFnmY5/6UFdSss7O08170NJZPGRYfnKvQwr+h+aV/pgF9zm3N0IpubxQ w==; X-IronPort-AV: E=McAfee;i="6200,9189,10248"; a="311730864" X-IronPort-AV: E=Sophos;i="5.88,343,1635231600"; d="scan'208";a="311730864" Received: from orsmga006.jf.intel.com ([10.7.209.51]) by orsmga105.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Feb 2022 11:32:06 -0800 X-IronPort-AV: E=Sophos;i="5.88,343,1635231600"; d="scan'208";a="483712290" Received: from ahofrock-mobl.amr.corp.intel.com (HELO spandruv-desk1.amr.corp.intel.com) ([10.209.64.72]) by orsmga006-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 04 Feb 2022 11:32:06 -0800 Message-ID: Subject: Re: [PATCH] tools/lib/thermal: Add a thermal library From: srinivas pandruvada To: "Rafael J. Wysocki" , Daniel Lezcano Cc: Linux PM , Linux Kernel Mailing List , Jonathan Cameron , William Breathitt Gray , Viktor Rosendahl , Sasha Levin , Colin Ian King , "Zhang, Rui" Date: Fri, 04 Feb 2022 11:32:05 -0800 In-Reply-To: References: <20220204161518.163536-1-daniel.lezcano@linaro.org> Content-Type: text/plain; charset="UTF-8" User-Agent: Evolution 3.42.3 (3.42.3-1.fc35) MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org 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 > 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 > > --- > >  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 > > +#include > > +#include > > +#include > > + > > +#include > > +#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 > > +#include > > +#include > > +#include > > + > > + > > +#include > > +#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 > > + > > +#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 > > +#include > > +#include > > +#include > > + > > +#include > > +#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 > > +#include > > + > > +#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 > > +#include > > +#include > > +#include > > + > > +#include > > + > > +#include > > + > > +#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 > > +#include > > +#include > > +#include > > + > > +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 > >