| /* |
| * Copyright 2007, Intel Corporation |
| * |
| * This file is part of PowerTOP |
| * |
| * This program file is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program in a file named COPYING; if not, write to the |
| * Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301 USA |
| * |
| * Authors: |
| * Arjan van de Ven <arjan@linux.intel.com> |
| */ |
| |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <sys/types.h> |
| #include <dirent.h> |
| #include <assert.h> |
| |
| #include "powertop.h" |
| |
| struct device_data; |
| |
| struct device_data { |
| struct device_data *next; |
| char pathname[4096]; |
| char human_name[4096]; |
| uint64_t urbs, active, connected; |
| uint64_t previous_urbs, previous_active, previous_connected; |
| int controller; |
| }; |
| |
| |
| static struct device_data *devices; |
| |
| static void cachunk_urbs(void) |
| { |
| struct device_data *ptr; |
| ptr = devices; |
| while (ptr) { |
| ptr->previous_urbs = ptr->urbs; |
| ptr->previous_active = ptr->active; |
| ptr->previous_connected = ptr->connected; |
| ptr = ptr->next; |
| } |
| } |
| |
| static void update_urbnum(char *path, uint64_t count, char *shortname) |
| { |
| struct device_data *ptr; |
| FILE *file; |
| char fullpath[4096]; |
| char name[4096], vendor[4096]; |
| ptr = devices; |
| |
| while (ptr) { |
| if (strcmp(ptr->pathname, path)==0) { |
| ptr->urbs = count; |
| sprintf(fullpath, "%s/power/active_duration", path); |
| file = fopen(fullpath, "r"); |
| if (!file) |
| return; |
| fgets(name, 4096, file); |
| ptr->active = strtoull(name, NULL, 10); |
| fclose(file); |
| sprintf(fullpath, "%s/power/connected_duration", path); |
| file = fopen(fullpath, "r"); |
| if (!file) |
| return; |
| fgets(name, 4096, file); |
| ptr->connected = strtoull(name, NULL, 10); |
| fclose(file); |
| |
| return; |
| } |
| ptr = ptr->next; |
| } |
| /* no luck, new one */ |
| ptr = malloc(sizeof(struct device_data)); |
| assert(ptr!=0); |
| memset(ptr, 0, sizeof(struct device_data)); |
| ptr->next = devices; |
| devices = ptr; |
| strcpy(ptr->pathname, path); |
| ptr->urbs = ptr->previous_urbs = count; |
| sprintf(fullpath, "%s/product", path); |
| file = fopen(fullpath, "r"); |
| memset(name, 0, 4096); |
| if (file) { |
| fgets(name, 4096, file); |
| fclose(file); |
| } |
| sprintf(fullpath, "%s/manufacturer", path); |
| file = fopen(fullpath, "r"); |
| memset(vendor, 0, 4096); |
| if (file) { |
| fgets(vendor, 4096, file); |
| fclose(file); |
| } |
| |
| if (strlen(name)>0 && name[strlen(name)-1]=='\n') |
| name[strlen(name)-1]=0; |
| if (strlen(vendor)>0 && vendor[strlen(vendor)-1]=='\n') |
| vendor[strlen(vendor)-1]=0; |
| /* some devices have bogus names */ |
| if (strlen(name)<4) |
| strcpy(ptr->human_name, path); |
| else |
| sprintf(ptr->human_name, _("USB device %4s : %s (%s)"), shortname, name, vendor); |
| |
| if (strstr(ptr->human_name, "Host Controller")) |
| ptr->controller = 1; |
| |
| } |
| |
| void count_usb_urbs(void) |
| { |
| DIR *dir; |
| struct dirent *dirent; |
| FILE *file; |
| char filename[PATH_MAX]; |
| char pathname[PATH_MAX]; |
| char buffer[4096]; |
| struct device_data *dev; |
| |
| dir = opendir("/sys/bus/usb/devices"); |
| if (!dir) |
| return; |
| |
| cachunk_urbs(); |
| while ((dirent = readdir(dir))) { |
| if (dirent->d_name[0]=='.') |
| continue; |
| sprintf(pathname, "/sys/bus/usb/devices/%s", dirent->d_name); |
| sprintf(filename, "%s/urbnum", pathname); |
| file = fopen(filename, "r"); |
| if (!file) |
| continue; |
| memset(buffer, 0, 4096); |
| fgets(buffer, 4095, file); |
| update_urbnum(pathname, strtoull(buffer, NULL, 10), dirent->d_name); |
| fclose(file); |
| } |
| |
| closedir(dir); |
| |
| dev = devices; |
| while (dev) { |
| if (dev->urbs != dev->previous_urbs) { |
| push_line(dev->human_name, dev->urbs - dev->previous_urbs); |
| } |
| dev = dev->next; |
| } |
| } |
| |
| |
| void display_usb_activity(void) |
| { |
| struct device_data *dev; |
| printf("\n"); |
| printf("%s\n", _("Recent USB suspend statistics")); |
| printf("%s\n", _("Active Device name")); |
| dev = devices; |
| while (dev) { |
| printf("%5.1f%%\t%s\n", 100.0*(dev->active - dev->previous_active) / |
| (0.00001 + dev->connected - dev->previous_connected), dev->human_name); |
| dev = dev->next; |
| } |
| |
| } |
| |
| void usb_activity_hint(void) |
| { |
| int total_active = 0; |
| int pick; |
| struct device_data *dev; |
| dev = devices; |
| while (dev) { |
| if (dev->active-1 > dev->previous_active && !dev->controller) |
| total_active++; |
| dev = dev->next; |
| } |
| if (!total_active) |
| return; |
| |
| pick = rand() % total_active; |
| total_active = 0; |
| dev = devices; |
| while (dev) { |
| if (dev->active-1 > dev->previous_active && !dev->controller) { |
| if (total_active == pick) { |
| char usb_hint[8000]; |
| sprintf(usb_hint, _("A USB device is active %4.1f%% of the time:\n%s"), |
| 100.0*(dev->active - dev->previous_active) / |
| (0.00001 + dev->connected - dev->previous_connected), |
| dev->human_name); |
| add_suggestion(usb_hint, |
| 1, 'U', _(" U - Enable USB suspend "), activate_usb_autosuspend); |
| } |
| total_active++; |
| } |
| dev = dev->next; |
| } |
| |
| } |