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$
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#include <net/if_var.h>
42238635Sadrian
43238635Sadrian#include <stdio.h>
44238635Sadrian#include <stdlib.h>
45238635Sadrian#include <stdint.h>
46238635Sadrian#include <signal.h>
47238635Sadrian#include <string.h>
48238635Sadrian#include <unistd.h>
49238635Sadrian#include <err.h>
50238635Sadrian
51240223Sadrian#include <curses.h>
52240223Sadrian
53238635Sadrian#include "ah.h"
54238635Sadrian#include "ah_desc.h"
55238635Sadrian#include "net80211/ieee80211_ioctl.h"
56238635Sadrian#include "net80211/ieee80211_radiotap.h"
57238635Sadrian#include "if_athioctl.h"
58238635Sadrian#include "if_athrate.h"
59238635Sadrian
60238635Sadrian#include "ath_rate/sample/sample.h"
61238635Sadrian
62240223Sadrianstatic int do_loop = 0;
63240223Sadrian
64238646Sadrian/*
65238646Sadrian * This needs to be big enough to fit the two TLVs, the rate table
66238646Sadrian * and the rate statistics table for a single node.
67238646Sadrian */
68238646Sadrian#define	STATS_BUF_SIZE	8192
69238646Sadrian
70240223Sadrian#define	PRINTMSG(...) do {			\
71240223Sadrian	if (do_loop == 0)			\
72240223Sadrian		printf(__VA_ARGS__);		\
73240223Sadrian	else					\
74240223Sadrian		printw(__VA_ARGS__);		\
75240223Sadrian	} while (0)
76240223Sadrian
77240298Sadrian#define	PRINTATTR_ON(_x) do {			\
78240298Sadrian	if (do_loop)				\
79240298Sadrian		attron(_x);			\
80240298Sadrian	} while(0)
81240298Sadrian
82240298Sadrian
83240298Sadrian#define	PRINTATTR_OFF(_x) do {			\
84240298Sadrian	if (do_loop)				\
85240298Sadrian		attroff(_x);			\
86240298Sadrian	} while(0)
87240298Sadrian
88238635Sadrianstruct ath_ratestats {
89238635Sadrian	int s;
90238635Sadrian	struct ath_rateioctl re;
91238635Sadrian};
92238635Sadrian
93238639Sadrianstatic inline int
94238639Sadriandot11rate(struct ath_rateioctl_rt *rt, int rix)
95238639Sadrian{
96238639Sadrian
97238639Sadrian	if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
98238639Sadrian		return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
99238639Sadrian	else
100238639Sadrian		return (rt->ratecode[rix] / 2);
101238639Sadrian}
102238639Sadrian
103238639Sadrianstatic const char *
104238639Sadriandot11str(struct ath_rateioctl_rt *rt, int rix)
105238639Sadrian{
106238639Sadrian	if (rix == -1)
107238639Sadrian		return "";
108238639Sadrian	else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
109238639Sadrian		return "MCS";
110238639Sadrian	else
111238639Sadrian		return " Mb";
112238639Sadrian}
113238639Sadrian
114238635Sadrianstatic void
115238639Sadrianath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
116238639Sadrian    struct sample_node *sn)
117238635Sadrian{
118238635Sadrian	uint32_t mask;
119238635Sadrian	int rix, y;
120238635Sadrian
121244966Sadrian	PRINTMSG("static_rix (%d) ratemask 0x%llx\n",
122238635Sadrian	    sn->static_rix,
123244966Sadrian	    (long long) sn->ratemask);
124238635Sadrian
125238635Sadrian	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
126240298Sadrian		PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD);
127240223Sadrian		PRINTMSG("[%4u] cur rate %d %s since switch: "
128238639Sadrian		    "packets %d ticks %u\n",
129238635Sadrian		    bin_to_size(y),
130238639Sadrian		    dot11rate(rt, sn->current_rix[y]),
131238639Sadrian		    dot11str(rt, sn->current_rix[y]),
132238635Sadrian		    sn->packets_since_switch[y],
133238635Sadrian		    sn->ticks_since_switch[y]);
134238635Sadrian
135240223Sadrian		PRINTMSG("[%4u] last sample (%d %s) cur sample (%d %s) "
136238635Sadrian		    "packets sent %d\n",
137238635Sadrian		    bin_to_size(y),
138238639Sadrian		    dot11rate(rt, sn->last_sample_rix[y]),
139238639Sadrian		    dot11str(rt, sn->last_sample_rix[y]),
140238639Sadrian		    dot11rate(rt, sn->current_sample_rix[y]),
141238639Sadrian		    dot11str(rt, sn->current_sample_rix[y]),
142238635Sadrian		    sn->packets_sent[y]);
143240298Sadrian		PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD);
144240298Sadrian
145240298Sadrian		PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD);
146240223Sadrian		PRINTMSG("[%4u] packets since sample %d sample tt %u\n",
147238635Sadrian		    bin_to_size(y),
148238635Sadrian		    sn->packets_since_sample[y],
149238635Sadrian		    sn->sample_tt[y]);
150240298Sadrian		PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
151240298Sadrian		PRINTMSG("\n");
152238635Sadrian	}
153240223Sadrian	PRINTMSG("   TX Rate     TXTOTAL:TXOK       EWMA          T/   F"
154240184Sadrian	    "     avg last xmit\n");
155238635Sadrian	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
156238635Sadrian		if ((mask & 1) == 0)
157238635Sadrian				continue;
158238635Sadrian		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
159238635Sadrian			if (sn->stats[y][rix].total_packets == 0)
160238635Sadrian				continue;
161240298Sadrian			if (rix == sn->current_rix[y])
162240298Sadrian				PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD);
163240298Sadrian			else if (rix == sn->last_sample_rix[y])
164240298Sadrian				PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD);
165240298Sadrian#if 0
166240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
167240298Sadrian				PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD);
168240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
169240298Sadrian				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
170240298Sadrian#endif
171240223Sadrian			PRINTMSG("[%2u %s:%4u] %8ju:%-8ju "
172240223Sadrian			    "(%3d.%1d%%) %8ju/%4d %5uuS %u\n",
173238639Sadrian			    dot11rate(rt, rix),
174238639Sadrian			    dot11str(rt, rix),
175238635Sadrian			    bin_to_size(y),
176238635Sadrian			    (uintmax_t) sn->stats[y][rix].total_packets,
177238635Sadrian			    (uintmax_t) sn->stats[y][rix].packets_acked,
178238635Sadrian			    sn->stats[y][rix].ewma_pct / 10,
179238635Sadrian			    sn->stats[y][rix].ewma_pct % 10,
180238635Sadrian			    (uintmax_t) sn->stats[y][rix].tries,
181238635Sadrian			    sn->stats[y][rix].successive_failures,
182238635Sadrian			    sn->stats[y][rix].average_tx_time,
183238635Sadrian			    sn->stats[y][rix].last_tx);
184240298Sadrian			if (rix == sn->current_rix[y])
185240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD);
186240298Sadrian			else if (rix == sn->last_sample_rix[y])
187240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
188240298Sadrian#if 0
189240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
190240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD);
191240298Sadrian			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
192240298Sadrian				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
193240298Sadrian#endif
194238635Sadrian		}
195238635Sadrian	}
196238635Sadrian}
197238635Sadrian
198238635Sadrianstatic void
199238635Sadrianath_setifname(struct ath_ratestats *r, const char *ifname)
200238635Sadrian{
201238635Sadrian
202238635Sadrian	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
203238635Sadrian}
204238635Sadrian
205238635Sadrianstatic void
206244967Sadrianath_setsta(struct ath_ratestats *r, uint8_t *mac)
207238635Sadrian{
208238635Sadrian
209238635Sadrian	memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
210238635Sadrian}
211238635Sadrian
212238635Sadrianstatic void
213238635Sadrianath_rate_ioctl(struct ath_ratestats *r)
214238635Sadrian{
215238646Sadrian
216238635Sadrian	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
217238635Sadrian		err(1, "ioctl");
218238635Sadrian}
219238635Sadrian
220238893Sadrianstatic int
221238893Sadrianrate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
222238635Sadrian{
223238635Sadrian	struct ath_rateioctl_tlv *av;
224238639Sadrian	struct sample_node *sn = NULL;
225238639Sadrian	struct ath_rateioctl_rt *rt = NULL;
226238893Sadrian	int error = 0;
227244967Sadrian	uint8_t *buf = (uint8_t *) r->re.buf;
228238893Sadrian
229238893Sadrian	/*
230238893Sadrian	 * For now, hard-code the TLV order and contents.  Ew!
231238893Sadrian	 */
232238893Sadrian	av = (struct ath_rateioctl_tlv *) buf;
233238893Sadrian	if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
234238893Sadrian		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
235238893Sadrian		    "expected 0x%x\n",
236238893Sadrian		    av->tlv_id,
237238893Sadrian		    ATH_RATE_TLV_RATETABLE);
238238893Sadrian		exit(127);
239238893Sadrian	}
240238893Sadrian	if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
241238893Sadrian		fprintf(stderr, "unexpected TLV len (got %d bytes, "
242238893Sadrian		    "expected %d bytes\n",
243238893Sadrian		    av->tlv_len,
244244966Sadrian		    (int) sizeof(struct ath_rateioctl_rt));
245238893Sadrian		exit(127);
246238893Sadrian	}
247238893Sadrian	rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));
248238893Sadrian
249238893Sadrian	/* Next */
250238893Sadrian	av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
251238893Sadrian	    sizeof(struct ath_rateioctl_rt));
252238893Sadrian	if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
253238893Sadrian		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
254238893Sadrian		    "expected 0x%x\n",
255238893Sadrian		    av->tlv_id,
256238893Sadrian		    ATH_RATE_TLV_SAMPLENODE);
257238893Sadrian		exit(127);
258238893Sadrian	}
259238893Sadrian	if (av->tlv_len != sizeof(struct sample_node)) {
260238893Sadrian		fprintf(stderr, "unexpected TLV len (got %d bytes, "
261238893Sadrian		    "expected %d bytes\n",
262238893Sadrian		    av->tlv_len,
263244966Sadrian		    (int) sizeof(struct sample_node));
264238893Sadrian		exit(127);
265238893Sadrian	}
266238893Sadrian	sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
267238893Sadrian	    sizeof(struct ath_rateioctl_rt) +
268238893Sadrian	    sizeof(struct ath_rateioctl_tlv));
269238893Sadrian
270238893Sadrian	ath_sample_stats(r, rt, sn);
271244966Sadrian
272244966Sadrian	return (0);
273238893Sadrian}
274238893Sadrian
275240223Sadrianstatic void
276240223Sadrianfetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e,
277240223Sadrian    uint8_t *buf)
278240223Sadrian{
279238893Sadrian
280240223Sadrian	/* Zero the buffer before it's passed in */
281240223Sadrian	memset(buf, '\0', STATS_BUF_SIZE);
282240223Sadrian
283240223Sadrian	/*
284240223Sadrian	 * Set the station address for this lookup.
285240223Sadrian	 */
286240223Sadrian	ath_setsta(r, e->octet);
287240223Sadrian
288240223Sadrian	/*
289240223Sadrian	 * Fetch the data from the driver.
290240223Sadrian	 */
291240223Sadrian	ath_rate_ioctl(r);
292240223Sadrian
293240223Sadrian	/*
294240223Sadrian	 * Decode and parse statistics.
295240223Sadrian	 */
296240223Sadrian	rate_node_stats(r, e);
297240223Sadrian}
298240223Sadrian
299238893Sadrianint
300238893Sadrianmain(int argc, char *argv[])
301238893Sadrian{
302238646Sadrian	char const *ifname = NULL, *macaddr = NULL;
303238646Sadrian	int c;
304238646Sadrian	int do_all = 0;
305238893Sadrian	struct ether_addr *e;
306238893Sadrian	struct ath_ratestats r;
307238893Sadrian	uint8_t *buf;
308240223Sadrian	useconds_t sleep_period;
309240223Sadrian	float f;
310240298Sadrian	short cf, cb;
311238635Sadrian
312238646Sadrian	ifname = getenv("ATH");
313238646Sadrian	if (ifname == NULL)
314238646Sadrian		ifname = "ath0";
315238646Sadrian
316240223Sadrian	while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) {
317238646Sadrian		switch (c) {
318238646Sadrian		case 'a':
319238646Sadrian			do_all = 1;
320238646Sadrian			break;
321238646Sadrian		case 'i':
322238646Sadrian			ifname = optarg;
323238646Sadrian			break;
324238646Sadrian		case 'm':
325238646Sadrian			macaddr = optarg;
326238646Sadrian			break;
327240223Sadrian		case 's':
328240223Sadrian			sscanf(optarg, "%f", &f);
329240223Sadrian			do_loop = 1;
330240223Sadrian			sleep_period = (useconds_t) (f * 1000000.0);
331240223Sadrian			break;
332238646Sadrian		default:
333238646Sadrian			errx(1,
334240223Sadrian			    "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n",
335238646Sadrian			    argv[0]);
336238646Sadrian			/* NOTREACHED */
337238646Sadrian		}
338238646Sadrian	}
339238646Sadrian
340238646Sadrian	if (macaddr == NULL) {
341238646Sadrian		errx(1, "%s: macaddress wasn't supplied and no -a given\n",
342238646Sadrian		    argv[0]);
343238646Sadrian		/* NOTREACHED */
344238646Sadrian	}
345238646Sadrian	e = ether_aton(macaddr);
346238646Sadrian	if (e == NULL)
347238646Sadrian		err(1, "ether_aton");
348238635Sadrian
349238893Sadrian	bzero(&r, sizeof(r));
350238893Sadrian
351238893Sadrian	/*
352238893Sadrian	 * Persistent buffer for each lookup
353238893Sadrian	 */
354238893Sadrian	buf = malloc(STATS_BUF_SIZE);
355238893Sadrian	if (buf == NULL)
356238893Sadrian		err(1, "calloc");
357238893Sadrian
358244967Sadrian	r.re.buf = (char *) buf;
359238635Sadrian	r.re.len = STATS_BUF_SIZE;
360238635Sadrian
361238893Sadrian	r.s = socket(AF_INET, SOCK_DGRAM, 0);
362238893Sadrian	if (r.s < 0) {
363238893Sadrian		err(1, "socket");
364238893Sadrian	}
365240223Sadrian
366238893Sadrian	/* XXX error check */
367238893Sadrian	ath_setifname(&r, ifname);
368238893Sadrian
369240223Sadrian	if (do_loop) {
370240223Sadrian		initscr();
371240223Sadrian		start_color();
372240223Sadrian		use_default_colors();
373240298Sadrian		pair_content(0, &cf, &cb);
374240298Sadrian		/* Error - medium */
375240298Sadrian		init_pair(1, COLOR_YELLOW, cb);
376240298Sadrian		/* Error - high */
377240298Sadrian		init_pair(2, COLOR_RED, cb);
378240298Sadrian		/* Sample */
379240298Sadrian		init_pair(3, COLOR_CYAN, cb);
380240298Sadrian		/* 250 byte frames */
381240298Sadrian		init_pair(4, COLOR_BLUE, cb);
382240298Sadrian		/* 1600 byte frames */
383240298Sadrian		init_pair(5, COLOR_MAGENTA, cb);
384240223Sadrian		cbreak();
385240223Sadrian		noecho();
386240223Sadrian		nonl();
387240223Sadrian		nodelay(stdscr, 1);
388240223Sadrian		intrflush(stdscr, FALSE);
389240223Sadrian		keypad(stdscr, TRUE);
390238893Sadrian
391240223Sadrian		while (1) {
392240223Sadrian			clear();
393240223Sadrian			move(0, 0);
394240223Sadrian			fetch_and_print_stats(&r, e, buf);
395240223Sadrian			refresh();
396240223Sadrian			usleep(sleep_period);
397240223Sadrian		}
398240223Sadrian	} else
399240223Sadrian		fetch_and_print_stats(&r, e, buf);
400238893Sadrian
401240223Sadrian	exit(0);
402238635Sadrian}
403