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