/*
* 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;
}
}