lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 9 Oct 2013 10:52:17 -0700
From:	Jacob Pan <jacob.jun.pan@...ux.intel.com>
To:	Eduardo Valentin <eduardo.valentin@...com>
Cc:	Zhang Rui <rui.zhang@...el.com>,
	Linux PM <linux-pm@...r.kernel.org>,
	LKML <linux-kernel@...r.kernel.org>,
	Rafael Wysocki <rafael.j.wysocki@...el.com>
Subject: Re: [PATCH] tools/thermal: Introduce tmon, a tool for thermal
 subsystem

On Tue, 8 Oct 2013 21:03:38 -0400
Eduardo Valentin <eduardo.valentin@...com> 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 <acme@...hat.com> (commit_signer:724/902=80%)
> Namhyung Kim <namhyung@...nel.org> (commit_signer:237/902=26%)
> Jiri Olsa <jolsa@...hat.com> (commit_signer:219/902=24%)
> David Ahern <dsahern@...il.com> (commit_signer:69/902=8%)
> Adrian Hunter <adrian.hunter@...el.com> (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 <jacob.jun.pan@...ux.intel.com>
> > ---
> >  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 <jacob.jun.pan@...ux.intel.com>
> > + *
> > + */
> > +
> > +#include <unistd.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <stdint.h>
> > +#include <sys/types.h>
> > +#include <dirent.h>
> > +#include <libintl.h>
> > +#include <ctype.h>
> > +#include <assert.h>
> > +#include <time.h>
> > +#include <limits.h>
> > +#include <math.h>
> > +#include <sys/stat.h>
> > +#include <syslog.h>
> > +
> > +#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 <jacob.jun.pan@...ux.intel.com>
> > + *
> > + */
> > +#include <unistd.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <stdint.h>
> > +#include <dirent.h>
> > +#include <libintl.h>
> > +#include <ctype.h>
> > +#include <time.h>
> > +#include <syslog.h>
> > +#include <sys/time.h>
> > +#include <errno.h>
> > +
> > +#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@...ob-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 <jacob.jun.pan@...ux.intel.com>
> > + *
> > + */
> > +
> > +#include <getopt.h>
> > +#include <unistd.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <ncurses.h>
> > +#include <ctype.h>
> > +#include <time.h>
> > +#include <signal.h>
> > +#include <limits.h>
> > +#include <sys/time.h>
> > +#include <pthread.h>
> > +#include <math.h>
> > +#include <stdarg.h>
> > +#include <syslog.h>
> > +
> > +#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<<j) ?
> > +				'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 <jacob.jun.pan@...ux.intel.com>
> > + *
> > + */
> > +
> > +#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 <jacob.jun.pan@...ux.intel.com>
> > + *
> > + */
> > +
> > +#include <unistd.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <string.h>
> > +#include <stdint.h>
> > +#include <ncurses.h>
> > +#include <time.h>
> > +#include <syslog.h>
> > +#include <panel.h>
> > +#include <pthread.h>
> > +#include <signal.h>
> > +
> > +#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@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ