Re: [PATCH] tools/thermal: Introduce tmon, a tool for thermalsubsystem

From: Jacob Pan
Date: Thu Oct 10 2013 - 10:18:39 EST


On Wed, 9 Oct 2013 19:37:08 -0400
Eduardo Valentin <eduardo.valentin@xxxxxx> wrote:

> On 09-10-2013 13:52, Jacob Pan wrote:
> > On Tue, 8 Oct 2013 21:03:38 -0400
> > Eduardo Valentin <eduardo.valentin@xxxxxx> 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?
>
> I am using a busybox based filesystem. And I have statically compiled
> tmon. It is against 3.12-rc1.
>
oh, I have a ubuntu image on the panda board. I guess the kernel is
old so the sensors do not show even when i enabled the following
options.
CONFIG_SENSORS_OMAP_TEMP_SENSOR=y
CONFIG_OMAP_THERMAL=y

what are the kernel options to enable OMAP thermal?
> >
> > At the same time, could you help me debug with showing me the
> > result of " tree -l -L 2 /sys/class/thermal/"
>
> I tried also on my x86 dell laptop. I gave up on this track to because
> tmon does not progress when there is no thermal zone.
> ebv@besouro:/sys/class/thermal$
> $ tree
> .
> âââ cooling_device0 -> ../../devices/virtual/thermal/cooling_device0
> âââ cooling_device1 -> ../../devices/virtual/thermal/cooling_device1
> âââ cooling_device2 -> ../../devices/virtual/thermal/cooling_device2
> âââ cooling_device3 -> ../../devices/virtual/thermal/cooling_device3
> âââ cooling_device4 -> ../../devices/virtual/thermal/cooling_device4
>
> 5 directories, 0 files
>
> The only problem is that tmon exists silently. I would be nice to have
> at least a message saying why it is not continuing.
>
I will fix that in the next version. also attached a screen shot of a
more loaded ultrabook.

I will try to reproduce the configuration on your omap and fix seg
fault. thanks for the data.

> >>> --- /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@xxxxxxxxxxxxxxx>
> >>> + *
> >>> + */
> >>> +#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@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 <jacob.jun.pan@xxxxxxxxxxxxxxx>
> >>> + *
> >>> + */
> >>> +
> >>> +#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@xxxxxxxxxxxxxxx>
> >>> + *
> >>> + */
> >>> +
> >>> +#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@xxxxxxxxxxxxxxx>
> >>> + *
> >>> + */
> >>> +
> >>> +#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]
> >
> >
>
>

[Jacob Pan]

Attachment: Screenshot from 2013-10-10 07:13:31.png
Description: PNG image