]> git.sur5r.net Git - i3/i3status/blob - src/print_wireless_info.c
Evaluate wireless quality average. Fix dBm calculations.
[i3/i3status] / src / print_wireless_info.c
1 // vim:ts=8:expandtab
2 #include <stdio.h>
3 #include <string.h>
4
5 #ifdef LINUX
6 #include <iwlib.h>
7 #endif
8
9 #include "i3status.h"
10
11 #define WIRELESS_INFO_FLAG_HAS_ESSID                    (1 << 0)
12 #define WIRELESS_INFO_FLAG_HAS_QUALITY                  (1 << 1)
13 #define WIRELESS_INFO_FLAG_HAS_SIGNAL                   (1 << 2)
14 #define WIRELESS_INFO_FLAG_HAS_NOISE                    (1 << 3)
15
16 #define PERCENT_VALUE(value, total) ((int)(value * 100 / (float)total + 0.5f))
17
18 typedef struct {
19         int flags;
20         char essid[IW_ESSID_MAX_SIZE + 1];
21         int quality;
22         int quality_max;
23         int quality_average;
24         int signal_level;
25         int signal_level_max;
26         int noise_level;
27         int noise_level_max;
28 } wireless_info_t;
29
30 static int get_wireless_info(const char *interface, wireless_info_t *info) {
31         memset(info, 0, sizeof(wireless_info_t));
32
33 #ifdef LINUX
34         int skfd = iw_sockets_open();
35         if (skfd < 0) {
36                 perror("iw_sockets_open");
37                 return 0;
38         }
39
40         wireless_config wcfg;
41         if (iw_get_basic_config(skfd, interface, &wcfg) < 0) {
42             perror("iw_get_basic_config");
43             return 0;
44         }
45
46         if (wcfg.has_essid && wcfg.essid_on) {
47                 info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
48                 strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE);
49                 info->essid[IW_ESSID_MAX_SIZE] = '\0';
50         }
51
52         /* Wireless quality is a relative value in a driver-specific range.
53            Signal and noise level can be either relative or absolute values
54            in dBm. Furthermore, noise and quality can be expressed directly
55            in dBm or in RCPI (802.11k), which we convert to dBm. When those
56            values are expressed directly in dBm, they range from -192 to 63,
57            and since the values are packed into 8 bits, we need to perform
58            8-bit arithmetic on them. Assume absolute values if everything
59            else fails (driver bug). */
60
61         iwrange range;
62         if (iw_get_range_info(skfd, interface, &range) < 0) {
63                 close(skfd);
64                 return 0;
65         }
66
67         iwstats stats;
68         if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) {
69                 close(skfd);
70                 return 0;
71         }
72
73         if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) {
74                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
75                         info->quality = stats.qual.qual;
76                         info->quality_max = range.max_qual.qual;
77                         info->quality_average = range.avg_qual.qual;
78                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
79                 }
80
81                 if (stats.qual.updated & IW_QUAL_RCPI) {
82                         if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
83                                 info->signal_level = stats.qual.level / 2.0 - 110 + 0.5;
84                                 info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
85                         }
86                         if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
87                                 info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5;
88                                 info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
89                         }
90                 }
91                 else {
92                         if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) {
93                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
94                                         info->signal_level = stats.qual.level;
95                                         if (info->signal_level > 63)
96                                                 info->signal_level -= 256;
97                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
98                                 }
99                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
100                                         info->noise_level = stats.qual.noise;
101                                         if (info->noise_level > 63)
102                                                 info->noise_level -= 256;
103                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
104                                 }
105                         }
106                         else {
107                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
108                                         info->signal_level = stats.qual.level;
109                                         info->signal_level_max = range.max_qual.level;
110                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
111                                 }
112                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
113                                         info->noise_level = stats.qual.noise;
114                                         info->noise_level_max = range.max_qual.noise;
115                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
116                                 }
117                         }
118                 }
119         }
120         else {
121                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
122                         info->quality = stats.qual.qual;
123                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
124                 }
125                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
126                         info->quality = stats.qual.level;
127                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
128                 }
129                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
130                         info->quality = stats.qual.noise;
131                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
132                 }
133         }
134
135         close(skfd);
136         return 1;
137 #endif
138 }
139
140 void print_wireless_info(const char *interface, const char *format_up, const char *format_down) {
141         const char *walk;
142         wireless_info_t info;
143         if (get_wireless_info(interface, &info)) {
144                 walk = format_up;
145                 if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
146                         printf("%s", info.quality < info.quality_average ? color("#FFFF00") : color("#00FF00"));
147         }
148         else {
149                 walk = format_down;
150                 printf("%s", color("#FF0000"));
151         }
152
153         for (; *walk != '\0'; walk++) {
154                 if (*walk != '%') {
155                         putchar(*walk);
156                         continue;
157                 }
158
159                 if (BEGINS_WITH(walk+1, "quality")) {
160                         if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) {
161                                 if (info.quality_max)
162                                         printf("%03d%%", PERCENT_VALUE(info.quality, info.quality_max));
163                                 else
164                                         printf("%d", info.quality);
165                         }
166                         else {
167                                 printf("no value");
168                         }
169                         walk += strlen("quality");
170                 }
171
172                 if (BEGINS_WITH(walk+1, "signal")) {
173                         if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) {
174                                 if (info.signal_level_max)
175                                         printf("%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max));
176                                 else
177                                         printf("%d dBm", info.signal_level);
178                         }
179                         else {
180                                 printf("no value");
181                         }
182                         walk += strlen("signal");
183                 }
184
185                 if (BEGINS_WITH(walk+1, "noise")) {
186                         if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) {
187                                 if (info.noise_level_max)
188                                         printf("%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max));
189                                 else
190                                         printf("%d dBm", info.noise_level);
191                         }
192                         else {
193                                 printf("no value");
194                         }
195                         walk += strlen("noise");
196                 }
197
198                 if (BEGINS_WITH(walk+1, "essid")) {
199                         if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID)
200                                 printf("%s", info.essid);
201                         else
202                                 printf("no value");
203                         walk += strlen("essid");
204                 }
205
206                 if (BEGINS_WITH(walk+1, "ip")) {
207                         const char *ip_address = get_ip_addr(interface);
208                         if (ip_address != NULL)
209                                 (void)printf("%s", get_ip_addr(interface));
210                         else (void)printf("no IP");
211                         walk += strlen("ip");
212                 }
213         }
214
215         (void)printf("%s", endcolor());
216 }