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 <assert.h>
     33 
     34 #include "powertop.h"
     35 
     36 struct device_data;
     37 
     38 struct device_data {
     39 	struct device_data *next;
     40 	char pathname[4096];
     41 	char human_name[4096];
     42 	uint64_t urbs, active, connected;
     43 	uint64_t previous_urbs, previous_active, previous_connected;
     44 	int controller;
     45 };
     46 
     47 
     48 static struct device_data *devices;
     49 
     50 static void cachunk_urbs(void)
     51 {
     52 	struct device_data *ptr;
     53 	ptr = devices;
     54 	while (ptr) {
     55 		ptr->previous_urbs = ptr->urbs;
     56 		ptr->previous_active = ptr->active;
     57 		ptr->previous_connected = ptr->connected;
     58 		ptr = ptr->next;
     59 	}
     60 }
     61 
     62 static void update_urbnum(char *path, uint64_t count, char *shortname)
     63 {
     64 	struct device_data *ptr;
     65 	FILE *file;
     66 	char fullpath[4096];
     67 	char name[4096], vendor[4096];
     68 	ptr = devices;
     69 
     70 	while (ptr) {
     71 		if (strcmp(ptr->pathname, path)==0) {
     72 			ptr->urbs = count;
     73 			sprintf(fullpath, "%s/power/active_duration", path);
     74 			file = fopen(fullpath, "r");
     75 			if (!file)
     76 				return;
     77 			fgets(name, 4096, file);
     78 			ptr->active = strtoull(name, NULL, 10);
     79 			fclose(file);
     80 			sprintf(fullpath, "%s/power/connected_duration", path);
     81 			file = fopen(fullpath, "r");
     82 			if (!file)
     83 				return;
     84 			fgets(name, 4096, file);
     85 			ptr->connected = strtoull(name, NULL, 10);
     86 			fclose(file);
     87 
     88 			return;
     89 		}
     90 		ptr = ptr->next;
     91 	}
     92 	/* no luck, new one */
     93 	ptr = malloc(sizeof(struct device_data));
     94 	assert(ptr!=0);
     95 	memset(ptr, 0, sizeof(struct device_data));
     96 	ptr->next = devices;
     97 	devices = ptr;
     98 	strcpy(ptr->pathname, path);
     99 	ptr->urbs = ptr->previous_urbs = count;
    100 	sprintf(fullpath, "%s/product", path);
    101 	file = fopen(fullpath, "r");
    102 	memset(name, 0, 4096);
    103 	if (file) {
    104 		fgets(name, 4096, file);
    105 		fclose(file);
    106 	}
    107 	sprintf(fullpath, "%s/manufacturer", path);
    108 	file = fopen(fullpath, "r");
    109 	memset(vendor, 0, 4096);
    110 	if (file) {
    111 		fgets(vendor, 4096, file);
    112 		fclose(file);
    113 	}
    114 
    115 	if (strlen(name)>0 && name[strlen(name)-1]=='\n')
    116 		name[strlen(name)-1]=0;
    117 	if (strlen(vendor)>0 && vendor[strlen(vendor)-1]=='\n')
    118 		vendor[strlen(vendor)-1]=0;
    119 	/* some devices have bogus names */
    120 	if (strlen(name)<4)
    121 		strcpy(ptr->human_name, path);
    122 	else
    123 		sprintf(ptr->human_name, _("USB device %4s : %s (%s)"), shortname, name, vendor);
    124 
    125 	if (strstr(ptr->human_name, "Host Controller"))
    126 		ptr->controller = 1;
    127 
    128 }
    129 
    130 void count_usb_urbs(void)
    131 {
    132 	DIR *dir;
    133 	struct dirent *dirent;
    134 	FILE *file;
    135 	char filename[PATH_MAX];
    136 	char pathname[PATH_MAX];
    137 	char buffer[4096];
    138 	struct device_data *dev;
    139 
    140 	dir = opendir("/sys/bus/usb/devices");
    141 	if (!dir)
    142 		return;
    143 
    144 	cachunk_urbs();
    145 	while ((dirent = readdir(dir))) {
    146 		if (dirent->d_name[0]=='.')
    147 			continue;
    148 		sprintf(pathname, "/sys/bus/usb/devices/%s", dirent->d_name);
    149 		sprintf(filename, "%s/urbnum", pathname);
    150 		file = fopen(filename, "r");
    151 		if (!file)
    152 			continue;
    153 		memset(buffer, 0, 4096);
    154 		fgets(buffer, 4095, file);
    155 		update_urbnum(pathname, strtoull(buffer, NULL, 10), dirent->d_name);
    156 		fclose(file);
    157 	}
    158 
    159 	closedir(dir);
    160 
    161 	dev = devices;
    162 	while (dev) {
    163 		if (dev->urbs != dev->previous_urbs) {
    164 			push_line(dev->human_name, dev->urbs - dev->previous_urbs);
    165 		}
    166 		dev = dev->next;
    167 	}
    168 }
    169 
    170 
    171 void display_usb_activity(void)
    172 {
    173 	struct device_data *dev;
    174 	printf("\n");
    175 	printf("%s\n", _("Recent USB suspend statistics"));
    176 	printf("%s\n", _("Active  Device name"));
    177 	dev = devices;
    178 	while (dev) {
    179 		printf("%5.1f%%\t%s\n", 100.0*(dev->active - dev->previous_active) /
    180 			(0.00001 + dev->connected - dev->previous_connected), dev->human_name);
    181 		dev = dev->next;
    182 	}
    183 
    184 }
    185 
    186 void usb_activity_hint(void)
    187 {
    188 	int total_active = 0;
    189 	int pick;
    190 	struct device_data *dev;
    191 	dev = devices;
    192 	while (dev) {
    193 		if (dev->active-1 > dev->previous_active && !dev->controller)
    194 			total_active++;
    195 		dev = dev->next;
    196 	}
    197 	if (!total_active)
    198 		return;
    199 
    200 	pick = rand() % total_active;
    201 	total_active = 0;
    202 	dev = devices;
    203 	while (dev) {
    204 		if (dev->active-1 > dev->previous_active && !dev->controller) {
    205 			if (total_active == pick) {
    206 				char usb_hint[8000];
    207 				sprintf(usb_hint, _("A USB device is active %4.1f%% of the time:\n%s"),
    208 				 100.0*(dev->active - dev->previous_active) /
    209 				(0.00001 + dev->connected - dev->previous_connected),
    210 				dev->human_name);
    211 				add_suggestion(usb_hint,
    212 				1, 'U', _(" U - Enable USB suspend "), activate_usb_autosuspend);
    213 			}
    214 			total_active++;
    215 		}
    216 		dev = dev->next;
    217 	}
    218 
    219 }
    220