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