]> git.sur5r.net Git - i3/i3status/blob - src/print_wireless_info.c
1aca346f3bdef1e9597405f1f99b78d45e90be53
[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 #ifndef __FreeBSD__
9 #define IW_ESSID_MAX_SIZE 32
10 #endif
11 #endif
12
13 #ifdef __FreeBSD__
14 #include <sys/param.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <ifaddrs.h>
18 #include <net/if.h>
19 #include <net/if_media.h>
20 #include <net80211/ieee80211.h>
21 #include <net80211/ieee80211_ioctl.h>
22 #include <unistd.h>
23 #define IW_ESSID_MAX_SIZE IEEE80211_NWID_LEN
24 #endif
25
26 #include "i3status.h"
27
28 #define WIRELESS_INFO_FLAG_HAS_ESSID                    (1 << 0)
29 #define WIRELESS_INFO_FLAG_HAS_QUALITY                  (1 << 1)
30 #define WIRELESS_INFO_FLAG_HAS_SIGNAL                   (1 << 2)
31 #define WIRELESS_INFO_FLAG_HAS_NOISE                    (1 << 3)
32
33 #define PERCENT_VALUE(value, total) ((int)(value * 100 / (float)total + 0.5f))
34
35 typedef struct {
36         int flags;
37         char essid[IW_ESSID_MAX_SIZE + 1];
38         int quality;
39         int quality_max;
40         int quality_average;
41         int signal_level;
42         int signal_level_max;
43         int noise_level;
44         int noise_level_max;
45         int bitrate;
46 } wireless_info_t;
47
48 static int get_wireless_info(const char *interface, wireless_info_t *info) {
49         memset(info, 0, sizeof(wireless_info_t));
50
51 #ifdef LINUX
52         int skfd = iw_sockets_open();
53         if (skfd < 0) {
54                 perror("iw_sockets_open");
55                 return 0;
56         }
57
58         wireless_config wcfg;
59         if (iw_get_basic_config(skfd, interface, &wcfg) < 0) {
60             close(skfd);
61             return 0;
62         }
63
64         if (wcfg.has_essid && wcfg.essid_on) {
65                 info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
66                 strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE);
67                 info->essid[IW_ESSID_MAX_SIZE] = '\0';
68         }
69
70         /* Wireless quality is a relative value in a driver-specific range.
71            Signal and noise level can be either relative or absolute values
72            in dBm. Furthermore, noise and quality can be expressed directly
73            in dBm or in RCPI (802.11k), which we convert to dBm. When those
74            values are expressed directly in dBm, they range from -192 to 63,
75            and since the values are packed into 8 bits, we need to perform
76            8-bit arithmetic on them. Assume absolute values if everything
77            else fails (driver bug). */
78
79         iwrange range;
80         if (iw_get_range_info(skfd, interface, &range) < 0) {
81                 close(skfd);
82                 return 0;
83         }
84
85         iwstats stats;
86         if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) {
87                 close(skfd);
88                 return 0;
89         }
90
91         if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) {
92                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
93                         info->quality = stats.qual.qual;
94                         info->quality_max = range.max_qual.qual;
95                         info->quality_average = range.avg_qual.qual;
96                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
97                 }
98
99                 if (stats.qual.updated & IW_QUAL_RCPI) {
100                         if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
101                                 info->signal_level = stats.qual.level / 2.0 - 110 + 0.5;
102                                 info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
103                         }
104                         if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
105                                 info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5;
106                                 info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
107                         }
108                 }
109                 else {
110                         if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) {
111                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
112                                         info->signal_level = stats.qual.level;
113                                         if (info->signal_level > 63)
114                                                 info->signal_level -= 256;
115                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
116                                 }
117                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
118                                         info->noise_level = stats.qual.noise;
119                                         if (info->noise_level > 63)
120                                                 info->noise_level -= 256;
121                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
122                                 }
123                         }
124                         else {
125                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
126                                         info->signal_level = stats.qual.level;
127                                         info->signal_level_max = range.max_qual.level;
128                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
129                                 }
130                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
131                                         info->noise_level = stats.qual.noise;
132                                         info->noise_level_max = range.max_qual.noise;
133                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
134                                 }
135                         }
136                 }
137         }
138         else {
139                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
140                         info->quality = stats.qual.qual;
141                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
142                 }
143                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
144                         info->quality = stats.qual.level;
145                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
146                 }
147                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
148                         info->quality = stats.qual.noise;
149                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
150                 }
151         }
152
153         struct iwreq wrq;
154         if (iw_get_ext(skfd, interface, SIOCGIWRATE, &wrq) >= 0)
155                 info->bitrate = wrq.u.bitrate.value;
156
157         close(skfd);
158         return 1;
159 #endif
160 #ifdef __FreeBSD__
161         int s, len, inwid;
162         uint8_t buf[24 * 1024], *cp;
163         struct ieee80211req na;
164         char network_id[IEEE80211_NWID_LEN + 1];
165
166         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
167                 return (0);
168
169         memset(&na, 0, sizeof(na));
170         strlcpy(na.i_name, interface, sizeof(na.i_name));
171         na.i_type = IEEE80211_IOC_SSID;
172         na.i_data = &info->essid[0];
173         na.i_len = IEEE80211_NWID_LEN + 1;
174         if ((inwid = ioctl(s, SIOCG80211, (caddr_t)&na)) == -1) {
175                 close(s);
176                 return (0);
177         }
178         if (inwid == 0) {
179                 if (na.i_len <= IEEE80211_NWID_LEN)
180                         len = na.i_len + 1;
181                 else
182                         len = IEEE80211_NWID_LEN + 1;
183                 info->essid[len -1] = '\0';
184         } else {
185                 close(s);
186                 return (0);
187         }
188         info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
189
190         memset(&na, 0, sizeof(na));
191         strlcpy(na.i_name, interface, sizeof(na.i_name));
192         na.i_type = IEEE80211_IOC_SCAN_RESULTS;
193         na.i_data = buf;
194         na.i_len = sizeof(buf);
195
196         if (ioctl(s, SIOCG80211, (caddr_t)&na) == -1) {
197                 printf("fail\n");
198                 close(s);
199                 return (0);
200         }
201
202         close(s);
203         len = na.i_len;
204         cp = buf;
205         struct ieee80211req_scan_result *sr;
206         uint8_t *vp;
207         sr = (struct ieee80211req_scan_result *)cp;
208         vp = (u_int8_t *)(sr + 1);
209         strlcpy(network_id, (const char *)vp, sr->isr_ssid_len + 1);
210         if (!strcmp(network_id, &info->essid[0])) {
211                 info->signal_level = sr->isr_rssi;
212                 info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
213                 info->noise_level = sr->isr_noise;
214                 info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
215                 info->quality = sr->isr_intval;
216                 info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
217         }
218
219         return 1;
220 #endif
221         return 0;
222 }
223
224 void print_wireless_info(const char *interface, const char *format_up, const char *format_down) {
225         const char *walk;
226         wireless_info_t info;
227         if (get_wireless_info(interface, &info)) {
228                 walk = format_up;
229                 if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
230                         printf("%s", info.quality < info.quality_average ? color("color_degraded") : color("color_good"));
231         }
232         else {
233                 walk = format_down;
234                 printf("%s", color("color_bad"));
235         }
236
237         for (; *walk != '\0'; walk++) {
238                 if (*walk != '%') {
239                         putchar(*walk);
240                         continue;
241                 }
242
243                 if (BEGINS_WITH(walk+1, "quality")) {
244                         if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) {
245                                 if (info.quality_max)
246                                         printf("%03d%%", PERCENT_VALUE(info.quality, info.quality_max));
247                                 else
248                                         printf("%d", info.quality);
249                         }
250                         else {
251                                 printf("no value");
252                         }
253                         walk += strlen("quality");
254                 }
255
256                 if (BEGINS_WITH(walk+1, "signal")) {
257                         if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) {
258                                 if (info.signal_level_max)
259                                         printf("%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max));
260                                 else
261                                         printf("%d dBm", info.signal_level);
262                         }
263                         else {
264                                 printf("no value");
265                         }
266                         walk += strlen("signal");
267                 }
268
269                 if (BEGINS_WITH(walk+1, "noise")) {
270                         if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) {
271                                 if (info.noise_level_max)
272                                         printf("%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max));
273                                 else
274                                         printf("%d dBm", info.noise_level);
275                         }
276                         else {
277                                 printf("no value");
278                         }
279                         walk += strlen("noise");
280                 }
281
282                 if (BEGINS_WITH(walk+1, "essid")) {
283                         if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID)
284                                 printf("%s", info.essid);
285                         else
286                                 printf("no value");
287                         walk += strlen("essid");
288                 }
289
290                 if (BEGINS_WITH(walk+1, "ip")) {
291                         const char *ip_address = get_ip_addr(interface);
292                         if (ip_address != NULL)
293                                 (void)printf("%s", get_ip_addr(interface));
294                         else (void)printf("no IP");
295                         walk += strlen("ip");
296                 }
297
298 #ifdef LINUX
299                 if (BEGINS_WITH(walk+1, "bitrate")) {
300                         char buffer[128];
301
302                         iw_print_bitrate(buffer, sizeof(buffer), info.bitrate);
303
304                         printf("%s", buffer);
305                         walk += strlen("bitrate");
306                 }
307 #endif
308         }
309
310         (void)printf("%s", endcolor());
311 }