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 <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 
cachunk_urbs(void)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 
update_urbnum(char * path,uint64_t count,char * shortname)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 
count_usb_urbs(void)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 
display_usb_activity(void)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 
usb_activity_hint(void)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