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