1 /*
2  * Copyright 2009, Intel Corporation
3  * Copyright 2009, Sun Microsystems, Inc
4  *
5  * This file is part of PowerTOP
6  *
7  * This program file is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; version 2 of the License.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program in a file named COPYING; if not, write to the
18  * Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301 USA
21  *
22  * Authors:
23  *      Arjan van de Ven <arjan@linux.intel.com>
24  *      Eric C Saxe <eric.saxe@sun.com>
25  *      Aubrey Li <aubrey.li@intel.com>
26  */
27 
28 /*
29  * GPL Disclaimer
30  *
31  * For the avoidance of doubt, except that if any license choice other
32  * than GPL or LGPL is available it will apply instead, Sun elects to
33  * use only the General Public License version 2 (GPLv2) at this time
34  * for any software where a choice of GPL license versions is made
35  * available with the language indicating that GPLv2 or any later
36  * version may be used, or where a choice of which version of the GPL
37  * is applied is otherwise unspecified.
38  */
39 
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include "powertop.h"
45 
46 /*
47  * Default number of intervals we display a suggestion before moving
48  * to the next.
49  */
50 #define	PT_SUGG_DEF_SLICE	3
51 
52 /*
53  * Global pointer to the current suggestion.
54  */
55 sugg_t	*g_curr_sugg;
56 
57 /*
58  * Head of the list of suggestions.
59  */
60 static sugg_t *sugg;
61 
62 /*
63  * Add a new suggestion. Only one suggestion per text allowed.
64  */
65 void
pt_sugg_add(char * text,int weight,char key,char * sb_msg,sugg_func_t * func)66 pt_sugg_add(char *text, int weight, char key, char *sb_msg, sugg_func_t *func)
67 {
68 	sugg_t *new, *n, *pos = NULL;
69 
70 	/*
71 	 * Text is a required field for suggestions
72 	 */
73 	if (text == NULL)
74 		return;
75 
76 	if (sugg == NULL) {
77 		/*
78 		 * Creating first element
79 		 */
80 		if ((new = calloc(1, sizeof (sugg_t))) == NULL)
81 			return;
82 
83 		if (sb_msg != NULL)
84 			new->sb_msg = strdup(sb_msg);
85 
86 		if (text != NULL)
87 			new->text = strdup(text);
88 
89 		new->weight = weight;
90 		new->key = key;
91 		new->func = func;
92 		new->slice = 0;
93 
94 		sugg = new;
95 		new->prev = NULL;
96 		new->next = NULL;
97 	} else {
98 		for (n = sugg; n != NULL; n = n->next) {
99 			if (strcmp(n->text, text) == 0)
100 				return;
101 
102 			if (weight > n->weight && pos == NULL)
103 				pos = n;
104 		}
105 		/*
106 		 * Create a new element
107 		 */
108 		if ((new = calloc(1, sizeof (sugg_t))) == NULL)
109 			return;
110 
111 		if (sb_msg != NULL)
112 			new->sb_msg = strdup(sb_msg);
113 
114 		new->text = strdup(text);
115 
116 		new->weight = weight;
117 		new->key = key;
118 		new->func = func;
119 		new->slice = 0;
120 
121 		if (pos == NULL) {
122 			/*
123 			 * Ordering placed the new element at the end
124 			 */
125 			for (n = sugg; n->next != NULL; n = n->next)
126 				;
127 
128 			n->next = new;
129 			new->prev = n;
130 			new->next = NULL;
131 		} else {
132 			if (pos == sugg) {
133 				/*
134 				 * Ordering placed the new element at the start
135 				 */
136 				new->next = sugg;
137 				new->prev = sugg;
138 				sugg->prev = new;
139 				sugg = new;
140 			} else {
141 				/*
142 				 * Ordering placed the new element somewhere in
143 				 * the middle
144 				 */
145 				new->next = pos;
146 				new->prev = pos->prev;
147 				pos->prev->next = new;
148 				pos->prev = new;
149 			}
150 		}
151 	}
152 }
153 
154 /*
155  * Removes a suggestion, returning 0 if not found and 1 if so.
156  */
157 int
pt_sugg_remove(sugg_func_t * func)158 pt_sugg_remove(sugg_func_t *func)
159 {
160 	sugg_t *n;
161 	int ret = 0;
162 
163 	for (n = sugg; n != NULL; n = n->next) {
164 		if (n->func == func) {
165 			/* Removing the first element */
166 			if (n == sugg) {
167 				if (sugg->next == NULL) {
168 					/* Removing the only element */
169 					sugg = NULL;
170 				} else {
171 					sugg = n->next;
172 					sugg->prev = NULL;
173 				}
174 			} else {
175 				if (n->next == NULL) {
176 					/* Removing the last element */
177 					n->prev->next = NULL;
178 				} else {
179 					/* Removing an intermediate element */
180 					n->prev->next = n->next;
181 					n->next->prev = n->prev;
182 				}
183 			}
184 
185 			/*
186 			 * If this suggestions is currently being suggested,
187 			 * remove it and update the screen.
188 			 */
189 			if (n == g_curr_sugg) {
190 				if (n->sb_msg != NULL) {
191 					pt_display_mod_status_bar(n->sb_msg);
192 					pt_display_status_bar();
193 				}
194 				if (n->text != NULL)
195 					pt_display_suggestions(NULL);
196 			}
197 
198 			free(n);
199 			ret = 1;
200 		}
201 	}
202 
203 	return (ret);
204 }
205 
206 /*
207  * Chose a suggestion to display. The list of suggestions is ordered by weight,
208  * so we only worry about fariness here. Each suggestion, starting with the
209  * first (the 'heaviest') is displayed during PT_SUGG_DEF_SLICE intervals.
210  */
211 void
pt_sugg_pick(void)212 pt_sugg_pick(void)
213 {
214 	sugg_t *n;
215 
216 	if (sugg == NULL) {
217 		g_curr_sugg = NULL;
218 		return;
219 	}
220 
221 search:
222 	for (n = sugg; n != NULL; n = n->next) {
223 
224 		if (n->slice++ < PT_SUGG_DEF_SLICE) {
225 
226 			/*
227 			 * Don't need to re-suggest the current suggestion.
228 			 */
229 			if (g_curr_sugg == n && !g_sig_resize)
230 				return;
231 
232 			/*
233 			 * Remove the current suggestion from screen.
234 			 */
235 			if (g_curr_sugg != NULL) {
236 				if (g_curr_sugg->sb_msg != NULL) {
237 					pt_display_mod_status_bar(
238 					    g_curr_sugg->sb_msg);
239 					pt_display_status_bar();
240 				}
241 				if (g_curr_sugg->text != NULL)
242 					pt_display_suggestions(NULL);
243 			}
244 
245 			if (n->sb_msg != NULL) {
246 				pt_display_mod_status_bar(n->sb_msg);
247 				pt_display_status_bar();
248 			}
249 
250 			pt_display_suggestions(n->text);
251 
252 			g_curr_sugg = n;
253 
254 			return;
255 		}
256 	}
257 
258 	/*
259 	 * All suggestions have run out of slice quotas, so we restart.
260 	 */
261 	for (n = sugg; n != NULL; n = n->next)
262 		n->slice = 0;
263 
264 	goto search;
265 }
266 
267 void
pt_sugg_as_root(void)268 pt_sugg_as_root(void)
269 {
270 	pt_sugg_add("Suggestion: run as root to get suggestions"
271 	    " for reducing system power consumption",  40, 0, NULL,
272 	    NULL);
273 }
274