Evaluate wireless quality average. Fix dBm calculations.

This commit is contained in:
Fernando Tarlá Cardoso Lemos 2010-06-25 00:38:47 -03:00 committed by Michael Stapelberg
parent 28ba2ae892
commit 1e0ad8d251

View File

@ -1,9 +1,6 @@
// vim:ts=8:expandtab
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#ifdef LINUX
#include <iwlib.h>
@ -11,85 +8,146 @@
#include "i3status.h"
#ifdef LINUX
static int skfd = -1;
#define WIRELESS_INFO_FLAG_HAS_ESSID (1 << 0)
#define WIRELESS_INFO_FLAG_HAS_QUALITY (1 << 1)
#define WIRELESS_INFO_FLAG_HAS_SIGNAL (1 << 2)
#define WIRELESS_INFO_FLAG_HAS_NOISE (1 << 3)
static int open_skfd() {
if (skfd == -1) {
skfd = iw_sockets_open();
#define PERCENT_VALUE(value, total) ((int)(value * 100 / (float)total + 0.5f))
typedef struct {
int flags;
char essid[IW_ESSID_MAX_SIZE + 1];
int quality;
int quality_max;
int quality_average;
int signal_level;
int signal_level_max;
int noise_level;
int noise_level_max;
} wireless_info_t;
static int get_wireless_info(const char *interface, wireless_info_t *info) {
memset(info, 0, sizeof(wireless_info_t));
#ifdef LINUX
int skfd = iw_sockets_open();
if (skfd < 0) {
perror("iw_sockets_open");
perror("iw_sockets_open");
return 0;
}
wireless_config wcfg;
if (iw_get_basic_config(skfd, interface, &wcfg) < 0) {
perror("iw_get_basic_config");
return 0;
}
}
return -1;
}
static void close_skfd() {
if (skfd != -1) {
close(skfd);
skfd = -1;
}
}
#endif
const char *get_wireless_essid(const char *interface) {
static char part[512];
part[0] = '\0';
#ifdef LINUX
if (open_skfd()) {
wireless_config wcfg;
if (iw_get_basic_config(skfd, interface, &wcfg) >= 0)
snprintf(part, sizeof(part), "%s", wcfg.essid);
}
#endif
return part;
}
int get_wireless_quality_max(const char *interface) {
#ifdef LINUX
if (open_skfd()) {
iwrange range;
if (iw_get_range_info(skfd, interface, &range) >= 0)
return range.max_qual.qual;
}
#endif
return 0;
}
/*
* Just parses /proc/net/wireless looking for lines beginning with
* wlan_interface, extracting the quality of the link and adding the
* current IP address of wlan_interface.
*
*/
void print_wireless_info(const char *interface, const char *format_up, const char *format_down) {
char buf[1024];
int quality = 0;
char *interfaces;
const char *walk;
memset(buf, 0, sizeof(buf));
if (!slurp("/proc/net/wireless", buf, sizeof(buf)))
die("Could not open \"/proc/net/wireless\"\n");
interfaces = skip_character(buf, '\n', 1) + 1;
while ((interfaces = skip_character(interfaces, '\n', 1)+1) < buf+strlen(buf)) {
while (isspace((int)*interfaces))
interfaces++;
if (!BEGINS_WITH(interfaces, interface))
continue;
if (sscanf(interfaces, "%*[^:]: 0000 %d", &quality) != 1)
continue;
break;
if (wcfg.has_essid && wcfg.essid_on) {
info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE);
info->essid[IW_ESSID_MAX_SIZE] = '\0';
}
if ((quality == UCHAR_MAX) || (quality == 0)) {
/* Wireless quality is a relative value in a driver-specific range.
Signal and noise level can be either relative or absolute values
in dBm. Furthermore, noise and quality can be expressed directly
in dBm or in RCPI (802.11k), which we convert to dBm. When those
values are expressed directly in dBm, they range from -192 to 63,
and since the values are packed into 8 bits, we need to perform
8-bit arithmetic on them. Assume absolute values if everything
else fails (driver bug). */
iwrange range;
if (iw_get_range_info(skfd, interface, &range) < 0) {
close(skfd);
return 0;
}
iwstats stats;
if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) {
close(skfd);
return 0;
}
if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) {
if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
info->quality = stats.qual.qual;
info->quality_max = range.max_qual.qual;
info->quality_average = range.avg_qual.qual;
info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
}
if (stats.qual.updated & IW_QUAL_RCPI) {
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
info->signal_level = stats.qual.level / 2.0 - 110 + 0.5;
info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
}
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5;
info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
}
}
else {
if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) {
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
info->signal_level = stats.qual.level;
if (info->signal_level > 63)
info->signal_level -= 256;
info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
}
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
info->noise_level = stats.qual.noise;
if (info->noise_level > 63)
info->noise_level -= 256;
info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
}
}
else {
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
info->signal_level = stats.qual.level;
info->signal_level_max = range.max_qual.level;
info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
}
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
info->noise_level = stats.qual.noise;
info->noise_level_max = range.max_qual.noise;
info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
}
}
}
}
else {
if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
info->quality = stats.qual.qual;
info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
}
if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
info->quality = stats.qual.level;
info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
}
if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
info->quality = stats.qual.noise;
info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
}
}
close(skfd);
return 1;
#endif
}
void print_wireless_info(const char *interface, const char *format_up, const char *format_down) {
const char *walk;
wireless_info_t info;
if (get_wireless_info(interface, &info)) {
walk = format_up;
if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
printf("%s", info.quality < info.quality_average ? color("#FFFF00") : color("#00FF00"));
}
else {
walk = format_down;
printf("%s", color("#FF0000"));
} else {
printf("%s", color("#00FF00"));
walk = format_up;
}
for (; *walk != '\0'; walk++) {
@ -99,16 +157,49 @@ void print_wireless_info(const char *interface, const char *format_up, const cha
}
if (BEGINS_WITH(walk+1, "quality")) {
int max_qual = get_wireless_quality_max(interface);
if (max_qual && max_qual >= quality)
printf("%03d%%", (int)(quality * (100.0 / max_qual)));
else
printf("%d dBm", quality);
if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) {
if (info.quality_max)
printf("%03d%%", PERCENT_VALUE(info.quality, info.quality_max));
else
printf("%d", info.quality);
}
else {
printf("no value");
}
walk += strlen("quality");
}
if (BEGINS_WITH(walk+1, "signal")) {
if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) {
if (info.signal_level_max)
printf("%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max));
else
printf("%d dBm", info.signal_level);
}
else {
printf("no value");
}
walk += strlen("signal");
}
if (BEGINS_WITH(walk+1, "noise")) {
if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) {
if (info.noise_level_max)
printf("%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max));
else
printf("%d dBm", info.noise_level);
}
else {
printf("no value");
}
walk += strlen("noise");
}
if (BEGINS_WITH(walk+1, "essid")) {
(void)printf("%s", get_wireless_essid(interface));
if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID)
printf("%s", info.essid);
else
printf("no value");
walk += strlen("essid");
}
@ -121,9 +212,5 @@ void print_wireless_info(const char *interface, const char *format_up, const cha
}
}
#ifdef LINUX
close_skfd();
#endif
(void)printf("%s", endcolor());
}