perf bench evlist-open-close: Use PRIu64 with u64 to fix build on 32-bit architectures
[linux-2.6-microblaze.git] / tools / thermal / tmon / tui.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * tui.c ncurses text user interface for TMON program
4  *
5  * Copyright (C) 2013 Intel Corporation. All rights reserved.
6  *
7  * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
8  */
9
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <stdint.h>
15 #include <ncurses.h>
16 #include <time.h>
17 #include <syslog.h>
18 #include <panel.h>
19 #include <pthread.h>
20 #include <signal.h>
21
22 #include "tmon.h"
23
24 #define min(x, y) ({                            \
25         typeof(x) _min1 = (x);                  \
26         typeof(y) _min2 = (y);                  \
27         (void) (&_min1 == &_min2);              \
28         _min1 < _min2 ? _min1 : _min2; })
29
30 #define max(x, y) ({                            \
31         typeof(x) _max1 = (x);                  \
32         typeof(y) _max2 = (y);                  \
33         (void) (&_max1 == &_max2);              \
34         _max1 > _max2 ? _max1 : _max2; })
35
36 static PANEL *data_panel;
37 static PANEL *dialogue_panel;
38 static PANEL *top;
39
40 static WINDOW *title_bar_window;
41 static WINDOW *tz_sensor_window;
42 static WINDOW *cooling_device_window;
43 static WINDOW *control_window;
44 static WINDOW *status_bar_window;
45 static WINDOW *thermal_data_window;
46 static WINDOW *dialogue_window;
47
48 char status_bar_slots[10][40];
49 static void draw_hbar(WINDOW *win, int y, int start, int len,
50                 unsigned long pattern, bool end);
51
52 static int maxx, maxy;
53 static int maxwidth = 200;
54
55 #define TITLE_BAR_HIGHT 1
56 #define SENSOR_WIN_HIGHT 4 /* one row for tz name, one for trip points */
57
58
59 /* daemon mode flag (set by startup parameter -d) */
60 static int  tui_disabled;
61
62 static void close_panel(PANEL *p)
63 {
64         if (p) {
65                 del_panel(p);
66                 p = NULL;
67         }
68 }
69
70 static void close_window(WINDOW *win)
71 {
72         if (win) {
73                 delwin(win);
74                 win = NULL;
75         }
76 }
77
78 void close_windows(void)
79 {
80         if (tui_disabled)
81                 return;
82         /* must delete panels before their attached windows */
83         if (dialogue_window)
84                 close_panel(dialogue_panel);
85         if (cooling_device_window)
86                 close_panel(data_panel);
87
88         close_window(title_bar_window);
89         close_window(tz_sensor_window);
90         close_window(status_bar_window);
91         close_window(cooling_device_window);
92         close_window(control_window);
93         close_window(thermal_data_window);
94         close_window(dialogue_window);
95
96 }
97
98 void write_status_bar(int x, char *line)
99 {
100         mvwprintw(status_bar_window, 0, x, "%s", line);
101         wrefresh(status_bar_window);
102 }
103
104 /* wrap at 5 */
105 #define DIAG_DEV_ROWS  5
106 /*
107  * list cooling devices + "set temp" entry; wraps after 5 rows, if they fit
108  */
109 static int diag_dev_rows(void)
110 {
111         int entries = ptdata.nr_cooling_dev + 1;
112         int rows = max(DIAG_DEV_ROWS, (entries + 1) / 2);
113         return min(rows, entries);
114 }
115
116 void setup_windows(void)
117 {
118         int y_begin = 1;
119
120         if (tui_disabled)
121                 return;
122
123         getmaxyx(stdscr, maxy, maxx);
124         resizeterm(maxy, maxx);
125
126         title_bar_window = subwin(stdscr, TITLE_BAR_HIGHT, maxx, 0, 0);
127         y_begin += TITLE_BAR_HIGHT;
128
129         tz_sensor_window = subwin(stdscr, SENSOR_WIN_HIGHT, maxx, y_begin, 0);
130         y_begin += SENSOR_WIN_HIGHT;
131
132         cooling_device_window = subwin(stdscr, ptdata.nr_cooling_dev + 3, maxx,
133                                 y_begin, 0);
134         y_begin += ptdata.nr_cooling_dev + 3; /* 2 lines for border */
135         /* two lines to show borders, one line per tz show trip point position
136          * and value.
137          * dialogue window is a pop-up, when needed it lays on top of cdev win
138          */
139
140         dialogue_window = subwin(stdscr, diag_dev_rows() + 5, maxx-50,
141                                 DIAG_Y, DIAG_X);
142
143         thermal_data_window = subwin(stdscr, ptdata.nr_tz_sensor *
144                                 NR_LINES_TZDATA + 3, maxx, y_begin, 0);
145         y_begin += ptdata.nr_tz_sensor * NR_LINES_TZDATA + 3;
146         control_window = subwin(stdscr, 4, maxx, y_begin, 0);
147
148         scrollok(cooling_device_window, TRUE);
149         maxwidth = maxx - 18;
150         status_bar_window = subwin(stdscr, 1, maxx, maxy-1, 0);
151
152         strcpy(status_bar_slots[0], " Ctrl-c - Quit ");
153         strcpy(status_bar_slots[1], " TAB - Tuning ");
154         wmove(status_bar_window, 1, 30);
155
156         /* prepare panels for dialogue, if panel already created then we must
157          * be doing resizing, so just replace windows with new ones, old ones
158          * should have been deleted by close_window
159          */
160         data_panel = new_panel(cooling_device_window);
161         if (!data_panel)
162                 syslog(LOG_DEBUG, "No data panel\n");
163         else {
164                 if (dialogue_window) {
165                         dialogue_panel = new_panel(dialogue_window);
166                         if (!dialogue_panel)
167                                 syslog(LOG_DEBUG, "No dialogue panel\n");
168                         else {
169                                 /* Set up the user pointer to the next panel*/
170                                 set_panel_userptr(data_panel, dialogue_panel);
171                                 set_panel_userptr(dialogue_panel, data_panel);
172                                 top = data_panel;
173                         }
174                 } else
175                         syslog(LOG_INFO, "no dialogue win, term too small\n");
176         }
177         doupdate();
178         werase(stdscr);
179         refresh();
180 }
181
182 void resize_handler(int sig)
183 {
184         /* start over when term gets resized, but first we clean up */
185         close_windows();
186         endwin();
187         refresh();
188         clear();
189         getmaxyx(stdscr, maxy, maxx);  /* get the new screen size */
190         setup_windows();
191         /* rate limit */
192         sleep(1);
193         syslog(LOG_DEBUG, "SIG %d, term resized to %d x %d\n",
194                 sig, maxy, maxx);
195         signal(SIGWINCH, resize_handler);
196 }
197
198 const char cdev_title[] = " COOLING DEVICES ";
199 void show_cooling_device(void)
200 {
201         int i, j, x, y = 0;
202
203         if (tui_disabled || !cooling_device_window)
204                 return;
205
206         werase(cooling_device_window);
207         wattron(cooling_device_window, A_BOLD);
208         mvwprintw(cooling_device_window,  1, 1,
209                 "ID  Cooling Dev   Cur    Max   Thermal Zone Binding");
210         wattroff(cooling_device_window, A_BOLD);
211         for (j = 0; j < ptdata.nr_cooling_dev; j++) {
212                 /* draw cooling device list on the left in the order of
213                  * cooling device instances. skip unused idr.
214                  */
215                 mvwprintw(cooling_device_window, j + 2, 1,
216                         "%02d %12.12s%6d %6d",
217                         ptdata.cdi[j].instance,
218                         ptdata.cdi[j].type,
219                         ptdata.cdi[j].cur_state,
220                         ptdata.cdi[j].max_state);
221         }
222
223         /* show cdev binding, y is the global cooling device instance */
224         for (i = 0; i < ptdata.nr_tz_sensor; i++) {
225                 int tz_inst = ptdata.tzi[i].instance;
226                 for (j = 0; j < ptdata.nr_cooling_dev; j++) {
227                         int cdev_inst;
228                         y = j;
229                         x = tz_inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN;
230
231                         draw_hbar(cooling_device_window, y+2, x,
232                                 TZONE_RECORD_SIZE-1, ACS_VLINE, false);
233
234                         /* draw a column of spaces to separate thermal zones */
235                         mvwprintw(cooling_device_window, y+2, x-1, " ");
236                         if (ptdata.tzi[i].cdev_binding) {
237                                 cdev_inst = ptdata.cdi[j].instance;
238                                 unsigned long trip_binding =
239                                         ptdata.tzi[i].trip_binding[cdev_inst];
240                                 int k = 0; /* per zone trip point id that
241                                             * binded to this cdev, one to
242                                             * many possible based on the
243                                             * binding bitmask.
244                                             */
245                                 syslog(LOG_DEBUG,
246                                         "bind tz%d cdev%d tp%lx %d cdev%lx\n",
247                                         i, j, trip_binding, y,
248                                         ptdata.tzi[i].cdev_binding);
249                                 /* draw each trip binding for the cdev */
250                                 while (trip_binding >>= 1) {
251                                         k++;
252                                         if (!(trip_binding & 1))
253                                                 continue;
254                                         /* draw '*' to show binding */
255                                         mvwprintw(cooling_device_window,
256                                                 y + 2,
257                                                 x + ptdata.tzi[i].nr_trip_pts -
258                                                 k - 1, "*");
259                                 }
260                         }
261                 }
262         }
263         /* draw border after data so that border will not be messed up
264          * even there is not enough space for all the data to be shown
265          */
266         wborder(cooling_device_window, 0, 0, 0, 0, 0, 0, 0, 0);
267         wattron(cooling_device_window, A_BOLD);
268         mvwprintw(cooling_device_window, 0, maxx/2 - sizeof(cdev_title),
269                 cdev_title);
270         wattroff(cooling_device_window, A_BOLD);
271
272         wrefresh(cooling_device_window);
273 }
274
275 const char DIAG_TITLE[] = "[ TUNABLES ]";
276 void show_dialogue(void)
277 {
278         int j, x = 0, y = 0;
279         int rows, cols;
280         WINDOW *w = dialogue_window;
281
282         if (tui_disabled || !w)
283                 return;
284
285         getmaxyx(w, rows, cols);
286
287         /* Silence compiler 'unused' warnings */
288         (void)cols;
289
290         werase(w);
291         box(w, 0, 0);
292         mvwprintw(w, 0, maxx/4, DIAG_TITLE);
293         /* list all the available tunables */
294         for (j = 0; j <= ptdata.nr_cooling_dev; j++) {
295                 y = j % diag_dev_rows();
296                 if (y == 0 && j != 0)
297                         x += 20;
298                 if (j == ptdata.nr_cooling_dev)
299                         /* save last choice for target temp */
300                         mvwprintw(w, y+1, x+1, "%C-%.12s", 'A'+j, "Set Temp");
301                 else
302                         mvwprintw(w, y+1, x+1, "%C-%.10s-%2d", 'A'+j,
303                                 ptdata.cdi[j].type, ptdata.cdi[j].instance);
304         }
305         wattron(w, A_BOLD);
306         mvwprintw(w, diag_dev_rows()+1, 1, "Enter Choice [A-Z]?");
307         wattroff(w, A_BOLD);
308         /* print legend at the bottom line */
309         mvwprintw(w, rows - 2, 1,
310                 "Legend: A=Active, P=Passive, C=Critical");
311
312         wrefresh(dialogue_window);
313 }
314
315 void write_dialogue_win(char *buf, int y, int x)
316 {
317         WINDOW *w = dialogue_window;
318
319         mvwprintw(w, y, x, "%s", buf);
320 }
321
322 const char control_title[] = " CONTROLS ";
323 void show_control_w(void)
324 {
325         unsigned long state;
326
327         get_ctrl_state(&state);
328
329         if (tui_disabled || !control_window)
330                 return;
331
332         werase(control_window);
333         mvwprintw(control_window, 1, 1,
334                 "PID gain: kp=%2.2f ki=%2.2f kd=%2.2f Output %2.2f",
335                 p_param.kp, p_param.ki, p_param.kd, p_param.y_k);
336
337         mvwprintw(control_window, 2, 1,
338                 "Target Temp: %2.1fC, Zone: %d, Control Device: %.12s",
339                 p_param.t_target, target_thermal_zone, ctrl_cdev);
340
341         /* draw border last such that everything is within boundary */
342         wborder(control_window, 0, 0, 0, 0, 0, 0, 0, 0);
343         wattron(control_window, A_BOLD);
344         mvwprintw(control_window, 0, maxx/2 - sizeof(control_title),
345                 control_title);
346         wattroff(control_window, A_BOLD);
347
348         wrefresh(control_window);
349 }
350
351 void initialize_curses(void)
352 {
353         if (tui_disabled)
354                 return;
355
356         initscr();
357         start_color();
358         keypad(stdscr, TRUE);   /* enable keyboard mapping */
359         nonl();                 /* tell curses not to do NL->CR/NL on output */
360         cbreak();               /* take input chars one at a time */
361         noecho();               /* dont echo input */
362         curs_set(0);            /* turn off cursor */
363         use_default_colors();
364
365         init_pair(PT_COLOR_DEFAULT, COLOR_WHITE, COLOR_BLACK);
366         init_pair(PT_COLOR_HEADER_BAR, COLOR_BLACK, COLOR_WHITE);
367         init_pair(PT_COLOR_ERROR, COLOR_BLACK, COLOR_RED);
368         init_pair(PT_COLOR_RED, COLOR_WHITE, COLOR_RED);
369         init_pair(PT_COLOR_YELLOW, COLOR_WHITE, COLOR_YELLOW);
370         init_pair(PT_COLOR_GREEN, COLOR_WHITE, COLOR_GREEN);
371         init_pair(PT_COLOR_BLUE, COLOR_WHITE, COLOR_BLUE);
372         init_pair(PT_COLOR_BRIGHT, COLOR_WHITE, COLOR_BLACK);
373
374 }
375
376 void show_title_bar(void)
377 {
378         int i;
379         int x = 0;
380
381         if (tui_disabled || !title_bar_window)
382                 return;
383
384         wattrset(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
385         wbkgd(title_bar_window, COLOR_PAIR(PT_COLOR_HEADER_BAR));
386         werase(title_bar_window);
387
388         mvwprintw(title_bar_window, 0, 0,
389                 "     TMON v%s", VERSION);
390
391         wrefresh(title_bar_window);
392
393         werase(status_bar_window);
394
395         for (i = 0; i < 10; i++) {
396                 if (strlen(status_bar_slots[i]) == 0)
397                         continue;
398                 wattron(status_bar_window, A_REVERSE);
399                 mvwprintw(status_bar_window, 0, x, "%s", status_bar_slots[i]);
400                 wattroff(status_bar_window, A_REVERSE);
401                 x += strlen(status_bar_slots[i]) + 1;
402         }
403         wrefresh(status_bar_window);
404 }
405
406 static void handle_input_val(int ch)
407 {
408         char buf[32];
409         int val;
410         char path[256];
411         WINDOW *w = dialogue_window;
412
413         echo();
414         keypad(w, TRUE);
415         wgetnstr(w, buf, 31);
416         val = atoi(buf);
417
418         if (ch == ptdata.nr_cooling_dev) {
419                 snprintf(buf, 31, "Invalid Temp %d! %d-%d", val,
420                         MIN_CTRL_TEMP, MAX_CTRL_TEMP);
421                 if (val < MIN_CTRL_TEMP || val > MAX_CTRL_TEMP)
422                         write_status_bar(40, buf);
423                 else {
424                         p_param.t_target = val;
425                         snprintf(buf, 31, "Set New Target Temp %d", val);
426                         write_status_bar(40, buf);
427                 }
428         } else {
429                 snprintf(path, 256, "%s/%s%d", THERMAL_SYSFS,
430                         CDEV, ptdata.cdi[ch].instance);
431                 sysfs_set_ulong(path, "cur_state", val);
432         }
433         noecho();
434         dialogue_on = 0;
435         show_data_w();
436         show_control_w();
437
438         top = (PANEL *)panel_userptr(top);
439         top_panel(top);
440 }
441
442 static void handle_input_choice(int ch)
443 {
444         char buf[48];
445         int base = 0;
446         int cdev_id = 0;
447
448         if ((ch >= 'A' && ch <= 'A' + ptdata.nr_cooling_dev) ||
449                 (ch >= 'a' && ch <= 'a' + ptdata.nr_cooling_dev)) {
450                 base = (ch < 'a') ? 'A' : 'a';
451                 cdev_id = ch - base;
452                 if (ptdata.nr_cooling_dev == cdev_id)
453                         snprintf(buf, sizeof(buf), "New Target Temp:");
454                 else
455                         snprintf(buf, sizeof(buf), "New Value for %.10s-%2d: ",
456                                 ptdata.cdi[cdev_id].type,
457                                 ptdata.cdi[cdev_id].instance);
458                 write_dialogue_win(buf, diag_dev_rows() + 2, 2);
459                 handle_input_val(cdev_id);
460         } else {
461                 snprintf(buf, sizeof(buf), "Invalid selection %d", ch);
462                 write_dialogue_win(buf, 8, 2);
463         }
464 }
465
466 void *handle_tui_events(void *arg)
467 {
468         int ch;
469
470         keypad(cooling_device_window, TRUE);
471         while ((ch = wgetch(cooling_device_window)) != EOF) {
472                 if (tmon_exit)
473                         break;
474                 /* when term size is too small, no dialogue panels are set.
475                  * we need to filter out such cases.
476                  */
477                 if (!data_panel || !dialogue_panel ||
478                         !cooling_device_window ||
479                         !dialogue_window) {
480
481                         continue;
482                 }
483                 pthread_mutex_lock(&input_lock);
484                 if (dialogue_on) {
485                         handle_input_choice(ch);
486                         /* top panel filter */
487                         if (ch == 'q' || ch == 'Q')
488                                 ch = 0;
489                 }
490                 switch (ch) {
491                 case KEY_LEFT:
492                         box(cooling_device_window, 10, 0);
493                         break;
494                 case 9: /* TAB */
495                         top = (PANEL *)panel_userptr(top);
496                         top_panel(top);
497                         if (top == dialogue_panel) {
498                                 dialogue_on = 1;
499                                 show_dialogue();
500                         } else {
501                                 dialogue_on = 0;
502                                 /* force refresh */
503                                 show_data_w();
504                                 show_control_w();
505                         }
506                         break;
507                 case 'q':
508                 case 'Q':
509                         tmon_exit = 1;
510                         break;
511                 }
512                 update_panels();
513                 doupdate();
514                 pthread_mutex_unlock(&input_lock);
515         }
516
517         if (arg)
518                 *(int *)arg = 0; /* make gcc happy */
519
520         return NULL;
521 }
522
523 /* draw a horizontal bar in given pattern */
524 static void draw_hbar(WINDOW *win, int y, int start, int len, unsigned long ptn,
525                 bool end)
526 {
527         mvwaddch(win, y, start, ptn);
528         whline(win, ptn, len);
529         if (end)
530                 mvwaddch(win, y, MAX_DISP_TEMP+TDATA_LEFT, ']');
531 }
532
533 static char trip_type_to_char(int type)
534 {
535         switch (type) {
536         case THERMAL_TRIP_CRITICAL: return 'C';
537         case THERMAL_TRIP_HOT: return 'H';
538         case THERMAL_TRIP_PASSIVE: return 'P';
539         case THERMAL_TRIP_ACTIVE: return 'A';
540         default:
541                 return '?';
542         }
543 }
544
545 /* fill a string with trip point type and value in one line
546  * e.g.      P(56)    C(106)
547  * maintain the distance one degree per char
548  */
549 static void draw_tp_line(int tz, int y)
550 {
551         int j;
552         int x;
553
554         for (j = 0; j < ptdata.tzi[tz].nr_trip_pts; j++) {
555                 x = ptdata.tzi[tz].tp[j].temp / 1000;
556                 mvwprintw(thermal_data_window, y + 0, x + TDATA_LEFT,
557                         "%c%d", trip_type_to_char(ptdata.tzi[tz].tp[j].type),
558                         x);
559                 syslog(LOG_INFO, "%s:tz %d tp %d temp = %lu\n", __func__,
560                         tz, j, ptdata.tzi[tz].tp[j].temp);
561         }
562 }
563
564 const char data_win_title[] = " THERMAL DATA ";
565 void show_data_w(void)
566 {
567         int i;
568
569
570         if (tui_disabled || !thermal_data_window)
571                 return;
572
573         werase(thermal_data_window);
574         wattron(thermal_data_window, A_BOLD);
575         mvwprintw(thermal_data_window, 0, maxx/2 - sizeof(data_win_title),
576                 data_win_title);
577         wattroff(thermal_data_window, A_BOLD);
578         /* draw a line as ruler */
579         for (i = 10; i < MAX_DISP_TEMP; i += 10)
580                 mvwprintw(thermal_data_window, 1, i+TDATA_LEFT, "%2d", i);
581
582         for (i = 0; i < ptdata.nr_tz_sensor; i++) {
583                 int temp = trec[cur_thermal_record].temp[i] / 1000;
584                 int y = 0;
585
586                 y = i * NR_LINES_TZDATA + 2;
587                 /* y at tz temp data line */
588                 mvwprintw(thermal_data_window, y, 1, "%6.6s%2d:[%3d][",
589                         ptdata.tzi[i].type,
590                         ptdata.tzi[i].instance, temp);
591                 draw_hbar(thermal_data_window, y, TDATA_LEFT, temp, ACS_RARROW,
592                         true);
593                 draw_tp_line(i, y);
594         }
595         wborder(thermal_data_window, 0, 0, 0, 0, 0, 0, 0, 0);
596         wrefresh(thermal_data_window);
597 }
598
599 const char tz_title[] = "THERMAL ZONES(SENSORS)";
600
601 void show_sensors_w(void)
602 {
603         int i, j;
604         char buffer[512];
605
606         if (tui_disabled || !tz_sensor_window)
607                 return;
608
609         werase(tz_sensor_window);
610
611         memset(buffer, 0, sizeof(buffer));
612         wattron(tz_sensor_window, A_BOLD);
613         mvwprintw(tz_sensor_window, 1, 1, "Thermal Zones:");
614         wattroff(tz_sensor_window, A_BOLD);
615
616         mvwprintw(tz_sensor_window, 1, TZ_LEFT_ALIGN, "%s", buffer);
617         /* fill trip points for each tzone */
618         wattron(tz_sensor_window, A_BOLD);
619         mvwprintw(tz_sensor_window, 2, 1, "Trip Points:");
620         wattroff(tz_sensor_window, A_BOLD);
621
622         /* draw trip point from low to high for each tz */
623         for (i = 0; i < ptdata.nr_tz_sensor; i++) {
624                 int inst = ptdata.tzi[i].instance;
625
626                 mvwprintw(tz_sensor_window, 1,
627                         TZ_LEFT_ALIGN+TZONE_RECORD_SIZE * inst, "%.9s%02d",
628                         ptdata.tzi[i].type, ptdata.tzi[i].instance);
629                 for (j = ptdata.tzi[i].nr_trip_pts - 1; j >= 0; j--) {
630                         /* loop through all trip points */
631                         char type;
632                         int tp_pos;
633                         /* reverse the order here since trips are sorted
634                          * in ascending order in terms of temperature.
635                          */
636                         tp_pos = ptdata.tzi[i].nr_trip_pts - j - 1;
637
638                         type = trip_type_to_char(ptdata.tzi[i].tp[j].type);
639                         mvwaddch(tz_sensor_window, 2,
640                                 inst * TZONE_RECORD_SIZE + TZ_LEFT_ALIGN +
641                                 tp_pos, type);
642                         syslog(LOG_DEBUG, "draw tz %d tp %d ch:%c\n",
643                                 inst, j, type);
644                 }
645         }
646         wborder(tz_sensor_window, 0, 0, 0, 0, 0, 0, 0, 0);
647         wattron(tz_sensor_window, A_BOLD);
648         mvwprintw(tz_sensor_window, 0, maxx/2 - sizeof(tz_title), tz_title);
649         wattroff(tz_sensor_window, A_BOLD);
650         wrefresh(tz_sensor_window);
651 }
652
653 void disable_tui(void)
654 {
655         tui_disabled = 1;
656 }