blob: 4dec496728baa7c37af828998ad3595e36edfd96 [file] [log] [blame]
Arjan van der Venaec529b2007-05-25 19:57:57 +00001/*
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>
Arjan van der Venf9533992007-06-03 19:18:31 +000032#include <linux/types.h>
33#include <net/if.h>
34#include <linux/sockios.h>
35#include <sys/ioctl.h>
Anurag Singhc5781f82011-10-10 10:44:44 -070036#include <sys/socket.h>
Arjan van der Venf9533992007-06-03 19:18:31 +000037
38/* work around a bug in debian -- it exposes kernel internal types to userspace */
39#define u64 __u64
40#define u32 __u32
41#define u16 __u16
42#define u8 __u8
43#include <linux/ethtool.h>
44#undef u64
45#undef u32
46#undef u16
47#undef u8
48
49
Arjan van der Venaec529b2007-05-25 19:57:57 +000050
51#include "powertop.h"
52
53
54static char wireless_nic[32];
Arjan van der Venf9533992007-06-03 19:18:31 +000055static char rfkill_path[PATH_MAX];
Arjan van der Vena8fa3d12007-09-26 23:23:31 +000056static char powersave_path[PATH_MAX];
Arjan van der Venf9533992007-06-03 19:18:31 +000057
Arjan van der Vencce285f2008-05-15 20:41:04 +000058static int rfkill_enabled(void)
59{
60 FILE *file;
61 char val;
62 if (strlen(rfkill_path)<2)
63 return 0;
64 if (access(rfkill_path, W_OK))
65 return 0;
66
67 file = fopen(rfkill_path, "r");
68 if (!file)
69 return 0;
70 val = fgetc(file);
71 fclose(file);
72 if (val != '0') /* already rfkill'd */
73 return 1;
74 return 0;
75}
76
77int check_unused_wiresless_up(void)
Arjan van der Venf9533992007-06-03 19:18:31 +000078{
79 FILE *file;
80 char val;
81 char line[1024];
82 if (strlen(rfkill_path)<2)
83 return 0;
84 if (access(rfkill_path, W_OK))
85 return 0;
86
87 file = fopen(rfkill_path, "r");
88 if (!file)
89 return 0;
90 val = fgetc(file);
91 fclose(file);
Arjan van der Ven87a082b2007-06-17 18:36:51 +000092 if (val != '0') /* already rfkill'd */
Arjan van der Venba9aa1b2007-06-23 19:05:20 +000093 return -1;
Arjan van der Venf9533992007-06-03 19:18:31 +000094
Arjan van der Ven646a8ca2007-06-08 14:13:46 +000095 sprintf(line,"iwconfig %s 2> /dev/null", wireless_nic);
Arjan van der Venf9533992007-06-03 19:18:31 +000096 file = popen(line, "r");
97 if (!file)
98 return 0;
99 while (!feof(file)) {
100 memset(line, 0, 1024);
101 if (fgets(line, 1023, file) == 0)
102 break;
103 if (strstr(line, "Mode:Managed") && strstr(line,"Access Point: Not-Associated")) {
104 pclose(file);
105 return 1;
106 }
107 }
108 pclose(file);
109 return 0;
110}
Arjan van der Venaec529b2007-05-25 19:57:57 +0000111
112
113static int need_wireless_suggest(char *iface)
114{
115 FILE *file;
116 char line[1024];
117 int ret = 0;
118
Arjan van der Vencce285f2008-05-15 20:41:04 +0000119 if (rfkill_enabled())
120 return 0;
121
Arjan van der Ven4f0e8152007-05-25 21:44:29 +0000122 sprintf(line, "/sbin/iwpriv %s get_power 2> /dev/null", iface);
Arjan van der Venaec529b2007-05-25 19:57:57 +0000123 file = popen(line, "r");
124 if (!file)
125 return 0;
126 while (!feof(file)) {
127 memset(line, 0, 1024);
128 if (fgets(line, 1023, file)==NULL)
129 break;
130 if (strstr(line, "Power save level: 6 (AC)")) {
131 ret = 1;
132 break;
133 }
134 }
135 pclose(file);
136 return ret;
137}
138
139
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000140static int need_wireless_suggest_new(void)
141{
142 FILE *file;
143 char val;
144 if (strlen(powersave_path)<2)
145 return 0;
146 if (access(powersave_path, W_OK))
147 return 0;
148
Arjan van der Vencce285f2008-05-15 20:41:04 +0000149 if (rfkill_enabled())
150 return 0;
151
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000152 file = fopen(powersave_path, "r");
153 if (!file)
154 return 0;
155 val = fgetc(file);
156 fclose(file);
157 if (val <= '5' && val >= '0') /* already in powersave */
158 return 0;
159
160 return 1;
161}
162
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000163void find_4965(void)
164{
165 static int tried_4965 = 0;
166 DIR *dir;
167 struct dirent *dirent;
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000168 char pathname[PATH_MAX];
169
170 if (tried_4965++)
171 return;
172
173 dir = opendir("/sys/bus/pci/drivers/iwl4965");
Arjan van der Ven9b838652008-03-27 04:25:49 +0000174 while (dir && (dirent = readdir(dir))) {
175 if (dirent->d_name[0]=='.')
176 continue;
177 sprintf(pathname, "/sys/bus/pci/drivers/iwl4965/%s/power_level", dirent->d_name);
178 if (!access(pathname, W_OK))
179 strcpy(powersave_path, pathname);
180 }
181 if (dir)
182 closedir(dir);
183 dir = opendir("/sys/bus/pci/drivers/iwl3945");
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000184 if (!dir)
185 return;
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000186 while ((dirent = readdir(dir))) {
187 if (dirent->d_name[0]=='.')
188 continue;
Arjan van der Vencce285f2008-05-15 20:41:04 +0000189 sprintf(pathname, "/sys/bus/pci/drivers/iwl3945/%s/power_level", dirent->d_name);
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000190 if (!access(pathname, W_OK))
191 strcpy(powersave_path, pathname);
192 }
193
194 closedir(dir);
195
196}
197
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000198
Arjan van der Venaec529b2007-05-25 19:57:57 +0000199void find_wireless_nic(void)
200{
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000201 static int found = 0;
Arjan van der Venaec529b2007-05-25 19:57:57 +0000202 FILE *file;
Arjan van der Venf9533992007-06-03 19:18:31 +0000203 int sock;
204 struct ifreq ifr;
205 struct ethtool_value ethtool;
206 struct ethtool_drvinfo driver;
207 int ifaceup = 0;
208 int ret;
209
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000210 if (found++)
211 return;
212
Arjan van der Venf9533992007-06-03 19:18:31 +0000213 wireless_nic[0] = 0;
214 rfkill_path[0] = 0;
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000215 powersave_path[0] = 0;
Arjan van der Venaec529b2007-05-25 19:57:57 +0000216
Arjan van der Vencce285f2008-05-15 20:41:04 +0000217 strcpy(wireless_nic, "wlan0");
218
Arjan van der Venaec529b2007-05-25 19:57:57 +0000219 file = popen("/sbin/iwpriv -a 2> /dev/null", "r");
220 if (!file)
221 return;
222 while (!feof(file)) {
223 char line[1024];
224 memset(line, 0, 1024);
225 if (fgets(line, 1023, file)==NULL)
226 break;
227 if (strstr(line, "get_power:Power save level")) {
228 char *c;
229 c = strchr(line, ' ');
230 if (c) *c = 0;
231 strcpy(wireless_nic, line);
232 }
Arjan van der Vencce285f2008-05-15 20:41:04 +0000233 if (strstr(line, "wlan0:"))
234 strcpy(wireless_nic, "wlan0");
Arjan van der Venaec529b2007-05-25 19:57:57 +0000235 }
236 pclose(file);
Arjan van der Venf9533992007-06-03 19:18:31 +0000237
238
239 if (strlen(wireless_nic)==0)
240 return;
241
242
243 memset(&ifr, 0, sizeof(struct ifreq));
244 memset(&ethtool, 0, sizeof(struct ethtool_value));
245
246 sock = socket(AF_INET, SOCK_DGRAM, 0);
247 if (sock<0)
248 return;
249
250 strcpy(ifr.ifr_name, wireless_nic);
251
252 /* Check if the interface is up */
253 ret = ioctl(sock, SIOCGIFFLAGS, &ifr);
Arjan van der Ven34b44942007-07-19 17:14:55 +0000254 if (ret<0) {
255 close(sock);
Arjan van der Venf9533992007-06-03 19:18:31 +0000256 return;
Arjan van der Ven34b44942007-07-19 17:14:55 +0000257 }
Arjan van der Venf9533992007-06-03 19:18:31 +0000258
259 ifaceup = 0;
260 if (ifr.ifr_flags & (IFF_UP | IFF_RUNNING))
261 ifaceup = 1;
262
263 memset(&driver, 0, sizeof(driver));
264 driver.cmd = ETHTOOL_GDRVINFO;
265 ifr.ifr_data = (void*) &driver;
266 ret = ioctl(sock, SIOCETHTOOL, &ifr);
267
Arjan van der Vencce285f2008-05-15 20:41:04 +0000268 sprintf(rfkill_path,"/sys/bus/pci/devices/%s/rfkill/rfkill0/state", driver.bus_info);
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000269 sprintf(powersave_path,"/sys/bus/pci/devices/%s/power_level", driver.bus_info);
Arjan van der Venf9533992007-06-03 19:18:31 +0000270 close(sock);
Arjan van der Venaec529b2007-05-25 19:57:57 +0000271}
272
273void activate_wireless_suggestion(void)
274{
275 char line[1024];
Arjan van der Ven810323c2007-05-25 21:47:54 +0000276 sprintf(line, "/sbin/iwpriv %s set_power 5 2> /dev/null", wireless_nic);
Arjan van der Venaec529b2007-05-25 19:57:57 +0000277 system(line);
278}
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000279void activate_wireless_suggestion_new(void)
280{
281 FILE *file;
282 file = fopen(powersave_path, "w");
283 if (!file)
284 return;
Arjan van der Ven9c107032008-03-27 04:27:37 +0000285 fprintf(file,"1\n");
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000286 fclose(file);
287}
Arjan van der Venf9533992007-06-03 19:18:31 +0000288
289void activate_rfkill_suggestion(void)
290{
291 FILE *file;
292 file = fopen(rfkill_path, "w");
293 if (!file)
294 return;
295 fprintf(file,"1\n");
296 fclose(file);
297}
Arjan van der Venaec529b2007-05-25 19:57:57 +0000298void suggest_wireless_powersave(void)
299{
300 char sug[1024];
Arjan van der Venba9aa1b2007-06-23 19:05:20 +0000301 int ret;
302
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000303 if (strlen(wireless_nic)==0)
Arjan van der Venaec529b2007-05-25 19:57:57 +0000304 find_wireless_nic();
Arjan van der Ven13f910f2008-03-27 04:24:34 +0000305 find_4965();
Arjan van der Venba9aa1b2007-06-23 19:05:20 +0000306 ret = check_unused_wiresless_up();
307
308 if (ret >= 0 && need_wireless_suggest(wireless_nic)) {
Arjan van der Venaec529b2007-05-25 19:57:57 +0000309 sprintf(sug, _("Suggestion: Enable wireless power saving mode by executing the following command:\n "
310 " iwpriv %s set_power 5 \n"
311 "This will sacrifice network performance slightly to save power."), wireless_nic);
312 add_suggestion(sug, 20, 'W', _(" W - Enable wireless power saving "), activate_wireless_suggestion);
313 }
Arjan van der Vena8fa3d12007-09-26 23:23:31 +0000314 if (ret >= 0 && need_wireless_suggest_new()) {
315 sprintf(sug, _("Suggestion: Enable wireless power saving mode by executing the following command:\n "
316 " echo 5 > %s \n"
317 "This will sacrifice network performance slightly to save power."), powersave_path);
318 add_suggestion(sug, 20, 'W', _(" W - Enable wireless power saving "), activate_wireless_suggestion_new);
319 }
Arjan van der Venba9aa1b2007-06-23 19:05:20 +0000320 if (ret>0) {
Arjan van der Venf9533992007-06-03 19:18:31 +0000321 sprintf(sug, _("Suggestion: Disable the unused WIFI radio by executing the following command:\n "
322 " echo 1 > %s \n"), rfkill_path);
Arjan van der Venaf7eb1f2007-06-03 22:29:09 +0000323 add_suggestion(sug, 60, 'I', _(" I - disable WIFI Radio "), activate_rfkill_suggestion);
Arjan van der Venf9533992007-06-03 19:18:31 +0000324
325 }
Arjan van der Venaec529b2007-05-25 19:57:57 +0000326}