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
62static 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
88struct ath_ratestats {
89	int s;
90	struct ath_rateioctl re;
91};
92
93static inline int
94dot11rate(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
103static const char *
104dot11str(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
114static void
115ath_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
198static void
199ath_setifname(struct ath_ratestats *r, const char *ifname)
200{
201
202	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
203}
204
205static void
206ath_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
212static void
213ath_rate_ioctl(struct ath_ratestats *r)
214{
215
216	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
217		err(1, "ioctl");
218}
219
220static int
221rate_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
275static void
276fetch_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
299int
300main(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