diff options
| -rw-r--r-- | src/print_wireless_info.c | 239 | 
1 files changed, 163 insertions, 76 deletions
| 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 <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) + +#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());  } | 
