Home | History | Annotate | Download | only in src
      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(&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 
    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