Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757590Ab3JIXh2 (ORCPT ); Wed, 9 Oct 2013 19:37:28 -0400 Received: from bear.ext.ti.com ([192.94.94.41]:53305 "EHLO bear.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753951Ab3JIXhZ (ORCPT ); Wed, 9 Oct 2013 19:37:25 -0400 Message-ID: <5255E8A4.6030400@ti.com> Date: Wed, 9 Oct 2013 19:37:08 -0400 From: Eduardo Valentin User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130510 Thunderbird/17.0.6 MIME-Version: 1.0 To: Jacob Pan CC: Eduardo Valentin , Zhang Rui , Linux PM , LKML , Rafael Wysocki Subject: Re: [PATCH] tools/thermal: Introduce tmon, a tool for thermal subsystem References: <1381259024-25433-1-git-send-email-jacob.jun.pan@linux.intel.com> <5254AB6A.1090600@ti.com> <20131009105217.0b23a6c3@ultegra> In-Reply-To: <20131009105217.0b23a6c3@ultegra> X-Enigmail-Version: 1.5.2 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="hwuJORj32HNeAtVoEQDsDKgaF5edWrGdA" Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 80105 Lines: 2615 --hwuJORj32HNeAtVoEQDsDKgaF5edWrGdA Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable On 09-10-2013 13:52, Jacob Pan wrote: > On Tue, 8 Oct 2013 21:03:38 -0400 > Eduardo Valentin wrote: >=20 >> On 08-10-2013 15:03, Jacob Pan wrote: >>> Increasingly, Linux is running on thermally constrained devices. >>> The simple thermal relationship between processor and fan has >>> become past for modern computers. >>> >>> As hardware vendors cope with the thermal constraints on their >>> products, more sensors are added, new cooling capabilities are >>> introduced. The complexity of the thermal relationship can grow >>> exponentially among cooling devices, zones, sensors, and trip >>> points. They can also change dynamically. >>> >>> To expose such relationship to the userspace, Linux generic thermal >>> layer introduced sysfs entry at /sys/class/thermal with a matrix of >>> symbolic links, trip point bindings, and device instances. To >>> traverse such matrix by hand is not a trivial task. Testing is also >>> difficult in that thermal conditions are often exception cases that >>> hard to reach in normal operations. >>> >>> TMON is conceived as a tool to help visualize, tune, and test the >>> complex thermal subsystem. >> >> Jacob, >> >> Very nice initiative. Thanks for providing tools on thermal area. We >> are lacking them. I have been using the linaro thermal testing >> scripts for smoking testing the systems I am working on. But I have >> been considering writing a ncurses based tool for long time. It is >> good anyway that you have started and even shared it already. >> >> I gave a very quick shot on my OMAP4460 panda board and tmon is >> crashing with segfault: >>> TMON v1.0 >>> >>> =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=90 >>> =E2=94=82Thermal Zones: >>> cpu_therm00 =E2=94=82 =E2=94=82Tri= p >>> Points: >>> CP =E2=94=82 >>> =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=98 >>> =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=90 >>> =E2=94=82ID Cooling Dev Cur Max Thermal Zone >>> Binding =E2=94=82 =E2=94=8200 thermal-cpuf = 0 3 >>> Segmentation fault =E2=94=82 >>> =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80[root@(none) ~]# >>> =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=98 [root@(none) ~]# ./tmont= mon >>> =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=90 >>> =E2=94=82 10 20 30 40 >>> 50 60 =E2=94=82 =E2=94=82cpu_th >>> 0:[ 0][> >>> =E2=94=82 >>> =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=98 >>> >>> >> >> I believe it is while updating the progress bar you've written to >> represent temperature on the thermal zone temperature. >> > Sorry about the crash, I admit i only tested on x86 systesm. I just > borrowed a Panda board. Could you tell me where you get the image to > run it on? I am using a busybox based filesystem. And I have statically compiled tmon. It is against 3.12-rc1. >=20 > At the same time, could you help me debug with showing me the result of= > " tree -l -L 2 /sys/class/thermal/" I tried also on my x86 dell laptop. I gave up on this track to because tmon does not progress when there is no thermal zone. ebv@besouro:/sys/class/thermal$ $ tree =2E =E2=94=9C=E2=94=80=E2=94=80 cooling_device0 -> ../../devices/virtual/ther= mal/cooling_device0 =E2=94=9C=E2=94=80=E2=94=80 cooling_device1 -> ../../devices/virtual/ther= mal/cooling_device1 =E2=94=9C=E2=94=80=E2=94=80 cooling_device2 -> ../../devices/virtual/ther= mal/cooling_device2 =E2=94=9C=E2=94=80=E2=94=80 cooling_device3 -> ../../devices/virtual/ther= mal/cooling_device3 =E2=94=94=E2=94=80=E2=94=80 cooling_device4 -> ../../devices/virtual/ther= mal/cooling_device4 5 directories, 0 files The only problem is that tmon exists silently. I would be nice to have at least a message saying why it is not continuing. On Panda, I have only one thermal zone: [root@(none) ~]# ls /sys/class/thermal/ cooling_device0@ thermal_zone0@ [root@(none) ~]# ls /sys/class/thermal/thermal_zone0/ emul_temp power/ trip_point_0_hyst trip_point_1_hyst type mode subsystem@ trip_point_0_temp trip_point_1_temp uevent policy temp trip_point_0_type trip_point_1_type [root@(none) ~]# ls /sys/class/thermal/cooling_device0/ cur_state max_state power/ subsystem@ type uevent >=20 >> I still need to have a proper look on your code though. Looks like you= >> do not add it to tools/Makefile? >> > i missed that. no issues. >> Also, please copy people that get_maintainer.pl -f tools/ outputs: >> ./scripts/get_maintainer.pl -f tools/ >> Arnaldo Carvalho de Melo (commit_signer:724/902=3D80= %) >> Namhyung Kim (commit_signer:237/902=3D26%) >> Jiri Olsa (commit_signer:219/902=3D24%) >> David Ahern (commit_signer:69/902=3D8%) >> Adrian Hunter (commit_signer:59/902=3D >> > I did run get_maintainer.pl on the patch but it did not find any. I > guess it is because I missed the Makefile. > Will fix it in the next round. ok >>> >>> Signed-off-by: Jacob Pan >>> --- >>> tools/thermal/tmon/Makefile | 47 ++++ >>> tools/thermal/tmon/README | 50 ++++ >>> tools/thermal/tmon/pid.c | 131 +++++++++ >>> tools/thermal/tmon/sysfs.c | 585 >>> +++++++++++++++++++++++++++++++++++++++ tools/thermal/tmon/tmon.8 >>> | 142 ++++++++++ tools/thermal/tmon/tmon.c | 350 >>> ++++++++++++++++++++++++ tools/thermal/tmon/tmon.h | 204 >>> ++++++++++++++ tools/thermal/tmon/tui.c | 631 >>> +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 2140 >>> insertions(+) create mode 100644 tools/thermal/tmon/Makefile >>> create mode 100644 tools/thermal/tmon/README >>> create mode 100644 tools/thermal/tmon/pid.c >>> create mode 100644 tools/thermal/tmon/sysfs.c >>> create mode 100644 tools/thermal/tmon/tmon.8 >>> create mode 100644 tools/thermal/tmon/tmon.c >>> create mode 100644 tools/thermal/tmon/tmon.h >>> create mode 100644 tools/thermal/tmon/tui.c >>> >>> diff --git a/tools/thermal/tmon/Makefile >>> b/tools/thermal/tmon/Makefile new file mode 100644 >>> index 0000000..c17131b >>> --- /dev/null >>> +++ b/tools/thermal/tmon/Makefile >>> @@ -0,0 +1,47 @@ >>> +VERSION =3D 1.0 >>> + >>> +BINDIR=3Dusr/bin >>> +WARNFLAGS=3D-Wall -Wshadow -W -Wformat >>> -Wimplicit-function-declaration -Wimplicit-int +CFLAGS=3D -O1 >>> ${WARNFLAGS} -fstack-protector +CC=3Dgcc >>> + >>> +CFLAGS+=3D-D VERSION=3D\"$(VERSION)\" >>> +LDFLAGS+=3D >>> +TARGET=3Dtmon >>> + >>> +INSTALL_PROGRAM=3Dinstall -m 755 -p >>> +DEL_FILE=3Drm -f >>> + >>> +INSTALL_CONFIGFILE=3Dinstall -m 644 -p >>> +CONFIG_FILE=3D >>> +CONFIG_PATH=3D >>> + >>> + >>> +OBJS =3D tmon.o tui.o sysfs.o pid.o >>> +OBJS +=3D >>> + >>> +tmon: $(OBJS) Makefile tmon.h >>> + $(CC) ${CFLAGS} $(LDFLAGS) $(OBJS) -o $(TARGET) >>> -lncursesw -lm -lpanel -lpthread + >>> +valgrind: tmon >>> + sudo valgrind -v --track-origins=3Dyes --tool=3Dmemcheck >>> --leak-check=3Dyes --show-reachable=3Dyes --num-callers=3D20 >>> --track-fds=3Dyes ./$(TARGET) 1> /dev/null + +install: >>> + - mkdir -p $(INSTALL_ROOT)/$(BINDIR) >>> + - $(INSTALL_PROGRAM) "$(TARGET)" >>> "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" >>> + - mkdir -p $(INSTALL_ROOT)/$(CONFIG_PATH) >>> + - $(INSTALL_CONFIGFILE) "$(CONFIG_FILE)" >>> "$(INSTALL_ROOT)/$(CONFIG_PATH)" + >>> +uninstall: >>> + $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" >>> + $(CONFIG_FILE) "$(CONFIG_PATH)" >>> + >>> + >>> +clean: >>> + find . -name "*.o" | xargs $(DEL_FILE) >>> + rm -f $(TARGET) >>> + >>> +dist: >>> + git tag v$(VERSION) >>> + git archive --format=3Dtar --prefix=3D"$(TARGET)-$(VERSION)/" >>> v$(VERSION) | \ >>> + gzip > $(TARGET)-$(VERSION).tar.gz >>> diff --git a/tools/thermal/tmon/README b/tools/thermal/tmon/README >>> new file mode 100644 >>> index 0000000..4579498 >>> --- /dev/null >>> +++ b/tools/thermal/tmon/README >>> @@ -0,0 +1,50 @@ >>> +TMON - A Monitoring and Testing Tool for Linux kernel thermal >>> subsystem + >>> +Why TMON? >>> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> +Increasingly, Linux is running on thermally constrained devices. >>> The simple +thermal relationship between processor and fan has >>> become past for modern +computers. >>> + >>> +As hardware vendors cope with the thermal constraints on their >>> products, more +and more sensors are added, new cooling >>> capabilities are introduced. The +complexity of the thermal >>> relationship can grow exponentially among cooling +devices, zones, >>> sensors, and trip points. They can also change dynamically. + >>> +To expose such relationship to the userspace, Linux generic >>> thermal layer +introduced sysfs entry at /sys/class/thermal with a >>> matrix of symbolic +links, trip point bindings, and device >>> instances. To traverse such +matrix by hand is not a trivial task. >>> Testing is also difficult in that +thermal conditions are often >>> exception cases that hard to reach in +normal operations. >>> + >>> +TMON is conceived as a tool to help visualize, tune, and test the >>> +complex thermal subsystem. >>> + >>> +Files >>> +=3D=3D=3D=3D=3D >>> + tmon.c : main function for set up and configurations. >>> + tui.c : handles ncurses based user interface >>> + sysfs.c : access to the generic thermal sysfs >>> + pid.c : a proportional-integral-derivative (PID) controller >>> + that can be used for thermal relationship training. >>> + >>> +Requirements >>> +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> +Depends on ncurses >>> + >>> +Build >>> +=3D=3D=3D=3D=3D=3D=3D=3D=3D >>> +$ make >>> +$ sudo ./tmon -h >>> +Usage: tmon [OPTION...] >>> + -c, --control cooling device in control >>> + -d, --daemon run as daemon, no TUI >>> + -l, --log log data to /var/tmp/tmon.log >>> + -h, --help show this help message >>> + -t, --time-interval set time interval for sampling >>> + -v, --version show version >>> + -g, --debug debug message in syslog >>> + >>> +1. For monitoring only: >>> +$ sudo ./tmon >>> diff --git a/tools/thermal/tmon/pid.c b/tools/thermal/tmon/pid.c >>> new file mode 100644 >>> index 0000000..fd7e9e9 >>> --- /dev/null >>> +++ b/tools/thermal/tmon/pid.c >>> @@ -0,0 +1,131 @@ >>> +/* >>> + * pid.c PID controller for testing cooling devices >>> + * >>> + * >>> + * >>> + * Copyright (C) 2012 Intel Corporation. All rights reserved. >>> + * >>> + * This program is free software; you can redistribute it and/or >>> + * modify it under the terms of the GNU General Public License >>> version >>> + * 2 or later as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * Author Name Jacob Pan >>> + * >>> + */ >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#include "tmon.h" >>> + >>> +/*******************************************************************= ******* >>> + * PID (Proportional-Integral-Derivative) controller is commonly >>> used in >>> + * linear control system, consider the the process. >>> + * G(s) =3D U(s)/E(s) >>> + * kp =3D proportional gain >>> + * ki =3D integral gain >>> + * kd =3D derivative gain >>> + * Ts >>> + * We use type C Alan Bradley equation which takes set point off >>> the >>> + * output dependency in P and D term. >>> + * >>> + * y[k] =3D y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] >>> + * - 2*x[k-1]+x[k-2])/Ts >>> + * >>> + * >>> + >>> *********************************************************************= **/ >>> +struct pid_params p_param; +/* cached data from previous loop */ >>> +static double xk_1, xk_2; /* input temperature x[k-#] */ >>> + >>> +/* >>> + * TODO: make PID parameters tuned automatically, >>> + * 1. use CPU burn to produce open loop unit step response >>> + * 2. calculate PID based on Ziegler-Nichols rule >>> + * >>> + * add a flag for tuning PID >>> + */ >>> +int init_thermal_controller(void) >>> +{ >>> + int ret =3D 0; >>> + >>> + /* init pid params */ >>> + p_param.ts =3D ticktime; >>> + /* TODO: get it from TUI tuning tab */ >>> + p_param.kp =3D .36; >>> + p_param.ki =3D 5.0; >>> + p_param.kd =3D 0.19; >>> + >>> + p_param.t_target =3D target_temp_user; >>> + >>> + return ret; >>> +} >>> + >>> +void controller_reset(void) >>> +{ >>> + /* TODO: relax control data when not over thermal limit */ >>> + syslog(LOG_DEBUG, "TC inactive, relax p-state\n"); >>> + p_param.y_k =3D 0.0; >>> + xk_1 =3D 0.0; >>> + xk_2 =3D 0.0; >>> + set_ctrl_state(0); >>> +} >>> + >>> +/* To be called at time interval Ts. Type C PID controller. >>> + * y[k] =3D y[k-1] - kp*(x[k] - x[k-1]) + Ki*Ts*e[k] - Kd*(x[k] >>> + * - 2*x[k-1]+x[k-2])/Ts >>> + * TODO: add low pass filter for D term >>> + */ >>> +#define GUARD_BAND (2) >>> +void controller_handler(const double xk, double *yk) >>> +{ >>> + double ek; >>> + double p_term, i_term, d_term; >>> + >>> + ek =3D p_param.t_target - xk; /* error */ >>> + if (ek >=3D 3.0) { >>> + syslog(LOG_DEBUG, "PID: %3.1f Below set point >>> %3.1f, stop\n", >>> + xk, p_param.t_target); >>> + controller_reset(); >>> + *yk =3D 0.0; >>> + return; >>> + } >>> + /* compute intermediate PID terms */ >>> + p_term =3D -p_param.kp * (xk - xk_1); >>> + i_term =3D p_param.kp * p_param.ki * p_param.ts * ek; >>> + d_term =3D -p_param.kp * p_param.kd * (xk - 2 * xk_1 + >>> xk_2) / p_param.ts; >>> + /* compute output */ >>> + *yk +=3D p_term + i_term + d_term; >>> + /* update sample data */ >>> + xk_1 =3D xk; >>> + xk_2 =3D xk_1; >>> + >>> + /* clamp output adjustment range */ >>> + if (*yk < -LIMIT_HIGH) >>> + *yk =3D -LIMIT_HIGH; >>> + else if (*yk > -LIMIT_LOW) >>> + *yk =3D -LIMIT_LOW; >>> + >>> + p_param.y_k =3D *yk; >>> + >>> + set_ctrl_state(lround(fabs(p_param.y_k))); >>> + >>> +} >>> diff --git a/tools/thermal/tmon/sysfs.c b/tools/thermal/tmon/sysfs.c >>> new file mode 100644 >>> index 0000000..54e24b3 >>> --- /dev/null >>> +++ b/tools/thermal/tmon/sysfs.c >>> @@ -0,0 +1,585 @@ >>> +/* >>> + * sysfs.c sysfs ABI access functions for TMON program >>> + * >>> + * Copyright (C) 2013 Intel Corporation. All rights reserved. >>> + * >>> + * This program is free software; you can redistribute it and/or >>> + * modify it under the terms of the GNU General Public License >>> version >>> + * 2 or later as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * Author: Jacob Pan >>> + * >>> + */ >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#include "tmon.h" >>> + >>> +struct tmon_platform_data ptdata; >>> +const char *trip_type_name[] =3D { >>> + "critical", >>> + "hot", >>> + "passive", >>> + "active", >>> +}; >>> + >>> +int sysfs_set_ulong(char *path, char *filename, unsigned long val) >>> +{ >>> + FILE *fd; >>> + int ret =3D -1; >>> + char filepath[256]; >>> + >>> + snprintf(filepath, 256, "%s/%s", path, filename); >>> + >>> + fd =3D fopen(filepath, "w"); >>> + if (!fd) { >>> + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, >>> filepath); >>> + return ret; >>> + } >>> + ret =3D fprintf(fd, "%lu", val); >>> + fclose(fd); >>> + >>> + return 0; >>> +} >>> + >>> +/* history of thermal data, used for control algo */ >>> +#define NR_THERMAL_RECORDS 3 >>> +struct thermal_data_record trec[NR_THERMAL_RECORDS]; >>> +int cur_thermal_record; /* index to the trec array */ >>> + >>> +static int sysfs_get_ulong(char *path, char *filename, unsigned >>> long *p_ulong) +{ >>> + FILE *fd; >>> + int ret =3D -1; >>> + char filepath[256]; >>> + >>> + snprintf(filepath, 256, "%s/%s", path, filename); >>> + >>> + fd =3D fopen(filepath, "r"); >>> + if (!fd) { >>> + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, >>> filepath); >>> + return ret; >>> + } >>> + ret =3D fscanf(fd, "%lu", p_ulong); >>> + fclose(fd); >>> + >>> + return 0; >>> +} >>> + >>> +static int sysfs_get_string(char *path, char *filename, char *str) >>> +{ >>> + FILE *fd; >>> + int ret =3D -1; >>> + char filepath[256]; >>> + >>> + snprintf(filepath, 256, "%s/%s", path, filename); >>> + >>> + fd =3D fopen(filepath, "r"); >>> + if (!fd) { >>> + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, >>> filepath); >>> + return ret; >>> + } >>> + ret =3D fscanf(fd, "%256s", str); >>> + fclose(fd); >>> + >>> + return ret; >>> +} >>> + >>> +/* get states of the cooling device instance */ >>> +static int probe_cdev(struct cdev_info *cdi, char *path) >>> +{ >>> + sysfs_get_string(path, "type", cdi->type); >>> + sysfs_get_ulong(path, "max_state", &cdi->max_state); >>> + sysfs_get_ulong(path, "cur_state", &cdi->cur_state); >>> + >>> + syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst >>> %d\n", >>> + __func__, path, >>> + cdi->type, cdi->max_state, cdi->cur_state, >>> cdi->instance); + >>> + return 0; >>> +} >>> + >>> +static int str_to_trip_type(char *name) >>> +{ >>> + int i; >>> + >>> + for (i =3D 0; i < NR_THERMAL_TRIP_TYPE; i++) { >>> + if (!strcmp(name, trip_type_name[i])) >>> + return i; >>> + } >>> + >>> + return -ENOENT; >>> +} >>> + >>> +/* scan and fill in trip point info for a thermal zone and trip >>> point id */ +static int get_trip_point_data(char *tz_path, int >>> tzid, int tpid) +{ >>> + char filename[256]; >>> + char temp_str[256]; >>> + int trip_type; >>> + >>> + if (tpid >=3D MAX_NR_TRIP) >>> + return -EINVAL; >>> + /* check trip point type */ >>> + snprintf(filename, sizeof(filename), "trip_point_%d_type", >>> tpid); >>> + sysfs_get_string(tz_path, filename, temp_str); >>> + trip_type =3D str_to_trip_type(temp_str); >>> + if (trip_type < 0) { >>> + syslog(LOG_ERR, "%s:%s no matching type\n", >>> __func__, temp_str); >>> + return -ENOENT; >>> + } >>> + ptdata.tzi[tzid].tp[tpid].type =3D trip_type; >>> + syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", >>> __func__, tzid, >>> + tpid, temp_str, trip_type); >>> + >>> + /* TODO: check attribute */ >>> + >>> + return 0; >>> +} >>> + >>> +/* return instance id for file format such as trip_point_4_temp */ >>> +static int get_instance_id(char *name, int pos, int skip) >>> +{ >>> + char *ch; >>> + int i =3D 0; >>> + >>> + ch =3D strtok(name, "_"); >>> + while (ch !=3D NULL) { >>> + ++i; >>> + syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, >>> ch, i); >>> + ch =3D strtok(NULL, "_"); >>> + if (pos =3D=3D i) >>> + return atol(ch + skip); >>> + } >>> + >>> + return -1; >>> +} >>> + >>> +/* Find trip point info of a thermal zone */ >>> +static int find_tzone_tp(char *tz_name, char *d_name, struct >>> tz_info *tzi, >>> + int tz_id) >>> +{ >>> + int tp_id; >>> + unsigned long temp_ulong; >>> + >>> + if (strstr(d_name, "trip_point") && >>> + strstr(d_name, "temp")) { >>> + /* check if trip point temp is non-zero >>> + * ignore 0/invalid trip points >>> + */ >>> + sysfs_get_ulong(tz_name, d_name, &temp_ulong); >>> + if (temp_ulong < MAX_TEMP_KC) { >>> + tzi->nr_trip_pts++; >>> + /* found a valid trip point */ >>> + tp_id =3D get_instance_id(d_name, 2, 0); >>> + syslog(LOG_DEBUG, "tzone %s trip %d temp >>> %lu tpnode %s", >>> + tz_name, tp_id, temp_ulong, >>> d_name); >>> + if (tp_id < 0 || tp_id >=3D MAX_NR_TRIP) { >>> + syslog(LOG_ERR, "Failed to find TP >>> inst %s\n", >>> + d_name); >>> + return -1; >>> + } >>> + get_trip_point_data(tz_name, tz_id, tp_id); >>> + tzi->tp[tp_id].temp =3D temp_ulong; >>> + } >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +/* check cooling devices for binding info. */ >>> +static int find_tzone_cdev(struct dirent *nl, char *tz_name, >>> + struct tz_info *tzi, int tz_id, int cid) >>> +{ >>> + unsigned long trip_instance =3D 0; >>> + char cdev_name_linked[256]; >>> + char cdev_name[256]; >>> + char cdev_trip_name[256]; >>> + int cdev_id; >>> + >>> + if (nl->d_type =3D=3D DT_LNK) { >>> + syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", >>> tz_id, nl->d_name, >>> + cid); >>> + tzi->nr_cdev++; >>> + if (tzi->nr_cdev > ptdata.nr_cooling_dev) { >>> + syslog(LOG_ERR, "Err: Too many cdev? %d\n", >>> + tzi->nr_cdev); >>> + return -EINVAL; >>> + } >>> + /* find the link to real cooling device record >>> binding */ >>> + snprintf(cdev_name, 256, "%s/%s", tz_name, >>> nl->d_name); >>> + memset(cdev_name_linked, 0, >>> sizeof(cdev_name_linked)); >>> + if (readlink(cdev_name, cdev_name_linked, >>> + sizeof(cdev_name_linked) - 1) !=3D >>> -1) { >>> + cdev_id =3D >>> get_instance_id(cdev_name_linked, 1, >>> + sizeof("device") - >>> 1); >>> + syslog(LOG_DEBUG, "cdev %s linked to %s : >>> %d\n", >>> + cdev_name, cdev_name_linked, >>> cdev_id); >>> + tzi->cdev_binding |=3D (1 << cdev_id); >>> + >>> + /* find the trip point in which the cdev >>> is binded to >>> + * in this tzone >>> + */ >>> + snprintf(cdev_trip_name, 256, "%s%s", >>> nl->d_name, >>> + "_trip_point"); >>> + sysfs_get_ulong(tz_name, cdev_trip_name, >>> + &trip_instance); >>> + /* validate trip point range, e.g. trip >>> could return -1 >>> + * when passive is enabled >>> + */ >>> + if (trip_instance > MAX_NR_TRIP) >>> + trip_instance =3D 0; >>> + tzi->trip_binding[cdev_id] |=3D 1 << >>> trip_instance; >>> + syslog(LOG_DEBUG, "cdev %s -> trip:%lu: >>> 0x%lx %d\n", >>> + cdev_name, trip_instance, >>> + tzi->trip_binding[cdev_id], >>> + cdev_id); >>> + >>> + >>> + } >>> + return 0; >>> + } >>> + >>> + return -ENODEV; >>> +} >>> + >>> + >>> + >>> +/*******************************************************************= ********** >>> + * Before calling scan_tzones, thermal sysfs must be probed to >>> determine >>> + * the number of thermal zones and cooling devices. >>> + * We loop through each thermal zone and fill in tz_info struct, >>> i.e. >>> + * ptdata.tzi[] >>> +root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0 >>> +/sys/class/thermal/thermal_zone0 >>> +|-- cdev0 -> ../cooling_device4 >>> +|-- cdev1 -> ../cooling_device3 >>> +|-- cdev10 -> ../cooling_device7 >>> +|-- cdev11 -> ../cooling_device6 >>> +|-- cdev12 -> ../cooling_device5 >>> +|-- cdev2 -> ../cooling_device2 >>> +|-- cdev3 -> ../cooling_device1 >>> +|-- cdev4 -> ../cooling_device0 >>> +|-- cdev5 -> ../cooling_device12 >>> +|-- cdev6 -> ../cooling_device11 >>> +|-- cdev7 -> ../cooling_device10 >>> +|-- cdev8 -> ../cooling_device9 >>> +|-- cdev9 -> ../cooling_device8 >>> +|-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00 >>> +|-- power >>> +`-- subsystem -> ../../../../class/thermal >>> +********************************************************************= *********/ >>> +static int scan_tzones(void) >>> +{ >>> + DIR *dir; >>> + struct dirent **namelist; >>> + char tz_name[256]; >>> + int i, j, n, k =3D 0; >>> + >>> + if (!ptdata.nr_tz_sensor) { >>> + syslog(LOG_ERR, "No thermal zones found!\n"); >>> + return -1; >>> + } >>> + >>> + for (i =3D 0; i <=3D ptdata.max_tz_instance; i++) { >>> + memset(tz_name, 0, sizeof(tz_name)); >>> + snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, >>> TZONE, i); + >>> + dir =3D opendir(tz_name); >>> + if (!dir) { >>> + syslog(LOG_INFO, "Thermal zone %s >>> skipped\n", tz_name); >>> + continue; >>> + } >>> + /* keep track of valid tzones */ >>> + n =3D scandir(tz_name, &namelist, 0, alphasort); >>> + if (n < 0) >>> + syslog(LOG_ERR, "scandir failed in %s", >>> tz_name); >>> + else { >>> + sysfs_get_string(tz_name, "type", >>> ptdata.tzi[k].type); >>> + ptdata.tzi[k].instance =3D i; >>> + /* detect trip points and cdev attached to >>> this tzone */ >>> + j =3D 0; /* index for cdev */ >>> + ptdata.tzi[k].nr_cdev =3D 0; >>> + ptdata.tzi[k].nr_trip_pts =3D 0; >>> + while (n--) { >>> + char *temp_str; >>> + >>> + if (find_tzone_tp(tz_name, >>> namelist[n]->d_name, >>> + >>> &ptdata.tzi[k], k)) >>> + break; >>> + temp_str =3D >>> strstr(namelist[n]->d_name, "cdev"); >>> + if (!temp_str) { >>> + free(namelist[n]); >>> + continue; >>> + } >>> + if (!find_tzone_cdev(namelist[n], >>> tz_name, >>> + >>> &ptdata.tzi[k], i, j)) >>> + j++; /* increment cdev >>> index */ >>> + free(namelist[n]); >>> + } >>> + free(namelist); >>> + } >>> + /*TODO: reverse trip points */ >>> + closedir(dir); >>> + syslog(LOG_INFO, "TZ %d has %d cdev\n", i, >>> + ptdata.tzi[k].nr_cdev); >>> + k++; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static int scan_cdevs(void) >>> +{ >>> + DIR *dir; >>> + struct dirent **namelist; >>> + char cdev_name[256]; >>> + int i, n, k =3D 0; >>> + >>> + for (i =3D 0; i <=3D ptdata.max_cdev_instance; i++) { >>> + memset(cdev_name, 0, sizeof(cdev_name)); >>> + snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, >>> CDEV, i); + >>> + dir =3D opendir(cdev_name); >>> + if (!dir) { >>> + syslog(LOG_INFO, "Cooling dev %s >>> skipped\n", cdev_name); >>> + /* there is a gap in cooling device id, >>> check again >>> + * for the same index. >>> + */ >>> + continue; >>> + } >>> + >>> + n =3D scandir(cdev_name, &namelist, 0, alphasort); >>> + if (n < 0) >>> + syslog(LOG_ERR, "scandir failed in %s", >>> cdev_name); >>> + else { >>> + sysfs_get_string(cdev_name, "type", >>> ptdata.cdi[k].type); >>> + ptdata.cdi[k].instance =3D i; >>> + if (strstr(ptdata.cdi[k].type, ctrl_cdev)) >>> { >>> + ptdata.cdi[k].flag |=3D >>> CDEV_FLAG_IN_CONTROL; >>> + syslog(LOG_DEBUG, "control cdev id >>> %d\n", i); >>> + } >>> + while (n--) >>> + free(namelist[n]); >>> + free(namelist); >>> + } >>> + closedir(dir); >>> + k++; >>> + } >>> + return 0; >>> +} >>> + >>> + >>> +int probe_thermal_sysfs(void) >>> +{ >>> + DIR *dir; >>> + struct dirent **namelist; >>> + int n; >>> + >>> + dir =3D opendir(THERMAL_SYSFS); >>> + if (!dir) { >>> + syslog(LOG_ERR, "No thermal sysfs\n"); >>> + return -1; >>> + } >>> + n =3D scandir(THERMAL_SYSFS, &namelist, 0, alphasort); >>> + if (n < 0) >>> + syslog(LOG_ERR, "scandir failed in thermal sysfs"); >>> + else { >>> + /* detect number of thermal zones and cooling >>> devices */ >>> + while (n--) { >>> + int inst; >>> + >>> + if (strstr(namelist[n]->d_name, CDEV)) { >>> + inst =3D >>> get_instance_id(namelist[n]->d_name, 1, >>> + sizeof("device") - >>> 1); >>> + /* keep track of the max cooling >>> device since >>> + * there may be gaps. >>> + */ >>> + if (inst > >>> ptdata.max_cdev_instance) >>> + ptdata.max_cdev_instance =3D >>> inst; + >>> + syslog(LOG_DEBUG, "found cdev: %s >>> %d %d\n", >>> + namelist[n]->d_name, >>> + ptdata.nr_cooling_dev, >>> + ptdata.max_cdev_instance); >>> + ptdata.nr_cooling_dev++; >>> + } else if (strstr(namelist[n]->d_name, >>> TZONE)) { >>> + inst =3D >>> get_instance_id(namelist[n]->d_name, 1, >>> + sizeof("zone") - >>> 1); >>> + if (inst > ptdata.max_tz_instance) >>> + ptdata.max_tz_instance =3D >>> inst; + >>> + syslog(LOG_DEBUG, "found tzone: %s >>> %d %d\n", >>> + namelist[n]->d_name, >>> + ptdata.nr_tz_sensor, >>> + ptdata.max_tz_instance); >>> + ptdata.nr_tz_sensor++; >>> + } >>> + free(namelist[n]); >>> + } >>> + free(namelist); >>> + } >>> + syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target >>> zone %d\n", >>> + ptdata.nr_tz_sensor, ptdata.nr_cooling_dev, >>> + target_thermal_zone); >>> + closedir(dir); >>> + >>> + ptdata.tzi =3D calloc(sizeof(struct tz_info), >>> ptdata.nr_tz_sensor+1); >>> + if (!ptdata.tzi) { >>> + syslog(LOG_ERR, "Err: allocate tz_info\n"); >>> + return -1; >>> + } >>> + >>> + ptdata.cdi =3D calloc(sizeof(struct cdev_info), >>> ptdata.nr_cooling_dev+1); >>> + if (!ptdata.cdi) { >>> + syslog(LOG_ERR, "Err: allocate cdev_info\n"); >>> + return -1; >>> + } >>> + >>> + /* now probe tzones */ >>> + if (scan_tzones()) >>> + return -1; >>> + if (scan_cdevs()) >>> + return -1; >>> + return 0; >>> +} >>> + >>> +/* convert sysfs zone instance to zone array index */ >>> +int zone_instance_to_index(int zone_inst) >>> +{ >>> + int i; >>> + >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) >>> + if (ptdata.tzi[i].instance =3D=3D zone_inst) >>> + return i; >>> + return -ENOENT; >>> +} >>> + >>> +/* read temperature of all thermal zones */ >>> +int update_thermal_data() >>> +{ >>> + int i; >>> + char tz_name[256]; >>> + static unsigned long samples; >>> + >>> + if (!ptdata.nr_tz_sensor) { >>> + syslog(LOG_ERR, "No thermal zones found!\n"); >>> + return -1; >>> + } >>> + >>> + /* circular buffer for keeping historic data */ >>> + if (cur_thermal_record >=3D NR_THERMAL_RECORDS) >>> + cur_thermal_record =3D 0; >>> + gettimeofday(&trec[cur_thermal_record].tv, NULL); >>> + if (tmon_log) { >>> + fprintf(tmon_log, "%lu ", ++samples); >>> + fprintf(tmon_log, "%3.1f ", p_param.t_target); >>> + } >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + memset(tz_name, 0, sizeof(tz_name)); >>> + snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, >>> TZONE, >>> + ptdata.tzi[i].instance); >>> + sysfs_get_ulong(tz_name, "temp", >>> + &trec[cur_thermal_record].temp[i]); >>> + if (tmon_log) >>> + fprintf(tmon_log, "%lu ", >>> + >>> trec[cur_thermal_record].temp[i]/1000); >>> + } >>> + for (i =3D 0; i < ptdata.nr_cooling_dev; i++) { >>> + char cdev_name[256]; >>> + unsigned long val; >>> + >>> + snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, >>> CDEV, >>> + ptdata.cdi[i].instance); >>> + probe_cdev(&ptdata.cdi[i], cdev_name); >>> + val =3D ptdata.cdi[i].cur_state; >>> + if (val > 1000000) >>> + val =3D 0; >>> + if (tmon_log) >>> + fprintf(tmon_log, "%lu ", val); >>> + } >>> + >>> + if (tmon_log) { >>> + fprintf(tmon_log, "\n"); >>> + fflush(tmon_log); >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +void set_ctrl_state(unsigned long state) >>> +{ >>> + char ctrl_cdev_path[256]; >>> + int i; >>> + unsigned long cdev_state; >>> + >>> + if (no_control) >>> + return; >>> + /* set all ctrl cdev to the same state */ >>> + for (i =3D 0; i < ptdata.nr_cooling_dev; i++) { >>> + if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { >>> + if (ptdata.cdi[i].max_state < 10) { >>> + syslog(LOG_WARNING, >>> + "not enough states in >>> control cdev\n"); >>> + return; >>> + } >>> + /* scale to percentage of max_state */ >>> + cdev_state =3D state * >>> ptdata.cdi[i].max_state/100; >>> + syslog(LOG_DEBUG, >>> + "ctrl cdev %d set state %lu scaled >>> to %lu\n", >>> + ptdata.cdi[i].instance, state, >>> cdev_state); >>> + snprintf(ctrl_cdev_path, 256, "%s/%s%d", >>> THERMAL_SYSFS, >>> + CDEV, ptdata.cdi[i].instance); >>> + syslog(LOG_DEBUG, "ctrl cdev path %s", >>> ctrl_cdev_path); >>> + sysfs_set_ulong(ctrl_cdev_path, >>> "cur_state", >>> + cdev_state); >>> + } >>> + } >>> +} >>> + >>> +void get_ctrl_state(unsigned long *state) >>> +{ >>> + char ctrl_cdev_path[256]; >>> + int ctrl_cdev_id =3D -1; >>> + int i; >>> + >>> + /* TODO: take average of all ctrl types. also consider >>> change based on >>> + * uevent. Take the first reading for now. >>> + */ >>> + for (i =3D 0; i < ptdata.nr_cooling_dev; i++) { >>> + if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { >>> + ctrl_cdev_id =3D ptdata.cdi[i].instance; >>> + syslog(LOG_INFO, "ctrl cdev %d get >>> state\n", >>> + ptdata.cdi[i].instance); >>> + break; >>> + } >>> + } >>> + if (ctrl_cdev_id =3D=3D -1) { >>> + *state =3D 0; >>> + return; >>> + } >>> + snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS, >>> + CDEV, ctrl_cdev_id); >>> + sysfs_get_ulong(ctrl_cdev_path, "cur_state", state); >>> +} >>> + >>> +void free_thermal_data(void) >>> +{ >>> + free(ptdata.tzi); >>> + free(ptdata.cdi); >>> +} >>> diff --git a/tools/thermal/tmon/tmon.8 b/tools/thermal/tmon/tmon.8 >>> new file mode 100644 >>> index 0000000..0be727c >>> --- /dev/null >>> +++ b/tools/thermal/tmon/tmon.8 >>> @@ -0,0 +1,142 @@ >>> +.TH TMON 8 >>> +.SH NAME >>> +\fBtmon\fP - A monitoring and testing tool for Linux kernel >>> thermal subsystem + >>> +.SH SYNOPSIS >>> +.ft B >>> +.B tmon >>> +.RB [ Options ] >>> +.br >>> +.SH DESCRIPTION >>> +\fBtmon \fP can be used to visualize thermal relationship and >>> +real-time thermal data; tune >>> +and test cooling devices and sensors; collect thermal data for >>> offline +analysis and plot. \fBtmon\fP must be run as root in order >>> to control device +states via sysfs. >>> +.PP >>> +\fBFunctions\fP >>> +.PP >>> +.nf >>> +1. Thermal relationships: >>> +- show thermal zone information >>> +- show cooling device information >>> +- show trip point binding within each thermal zone >>> +- show trip point and cooling device instance bindings >>> +.PP >>> +2. Real time data display >>> +- show temperature of all thermal zones w.r.t. its trip points and >>> types +- show states of all cooling devices >>> +.PP >>> +3. Thermal relationship learning and device tuning >>> +- with a built-in Proportional Integral Derivative (\fBPID\fP) >>> +controller, user can pair a cooling device to a thermal sensor for >>> +testing the effectiveness and learn about the thermal distance >>> between the two +- allow manual control of cooling device states >>> and target temperature +.PP >>> +4. Data logging in /var/tmp/tmon.log >>> +- contains thermal configuration data, i.e. cooling device, thermal >>> + zones, and trip points. Can be used for data collection in remote >>> + debugging. >>> +- log real-time thermal data into space separated format that can >>> be >>> + directly consumed by plotting tools such as Rscript. >>> + >>> +.SS Options >>> +.PP >>> +The \fB-c --control\fP option sets a cooling device type to >>> control temperature +of a thermal zone >>> +.PP >>> +The \fB-d --daemon\fP option runs \fBtmon \fP as daemon without >>> user interface +.PP >>> +The \fB-g --debug\fP option allow debug messages to be stored in >>> syslog +.PP >>> +The \fB-h --help\fP option shows help message >>> +.PP >>> +The \fB-l --log\fP option write data to /var/tmp/tmon.log >>> +.PP >>> +The \fB-t --time-interval\fP option sets the polling interval in >>> seconds +.PP >>> +The \fB-v --version\fP option shows the version of \fBtmon \fP >>> +.PP >>> +The \fB-z --zone\fP option sets the target therma zone instance to >>> be controlled +.PP >>> + >>> +.SH FIELD DESCRIPTIONS >>> +.nf >>> +.PP >>> +\fBP \fP passive cooling trip point type >>> +\fBA \fP active cooling trip point type (fan) >>> +\fBC \fP critical trip point type >>> +\fBA \fP hot trip point type >>> +\fBkp \fP proportional gain of \fBPID\fP controller >>> +\fBki \fP integral gain of \fBPID\fP controller >>> +\fBkd \fP derivative gain of \fBPID\fP controller >>> + >>> +.SH REQUIREMENT >>> +Build depends on ncurses >>> +.PP >>> +Runtime depends on window size large enough to show the number of >>> +devices found on the system. >>> + >>> +.PP >>> + >>> +.SH INTERACTIVE COMMANDS >>> +.pp >>> +.nf >>> +\fBCtrl-C, q/Q\fP stops \fBtmon\fP >>> +\fBTAB\fP shows tuning pop up panel, choose a letter to modify >>> + >>> +.SH EXAMPLES >>> +Without any parameters, tmon is in monitoring only mode and refresh >>> +screen every 1 second. >>> +.PP >>> +1. For monitoring only: >>> +.nf >>> +$ sudo ./tmon >>> + >>> +2. Use Processor cooling device to control thermal zone 0 at >>> default 65C. +$ sudo ./tmon -c Processor -z 0 >>> + >>> +3. Use intel_powerclamp(idle injection) cooling device to control >>> thermal zone 1 +$ sudo ./tmon -c intel_powerclamp -z 1 >>> + >>> +4. Turn on debug and collect data log at /var/tmp/tmon.log >>> +$ sudo ./tmon -g -l >>> + >>> +For example, the log below shows PID controller was adjusting >>> current states +for all cooling devices with "Processor" type such >>> that thermal zone 0 +can stay below 65 dC. >>> + >>> +#---------- THERMAL DATA LOG STARTED ----------- >>> +Samples TargetTemp acpitz0 acpitz1 Fan0 Fan1 Fan2 Fan3 Fan4 >>> Fan5 +Fan6 Fan7 Fan8 Fan9 Processor10 Processor11 Processor12 >>> Processor13 +LCD14 intel_powerclamp15 1 65.0 65 65 0 0 0 0 0 0 0 0 >>> 0 0 0 0 0 0 6 0 2 +65.0 66 65 0 0 0 0 0 0 0 0 0 0 4 4 4 4 6 0 3 >>> 65.0 60 54 0 0 0 0 0 0 0 0 +0 0 4 4 4 4 6 0 4 65.0 53 53 0 0 0 0 0 >>> 0 0 0 0 0 4 4 4 4 6 0 +5 65.0 52 52 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 >>> +6 65.0 53 65 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 >>> +7 65.0 68 70 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 0 >>> +8 65.0 68 68 0 0 0 0 0 0 0 0 0 0 5 5 5 5 6 0 >>> +9 65.0 68 68 0 0 0 0 0 0 0 0 0 0 6 6 6 6 6 0 >>> +10 65.0 67 67 0 0 0 0 0 0 0 0 0 0 7 7 7 7 6 0 >>> +11 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0 >>> +12 65.0 67 67 0 0 0 0 0 0 0 0 0 0 8 8 8 8 6 0 >>> +13 65.0 67 67 0 0 0 0 0 0 0 0 0 0 9 9 9 9 6 0 >>> +14 65.0 66 66 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0 >>> +15 65.0 66 67 0 0 0 0 0 0 0 0 0 0 10 10 10 10 6 0 >>> +16 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 >>> +17 65.0 66 66 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 >>> +18 65.0 64 61 0 0 0 0 0 0 0 0 0 0 11 11 11 11 6 0 >>> +19 65.0 60 59 0 0 0 0 0 0 0 0 0 0 12 12 12 12 6 0 >>> + >>> +Data can be read directly into an array by an example R-script >>> below: + >>> +#!/usr/bin/Rscript >>> +tdata <- read.table("/var/tmp/tmon.log", header=3DT, >>> comment.char=3D"#") +attach(tdata) >>> +jpeg("tmon.jpg") >>> +X11() >>> +g_range <- range(0, intel_powerclamp15, TargetTemp, acpitz0) >>> +plot( Samples, intel_powerclamp15, col=3D"blue", ylim=3Dg_range, >>> axes=3DFALSE, ann=3DFALSE) +par(new=3DTRUE) >>> +lines(TargetTemp, type=3D"o", pch=3D22, lty=3D2, col=3D"red") >>> +dev.off() >>> diff --git a/tools/thermal/tmon/tmon.c b/tools/thermal/tmon/tmon.c >>> new file mode 100644 >>> index 0000000..5f13fb1 >>> --- /dev/null >>> +++ b/tools/thermal/tmon/tmon.c >>> @@ -0,0 +1,350 @@ >>> +/* >>> + * tmon.c Thermal Monitor (TMON) main function and entry point >>> + * >>> + * Copyright (C) 2012 Intel Corporation. All rights reserved. >>> + * >>> + * This program is free software; you can redistribute it and/or >>> + * modify it under the terms of the GNU General Public License >>> version >>> + * 2 or later as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * Author: Jacob Pan >>> + * >>> + */ >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#include "tmon.h" >>> + >>> +unsigned long ticktime =3D 1; /* seconds */ >>> +unsigned long no_control =3D 1; /* monitoring only or use cooling >>> device for >>> + * temperature control. >>> + */ >>> +double time_elapsed =3D 0.0; >>> +unsigned long target_temp_user =3D 65; /* can be select by tui later= >>> */ +int dialogue_on; >>> +int tmon_exit; >>> +static short daemon_mode; >>> +static int logging; /* for recording thermal data to a file */ >>> +static int debug_on; >>> +FILE *tmon_log; >>> +char ctrl_cdev[CDEV_NAME_SIZE]; /*cooling device used for the PID >>> controller */ +int target_thermal_zone; /* user selected target >>> zone instance */ +static void start_daemon_mode(void); >>> + >>> +pthread_t event_tid; >>> +pthread_mutex_t input_lock; >>> +void usage() >>> +{ >>> + printf("Usage: tmon [OPTION...]\n"); >>> + printf(" -c, --control cooling device in >>> control\n"); >>> + printf(" -d, --daemon run as daemon, no TUI\n"); >>> + printf(" -g, --debug debug message in >>> syslog\n"); >>> + printf(" -h, --help show this help message\n"); >>> + printf(" -l, --log log data >>> to /var/tmp/tmon.log\n"); >>> + printf(" -t, --time-interval sampling time interval, > >>> 1 sec.\n"); >>> + printf(" -v, --version show version\n"); >>> + printf(" -z, --zone target thermal zone id\n"); >>> + >>> + exit(0); >>> +} >>> + >>> +void version() >>> +{ >>> + printf("TMON version %s\n", VERSION); >>> + exit(EXIT_SUCCESS); >>> +} >>> + >>> +static void tmon_cleanup(void) >>> +{ >>> + >>> + syslog(LOG_INFO, "TMON exit cleanup\n"); >>> + fflush(stdout); >>> + refresh(); >>> + if (tmon_log) >>> + fclose(tmon_log); >>> + if (event_tid) { >>> + pthread_mutex_lock(&input_lock); >>> + pthread_cancel(event_tid); >>> + pthread_mutex_unlock(&input_lock); >>> + pthread_mutex_destroy(&input_lock); >>> + } >>> + closelog(); >>> + /* relax control knobs, undo throttling */ >>> + set_ctrl_state(0); >>> + >>> + keypad(stdscr, FALSE); >>> + echo(); >>> + nocbreak(); >>> + close_windows(); >>> + endwin(); >>> + free_thermal_data(); >>> + >>> + exit(1); >>> +} >>> + >>> + >>> +static void tmon_sig_handler(int sig) >>> +{ >>> + syslog(LOG_INFO, "TMON caught signal %d\n", sig); >>> + refresh(); >>> + switch (sig) { >>> + case SIGTERM: >>> + printf("sigterm, exit and clean up\n"); >>> + fflush(stdout); >>> + break; >>> + case SIGKILL: >>> + printf("sigkill, exit and clean up\n"); >>> + fflush(stdout); >>> + break; >>> + case SIGINT: >>> + printf("ctrl-c, exit and clean up\n"); >>> + fflush(stdout); >>> + break; >>> + default: >>> + break; >>> + } >>> + tmon_exit =3D true; >>> +} >>> + >>> + >>> +static void start_syslog(void) >>> +{ >>> + if (debug_on) >>> + setlogmask(LOG_UPTO(LOG_DEBUG)); >>> + else >>> + setlogmask(LOG_UPTO(LOG_ERR)); >>> + openlog("tmon.log", LOG_CONS | LOG_PID | LOG_NDELAY, >>> LOG_LOCAL0); >>> + syslog(LOG_NOTICE, "TMON started by User %d", getuid()); >>> +} >>> + >>> +static void prepare_logging(void) >>> +{ >>> + int i; >>> + >>> + if (!logging) >>> + return; >>> + /* open local data log file */ >>> + tmon_log =3D fopen(TMON_LOG_FILE, "w+"); >>> + if (!tmon_log) { >>> + syslog(LOG_ERR, "failed to open log file %s\n", >>> TMON_LOG_FILE); >>> + return; >>> + } >>> + >>> + fprintf(tmon_log, "#----------- THERMAL SYSTEM CONFIG >>> -------------\n"); >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + char binding_str[33]; /* size of long + 1 */ >>> + int j; >>> + >>> + memset(binding_str, 0, sizeof(binding_str)); >>> + for (j =3D 0; j < 32; j++) >>> + binding_str[j] =3D >>> (ptdata.tzi[i].cdev_binding & 1<>> + '1' : '0'; >>> + >>> + fprintf(tmon_log, "#thermal zone %s%02d cdevs >>> binding: %32s\n", >>> + ptdata.tzi[i].type, >>> + ptdata.tzi[i].instance, >>> + binding_str); >>> + for (j =3D 0; j < ptdata.tzi[i].nr_trip_pts; >>> j++) { >>> + fprintf(tmon_log, "#\tTP%02d type:%s, >>> temp:%lu\n", j, >>> + >>> trip_type_name[ptdata.tzi[i].tp[j].type], >>> + ptdata.tzi[i].tp[j].temp); >>> + } >>> + >>> + } >>> + >>> + for (i =3D 0; i < ptdata.nr_cooling_dev; i++) >>> + fprintf(tmon_log, "#cooling devices%02d: %s\n", >>> + i, ptdata.cdi[i].type); >>> + >>> + fprintf(tmon_log, "#---------- THERMAL DATA LOG STARTED >>> -----------\n"); >>> + fprintf(tmon_log, "Samples TargetTemp "); >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + fprintf(tmon_log, "%s%d ", ptdata.tzi[i].type, >>> + ptdata.tzi[i].instance); >>> + } >>> + for (i =3D 0; i < ptdata.nr_cooling_dev; i++) >>> + fprintf(tmon_log, "%s%d ", ptdata.cdi[i].type, >>> + ptdata.cdi[i].instance); >>> + >>> + fprintf(tmon_log, "\n"); >>> +} >>> + >>> +static struct option opts[] =3D { >>> + { "control", 1, NULL, 'c' }, >>> + { "daemon", 0, NULL, 'd' }, >>> + { "time-interval", 1, NULL, 't' }, >>> + { "log", 0, NULL, 'l' }, >>> + { "help", 0, NULL, 'h' }, >>> + { "version", 0, NULL, 'v' }, >>> + { "debug", 0, NULL, 'g' }, >>> + { 0, 0, NULL, 0 } >>> +}; >>> + >>> + >>> +int main(int argc, char **argv) >>> +{ >>> + int err =3D 0; >>> + int id2 =3D 0, c; >>> + double yk =3D 0.0; /* controller output */ >>> + int target_tz_index; >>> + >>> + if (geteuid() !=3D 0) { >>> + printf("TMON needs to be run as root\n"); >>> + exit(EXIT_FAILURE); >>> + } >>> + >>> + while ((c =3D getopt_long(argc, argv, "c:dlht:vgz:", opts, >>> &id2)) !=3D -1) { >>> + switch (c) { >>> + case 'c': >>> + no_control =3D 0; >>> + strncpy(ctrl_cdev, optarg, CDEV_NAME_SIZE); >>> + break; >>> + case 'd': >>> + start_daemon_mode(); >>> + printf("Run TMON in daemon mode\n"); >>> + break; >>> + case 't': >>> + ticktime =3D strtod(optarg, NULL); >>> + if (ticktime < 1) >>> + ticktime =3D 1; >>> + break; >>> + case 'l': >>> + printf("Logging data >>> to /var/tmp/tmon.log\n"); >>> + logging =3D 1; >>> + break; >>> + case 'h': >>> + usage(); >>> + break; >>> + case 'v': >>> + version(); >>> + break; >>> + case 'g': >>> + debug_on =3D 1; >>> + break; >>> + case 'z': >>> + target_thermal_zone =3D strtod(optarg, NULL); >>> + break; >>> + default: >>> + break; >>> + } >>> + } >>> + if (pthread_mutex_init(&input_lock, NULL) !=3D 0) { >>> + printf("\n mutex init failed\n"); >>> + return 1; >>> + } >>> + start_syslog(); >>> + if (signal(SIGINT, tmon_sig_handler) =3D=3D SIG_ERR) >>> + syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); >>> + if (signal(SIGTERM, tmon_sig_handler) =3D=3D SIG_ERR) >>> + syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); >>> + >>> + if (probe_thermal_sysfs()) { >>> + closelog(); >>> + return -1; >>> + } >>> + initialize_curses(); >>> + setup_windows(); >>> + signal(SIGWINCH, resize_handler); >>> + show_title_bar(); >>> + show_sensors_w(); >>> + show_cooling_device(); >>> + update_thermal_data(); >>> + show_data_w(); >>> + prepare_logging(); >>> + init_thermal_controller(); >>> + >>> + nodelay(stdscr, TRUE); >>> + err =3D pthread_create(&event_tid, NULL, &handle_tui_events, >>> NULL); >>> + if (err !=3D 0) { >>> + printf("\ncan't create thread :[%s]", >>> strerror(err)); >>> + tmon_cleanup(); >>> + exit(EXIT_FAILURE); >>> + } >>> + >>> + /* validate range of user selected target zone, default to >>> the first >>> + * instance if out of range >>> + */ >>> + target_tz_index =3D >>> zone_instance_to_index(target_thermal_zone); >>> + if (target_tz_index < 0) { >>> + target_thermal_zone =3D ptdata.tzi[0].instance; >>> + syslog(LOG_ERR, "target zone is not found, default >>> to %d\n", >>> + target_thermal_zone); >>> + } >>> + while (1) { >>> + sleep(ticktime); >>> + show_title_bar(); >>> + show_sensors_w(); >>> + update_thermal_data(); >>> + if (!dialogue_on) { >>> + show_data_w(); >>> + show_cooling_device(); >>> + } >>> + cur_thermal_record++; >>> + time_elapsed +=3D ticktime; >>> + controller_handler(trec[0].temp[target_tz_index] / >>> 1000, >>> + &yk); >>> + trec[0].pid_out_pct =3D yk; >>> + if (!dialogue_on) >>> + show_control_w(); >>> + if (tmon_exit) >>> + break; >>> + } >>> + tmon_cleanup(); >>> + return 0; >>> +} >>> + >>> +static void start_daemon_mode() >>> +{ >>> + daemon_mode =3D 1; >>> + /* fork */ >>> + pid_t sid, pid =3D fork(); >>> + if (pid < 0) { >>> + exit(EXIT_FAILURE); >>> + } else if (pid > 0) >>> + /* kill parent */ >>> + exit(EXIT_SUCCESS); >>> + >>> + /* disable TUI, it may not be necessary, but saves some >>> resource */ >>> + disable_tui(); >>> + >>> + /* change the file mode mask */ >>> + umask(0); >>> + >>> + /* new SID for the daemon process */ >>> + sid =3D setsid(); >>> + if (sid < 0) >>> + exit(EXIT_FAILURE); >>> + >>> + /* change working directory */ >>> + if ((chdir("/")) < 0) >>> + exit(EXIT_FAILURE); >>> + >>> + >>> + sleep(10); >>> + >>> + close(STDIN_FILENO); >>> + close(STDOUT_FILENO); >>> + close(STDERR_FILENO); >>> + >>> +} >>> diff --git a/tools/thermal/tmon/tmon.h b/tools/thermal/tmon/tmon.h >>> new file mode 100644 >>> index 0000000..9e3c49c >>> --- /dev/null >>> +++ b/tools/thermal/tmon/tmon.h >>> @@ -0,0 +1,204 @@ >>> +/* >>> + * tmon.h contains data structures and constants used by TMON >>> + * >>> + * Copyright (C) 2012 Intel Corporation. All rights reserved. >>> + * >>> + * This program is free software; you can redistribute it and/or >>> + * modify it under the terms of the GNU General Public License >>> version >>> + * 2 or later as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * Author Name Jacob Pan >>> + * >>> + */ >>> + >>> +#ifndef TMON_H >>> +#define TMON_H >>> + >>> +#define MAX_DISP_TEMP 125 >>> +#define MAX_CTRL_TEMP 105 >>> +#define MIN_CTRL_TEMP 40 >>> +#define MAX_NR_TZONE 16 >>> +#define MAX_NR_CDEV 32 >>> +#define MAX_NR_TRIP 16 >>> +#define MAX_NR_CDEV_TRIP 12 /* number of cooling devices that can >>> bind >>> + * to a thermal zone trip. >>> + */ >>> +#define MAX_TEMP_KC 140000 >>> +/* starting char position to draw sensor data, such as tz names >>> + * trip point list, etc. >>> + */ >>> +#define DATA_LEFT_ALIGN 10 >>> +#define NR_LINES_TZDATA 1 >>> +#define TMON_LOG_FILE "/var/tmp/tmon.log" >>> + >>> +extern unsigned long ticktime; >>> +extern double time_elapsed; >>> +extern unsigned long target_temp_user; >>> +extern int dialogue_on; >>> +extern char ctrl_cdev[]; >>> +extern pthread_mutex_t input_lock; >>> +extern int tmon_exit; >>> +extern int target_thermal_zone; >>> +/* use fixed size record to simplify data processing and transfer >>> + * TBD: more info to be added, e.g. programmable trip point data. >>> +*/ >>> +struct thermal_data_record { >>> + struct timeval tv; >>> + unsigned long temp[MAX_NR_TZONE]; >>> + double pid_out_pct; >>> +}; >>> + >>> +struct cdev_info { >>> + char type[64]; >>> + int instance; >>> + unsigned long max_state; >>> + unsigned long cur_state; >>> + unsigned long flag; >>> +}; >>> + >>> +enum trip_type { >>> + THERMAL_TRIP_CRITICAL, >>> + THERMAL_TRIP_HOT, >>> + THERMAL_TRIP_PASSIVE, >>> + THERMAL_TRIP_ACTIVE, >>> + NR_THERMAL_TRIP_TYPE, >>> +}; >>> + >>> +struct trip_point { >>> + enum trip_type type; >>> + unsigned long temp; >>> + unsigned long hysteresis; >>> + int attribute; /* programmability etc. */ >>> +}; >>> + >>> +/* thermal zone configuration information, binding with cooling >>> devices could >>> + * change at runtime. >>> + */ >>> +struct tz_info { >>> + char type[256]; /* e.g. acpitz */ >>> + int instance; >>> + int passive; /* active zone has passive node to force >>> passive mode */ >>> + int nr_cdev; /* number of cooling device binded */ >>> + int nr_trip_pts; >>> + struct trip_point tp[MAX_NR_TRIP]; >>> + unsigned long cdev_binding; /* bitmap for attached cdevs */ >>> + /* cdev bind trip points, allow one cdev bind to multiple >>> trips */ >>> + unsigned long trip_binding[MAX_NR_CDEV]; >>> +}; >>> + >>> +struct tmon_platform_data { >>> + int nr_tz_sensor; >>> + int nr_cooling_dev; >>> + /* keep track of instance ids since there might be gaps */ >>> + int max_tz_instance; >>> + int max_cdev_instance; >>> + struct tz_info *tzi; >>> + struct cdev_info *cdi; >>> +}; >>> + >>> +struct control_ops { >>> + void (*set_ratio)(unsigned long ratio); >>> + unsigned long (*get_ratio)(unsigned long ratio); >>> + >>> +}; >>> + >>> +enum cdev_types { >>> + CDEV_TYPE_PROC, >>> + CDEV_TYPE_FAN, >>> + CDEV_TYPE_MEM, >>> + CDEV_TYPE_NR, >>> +}; >>> + >>> +/* REVISIT: the idea is to group sensors if possible, e.g. on >>> intel mid >>> + * we have "skin0", "skin1", "sys", "msicdie" >>> + * on DPTF enabled systems, we might have PCH, TSKN, TAMB, etc. >>> + */ >>> +enum tzone_types { >>> + TZONE_TYPE_ACPI, >>> + TZONE_TYPE_PCH, >>> + TZONE_TYPE_NR, >>> +}; >>> + >>> +/* limit the output of PID controller adjustment */ >>> +#define LIMIT_HIGH (95) >>> +#define LIMIT_LOW (2) >>> + >>> +struct pid_params { >>> + double kp; /* Controller gain from Dialog Box */ >>> + double ki; /* Time-constant for I action from Dialog Box >>> */ >>> + double kd; /* Time-constant for D action from Dialog Box >>> */ >>> + double ts; >>> + double k_lpf; >>> + >>> + double t_target; >>> + double y_k; >>> +}; >>> + >>> +extern int init_thermal_controller(void); >>> +extern void controller_handler(const double xk, double *yk); >>> + >>> +extern struct tmon_platform_data ptdata; >>> +extern struct pid_params p_param; >>> + >>> +extern FILE *tmon_log; >>> +extern int cur_thermal_record; /* index to the trec array */ >>> +extern struct thermal_data_record trec[]; >>> +extern const char *trip_type_name[]; >>> +extern unsigned long no_control; >>> + >>> +extern void initialize_curses(void); >>> +extern void show_controller_stats(char *line); >>> +extern void show_title_bar(void); >>> +extern void setup_windows(void); >>> +extern void disable_tui(void); >>> +extern void show_sensors_w(void); >>> +extern void show_data_w(void); >>> +extern void write_status_bar(int x, char *line); >>> +extern void show_control_w(); >>> + >>> +extern void show_cooling_device(void); >>> +extern void show_dialogue(void); >>> +extern int update_thermal_data(void); >>> + >>> +extern int probe_thermal_sysfs(void); >>> +extern void free_thermal_data(void); >>> +extern void resize_handler(int sig); >>> +extern void set_ctrl_state(unsigned long state); >>> +extern void get_ctrl_state(unsigned long *state); >>> +extern void *handle_tui_events(void *arg); >>> +extern int sysfs_set_ulong(char *path, char *filename, unsigned >>> long val); +extern int zone_instance_to_index(int zone_inst); >>> +extern void close_windows(void); >>> + >>> +#define PT_COLOR_DEFAULT 1 >>> +#define PT_COLOR_HEADER_BAR 2 >>> +#define PT_COLOR_ERROR 3 >>> +#define PT_COLOR_RED 4 >>> +#define PT_COLOR_YELLOW 5 >>> +#define PT_COLOR_GREEN 6 >>> +#define PT_COLOR_BRIGHT 7 >>> +#define PT_COLOR_BLUE 8 >>> + >>> +/* each thermal zone uses 12 chars, 8 for name, 2 for instance, 2 >>> space >>> + * also used to list trip points in forms of AAAC, which represents >>> + * A: Active >>> + * C: Critical >>> + */ >>> +#define TZONE_RECORD_SIZE 12 >>> +#define TZ_LEFT_ALIGN 32 >>> +#define CDEV_NAME_SIZE 20 >>> +#define CDEV_FLAG_IN_CONTROL (1 << 0) >>> + >>> +/* dialogue box starts */ >>> +#define DIAG_X 48 >>> +#define DIAG_Y 8 >>> +#define THERMAL_SYSFS "/sys/class/thermal" >>> +#define CDEV "cooling_device" >>> +#define TZONE "thermal_zone" >>> +#define TDATA_LEFT 16 >>> +#endif /* TMON_H */ >>> diff --git a/tools/thermal/tmon/tui.c b/tools/thermal/tmon/tui.c >>> new file mode 100644 >>> index 0000000..957ecf3 >>> --- /dev/null >>> +++ b/tools/thermal/tmon/tui.c >>> @@ -0,0 +1,631 @@ >>> +/* >>> + * tui.c ncurses text user interface for TMON program >>> + * >>> + * Copyright (C) 2013 Intel Corporation. All rights reserved. >>> + * >>> + * This program is free software; you can redistribute it and/or >>> + * modify it under the terms of the GNU General Public License >>> version >>> + * 2 or later as published by the Free Software Foundation. >>> + * >>> + * This program is distributed in the hope that it will be useful, >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> + * GNU General Public License for more details. >>> + * >>> + * Author: Jacob Pan >>> + * >>> + */ >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#include "tmon.h" >>> + >>> +static PANEL *data_panel; >>> +static PANEL *dialogue_panel; >>> +static PANEL *top; >>> + >>> +static WINDOW *title_bar_window; >>> +static WINDOW *tz_sensor_window; >>> +static WINDOW *cooling_device_window; >>> +static WINDOW *control_window; >>> +static WINDOW *status_bar_window; >>> +static WINDOW *thermal_data_window; >>> +static WINDOW *dialogue_window; >>> + >>> +char status_bar_slots[10][40]; >>> +static void draw_hbar(WINDOW *win, int y, int start, int len, >>> + unsigned long pattern, bool end); >>> + >>> +static int maxx, maxy; >>> +static int maxwidth =3D 200; >>> + >>> +#define TITLE_BAR_HIGHT 1 >>> +#define SENSOR_WIN_HIGHT 4 /* one row for tz name, one for trip >>> points */ + >>> + >>> +/* daemon mode flag (set by startup parameter -d) */ >>> +static int tui_disabled; >>> + >>> +static void close_panel(PANEL *p) >>> +{ >>> + if (p) { >>> + del_panel(p); >>> + p =3D NULL; >>> + } >>> +} >>> + >>> +static void close_window(WINDOW *win) >>> +{ >>> + if (win) { >>> + delwin(win); >>> + win =3D NULL; >>> + } >>> +} >>> + >>> +void close_windows(void) >>> +{ >>> + if (tui_disabled) >>> + return; >>> + /* must delete panels before their attached windows */ >>> + if (dialogue_window) >>> + close_panel(dialogue_panel); >>> + if (cooling_device_window) >>> + close_panel(data_panel); >>> + >>> + close_window(title_bar_window); >>> + close_window(tz_sensor_window); >>> + close_window(status_bar_window); >>> + close_window(cooling_device_window); >>> + close_window(control_window); >>> + close_window(thermal_data_window); >>> + close_window(dialogue_window); >>> + >>> +} >>> + >>> +void write_status_bar(int x, char *line) >>> +{ >>> + mvwprintw(status_bar_window, 0, x, "%s", line); >>> + wrefresh(status_bar_window); >>> +} >>> + >>> +void setup_windows(void) >>> +{ >>> + int y_begin =3D 1; >>> + >>> + if (tui_disabled) >>> + return; >>> + >>> + getmaxyx(stdscr, maxy, maxx); >>> + resizeterm(maxy, maxx); >>> + >>> + title_bar_window =3D subwin(stdscr, TITLE_BAR_HIGHT, maxx, >>> 0, 0); >>> + y_begin +=3D TITLE_BAR_HIGHT; >>> + >>> + tz_sensor_window =3D subwin(stdscr, SENSOR_WIN_HIGHT, maxx, >>> y_begin, 0); >>> + y_begin +=3D SENSOR_WIN_HIGHT; >>> + >>> + cooling_device_window =3D subwin(stdscr, >>> ptdata.nr_cooling_dev + 3, maxx, >>> + y_begin, 0); >>> + y_begin +=3D ptdata.nr_cooling_dev + 3; /* 2 lines for >>> border */ >>> + /* two lines to show borders, one line per tz show trip >>> point position >>> + * and value. >>> + * dialogue window is a pop-up, when needed it lays on top >>> of cdev win >>> + */ >>> + >>> + dialogue_window =3D subwin(stdscr, ptdata.nr_cooling_dev+5, >>> maxx-50, >>> + DIAG_Y, DIAG_X); >>> + >>> + thermal_data_window =3D subwin(stdscr, ptdata.nr_tz_sensor * >>> + NR_LINES_TZDATA + 3, maxx, >>> y_begin, 0); >>> + y_begin +=3D ptdata.nr_tz_sensor * NR_LINES_TZDATA + 3; >>> + control_window =3D subwin(stdscr, 4, maxx, y_begin, 0); >>> + >>> + scrollok(cooling_device_window, TRUE); >>> + maxwidth =3D maxx - 18; >>> + status_bar_window =3D subwin(stdscr, 1, maxx, maxy-1, 0); >>> + >>> + strcpy(status_bar_slots[0], " Ctrl-c - Quit "); >>> + strcpy(status_bar_slots[1], " TAB - Tuning "); >>> + wmove(status_bar_window, 1, 30); >>> + >>> + /* prepare panels for dialogue, if panel already created >>> then we must >>> + * be doing resizing, so just replace windows with new >>> ones, old ones >>> + * should have been deleted by close_window >>> + */ >>> + data_panel =3D new_panel(cooling_device_window); >>> + if (!data_panel) >>> + syslog(LOG_DEBUG, "No data panel\n"); >>> + else { >>> + if (dialogue_window) { >>> + dialogue_panel =3D >>> new_panel(dialogue_window); >>> + if (!dialogue_panel) >>> + syslog(LOG_DEBUG, "No dialogue >>> panel\n"); >>> + else { >>> + /* Set up the user pointer to the >>> next panel*/ >>> + set_panel_userptr(data_panel, >>> dialogue_panel); >>> + set_panel_userptr(dialogue_panel, >>> data_panel); >>> + top =3D data_panel; >>> + } >>> + } else >>> + syslog(LOG_INFO, "no dialogue win, term >>> too small\n"); >>> + } >>> + doupdate(); >>> + werase(stdscr); >>> + refresh(); >>> +} >>> + >>> +void resize_handler(int sig) >>> +{ >>> + /* start over when term gets resized, but first we clean >>> up */ >>> + close_windows(); >>> + endwin(); >>> + refresh(); >>> + clear(); >>> + getmaxyx(stdscr, maxy, maxx); /* get the new screen size >>> */ >>> + setup_windows(); >>> + /* rate limit */ >>> + sleep(1); >>> + syslog(LOG_DEBUG, "SIG %d, term resized to %d x %d\n", >>> + sig, maxy, maxx); >>> + signal(SIGWINCH, resize_handler); >>> +} >>> + >>> +const char cdev_title[] =3D " COOLING DEVICES "; >>> +void show_cooling_device(void) >>> +{ >>> + int i, j, x, y =3D 0; >>> + >>> + if (tui_disabled || !cooling_device_window) >>> + return; >>> + >>> + werase(cooling_device_window); >>> + >>> + wattron(cooling_device_window, A_BOLD); >>> + mvwprintw(cooling_device_window, 0, maxx/2 - >>> sizeof(cdev_title), >>> + cdev_title); >>> + >>> + mvwprintw(cooling_device_window, 1, 1, >>> + "ID Cooling Dev Cur Max Thermal Zone >>> Binding"); >>> + wattroff(cooling_device_window, A_BOLD); >>> + for (j =3D 0; j < ptdata.nr_cooling_dev; j++) { >>> + /* draw cooling device list on the left in the >>> order of >>> + * cooling device instances. skip unused idr. >>> + */ >>> + mvwprintw(cooling_device_window, j + 2, 1, >>> + "%02d %12.12s%6d %6d", >>> + ptdata.cdi[j].instance, >>> + ptdata.cdi[j].type, >>> + ptdata.cdi[j].cur_state, >>> + ptdata.cdi[j].max_state); >>> + } >>> + >>> + /* show cdev binding, y is the global cooling device >>> instance */ >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + int tz_inst =3D ptdata.tzi[i].instance; >>> + for (j =3D 0; j < ptdata.nr_cooling_dev; j++) { >>> + int cdev_inst; >>> + y =3D j; >>> + x =3D tz_inst * TZONE_RECORD_SIZE + >>> TZ_LEFT_ALIGN; + >>> + draw_hbar(cooling_device_window, y+2, x, >>> + TZONE_RECORD_SIZE-1, ACS_VLINE, >>> false); + >>> + /* draw a column of spaces to separate >>> thermal zones */ >>> + mvwprintw(cooling_device_window, y+2, x-1, >>> " "); >>> + if (ptdata.tzi[i].cdev_binding) { >>> + cdev_inst =3D ptdata.cdi[j].instance; >>> + unsigned long trip_binding =3D >>> + >>> ptdata.tzi[i].trip_binding[cdev_inst]; >>> + int k =3D 0; /* per zone trip point >>> id that >>> + * binded to this cdev, >>> one to >>> + * many possible based >>> on the >>> + * binding bitmask. >>> + */ >>> + syslog(LOG_DEBUG, >>> + "bind tz%d cdev%d tp%lx %d >>> cdev%lx\n", >>> + i, j, trip_binding, y, >>> + >>> ptdata.tzi[i].cdev_binding); >>> + /* draw each trip binding for the >>> cdev */ >>> + while (trip_binding >>=3D 1) { >>> + k++; >>> + if (!(trip_binding & 1)) >>> + continue; >>> + /* draw '*' to show >>> binding */ >>> + >>> mvwprintw(cooling_device_window, >>> + y + 2, >>> + x + >>> ptdata.tzi[i].nr_trip_pts - >>> + k - 1, "*"); >>> + } >>> + } >>> + } >>> + } >>> + wborder(cooling_device_window, 0, 0, 0, 0, 0, 0, 0, 0); >>> + wrefresh(cooling_device_window); >>> +} >>> + >>> +const char DIAG_TITLE[] =3D "[ TUNABLES ]"; >>> +#define DIAG_DEV_ROWS 5 >>> +void show_dialogue(void) >>> +{ >>> + int j, x =3D 0, y =3D 0; >>> + WINDOW *w =3D dialogue_window; >>> + >>> + if (tui_disabled || !w) >>> + return; >>> + >>> + werase(w); >>> + box(w, 0, 0); >>> + mvwprintw(w, 0, maxx/4, DIAG_TITLE); >>> + /* list all the available tunables */ >>> + for (j =3D 0; j <=3D ptdata.nr_cooling_dev; j++) { >>> + y =3D j % DIAG_DEV_ROWS; >>> + if (y =3D=3D 0 && j !=3D 0) >>> + x +=3D 20; >>> + if (j =3D=3D ptdata.nr_cooling_dev) >>> + /* save last choice for target temp */ >>> + mvwprintw(w, y+1, x+1, "%C-%.12s", 'A'+j, >>> "Set Temp"); >>> + else >>> + mvwprintw(w, y+1, x+1, "%C-%.10s-%2d", >>> 'A'+j, >>> + ptdata.cdi[j].type, >>> ptdata.cdi[j].instance); >>> + } >>> + wattron(w, A_BOLD); >>> + mvwprintw(w, DIAG_DEV_ROWS+1, 1, "Enter Choice [A-Z]?"); >>> + wattroff(w, A_BOLD); >>> + /* y size of dialogue win is nr cdev + 5, so print legend >>> + * at the bottom line >>> + */ >>> + mvwprintw(w, ptdata.nr_cooling_dev+3, 1, >>> + "Legend: A=3DActive, P=3DPassive, C=3DCritical"); >>> + >>> + wrefresh(dialogue_window); >>> +} >>> + >>> +void write_dialogue_win(char *buf, int y, int x) >>> +{ >>> + WINDOW *w =3D dialogue_window; >>> + >>> + mvwprintw(w, y, x, "%s", buf); >>> +} >>> + >>> +const char control_title[] =3D " CONTROLS "; >>> +void show_control_w(void) >>> +{ >>> + unsigned long state; >>> + >>> + get_ctrl_state(&state); >>> + >>> + if (tui_disabled || !control_window) >>> + return; >>> + >>> + werase(control_window); >>> + wattron(control_window, A_BOLD); >>> + mvwprintw(control_window, 0, maxx/2 - >>> sizeof(control_title), >>> + control_title); >>> + wattroff(control_window, A_BOLD); >>> + >>> + mvwprintw(control_window, 1, 1, "PID gain: kp=3D%2.2f >>> ki=3D%2.2f, kd=3D%2.2f", >>> + p_param.kp, p_param.ki, p_param.kd); >>> + >>> + mvwprintw(control_window, 2, 1, >>> + "Target Temp: %2.1f, Zone: %d, Control Device: >>> %.12s, PID output: %2.2f, state: %d", >>> + target_thermal_zone, ctrl_cdev, >>> + p_param.t_target, p_param.y_k, state); >>> + /* draw border last such that everything is within >>> boundary */ >>> + wborder(control_window, 0, 0, 0, 0, 0, 0, 0, 0); >>> + wrefresh(control_window); >>> +} >>> + >>> +void initialize_curses(void) >>> +{ >>> + if (tui_disabled) >>> + return; >>> + >>> + initscr(); >>> + start_color(); >>> + keypad(stdscr, TRUE); /* enable keyboard mapping */ >>> + nonl(); /* tell curses not to do >>> NL->CR/NL on output */ >>> + cbreak(); /* take input chars one at a time >>> */ >>> + noecho(); /* dont echo input */ >>> + curs_set(0); /* turn off cursor */ >>> + use_default_colors(); >>> + >>> + init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK); >>> + init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE); >>> + init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED); >>> + init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED); >>> + init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW); >>> + init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN); >>> + init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE); >>> + init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK); >>> + >>> +} >>> + >>> +void show_title_bar(void) >>> +{ >>> + int i; >>> + int x =3D 0; >>> + >>> + if (tui_disabled || !title_bar_window) >>> + return; >>> + >>> + wattrset(title_bar_window, >>> COLOR_PAIR(PT_COLOR_HEADER_BAR)); >>> + wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR)); >>> + werase(title_bar_window); >>> + >>> + mvwprintw(title_bar_window, 0, 0, >>> + " TMON v%s", VERSION); >>> + >>> + wrefresh(title_bar_window); >>> + >>> + werase(status_bar_window); >>> + >>> + for (i =3D 0; i < 10; i++) { >>> + if (strlen(status_bar_slots[i]) =3D=3D 0) >>> + continue; >>> + wattron(status_bar_window, A_REVERSE); >>> + mvwprintw(status_bar_window, 0, x, "%s", >>> status_bar_slots[i]); >>> + wattroff(status_bar_window, A_REVERSE); >>> + x +=3D strlen(status_bar_slots[i]) + 1; >>> + } >>> + wrefresh(status_bar_window); >>> +} >>> + >>> +static void handle_input_val(int ch) >>> +{ >>> + char buf[32]; >>> + int val; >>> + char path[256]; >>> + WINDOW *w =3D dialogue_window; >>> + >>> + echo(); >>> + keypad(w, TRUE); >>> + wgetnstr(w, buf, 31); >>> + val =3D atoi(buf); >>> + >>> + if (ch =3D=3D ptdata.nr_cooling_dev) { >>> + snprintf(buf, 31, "Invalid Temp %d! %d-%d", val, >>> + MIN_CTRL_TEMP, MAX_CTRL_TEMP); >>> + if (val < MIN_CTRL_TEMP || val > MAX_CTRL_TEMP) >>> + write_status_bar(40, buf); >>> + else { >>> + p_param.t_target =3D val; >>> + snprintf(buf, 31, "Set New Target Temp >>> %d", val); >>> + write_status_bar(40, buf); >>> + } >>> + } else { >>> + snprintf(path, 256, "%s/%s%d", THERMAL_SYSFS, >>> + CDEV, ptdata.cdi[ch].instance); >>> + sysfs_set_ulong(path, "cur_state", val); >>> + } >>> + noecho(); >>> + dialogue_on =3D 0; >>> + show_data_w(); >>> + show_control_w(); >>> + >>> + top =3D (PANEL *)panel_userptr(top); >>> + top_panel(top); >>> +} >>> + >>> +static void handle_input_choice(int ch) >>> +{ >>> + char buf[48]; >>> + int base =3D 0; >>> + int cdev_id =3D 0; >>> + >>> + if ((ch >=3D 'A' && ch <=3D 'A' + ptdata.nr_cooling_dev) || >>> + (ch >=3D 'a' && ch <=3D 'a' + ptdata.nr_cooling_dev)) { >>> + base =3D (ch < 'a') ? 'A' : 'a'; >>> + cdev_id =3D ch - base; >>> + if (ptdata.nr_cooling_dev =3D=3D cdev_id) >>> + snprintf(buf, sizeof(buf), "New Target >>> Temp:"); >>> + else >>> + snprintf(buf, sizeof(buf), "New Value for >>> %.10s-%2d: ", >>> + ptdata.cdi[cdev_id].type, >>> + ptdata.cdi[cdev_id].instance); >>> + write_dialogue_win(buf, DIAG_DEV_ROWS+2, 2); >>> + handle_input_val(cdev_id); >>> + } else { >>> + snprintf(buf, sizeof(buf), "Invalid selection %d", >>> ch); >>> + write_dialogue_win(buf, 8, 2); >>> + } >>> +} >>> + >>> +void *handle_tui_events(void *arg) >>> +{ >>> + int ch; >>> + >>> + keypad(cooling_device_window, TRUE); >>> + while ((ch =3D wgetch(cooling_device_window)) !=3D EOF) { >>> + if (tmon_exit) >>> + break; >>> + /* when term size is too small, no dialogue panels >>> are set. >>> + * we need to filter out such cases. >>> + */ >>> + if (!data_panel || !dialogue_panel || >>> + !cooling_device_window || >>> + !dialogue_window) { >>> + >>> + continue; >>> + } >>> + pthread_mutex_lock(&input_lock); >>> + if (dialogue_on) { >>> + handle_input_choice(ch); >>> + /* top panel filter */ >>> + if (ch =3D=3D 'q' || ch =3D=3D 'Q') >>> + ch =3D 0; >>> + } >>> + switch (ch) { >>> + case KEY_LEFT: >>> + box(cooling_device_window, 10, 0); >>> + break; >>> + case 9: /* TAB */ >>> + top =3D (PANEL *)panel_userptr(top); >>> + top_panel(top); >>> + if (top =3D=3D dialogue_panel) { >>> + dialogue_on =3D 1; >>> + show_dialogue(); >>> + } else { >>> + dialogue_on =3D 0; >>> + /* force refresh */ >>> + show_data_w(); >>> + show_control_w(); >>> + } >>> + break; >>> + case 'q': >>> + case 'Q': >>> + tmon_exit =3D 1; >>> + break; >>> + } >>> + update_panels(); >>> + doupdate(); >>> + pthread_mutex_unlock(&input_lock); >>> + } >>> + >>> + if (arg) >>> + *(int *)arg =3D 0; /* make gcc happy */ >>> + >>> + return NULL; >>> +} >>> + >>> +/* draw a horizontal bar in given pattern */ >>> +static void draw_hbar(WINDOW *win, int y, int start, int len, >>> unsigned long ptn, >>> + bool end) >>> +{ >>> + mvwaddch(win, y, start, ptn); >>> + whline(win, ptn, len); >>> + if (end) >>> + mvwaddch(win, y, MAX_DISP_TEMP+TDATA_LEFT, ']'); >>> +} >>> + >>> +static char trip_type_to_char(int type) >>> +{ >>> + switch (type) { >>> + case THERMAL_TRIP_CRITICAL: return 'C'; >>> + case THERMAL_TRIP_HOT: return 'H'; >>> + case THERMAL_TRIP_PASSIVE: return 'P'; >>> + case THERMAL_TRIP_ACTIVE: return 'A'; >>> + default: >>> + return '?'; >>> + } >>> +} >>> + >>> +/* fill a string with trip point type and value in one line >>> + * e.g. P(56) C(106) >>> + * maintain the distance one degree per char >>> + */ >>> +static void draw_tp_line(int tz, int y) >>> +{ >>> + int j; >>> + int x; >>> + >>> + for (j =3D 0; j < ptdata.tzi[tz].nr_trip_pts; j++) { >>> + x =3D ptdata.tzi[tz].tp[j].temp / 1000; >>> + mvwprintw(thermal_data_window, y + 0, x + >>> TDATA_LEFT, >>> + "%c%d", >>> trip_type_to_char(ptdata.tzi[tz].tp[j].type), >>> + x); >>> + syslog(LOG_INFO, "%s:tz %d tp %d temp =3D %lu\n", >>> __func__, >>> + tz, j, ptdata.tzi[tz].tp[j].temp); >>> + } >>> +} >>> + >>> +const char data_win_title[] =3D " THERMAL DATA "; >>> +void show_data_w(void) >>> +{ >>> + int i; >>> + >>> + >>> + if (tui_disabled || !thermal_data_window) >>> + return; >>> + >>> + werase(thermal_data_window); >>> + wattron(thermal_data_window, A_BOLD); >>> + mvwprintw(thermal_data_window, 0, maxx/2 - >>> sizeof(data_win_title), >>> + data_win_title); >>> + wattroff(thermal_data_window, A_BOLD); >>> + /* draw a line as ruler */ >>> + for (i =3D 10; i < MAX_DISP_TEMP; i +=3D 10) >>> + mvwprintw(thermal_data_window, 1, i+TDATA_LEFT, >>> "%2d", i); + >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + int temp =3D trec[cur_thermal_record].temp[i] / 1000; >>> + int y =3D 0; >>> + >>> + y =3D i * NR_LINES_TZDATA + 2; >>> + /* y at tz temp data line */ >>> + mvwprintw(thermal_data_window, y, 1, >>> "%6.6s%2d:[%3d][", >>> + ptdata.tzi[i].type, >>> + ptdata.tzi[i].instance, temp); >>> + draw_hbar(thermal_data_window, y, TDATA_LEFT, >>> temp, ACS_RARROW, >>> + true); >>> + draw_tp_line(i, y); >>> + } >>> + wborder(thermal_data_window, 0, 0, 0, 0, 0, 0, 0, 0); >>> + wrefresh(thermal_data_window); >>> +} >>> + >>> +const char tz_title[] =3D "THERMAL ZONES/SENSORS"; >>> + >>> +void show_sensors_w(void) >>> +{ >>> + int i, j; >>> + char buffer[512]; >>> + >>> + if (tui_disabled || !tz_sensor_window) >>> + return; >>> + >>> + werase(tz_sensor_window); >>> + >>> + memset(buffer, 0, sizeof(buffer)); >>> + wattron(tz_sensor_window, A_BOLD); >>> + mvwprintw(tz_sensor_window, 0, maxx/2 - sizeof(tz_title), >>> tz_title); >>> + mvwprintw(tz_sensor_window, 1, 1, "Thermal Zones:"); >>> + wattroff(tz_sensor_window, A_BOLD); >>> + >>> + mvwprintw(tz_sensor_window, 1, TZ_LEFT_ALIGN, "%s", >>> buffer); >>> + /* fill trip points for each tzone */ >>> + wattron(tz_sensor_window, A_BOLD); >>> + mvwprintw(tz_sensor_window, 2, 1, "Trip Points:"); >>> + wattroff(tz_sensor_window, A_BOLD); >>> + >>> + /* draw trip point from low to high for each tz */ >>> + for (i =3D 0; i < ptdata.nr_tz_sensor; i++) { >>> + int inst =3D ptdata.tzi[i].instance; >>> + >>> + mvwprintw(tz_sensor_window, 1, >>> + TZ_LEFT_ALIGN+TZONE_RECORD_SIZE * inst, >>> "%.9s%02d", >>> + ptdata.tzi[i].type, >>> ptdata.tzi[i].instance); >>> + for (j =3D ptdata.tzi[i].nr_trip_pts - 1; j >=3D 0; >>> j--) { >>> + /* loop through all trip points */ >>> + char type; >>> + int tp_pos; >>> + /* reverse the order here since trips are >>> sorted >>> + * in ascending order in terms of >>> temperature. >>> + */ >>> + tp_pos =3D ptdata.tzi[i].nr_trip_pts - j - 1; >>> + >>> + type =3D >>> trip_type_to_char(ptdata.tzi[i].tp[j].type); >>> + mvwaddch(tz_sensor_window, 2, >>> + inst * TZONE_RECORD_SIZE + >>> TZ_LEFT_ALIGN + >>> + tp_pos, type); >>> + syslog(LOG_DEBUG, "draw tz %d tp %d >>> ch:%c\n", >>> + inst, j, type); >>> + } >>> + } >>> + wborder(tz_sensor_window, 0, 0, 0, 0, 0, 0, 0, 0); >>> + wrefresh(tz_sensor_window); >>> +} >>> + >>> +void disable_tui(void) >>> +{ >>> + tui_disabled =3D 1; >>> +} >>> >> >> >=20 > [Jacob Pan] >=20 >=20 --=20 You have got to be excited about what you are doing. (L. Lamport) Eduardo Valentin --hwuJORj32HNeAtVoEQDsDKgaF5edWrGdA Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/ iF4EAREIAAYFAlJV6KgACgkQCXcVR3XQvP21EwD/bmM0ynGdtmawPk6wV/oBOAF6 FMFEdSs+vv2ha4jvEO8BANechnCIcvHszcQfaWZ0UaTwMqSiTTiJ9kix2klrhsed =L/TQ -----END PGP SIGNATURE----- --hwuJORj32HNeAtVoEQDsDKgaF5edWrGdA-- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/