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