From 1e0ad8d2513e2f9259ad6003ae5c044bb01f20fb Mon Sep 17 00:00:00 2001 From: Fernando Tarlá Cardoso Lemos Date: Fri, 25 Jun 2010 00:38:47 -0300 Subject: Evaluate wireless quality average. Fix dBm calculations. --- src/print_wireless_info.c | 239 +++++++++++++++++++++++++++++++--------------- 1 file changed, 163 insertions(+), 76 deletions(-) (limited to 'src') diff --git a/src/print_wireless_info.c b/src/print_wireless_info.c index f67626b..50cf8e4 100644 --- a/src/print_wireless_info.c +++ b/src/print_wireless_info.c @@ -1,9 +1,6 @@ // vim:ts=8:expandtab #include -#include #include -#include -#include #ifdef LINUX #include @@ -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) + +#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)); -static int open_skfd() { - if (skfd == -1) { - skfd = iw_sockets_open(); +#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 + 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'; + } -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; -} + /* 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). */ -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; - } + 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 - 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; + 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")); } - - if ((quality == UCHAR_MAX) || (quality == 0)) { + 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()); } -- cgit v1.2.3