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 (at) 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 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 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 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 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 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 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(ðtool, 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 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 } 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 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 } 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