• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007, Intel Corporation
3  *
4  * This file is part of PowerTOP
5  *
6  * This program file is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program in a file named COPYING; if not, write to the
17  * Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301 USA
20  *
21  * Authors:
22  * 	Arjan van de Ven <arjan@linux.intel.com>
23  */
24 
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdint.h>
30 #include <sys/types.h>
31 #include <dirent.h>
32 #include <linux/types.h>
33 #include <net/if.h>
34 #include <linux/sockios.h>
35 #include <sys/ioctl.h>
36 
37 /* work around a bug in debian -- it exposes kernel internal types to userspace */
38 #define u64 __u64
39 #define u32 __u32
40 #define u16 __u16
41 #define u8 __u8
42 #include <linux/ethtool.h>
43 #undef u64
44 #undef u32
45 #undef u16
46 #undef u8
47 
48 
49 
50 #include "powertop.h"
51 
52 
53 static char wireless_nic[32];
54 static char rfkill_path[PATH_MAX];
55 static char powersave_path[PATH_MAX];
56 
rfkill_enabled(void)57 static int rfkill_enabled(void)
58 {
59 	FILE *file;
60 	char val;
61 	if (strlen(rfkill_path)<2)
62 		return 0;
63 	if (access(rfkill_path, W_OK))
64 		return 0;
65 
66 	file = fopen(rfkill_path, "r");
67 	if (!file)
68 		return 0;
69 	val = fgetc(file);
70 	fclose(file);
71 	if (val != '0') /* already rfkill'd */
72 		return 1;
73 	return 0;
74 }
75 
check_unused_wiresless_up(void)76 int check_unused_wiresless_up(void)
77 {
78 	FILE *file;
79 	char val;
80 	char line[1024];
81 	if (strlen(rfkill_path)<2)
82 		return 0;
83 	if (access(rfkill_path, W_OK))
84 		return 0;
85 
86 	file = fopen(rfkill_path, "r");
87 	if (!file)
88 		return 0;
89 	val = fgetc(file);
90 	fclose(file);
91 	if (val != '0') /* already rfkill'd */
92 		return -1;
93 
94 	sprintf(line,"iwconfig %s 2> /dev/null", wireless_nic);
95 	file = popen(line, "r");
96 	if (!file)
97 		return 0;
98 	while (!feof(file)) {
99 		memset(line, 0, 1024);
100 		if (fgets(line, 1023, file) == 0)
101 			break;
102 		if (strstr(line, "Mode:Managed") && strstr(line,"Access Point: Not-Associated")) {
103 			pclose(file);
104 			return 1;
105 		}
106 	}
107 	pclose(file);
108 	return 0;
109 }
110 
111 
need_wireless_suggest(char * iface)112 static int need_wireless_suggest(char *iface)
113 {
114 	FILE *file;
115 	char line[1024];
116 	int ret = 0;
117 
118 	if (rfkill_enabled())
119 		return 0;
120 
121 	sprintf(line, "/sbin/iwpriv %s get_power 2> /dev/null", iface);
122 	file = popen(line, "r");
123 	if (!file)
124 		return 0;
125 	while (!feof(file)) {
126 		memset(line, 0, 1024);
127 		if (fgets(line, 1023, file)==NULL)
128 			break;
129 		if (strstr(line, "Power save level: 6 (AC)")) {
130 			ret = 1;
131 			break;
132 		}
133 	}
134 	pclose(file);
135 	return ret;
136 }
137 
138 
need_wireless_suggest_new(void)139 static int need_wireless_suggest_new(void)
140 {
141 	FILE *file;
142 	char val;
143 	if (strlen(powersave_path)<2)
144 		return 0;
145 	if (access(powersave_path, W_OK))
146 		return 0;
147 
148 	if (rfkill_enabled())
149 		return 0;
150 
151 	file = fopen(powersave_path, "r");
152 	if (!file)
153 		return 0;
154 	val = fgetc(file);
155 	fclose(file);
156 	if (val <= '5' && val >= '0') /* already in powersave */
157 		return 0;
158 
159 	return 1;
160 }
161 
find_4965(void)162 void find_4965(void)
163 {
164 	static int tried_4965 = 0;
165 	DIR *dir;
166 	struct dirent *dirent;
167 	char pathname[PATH_MAX];
168 
169 	if (tried_4965++)
170 		return;
171 
172 	dir = opendir("/sys/bus/pci/drivers/iwl4965");
173 	while (dir && (dirent = readdir(dir))) {
174 		if (dirent->d_name[0]=='.')
175 			continue;
176 		sprintf(pathname, "/sys/bus/pci/drivers/iwl4965/%s/power_level", dirent->d_name);
177 		if (!access(pathname, W_OK))
178 			strcpy(powersave_path, pathname);
179 	}
180 	if (dir)
181 		closedir(dir);
182 	dir = opendir("/sys/bus/pci/drivers/iwl3945");
183 	if (!dir)
184 		return;
185 	while ((dirent = readdir(dir))) {
186 		if (dirent->d_name[0]=='.')
187 			continue;
188 		sprintf(pathname, "/sys/bus/pci/drivers/iwl3945/%s/power_level", dirent->d_name);
189 		if (!access(pathname, W_OK))
190 			strcpy(powersave_path, pathname);
191 	}
192 
193 	closedir(dir);
194 
195 }
196 
197 
find_wireless_nic(void)198 void find_wireless_nic(void)
199 {
200 	static int found = 0;
201 	FILE *file;
202 	int sock;
203 	struct ifreq ifr;
204 	struct ethtool_value ethtool;
205 	struct ethtool_drvinfo driver;
206 	int ifaceup = 0;
207 	int ret;
208 
209 	if (found++)
210 		return;
211 
212 	wireless_nic[0] = 0;
213 	rfkill_path[0] = 0;
214 	powersave_path[0] = 0;
215 
216 	strcpy(wireless_nic, "wlan0");
217 
218 	file = popen("/sbin/iwpriv -a 2> /dev/null", "r");
219 	if (!file)
220 		return;
221 	while (!feof(file)) {
222 		char line[1024];
223 		memset(line, 0, 1024);
224 		if (fgets(line, 1023, file)==NULL)
225 			break;
226 		if (strstr(line, "get_power:Power save level")) {
227 			char *c;
228 			c = strchr(line, ' ');
229 			if (c) *c = 0;
230 			strcpy(wireless_nic, line);
231 		}
232 		if (strstr(line, "wlan0:"))
233 			strcpy(wireless_nic, "wlan0");
234 	}
235 	pclose(file);
236 
237 
238 	if (strlen(wireless_nic)==0)
239 		return;
240 
241 
242 	memset(&ifr, 0, sizeof(struct ifreq));
243 	memset(&ethtool, 0, sizeof(struct ethtool_value));
244 
245 	sock = socket(AF_INET, SOCK_DGRAM, 0);
246 	if (sock<0)
247 		return;
248 
249 	strcpy(ifr.ifr_name, wireless_nic);
250 
251 	/* Check if the interface is up */
252 	ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
253 	if (ret<0) {
254 		close(sock);
255 		return;
256 	}
257 
258 	ifaceup = 0;
259 	if (ifr.ifr_flags & (IFF_UP | IFF_RUNNING))
260 		ifaceup = 1;
261 
262 	memset(&driver, 0, sizeof(driver));
263 	driver.cmd = ETHTOOL_GDRVINFO;
264         ifr.ifr_data = (void*) &driver;
265         ret = ioctl(sock, SIOCETHTOOL, &ifr);
266 
267 	sprintf(rfkill_path,"/sys/bus/pci/devices/%s/rfkill/rfkill0/state", driver.bus_info);
268 	sprintf(powersave_path,"/sys/bus/pci/devices/%s/power_level", driver.bus_info);
269 	close(sock);
270 }
271 
activate_wireless_suggestion(void)272 void activate_wireless_suggestion(void)
273 {
274 	char line[1024];
275 	sprintf(line, "/sbin/iwpriv %s set_power 5 2> /dev/null", wireless_nic);
276 	system(line);
277 }
activate_wireless_suggestion_new(void)278 void activate_wireless_suggestion_new(void)
279 {
280 	FILE *file;
281 	file = fopen(powersave_path, "w");
282 	if (!file)
283 		return;
284 	fprintf(file,"1\n");
285 	fclose(file);
286 }
287 
activate_rfkill_suggestion(void)288 void activate_rfkill_suggestion(void)
289 {
290 	FILE *file;
291 	file = fopen(rfkill_path, "w");
292 	if (!file)
293 		return;
294 	fprintf(file,"1\n");
295 	fclose(file);
296 }
suggest_wireless_powersave(void)297 void suggest_wireless_powersave(void)
298 {
299 	char sug[1024];
300 	int ret;
301 
302 	if (strlen(wireless_nic)==0)
303 		find_wireless_nic();
304 	find_4965();
305 	ret = check_unused_wiresless_up();
306 
307 	if (ret >= 0 && need_wireless_suggest(wireless_nic)) {
308 		sprintf(sug, _("Suggestion: Enable wireless power saving mode by executing the following command:\n "
309 			       " iwpriv %s set_power 5 \n"
310 			       "This will sacrifice network performance slightly to save power."), wireless_nic);
311 		add_suggestion(sug, 20, 'W', _(" W - Enable wireless power saving "), activate_wireless_suggestion);
312 	}
313 	if (ret >= 0 && need_wireless_suggest_new()) {
314 		sprintf(sug, _("Suggestion: Enable wireless power saving mode by executing the following command:\n "
315 			       " echo 5 > %s \n"
316 			       "This will sacrifice network performance slightly to save power."), powersave_path);
317 		add_suggestion(sug, 20, 'W', _(" W - Enable wireless power saving "), activate_wireless_suggestion_new);
318 	}
319 	if (ret>0) {
320 		sprintf(sug, _("Suggestion: Disable the unused WIFI radio by executing the following command:\n "
321 			       " echo 1 > %s \n"), rfkill_path);
322 		add_suggestion(sug, 60, 'I', _(" I - disable WIFI Radio "), activate_rfkill_suggestion);
323 
324 	}
325 }
326