Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932183Ab3JIRw0 (ORCPT ); Wed, 9 Oct 2013 13:52:26 -0400 Received: from mga01.intel.com ([192.55.52.88]:42366 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756630Ab3JIRwW convert rfc822-to-8bit (ORCPT ); Wed, 9 Oct 2013 13:52:22 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.90,1064,1371106800"; d="scan'208";a="414420192" Date: Wed, 9 Oct 2013 10:52:17 -0700 From: Jacob Pan To: Eduardo Valentin Cc: Zhang Rui , Linux PM , LKML , Rafael Wysocki Subject: Re: [PATCH] tools/thermal: Introduce tmon, a tool for thermal subsystem Message-ID: <20131009105217.0b23a6c3@ultegra> In-Reply-To: <5254AB6A.1090600@ti.com> References: <1381259024-25433-1-git-send-email-jacob.jun.pan@linux.intel.com> <5254AB6A.1090600@ti.com> Organization: OTC X-Mailer: Claws Mail 3.8.0 (GTK+ 2.24.10; x86_64-pc-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 74419 Lines: 2481 On Tue, 8 Oct 2013 21:03:38 -0400 Eduardo Valentin wrote: > 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 > > > > ┌──────────────────────────────────────────────────────────────────────────────┐ > > │Thermal Zones: > > cpu_therm00 │ │Trip > > Points: > > CP │ > > └──────────────────────────────────────────────────────────────────────────────┘ > > ┌──────────────────────────────────────────────────────────────────────────────┐ > > │ID Cooling Dev Cur Max Thermal Zone > > Binding │ │00 thermal-cpuf 0 3 > > Segmentation fault │ > > └─────────────────────────────────────────────────[root@(none) ~]# > > ────────────┘ [root@(none) ~]# ./tmontmon > > ───────────────────────────────────────────────────┐ > > │ 10 20 30 40 > > 50 60 │ │cpu_th > > 0:[ 0][> > > │ > > └──────────────────────────────────────────────────────────────────────────────┘ > > > > > > 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? At the same time, could you help me debug with showing me the result of " tree -l -L 2 /sys/class/thermal/" > 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. > 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=80%) > Namhyung Kim (commit_signer:237/902=26%) > Jiri Olsa (commit_signer:219/902=24%) > David Ahern (commit_signer:69/902=8%) > Adrian Hunter (commit_signer:59/902= > 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. > > > > 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 = 1.0 > > + > > +BINDIR=usr/bin > > +WARNFLAGS=-Wall -Wshadow -W -Wformat > > -Wimplicit-function-declaration -Wimplicit-int +CFLAGS= -O1 > > ${WARNFLAGS} -fstack-protector +CC=gcc > > + > > +CFLAGS+=-D VERSION=\"$(VERSION)\" > > +LDFLAGS+= > > +TARGET=tmon > > + > > +INSTALL_PROGRAM=install -m 755 -p > > +DEL_FILE=rm -f > > + > > +INSTALL_CONFIGFILE=install -m 644 -p > > +CONFIG_FILE= > > +CONFIG_PATH= > > + > > + > > +OBJS = tmon.o tui.o sysfs.o pid.o > > +OBJS += > > + > > +tmon: $(OBJS) Makefile tmon.h > > + $(CC) ${CFLAGS} $(LDFLAGS) $(OBJS) -o $(TARGET) > > -lncursesw -lm -lpanel -lpthread + > > +valgrind: tmon > > + sudo valgrind -v --track-origins=yes --tool=memcheck > > --leak-check=yes --show-reachable=yes --num-callers=20 > > --track-fds=yes ./$(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=tar --prefix="$(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? > > +========== > > +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 > > +===== > > + 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 > > +============ > > +Depends on ncurses > > + > > +Build > > +========= > > +$ 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) = U(s)/E(s) > > + * kp = proportional gain > > + * ki = integral gain > > + * kd = 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] = 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 = 0; > > + > > + /* init pid params */ > > + p_param.ts = ticktime; > > + /* TODO: get it from TUI tuning tab */ > > + p_param.kp = .36; > > + p_param.ki = 5.0; > > + p_param.kd = 0.19; > > + > > + p_param.t_target = 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 = 0.0; > > + xk_1 = 0.0; > > + xk_2 = 0.0; > > + set_ctrl_state(0); > > +} > > + > > +/* To be called at time interval Ts. Type C PID controller. > > + * y[k] = 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 = p_param.t_target - xk; /* error */ > > + if (ek >= 3.0) { > > + syslog(LOG_DEBUG, "PID: %3.1f Below set point > > %3.1f, stop\n", > > + xk, p_param.t_target); > > + controller_reset(); > > + *yk = 0.0; > > + return; > > + } > > + /* compute intermediate PID terms */ > > + p_term = -p_param.kp * (xk - xk_1); > > + i_term = p_param.kp * p_param.ki * p_param.ts * ek; > > + d_term = -p_param.kp * p_param.kd * (xk - 2 * xk_1 + > > xk_2) / p_param.ts; > > + /* compute output */ > > + *yk += p_term + i_term + d_term; > > + /* update sample data */ > > + xk_1 = xk; > > + xk_2 = xk_1; > > + > > + /* clamp output adjustment range */ > > + if (*yk < -LIMIT_HIGH) > > + *yk = -LIMIT_HIGH; > > + else if (*yk > -LIMIT_LOW) > > + *yk = -LIMIT_LOW; > > + > > + p_param.y_k = *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[] = { > > + "critical", > > + "hot", > > + "passive", > > + "active", > > +}; > > + > > +int sysfs_set_ulong(char *path, char *filename, unsigned long val) > > +{ > > + FILE *fd; > > + int ret = -1; > > + char filepath[256]; > > + > > + snprintf(filepath, 256, "%s/%s", path, filename); > > + > > + fd = fopen(filepath, "w"); > > + if (!fd) { > > + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, > > filepath); > > + return ret; > > + } > > + ret = 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 = -1; > > + char filepath[256]; > > + > > + snprintf(filepath, 256, "%s/%s", path, filename); > > + > > + fd = fopen(filepath, "r"); > > + if (!fd) { > > + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, > > filepath); > > + return ret; > > + } > > + ret = fscanf(fd, "%lu", p_ulong); > > + fclose(fd); > > + > > + return 0; > > +} > > + > > +static int sysfs_get_string(char *path, char *filename, char *str) > > +{ > > + FILE *fd; > > + int ret = -1; > > + char filepath[256]; > > + > > + snprintf(filepath, 256, "%s/%s", path, filename); > > + > > + fd = fopen(filepath, "r"); > > + if (!fd) { > > + syslog(LOG_ERR, "Err: open %s: %s\n", __func__, > > filepath); > > + return ret; > > + } > > + ret = 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 = 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 >= 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 = 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 = 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 = 0; > > + > > + ch = strtok(name, "_"); > > + while (ch != NULL) { > > + ++i; > > + syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, > > ch, i); > > + ch = strtok(NULL, "_"); > > + if (pos == 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 = 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 >= 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 = 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 = 0; > > + char cdev_name_linked[256]; > > + char cdev_name[256]; > > + char cdev_trip_name[256]; > > + int cdev_id; > > + > > + if (nl->d_type == 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) != > > -1) { > > + cdev_id = > > 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 |= (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 = 0; > > + tzi->trip_binding[cdev_id] |= 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 = 0; > > + > > + if (!ptdata.nr_tz_sensor) { > > + syslog(LOG_ERR, "No thermal zones found!\n"); > > + return -1; > > + } > > + > > + for (i = 0; i <= ptdata.max_tz_instance; i++) { > > + memset(tz_name, 0, sizeof(tz_name)); > > + snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, > > TZONE, i); + > > + dir = opendir(tz_name); > > + if (!dir) { > > + syslog(LOG_INFO, "Thermal zone %s > > skipped\n", tz_name); > > + continue; > > + } > > + /* keep track of valid tzones */ > > + n = 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 = i; > > + /* detect trip points and cdev attached to > > this tzone */ > > + j = 0; /* index for cdev */ > > + ptdata.tzi[k].nr_cdev = 0; > > + ptdata.tzi[k].nr_trip_pts = 0; > > + while (n--) { > > + char *temp_str; > > + > > + if (find_tzone_tp(tz_name, > > namelist[n]->d_name, > > + > > &ptdata.tzi[k], k)) > > + break; > > + temp_str = > > 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 = 0; > > + > > + for (i = 0; i <= ptdata.max_cdev_instance; i++) { > > + memset(cdev_name, 0, sizeof(cdev_name)); > > + snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, > > CDEV, i); + > > + dir = 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 = 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 = i; > > + if (strstr(ptdata.cdi[k].type, ctrl_cdev)) > > { > > + ptdata.cdi[k].flag |= > > 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 = opendir(THERMAL_SYSFS); > > + if (!dir) { > > + syslog(LOG_ERR, "No thermal sysfs\n"); > > + return -1; > > + } > > + n = 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 = > > 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 = > > 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 = > > get_instance_id(namelist[n]->d_name, 1, > > + sizeof("zone") - > > 1); > > + if (inst > ptdata.max_tz_instance) > > + ptdata.max_tz_instance = > > 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 = 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 = 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 = 0; i < ptdata.nr_tz_sensor; i++) > > + if (ptdata.tzi[i].instance == 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 >= NR_THERMAL_RECORDS) > > + cur_thermal_record = 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 = 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 = 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 = ptdata.cdi[i].cur_state; > > + if (val > 1000000) > > + val = 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 = 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 = 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 = -1; > > + int i; > > + > > + /* TODO: take average of all ctrl types. also consider > > change based on > > + * uevent. Take the first reading for now. > > + */ > > + for (i = 0; i < ptdata.nr_cooling_dev; i++) { > > + if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) { > > + ctrl_cdev_id = ptdata.cdi[i].instance; > > + syslog(LOG_INFO, "ctrl cdev %d get > > state\n", > > + ptdata.cdi[i].instance); > > + break; > > + } > > + } > > + if (ctrl_cdev_id == -1) { > > + *state = 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=T, > > comment.char="#") +attach(tdata) > > +jpeg("tmon.jpg") > > +X11() > > +g_range <- range(0, intel_powerclamp15, TargetTemp, acpitz0) > > +plot( Samples, intel_powerclamp15, col="blue", ylim=g_range, > > axes=FALSE, ann=FALSE) +par(new=TRUE) > > +lines(TargetTemp, type="o", pch=22, lty=2, col="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 = 1; /* seconds */ > > +unsigned long no_control = 1; /* monitoring only or use cooling > > device for > > + * temperature control. > > + */ > > +double time_elapsed = 0.0; > > +unsigned long target_temp_user = 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 = 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 = 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 = 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 = 0; j < 32; j++) > > + binding_str[j] = > > (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 = 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 = 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 = 0; i < ptdata.nr_tz_sensor; i++) { > > + fprintf(tmon_log, "%s%d ", ptdata.tzi[i].type, > > + ptdata.tzi[i].instance); > > + } > > + for (i = 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[] = { > > + { "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 = 0; > > + int id2 = 0, c; > > + double yk = 0.0; /* controller output */ > > + int target_tz_index; > > + > > + if (geteuid() != 0) { > > + printf("TMON needs to be run as root\n"); > > + exit(EXIT_FAILURE); > > + } > > + > > + while ((c = getopt_long(argc, argv, "c:dlht:vgz:", opts, > > &id2)) != -1) { > > + switch (c) { > > + case 'c': > > + no_control = 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 = strtod(optarg, NULL); > > + if (ticktime < 1) > > + ticktime = 1; > > + break; > > + case 'l': > > + printf("Logging data > > to /var/tmp/tmon.log\n"); > > + logging = 1; > > + break; > > + case 'h': > > + usage(); > > + break; > > + case 'v': > > + version(); > > + break; > > + case 'g': > > + debug_on = 1; > > + break; > > + case 'z': > > + target_thermal_zone = strtod(optarg, NULL); > > + break; > > + default: > > + break; > > + } > > + } > > + if (pthread_mutex_init(&input_lock, NULL) != 0) { > > + printf("\n mutex init failed\n"); > > + return 1; > > + } > > + start_syslog(); > > + if (signal(SIGINT, tmon_sig_handler) == SIG_ERR) > > + syslog(LOG_DEBUG, "Cannot handle SIGINT\n"); > > + if (signal(SIGTERM, tmon_sig_handler) == 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 = pthread_create(&event_tid, NULL, &handle_tui_events, > > NULL); > > + if (err != 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 = > > zone_instance_to_index(target_thermal_zone); > > + if (target_tz_index < 0) { > > + target_thermal_zone = 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 += ticktime; > > + controller_handler(trec[0].temp[target_tz_index] / > > 1000, > > + &yk); > > + trec[0].pid_out_pct = yk; > > + if (!dialogue_on) > > + show_control_w(); > > + if (tmon_exit) > > + break; > > + } > > + tmon_cleanup(); > > + return 0; > > +} > > + > > +static void start_daemon_mode() > > +{ > > + daemon_mode = 1; > > + /* fork */ > > + pid_t sid, pid = 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 = 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 = 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 = NULL; > > + } > > +} > > + > > +static void close_window(WINDOW *win) > > +{ > > + if (win) { > > + delwin(win); > > + win = 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 = 1; > > + > > + if (tui_disabled) > > + return; > > + > > + getmaxyx(stdscr, maxy, maxx); > > + resizeterm(maxy, maxx); > > + > > + title_bar_window = subwin(stdscr, TITLE_BAR_HIGHT, maxx, > > 0, 0); > > + y_begin += TITLE_BAR_HIGHT; > > + > > + tz_sensor_window = subwin(stdscr, SENSOR_WIN_HIGHT, maxx, > > y_begin, 0); > > + y_begin += SENSOR_WIN_HIGHT; > > + > > + cooling_device_window = subwin(stdscr, > > ptdata.nr_cooling_dev + 3, maxx, > > + y_begin, 0); > > + y_begin += 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 = subwin(stdscr, ptdata.nr_cooling_dev+5, > > maxx-50, > > + DIAG_Y, DIAG_X); > > + > > + thermal_data_window = subwin(stdscr, ptdata.nr_tz_sensor * > > + NR_LINES_TZDATA + 3, maxx, > > y_begin, 0); > > + y_begin += ptdata.nr_tz_sensor * NR_LINES_TZDATA + 3; > > + control_window = subwin(stdscr, 4, maxx, y_begin, 0); > > + > > + scrollok(cooling_device_window, TRUE); > > + maxwidth = maxx - 18; > > + status_bar_window = 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 = new_panel(cooling_device_window); > > + if (!data_panel) > > + syslog(LOG_DEBUG, "No data panel\n"); > > + else { > > + if (dialogue_window) { > > + dialogue_panel = > > 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 = 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[] = " COOLING DEVICES "; > > +void show_cooling_device(void) > > +{ > > + int i, j, x, y = 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 = 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 = 0; i < ptdata.nr_tz_sensor; i++) { > > + int tz_inst = ptdata.tzi[i].instance; > > + for (j = 0; j < ptdata.nr_cooling_dev; j++) { > > + int cdev_inst; > > + y = j; > > + x = 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 = ptdata.cdi[j].instance; > > + unsigned long trip_binding = > > + > > ptdata.tzi[i].trip_binding[cdev_inst]; > > + int k = 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 >>= 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[] = "[ TUNABLES ]"; > > +#define DIAG_DEV_ROWS 5 > > +void show_dialogue(void) > > +{ > > + int j, x = 0, y = 0; > > + WINDOW *w = 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 = 0; j <= ptdata.nr_cooling_dev; j++) { > > + y = j % DIAG_DEV_ROWS; > > + if (y == 0 && j != 0) > > + x += 20; > > + if (j == 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=Active, P=Passive, C=Critical"); > > + > > + wrefresh(dialogue_window); > > +} > > + > > +void write_dialogue_win(char *buf, int y, int x) > > +{ > > + WINDOW *w = dialogue_window; > > + > > + mvwprintw(w, y, x, "%s", buf); > > +} > > + > > +const char control_title[] = " 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=%2.2f > > ki=%2.2f, kd=%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 = 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 = 0; i < 10; i++) { > > + if (strlen(status_bar_slots[i]) == 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 += 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 = dialogue_window; > > + > > + echo(); > > + keypad(w, TRUE); > > + wgetnstr(w, buf, 31); > > + val = atoi(buf); > > + > > + if (ch == 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 = 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 = 0; > > + show_data_w(); > > + show_control_w(); > > + > > + top = (PANEL *)panel_userptr(top); > > + top_panel(top); > > +} > > + > > +static void handle_input_choice(int ch) > > +{ > > + char buf[48]; > > + int base = 0; > > + int cdev_id = 0; > > + > > + if ((ch >= 'A' && ch <= 'A' + ptdata.nr_cooling_dev) || > > + (ch >= 'a' && ch <= 'a' + ptdata.nr_cooling_dev)) { > > + base = (ch < 'a') ? 'A' : 'a'; > > + cdev_id = ch - base; > > + if (ptdata.nr_cooling_dev == 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 = wgetch(cooling_device_window)) != 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 == 'q' || ch == 'Q') > > + ch = 0; > > + } > > + switch (ch) { > > + case KEY_LEFT: > > + box(cooling_device_window, 10, 0); > > + break; > > + case 9: /* TAB */ > > + top = (PANEL *)panel_userptr(top); > > + top_panel(top); > > + if (top == dialogue_panel) { > > + dialogue_on = 1; > > + show_dialogue(); > > + } else { > > + dialogue_on = 0; > > + /* force refresh */ > > + show_data_w(); > > + show_control_w(); > > + } > > + break; > > + case 'q': > > + case 'Q': > > + tmon_exit = 1; > > + break; > > + } > > + update_panels(); > > + doupdate(); > > + pthread_mutex_unlock(&input_lock); > > + } > > + > > + if (arg) > > + *(int *)arg = 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 = 0; j < ptdata.tzi[tz].nr_trip_pts; j++) { > > + x = 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 = %lu\n", > > __func__, > > + tz, j, ptdata.tzi[tz].tp[j].temp); > > + } > > +} > > + > > +const char data_win_title[] = " 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 = 10; i < MAX_DISP_TEMP; i += 10) > > + mvwprintw(thermal_data_window, 1, i+TDATA_LEFT, > > "%2d", i); + > > + for (i = 0; i < ptdata.nr_tz_sensor; i++) { > > + int temp = trec[cur_thermal_record].temp[i] / 1000; > > + int y = 0; > > + > > + y = 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[] = "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 = 0; i < ptdata.nr_tz_sensor; i++) { > > + int inst = 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 = ptdata.tzi[i].nr_trip_pts - 1; j >= 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 = ptdata.tzi[i].nr_trip_pts - j - 1; > > + > > + type = > > 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 = 1; > > +} > > > > [Jacob Pan] -- 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/