1238635Sadrian/*-
2238635Sadrian * Copyright (c) 2012, Adrian Chadd.
3238635Sadrian * All rights reserved.
4238635Sadrian *
5238635Sadrian * Redistribution and use in source and binary forms, with or without
6238635Sadrian * modification, are permitted provided that the following conditions
7238635Sadrian * are met:
8238635Sadrian * 1. Redistributions of source code must retain the above copyright
9238635Sadrian *    notice, this list of conditions and the following disclaimer,
10238635Sadrian *    without modification.
11238635Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12238635Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13238635Sadrian *    redistribution must be conditioned upon including a substantially
14238635Sadrian *    similar Disclaimer requirement for further binary redistribution.
15238635Sadrian *
16238635Sadrian * NO WARRANTY
17238635Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18238635Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19238635Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20238635Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21238635Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22238635Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23238635Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24238635Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25238635Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26238635Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27238635Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28238635Sadrian *
29238635Sadrian * $FreeBSD: releng/11.0/tools/tools/ath/athratestats/main.c 295363 2016-02-07 04:28:03Z adrian $
30238635Sadrian */
31238635Sadrian
32238635Sadrian#include "opt_ah.h"
33238635Sadrian
34238635Sadrian#include <sys/types.h>
35238635Sadrian#include <sys/file.h>
36238635Sadrian#include <sys/sockio.h>
37238635Sadrian#include <sys/socket.h>
38238635Sadrian#include <net/ethernet.h>
39238635Sadrian#include <net/if.h>
40238635Sadrian#include <net/if_media.h>
41238635Sadrian
42238635Sadrian#include <stdio.h>
43238635Sadrian#include <stdlib.h>
44238635Sadrian#include <stdint.h>
45238635Sadrian#include <signal.h>
46238635Sadrian#include <string.h>
47238635Sadrian#include <unistd.h>
48238635Sadrian#include <err.h>
49238635Sadrian
50240223Sadrian#include <curses.h>
51240223Sadrian
52238635Sadrian#include "ah.h"
53238635Sadrian#include "ah_desc.h"
54238635Sadrian#include "net80211/ieee80211_ioctl.h"
55238635Sadrian#include "net80211/ieee80211_radiotap.h"
56238635Sadrian#include "if_athioctl.h"
57238635Sadrian#include "if_athrate.h"
58238635Sadrian
59238635Sadrian#include "ath_rate/sample/sample.h"
60238635Sadrian
61240223Sadrianstatic int do_loop = 0;
62240223Sadrian
63238646Sadrian/*
64238646Sadrian * This needs to be big enough to fit the two TLVs, the rate table
65238646Sadrian * and the rate statistics table for a single node.
66238646Sadrian */
67238646Sadrian#define	STATS_BUF_SIZE	8192
68238646Sadrian
69240223Sadrian#define	PRINTMSG(...) do {			\
70240223Sadrian	if (do_loop == 0)			\
71240223Sadrian		printf(__VA_ARGS__);		\
72240223Sadrian	else					\
73240223Sadrian		printw(__VA_ARGS__);		\
74240223Sadrian	} while (0)
75240223Sadrian
76240298Sadrian#define	PRINTATTR_ON(_x) do {			\
77240298Sadrian	if (do_loop)				\
78240298Sadrian		attron(_x);			\
79240298Sadrian	} while(0)
80240298Sadrian
81240298Sadrian
82240298Sadrian#define	PRINTATTR_OFF(_x) do {			\
83240298Sadrian	if (do_loop)				\
84240298Sadrian		attroff(_x);			\
85240298Sadrian	} while(0)
86240298Sadrian
87238635Sadrianstruct ath_ratestats {
88238635Sadrian	int s;
89238635Sadrian	struct ath_rateioctl re;
90238635Sadrian};
91238635Sadrian
92238639Sadrianstatic inline int
93238639Sadriandot11rate(struct ath_rateioctl_rt *rt, int rix)
94238639Sadrian{
95238639Sadrian
96238639Sadrian	if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
97238639Sadrian		return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
98238639Sadrian	else
99238639Sadrian		return (rt->ratecode[rix] / 2);
100238639Sadrian}
101238639Sadrian
102238639Sadrianstatic const char *
103238639Sadriandot11str(struct ath_rateioctl_rt *rt, int rix)
104238639Sadrian{
105238639Sadrian	if (rix == -1)
106238639Sadrian		return "";
107238639Sadrian	else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
108238639Sadrian		return "MCS";
109238639Sadrian	else
110238639Sadrian		return " Mb";
111238639Sadrian}
112238639Sadrian
113238635Sadrianstatic void
114238639Sadrianath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
115238639Sadrian    struct sample_node *sn)
116238635Sadrian{
117280361Sadrian	uint64_t mask;
118238635Sadrian	int rix, y;
119238635Sadrian
120244966Sadrian	PRINTMSG("static_rix (%d) ratemask 0x%llx\n",
121238635Sadrian	    sn->static_rix,
122244966Sadrian	    (long long) sn->ratemask);
123238635Sadrian
124238635Sadrian	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
125240298Sadrian		PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD);
126240223Sadrian		PRINTMSG("[%4u] cur rate %d %s since switch: "
127238639Sadrian		    "packets %d ticks %u\n",
128238635Sadrian		    bin_to_size(y),
129238639Sadrian		    dot11rate(rt, sn->current_rix[y]),
130238639Sadrian		    dot11str(rt, sn->current_rix[y]),
131238635Sadrian		    sn->packets_since_switch[y],
132238635Sadrian		    sn->ticks_since_switch[y]);
133238635Sadrian
134240223Sadrian		PRINTMSG("[%4u] last sample (%d %s) cur sample (%d %s) "
135238635Sadrian		    "packets sent %d\n",
136238635Sadrian		    bin_to_size(y),
137238639Sadrian		    dot11rate(rt, sn->last_sample_rix[y]),
138238639Sadrian		    dot11str(rt, sn->last_sample_rix[y]),
139238639Sadrian		    dot11rate(rt, sn->current_sample_rix[y]),
140238639Sadrian		    dot11str(rt, sn->current_sample_rix[y]),
141238635Sadrian		    sn->packets_sent[y]);
142240298Sadrian		PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD);
143240298Sadrian
144240298Sadrian		PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD);
145240223Sadrian		PRINTMSG("[%4u] packets since sample %d sample tt %u\n",
146238635Sadrian		    bin_to_size(y),
147238635Sadrian		    sn->packets_since_sample[y],
148238635Sadrian		    sn->sample_tt[y]);
149240298Sadrian		PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
150240298Sadrian		PRINTMSG("\n");
151238635Sadrian	}
152240223Sadrian	PRINTMSG("   TX Rate     TXTOTAL:TXOK       EWMA          T/   F"
153240184Sadrian	    "     avg last xmit\n");
154238635Sadrian	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
155238635Sadrian		if ((mask & 1) == 0)
156238635Sadrian				continue;
157238635Sadrian		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
158238635Sadrian			if (sn->stats[y][rix].total_packets == 0)
159238635Sadrian				continue;
160240298Sadrian			if (rix == sn->current_rix[y])
161240298Sadrian				PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD);
162240298Sadrian			else if (rix == sn->last_sample_rix[y])
163240298Sadrian				PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD);
164240298Sadrian#if 0
165240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
166240298Sadrian				PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD);
167240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
168240298Sadrian				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
169240298Sadrian#endif
170240223Sadrian			PRINTMSG("[%2u %s:%4u] %8ju:%-8ju "
171240223Sadrian			    "(%3d.%1d%%) %8ju/%4d %5uuS %u\n",
172238639Sadrian			    dot11rate(rt, rix),
173238639Sadrian			    dot11str(rt, rix),
174238635Sadrian			    bin_to_size(y),
175238635Sadrian			    (uintmax_t) sn->stats[y][rix].total_packets,
176238635Sadrian			    (uintmax_t) sn->stats[y][rix].packets_acked,
177238635Sadrian			    sn->stats[y][rix].ewma_pct / 10,
178238635Sadrian			    sn->stats[y][rix].ewma_pct % 10,
179238635Sadrian			    (uintmax_t) sn->stats[y][rix].tries,
180238635Sadrian			    sn->stats[y][rix].successive_failures,
181238635Sadrian			    sn->stats[y][rix].average_tx_time,
182238635Sadrian			    sn->stats[y][rix].last_tx);
183240298Sadrian			if (rix == sn->current_rix[y])
184240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD);
185240298Sadrian			else if (rix == sn->last_sample_rix[y])
186240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
187240298Sadrian#if 0
188240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
189240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD);
190240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
191240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
192240298Sadrian#endif
193238635Sadrian		}
194238635Sadrian	}
195238635Sadrian}
196238635Sadrian
197238635Sadrianstatic void
198238635Sadrianath_setifname(struct ath_ratestats *r, const char *ifname)
199238635Sadrian{
200238635Sadrian
201238635Sadrian	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
202238635Sadrian}
203238635Sadrian
204238635Sadrianstatic void
205244967Sadrianath_setsta(struct ath_ratestats *r, uint8_t *mac)
206238635Sadrian{
207238635Sadrian
208238635Sadrian	memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
209238635Sadrian}
210238635Sadrian
211238635Sadrianstatic void
212238635Sadrianath_rate_ioctl(struct ath_ratestats *r)
213238635Sadrian{
214238646Sadrian
215238635Sadrian	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
216238635Sadrian		err(1, "ioctl");
217238635Sadrian}
218238635Sadrian
219238893Sadrianstatic int
220238893Sadrianrate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
221238635Sadrian{
222238635Sadrian	struct ath_rateioctl_tlv *av;
223238639Sadrian	struct sample_node *sn = NULL;
224238639Sadrian	struct ath_rateioctl_rt *rt = NULL;
225238893Sadrian	int error = 0;
226244967Sadrian	uint8_t *buf = (uint8_t *) r->re.buf;
227238893Sadrian
228238893Sadrian	/*
229238893Sadrian	 * For now, hard-code the TLV order and contents.  Ew!
230238893Sadrian	 */
231238893Sadrian	av = (struct ath_rateioctl_tlv *) buf;
232238893Sadrian	if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
233238893Sadrian		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
234238893Sadrian		    "expected 0x%x\n",
235238893Sadrian		    av->tlv_id,
236238893Sadrian		    ATH_RATE_TLV_RATETABLE);
237238893Sadrian		exit(127);
238238893Sadrian	}
239238893Sadrian	if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
240238893Sadrian		fprintf(stderr, "unexpected TLV len (got %d bytes, "
241238893Sadrian		    "expected %d bytes\n",
242238893Sadrian		    av->tlv_len,
243244966Sadrian		    (int) sizeof(struct ath_rateioctl_rt));
244238893Sadrian		exit(127);
245238893Sadrian	}
246238893Sadrian	rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));
247238893Sadrian
248238893Sadrian	/* Next */
249238893Sadrian	av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
250238893Sadrian	    sizeof(struct ath_rateioctl_rt));
251238893Sadrian	if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
252238893Sadrian		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
253238893Sadrian		    "expected 0x%x\n",
254238893Sadrian		    av->tlv_id,
255238893Sadrian		    ATH_RATE_TLV_SAMPLENODE);
256238893Sadrian		exit(127);
257238893Sadrian	}
258238893Sadrian	if (av->tlv_len != sizeof(struct sample_node)) {
259238893Sadrian		fprintf(stderr, "unexpected TLV len (got %d bytes, "
260238893Sadrian		    "expected %d bytes\n",
261238893Sadrian		    av->tlv_len,
262244966Sadrian		    (int) sizeof(struct sample_node));
263238893Sadrian		exit(127);
264238893Sadrian	}
265238893Sadrian	sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
266238893Sadrian	    sizeof(struct ath_rateioctl_rt) +
267238893Sadrian	    sizeof(struct ath_rateioctl_tlv));
268238893Sadrian
269238893Sadrian	ath_sample_stats(r, rt, sn);
270244966Sadrian
271244966Sadrian	return (0);
272238893Sadrian}
273238893Sadrian
274240223Sadrianstatic void
275240223Sadrianfetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e,
276240223Sadrian    uint8_t *buf)
277240223Sadrian{
278238893Sadrian
279240223Sadrian	/* Zero the buffer before it's passed in */
280240223Sadrian	memset(buf, '\0', STATS_BUF_SIZE);
281240223Sadrian
282240223Sadrian	/*
283240223Sadrian	 * Set the station address for this lookup.
284240223Sadrian	 */
285240223Sadrian	ath_setsta(r, e->octet);
286240223Sadrian
287240223Sadrian	/*
288240223Sadrian	 * Fetch the data from the driver.
289240223Sadrian	 */
290240223Sadrian	ath_rate_ioctl(r);
291240223Sadrian
292240223Sadrian	/*
293240223Sadrian	 * Decode and parse statistics.
294240223Sadrian	 */
295240223Sadrian	rate_node_stats(r, e);
296240223Sadrian}
297240223Sadrian
298238893Sadrianint
299238893Sadrianmain(int argc, char *argv[])
300238893Sadrian{
301238646Sadrian	char const *ifname = NULL, *macaddr = NULL;
302238646Sadrian	int c;
303238646Sadrian	int do_all = 0;
304238893Sadrian	struct ether_addr *e;
305238893Sadrian	struct ath_ratestats r;
306238893Sadrian	uint8_t *buf;
307240223Sadrian	useconds_t sleep_period;
308240223Sadrian	float f;
309240298Sadrian	short cf, cb;
310238635Sadrian
311238646Sadrian	ifname = getenv("ATH");
312238646Sadrian	if (ifname == NULL)
313295363Sadrian		ifname = ATH_DEFAULT;
314238646Sadrian
315240223Sadrian	while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) {
316238646Sadrian		switch (c) {
317238646Sadrian		case 'a':
318238646Sadrian			do_all = 1;
319238646Sadrian			break;
320238646Sadrian		case 'i':
321238646Sadrian			ifname = optarg;
322238646Sadrian			break;
323238646Sadrian		case 'm':
324238646Sadrian			macaddr = optarg;
325238646Sadrian			break;
326240223Sadrian		case 's':
327240223Sadrian			sscanf(optarg, "%f", &f);
328240223Sadrian			do_loop = 1;
329240223Sadrian			sleep_period = (useconds_t) (f * 1000000.0);
330240223Sadrian			break;
331238646Sadrian		default:
332238646Sadrian			errx(1,
333240223Sadrian			    "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n",
334238646Sadrian			    argv[0]);
335238646Sadrian			/* NOTREACHED */
336238646Sadrian		}
337238646Sadrian	}
338238646Sadrian
339238646Sadrian	if (macaddr == NULL) {
340238646Sadrian		errx(1, "%s: macaddress wasn't supplied and no -a given\n",
341238646Sadrian		    argv[0]);
342238646Sadrian		/* NOTREACHED */
343238646Sadrian	}
344238646Sadrian	e = ether_aton(macaddr);
345238646Sadrian	if (e == NULL)
346238646Sadrian		err(1, "ether_aton");
347238635Sadrian
348238893Sadrian	bzero(&r, sizeof(r));
349238893Sadrian
350238893Sadrian	/*
351238893Sadrian	 * Persistent buffer for each lookup
352238893Sadrian	 */
353238893Sadrian	buf = malloc(STATS_BUF_SIZE);
354238893Sadrian	if (buf == NULL)
355238893Sadrian		err(1, "calloc");
356238893Sadrian
357244967Sadrian	r.re.buf = (char *) buf;
358238635Sadrian	r.re.len = STATS_BUF_SIZE;
359238635Sadrian
360238893Sadrian	r.s = socket(AF_INET, SOCK_DGRAM, 0);
361238893Sadrian	if (r.s < 0) {
362238893Sadrian		err(1, "socket");
363238893Sadrian	}
364240223Sadrian
365238893Sadrian	/* XXX error check */
366238893Sadrian	ath_setifname(&r, ifname);
367238893Sadrian
368240223Sadrian	if (do_loop) {
369240223Sadrian		initscr();
370240223Sadrian		start_color();
371240223Sadrian		use_default_colors();
372240298Sadrian		pair_content(0, &cf, &cb);
373240298Sadrian		/* Error - medium */
374240298Sadrian		init_pair(1, COLOR_YELLOW, cb);
375240298Sadrian		/* Error - high */
376240298Sadrian		init_pair(2, COLOR_RED, cb);
377240298Sadrian		/* Sample */
378240298Sadrian		init_pair(3, COLOR_CYAN, cb);
379240298Sadrian		/* 250 byte frames */
380240298Sadrian		init_pair(4, COLOR_BLUE, cb);
381240298Sadrian		/* 1600 byte frames */
382240298Sadrian		init_pair(5, COLOR_MAGENTA, cb);
383240223Sadrian		cbreak();
384240223Sadrian		noecho();
385240223Sadrian		nonl();
386240223Sadrian		nodelay(stdscr, 1);
387240223Sadrian		intrflush(stdscr, FALSE);
388240223Sadrian		keypad(stdscr, TRUE);
389238893Sadrian
390240223Sadrian		while (1) {
391240223Sadrian			clear();
392240223Sadrian			move(0, 0);
393240223Sadrian			fetch_and_print_stats(&r, e, buf);
394240223Sadrian			refresh();
395240223Sadrian			usleep(sleep_period);
396240223Sadrian		}
397240223Sadrian	} else
398240223Sadrian		fetch_and_print_stats(&r, e, buf);
399238893Sadrian
400240223Sadrian	exit(0);
401238635Sadrian}
402