xref: /freebsd/tools/tools/ath/athratestats/main.c (revision 61e21613)
1 /*-
2  * Copyright (c) 2012, Adrian Chadd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include "opt_ah.h"
31 
32 #include <sys/types.h>
33 #include <sys/file.h>
34 #include <sys/sockio.h>
35 #include <sys/socket.h>
36 #include <net/ethernet.h>
37 #include <net/if.h>
38 #include <net/if_media.h>
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <stdint.h>
43 #include <signal.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <err.h>
47 
48 #include <curses.h>
49 
50 #include "ah.h"
51 #include "ah_desc.h"
52 #include "net80211/ieee80211_ioctl.h"
53 #include "net80211/ieee80211_radiotap.h"
54 #include "if_athioctl.h"
55 #include "if_athrate.h"
56 
57 #include "ath_rate/sample/sample.h"
58 
59 static int do_loop = 0;
60 
61 /*
62  * This needs to be big enough to fit the two TLVs, the rate table
63  * and the rate statistics table for a single node.
64  */
65 #define	STATS_BUF_SIZE	65536
66 
67 #define	PRINTMSG(...) do {			\
68 	if (do_loop == 0)			\
69 		printf(__VA_ARGS__);		\
70 	else					\
71 		printw(__VA_ARGS__);		\
72 	} while (0)
73 
74 #define	PRINTATTR_ON(_x) do {			\
75 	if (do_loop)				\
76 		attron(_x);			\
77 	} while(0)
78 
79 
80 #define	PRINTATTR_OFF(_x) do {			\
81 	if (do_loop)				\
82 		attroff(_x);			\
83 	} while(0)
84 
85 struct ath_ratestats {
86 	int s;
87 	struct ath_rateioctl re;
88 };
89 
90 static inline int
91 dot11rate(struct ath_rateioctl_rt *rt, int rix)
92 {
93 
94 	if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
95 		return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
96 	else
97 		return (rt->ratecode[rix] / 2);
98 }
99 
100 static const char *
101 dot11str(struct ath_rateioctl_rt *rt, int rix)
102 {
103 	if (rix == -1)
104 		return "";
105 	else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
106 		return "MCS";
107 	else
108 		return " Mb";
109 }
110 
111 static void
112 ath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
113     struct sample_node *sn)
114 {
115 	uint64_t mask;
116 	int rix, y;
117 
118 	PRINTMSG("static_rix (%d) ratemask 0x%llx\n",
119 	    sn->static_rix,
120 	    (long long) sn->ratemask);
121 
122 	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
123 		PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
124 		PRINTMSG("[%4u] cur rate %d %s since switch: "
125 		    "packets %d ticks %u ",
126 		    bin_to_size(y),
127 		    dot11rate(rt, sn->current_rix[y]),
128 		    dot11str(rt, sn->current_rix[y]),
129 		    sn->packets_since_switch[y],
130 		    sn->ticks_since_switch[y]);
131 
132 		PRINTMSG("last sample (%d %s) cur sample (%d %s) "
133 		    "packets sent %d ",
134 		    dot11rate(rt, sn->last_sample_rix[y]),
135 		    dot11str(rt, sn->last_sample_rix[y]),
136 		    dot11rate(rt, sn->current_sample_rix[y]),
137 		    dot11str(rt, sn->current_sample_rix[y]),
138 		    sn->packets_sent[y]);
139 		PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
140 
141 		PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
142 		PRINTMSG("packets since sample %d sample tt %u\n",
143 		    sn->packets_since_sample[y],
144 		    sn->sample_tt[y]);
145 		PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
146 	}
147 	PRINTMSG("   TX Rate      TXTOTAL:TXOK       EWMA          T/   F"
148 	    "     avg last xmit  ");
149 	PRINTMSG("   TX Rate      TXTOTAL:TXOK       EWMA          T/   F"
150 	    "     avg last xmit\n");
151 	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
152 		int c = 0;
153 		if ((mask & 1) == 0)
154 				continue;
155 		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
156 			if (sn->stats[y][rix].total_packets == 0)
157 				continue;
158 			if (rix == sn->current_rix[y])
159 				PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
160 			else if (rix == sn->last_sample_rix[y])
161 				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
162 #if 0
163 			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
164 				PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD);
165 			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
166 				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
167 #endif
168 			PRINTMSG("[%2u %s:%5u] %8ju:%-8ju "
169 			    "(%3d.%1d%%) %8ju/%4d %5uuS %u ",
170 			    dot11rate(rt, rix),
171 			    dot11str(rt, rix),
172 			    bin_to_size(y),
173 			    (uintmax_t) sn->stats[y][rix].total_packets,
174 			    (uintmax_t) sn->stats[y][rix].packets_acked,
175 			    sn->stats[y][rix].ewma_pct / 10,
176 			    sn->stats[y][rix].ewma_pct % 10,
177 			    (uintmax_t) sn->stats[y][rix].tries,
178 			    sn->stats[y][rix].successive_failures,
179 			    sn->stats[y][rix].average_tx_time,
180 			    sn->stats[y][rix].last_tx);
181 			if (rix == sn->current_rix[y])
182 				PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
183 			else if (rix == sn->last_sample_rix[y])
184 				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
185 #if 0
186 			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
187 				PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD);
188 			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
189 				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
190 #endif
191 			c++;
192 			if (c == 2) {
193 				PRINTMSG("\n");
194 				c = 0;
195 			}
196 		}
197 		if (c != 0)
198 			PRINTMSG("\n");
199 	}
200 }
201 
202 static void
203 ath_setifname(struct ath_ratestats *r, const char *ifname)
204 {
205 
206 	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
207 }
208 
209 static void
210 ath_setsta(struct ath_ratestats *r, uint8_t *mac)
211 {
212 
213 	memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
214 }
215 
216 static void
217 ath_rate_ioctl(struct ath_ratestats *r)
218 {
219 
220 	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
221 		err(1, "ioctl");
222 }
223 
224 static int
225 rate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
226 {
227 	struct ath_rateioctl_tlv *av;
228 	struct sample_node *sn = NULL;
229 	struct ath_rateioctl_rt *rt = NULL;
230 	int error = 0;
231 	uint8_t *buf = (uint8_t *) r->re.buf;
232 
233 	/*
234 	 * For now, hard-code the TLV order and contents.  Ew!
235 	 */
236 	av = (struct ath_rateioctl_tlv *) buf;
237 	if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
238 		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
239 		    "expected 0x%x\n",
240 		    av->tlv_id,
241 		    ATH_RATE_TLV_RATETABLE);
242 		exit(127);
243 	}
244 	if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
245 		fprintf(stderr, "unexpected TLV len (got %d bytes, "
246 		    "expected %d bytes\n",
247 		    av->tlv_len,
248 		    (int) sizeof(struct ath_rateioctl_rt));
249 		exit(127);
250 	}
251 	rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));
252 
253 	/* Next */
254 	av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
255 	    sizeof(struct ath_rateioctl_rt));
256 	if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
257 		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
258 		    "expected 0x%x\n",
259 		    av->tlv_id,
260 		    ATH_RATE_TLV_SAMPLENODE);
261 		exit(127);
262 	}
263 	if (av->tlv_len != sizeof(struct sample_node)) {
264 		fprintf(stderr, "unexpected TLV len (got %d bytes, "
265 		    "expected %d bytes\n",
266 		    av->tlv_len,
267 		    (int) sizeof(struct sample_node));
268 		exit(127);
269 	}
270 	sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
271 	    sizeof(struct ath_rateioctl_rt) +
272 	    sizeof(struct ath_rateioctl_tlv));
273 
274 	ath_sample_stats(r, rt, sn);
275 
276 	return (0);
277 }
278 
279 static void
280 fetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e,
281     uint8_t *buf)
282 {
283 
284 	/* Zero the buffer before it's passed in */
285 	memset(buf, '\0', STATS_BUF_SIZE);
286 
287 	/*
288 	 * Set the station address for this lookup.
289 	 */
290 	ath_setsta(r, e->octet);
291 
292 	/*
293 	 * Fetch the data from the driver.
294 	 */
295 	ath_rate_ioctl(r);
296 
297 	/*
298 	 * Decode and parse statistics.
299 	 */
300 	rate_node_stats(r, e);
301 }
302 
303 int
304 main(int argc, char *argv[])
305 {
306 	char const *ifname = NULL, *macaddr = NULL;
307 	int c;
308 	int do_all = 0;
309 	struct ether_addr *e;
310 	struct ath_ratestats r;
311 	uint8_t *buf;
312 	useconds_t sleep_period;
313 	float f;
314 	short cf, cb;
315 
316 	ifname = getenv("ATH");
317 	if (ifname == NULL)
318 		ifname = ATH_DEFAULT;
319 
320 	while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) {
321 		switch (c) {
322 		case 'a':
323 			do_all = 1;
324 			break;
325 		case 'i':
326 			ifname = optarg;
327 			break;
328 		case 'm':
329 			macaddr = optarg;
330 			break;
331 		case 's':
332 			sscanf(optarg, "%f", &f);
333 			do_loop = 1;
334 			sleep_period = (useconds_t) (f * 1000000.0);
335 			break;
336 		default:
337 			errx(1,
338 			    "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n",
339 			    argv[0]);
340 			/* NOTREACHED */
341 		}
342 	}
343 
344 	if (macaddr == NULL) {
345 		errx(1, "%s: macaddress wasn't supplied and no -a given\n",
346 		    argv[0]);
347 		/* NOTREACHED */
348 	}
349 	e = ether_aton(macaddr);
350 	if (e == NULL)
351 		err(1, "ether_aton");
352 
353 	bzero(&r, sizeof(r));
354 
355 	/*
356 	 * Persistent buffer for each lookup
357 	 */
358 	buf = malloc(STATS_BUF_SIZE);
359 	if (buf == NULL)
360 		err(1, "calloc");
361 
362 	r.re.buf = (char *) buf;
363 	r.re.len = STATS_BUF_SIZE;
364 
365 	r.s = socket(AF_INET, SOCK_DGRAM, 0);
366 	if (r.s < 0) {
367 		err(1, "socket");
368 	}
369 
370 	/* XXX error check */
371 	ath_setifname(&r, ifname);
372 
373 	if (do_loop) {
374 		initscr();
375 		start_color();
376 		use_default_colors();
377 		pair_content(0, &cf, &cb);
378 		/* Error - medium */
379 		init_pair(1, COLOR_YELLOW, cb);
380 		/* Error - high */
381 		init_pair(2, COLOR_RED, cb);
382 		/* Sample */
383 		init_pair(3, COLOR_CYAN, cb);
384 		/* 250 byte frames */
385 		init_pair(4, COLOR_BLUE, cb);
386 		/* 1600 byte frames */
387 		init_pair(5, COLOR_MAGENTA, cb);
388 		cbreak();
389 		noecho();
390 		nonl();
391 		nodelay(stdscr, 1);
392 		intrflush(stdscr, FALSE);
393 		keypad(stdscr, TRUE);
394 
395 		while (1) {
396 			clear();
397 			move(0, 0);
398 			fetch_and_print_stats(&r, e, buf);
399 			refresh();
400 			usleep(sleep_period);
401 		}
402 	} else
403 		fetch_and_print_stats(&r, e, buf);
404 
405 	exit(0);
406 }
407