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
59static 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
85struct ath_ratestats {
86	int s;
87	struct ath_rateioctl re;
88};
89
90static inline int
91dot11rate(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
100static const char *
101dot11str(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
111static void
112ath_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
202static void
203ath_setifname(struct ath_ratestats *r, const char *ifname)
204{
205
206	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
207}
208
209static void
210ath_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
216static void
217ath_rate_ioctl(struct ath_ratestats *r)
218{
219
220	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
221		err(1, "ioctl");
222}
223
224static int
225rate_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
279static void
280fetch_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
303int
304main(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