1262423Sadrian/*-
2262423Sadrian * Copyright (c) 2014 Adrian Chadd <adrian@FreeBSD.org>
3262423Sadrian * All rights reserved.
4262423Sadrian *
5262423Sadrian * Redistribution and use in source and binary forms, with or without
6262423Sadrian * modification, are permitted provided that the following conditions
7262423Sadrian * are met:
8262423Sadrian * 1. Redistributions of source code must retain the above copyright
9262423Sadrian *    notice, this list of conditions and the following disclaimer,
10262423Sadrian *    without modification.
11262423Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12262423Sadrian *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13262423Sadrian *    redistribution must be conditioned upon including a substantially
14262423Sadrian *    similar Disclaimer requirement for further binary redistribution.
15262423Sadrian *
16262423Sadrian * NO WARRANTY
17262423Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18262423Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19262423Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20262423Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21262423Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22262423Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23262423Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24262423Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25262423Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26262423Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27262423Sadrian * THE POSSIBILITY OF SUCH DAMAGES.
28262423Sadrian *
29262423Sadrian * $FreeBSD$
30262423Sadrian */
31262423Sadrian
32262766Seadler#include <stdbool.h>
33262423Sadrian#include <stdio.h>
34262423Sadrian#include <stdlib.h>
35262423Sadrian#include <signal.h>
36287313Sadrian#include <fcntl.h>
37262423Sadrian#include <unistd.h>
38262423Sadrian#include <string.h>
39262423Sadrian#include <err.h>
40262423Sadrian#include <net/if.h>
41262423Sadrian#include <sys/endian.h>
42263093Seadler#include <sys/time.h>
43262893Seadler#include <sys/types.h>
44262893Seadler#include <sys/sysctl.h>
45262423Sadrian
46262423Sadrian#include "net80211/ieee80211_ioctl.h"
47262423Sadrian#include "net80211/ieee80211_radiotap.h"
48262423Sadrian
49262423Sadrian#include "if_iwn_ioctl.h"
50262423Sadrian#include "if_iwnreg.h"
51262423Sadrian#include "iwnstats.h"
52262423Sadrian#include "iwn_ioctl.h"
53262423Sadrian
54287313Sadrian#define	IWN_DEFAULT_IF		"iwn0"
55262423Sadrian
56262768Seadlerstatic struct iwnstats *
57262423Sadrianiwnstats_new(const char *ifname)
58262423Sadrian{
59262423Sadrian	struct iwnstats *is;
60287313Sadrian	char buf[128];
61262423Sadrian
62262423Sadrian	is = calloc(1, sizeof(struct iwnstats));
63262423Sadrian	if (is == NULL)
64262423Sadrian		return (NULL);
65262423Sadrian
66287313Sadrian	snprintf(buf, sizeof(buf), "/dev/%s", ifname);
67287313Sadrian	is->s = open(buf, O_RDWR);
68262423Sadrian	if (is->s < 0)
69287313Sadrian		err(1, "open");
70262423Sadrian
71262423Sadrian	return (is);
72262423Sadrian}
73262423Sadrian
74262423Sadrianstatic void
75262423Sadrianiwn_stats_phy_print(struct iwnstats *is, struct iwn_rx_phy_stats *rxphy,
76262423Sadrian    const char *prefix)
77262423Sadrian{
78262423Sadrian
79262423Sadrian	printf("%s: %s: ina=%d, fina=%d, bad_plcp=%d, bad_crc32=%d, overrun=%d, eoverrun=%d\n",
80262423Sadrian	        __func__,
81262423Sadrian		prefix,
82262423Sadrian		le32toh(rxphy->ina),
83262423Sadrian		le32toh(rxphy->fina),
84262423Sadrian		le32toh(rxphy->bad_plcp),
85262423Sadrian		le32toh(rxphy->bad_crc32),
86262423Sadrian		le32toh(rxphy->overrun),
87262423Sadrian		le32toh(rxphy->eoverrun));
88262423Sadrian
89262423Sadrian	printf("%s: %s: fa=%d, bad_fina_sync=%d, sfd_timeout=%d, fina_timeout=%d, no_rts_ack=%d\n",
90262423Sadrian	        __func__,
91262423Sadrian		prefix,
92262423Sadrian		le32toh(rxphy->fa),
93262423Sadrian		le32toh(rxphy->bad_fina_sync),
94262423Sadrian		le32toh(rxphy->sfd_timeout),
95262423Sadrian		le32toh(rxphy->fina_timeout),
96262423Sadrian		le32toh(rxphy->no_rts_ack));
97262423Sadrian
98262423Sadrian	printf("%s: %s: rxe_limit=%d, ack=%d, cts=%d, ba_resp=%d, dsp_kill=%d, bad_mh=%d, rssi_sum=%d\n",
99262423Sadrian	        __func__,
100262423Sadrian		prefix,
101262423Sadrian		le32toh(rxphy->rxe_limit),
102262423Sadrian		le32toh(rxphy->ack),
103262423Sadrian		le32toh(rxphy->cts),
104262423Sadrian		le32toh(rxphy->ba_resp),
105262423Sadrian		le32toh(rxphy->dsp_kill),
106262423Sadrian		le32toh(rxphy->bad_mh),
107262423Sadrian		le32toh(rxphy->rssi_sum));
108262423Sadrian}
109262423Sadrian
110262423Sadrianstatic void
111262423Sadrianiwn_stats_rx_general_print(struct iwnstats *is, struct iwn_rx_general_stats *g)
112262423Sadrian{
113262423Sadrian
114262423Sadrian	printf("%s: bad_cts=%d, bad_ack=%d, not_bss=%d, filtered=%d, bad_chan=%d, beacons=%d\n",
115262423Sadrian	    __func__,
116262423Sadrian	    le32toh(g->bad_cts),
117262423Sadrian	    le32toh(g->bad_ack),
118262423Sadrian	    le32toh(g->not_bss),
119262423Sadrian	    le32toh(g->filtered),
120262423Sadrian	    le32toh(g->bad_chan),
121262423Sadrian	    le32toh(g->beacons));
122262423Sadrian
123262423Sadrian	/* XXX it'd be nice to have adc/ina saturated as a % of time */
124262423Sadrian	printf("%s: missed_beacons=%d, adc_saturated=%d, ina_searched=%d\n",
125262423Sadrian	    __func__,
126262423Sadrian	    le32toh(g->missed_beacons),
127262423Sadrian	    le32toh(g->adc_saturated),
128262423Sadrian	    le32toh(g->ina_searched));
129262423Sadrian
130262423Sadrian	printf("%s: noise=[%d, %d, %d] flags=0x%08x, load=%d, fa=%d\n",
131262423Sadrian	    __func__,
132262423Sadrian	    le32toh(g->noise[0]),
133262423Sadrian	    le32toh(g->noise[1]),
134262423Sadrian	    le32toh(g->noise[2]),
135262423Sadrian	    le32toh(g->flags),
136262423Sadrian	    le32toh(g->load),
137262423Sadrian	    le32toh(g->fa));
138262423Sadrian
139262423Sadrian	printf("%s: rssi=[%d, %d, %d] energy=[%d %d %d]\n",
140262423Sadrian	    __func__,
141262423Sadrian	    le32toh(g->rssi[0]),
142262423Sadrian	    le32toh(g->rssi[1]),
143262423Sadrian	    le32toh(g->rssi[2]),
144262423Sadrian	    le32toh(g->energy[0]),
145262423Sadrian	    le32toh(g->energy[1]),
146262423Sadrian	    le32toh(g->energy[2]));
147262423Sadrian}
148262423Sadrian
149262423Sadrianstatic void
150262423Sadrianiwn_stats_tx_print(struct iwnstats *is, struct iwn_tx_stats *tx)
151262423Sadrian{
152262423Sadrian
153262423Sadrian	printf("%s: preamble=%d, rx_detected=%d, bt_defer=%d, bt_kill=%d, short_len=%d\n",
154262423Sadrian	    __func__,
155262423Sadrian	    le32toh(tx->preamble),
156262423Sadrian	    le32toh(tx->rx_detected),
157262423Sadrian	    le32toh(tx->bt_defer),
158262423Sadrian	    le32toh(tx->bt_kill),
159262423Sadrian	    le32toh(tx->short_len));
160262423Sadrian
161262423Sadrian	printf("%s: cts_timeout=%d, ack_timeout=%d, exp_ack=%d, ack=%d, msdu=%d\n",
162262423Sadrian	    __func__,
163262423Sadrian	    le32toh(tx->cts_timeout),
164262423Sadrian	    le32toh(tx->ack_timeout),
165262423Sadrian	    le32toh(tx->exp_ack),
166262423Sadrian	    le32toh(tx->ack),
167262423Sadrian	    le32toh(tx->msdu));
168262423Sadrian
169262423Sadrian	printf("%s: burst_err1=%d, burst_err2=%d, cts_collision=%d, ack_collision=%d\n",
170262423Sadrian	    __func__,
171262423Sadrian	    le32toh(tx->burst_err1),
172262423Sadrian	    le32toh(tx->burst_err2),
173262423Sadrian	    le32toh(tx->cts_collision),
174262423Sadrian	    le32toh(tx->ack_collision));
175262423Sadrian
176262423Sadrian	printf("%s: ba_timeout=%d, ba_resched=%d, query_ampdu=%d, query=%d, query_ampdu_frag=%d\n",
177262423Sadrian	    __func__,
178262423Sadrian	    le32toh(tx->ba_timeout),
179262423Sadrian	    le32toh(tx->ba_resched),
180262423Sadrian	    le32toh(tx->query_ampdu),
181262423Sadrian	    le32toh(tx->query),
182262423Sadrian	    le32toh(tx->query_ampdu_frag));
183262423Sadrian
184262423Sadrian	printf("%s: query_mismatch=%d, not_ready=%d, underrun=%d, bt_ht_kill=%d, rx_ba_resp=%d\n",
185262423Sadrian	    __func__,
186262423Sadrian	    le32toh(tx->query_mismatch),
187262423Sadrian	    le32toh(tx->not_ready),
188262423Sadrian	    le32toh(tx->underrun),
189262423Sadrian	    le32toh(tx->bt_ht_kill),
190262423Sadrian	    le32toh(tx->rx_ba_resp));
191262423Sadrian}
192262423Sadrian
193262423Sadrianstatic void
194262423Sadrianiwn_stats_ht_phy_print(struct iwnstats *is, struct iwn_rx_ht_phy_stats *ht)
195262423Sadrian{
196262423Sadrian
197262423Sadrian	printf("%s: bad_plcp=%d, overrun=%d, eoverrun=%d, good_crc32=%d, bad_crc32=%d\n",
198262423Sadrian	    __func__,
199262423Sadrian	    le32toh(ht->bad_plcp),
200262423Sadrian	    le32toh(ht->overrun),
201262423Sadrian	    le32toh(ht->eoverrun),
202262423Sadrian	    le32toh(ht->good_crc32),
203262423Sadrian	    le32toh(ht->bad_crc32));
204262423Sadrian
205262423Sadrian	printf("%s: bad_mh=%d, good_ampdu_crc32=%d, ampdu=%d, fragment=%d\n",
206262423Sadrian	    __func__,
207262423Sadrian	    le32toh(ht->bad_plcp),
208262423Sadrian	    le32toh(ht->good_ampdu_crc32),
209262423Sadrian	    le32toh(ht->ampdu),
210262423Sadrian	    le32toh(ht->fragment));
211262423Sadrian}
212262423Sadrian
213262423Sadrian
214262423Sadrianstatic void
215262423Sadrianiwn_stats_general_print(struct iwnstats *is, struct iwn_stats *stats)
216262423Sadrian{
217262423Sadrian
218262423Sadrian	/* General */
219262423Sadrian	printf("%s: temp=%d, temp_m=%d, burst_check=%d, burst=%d, sleep=%d, slot_out=%d, slot_idle=%d\n",
220262423Sadrian	        __func__,
221262423Sadrian		le32toh(stats->general.temp),
222262423Sadrian		le32toh(stats->general.temp_m),
223262423Sadrian		le32toh(stats->general.burst_check),
224262423Sadrian		le32toh(stats->general.burst),
225262423Sadrian		le32toh(stats->general.sleep),
226262423Sadrian		le32toh(stats->general.slot_out),
227262423Sadrian		le32toh(stats->general.slot_idle));
228262423Sadrian	printf("%s: slot_out=%d, ttl_tstamp=0x%08x, tx_ant_a=%d, tx_ant_b=%d, exec=%d, probe=%d\n",
229262423Sadrian	        __func__,
230262423Sadrian		le32toh(stats->general.slot_out),
231262423Sadrian		le32toh(stats->general.ttl_tstamp),
232262423Sadrian		le32toh(stats->general.tx_ant_a),
233262423Sadrian		le32toh(stats->general.tx_ant_b),
234262423Sadrian		le32toh(stats->general.exec),
235262423Sadrian		le32toh(stats->general.probe));
236262423Sadrian	printf("%s: rx_enabled=%d\n",
237262423Sadrian	        __func__,
238262423Sadrian		le32toh(stats->general.rx_enabled));
239262423Sadrian}
240262423Sadrian
241262423Sadrianstatic void
242262423Sadrianiwn_print(struct iwnstats *is)
243262423Sadrian{
244262423Sadrian	struct iwn_stats *s;
245263093Seadler	struct timeval tv;
246262423Sadrian
247262423Sadrian	s = &is->st;
248262423Sadrian
249263093Seadler	gettimeofday(&tv, NULL);
250263093Seadler	printf("time=%ld.%.6ld\n", (long)tv.tv_sec, (long)tv.tv_usec);
251263093Seadler
252262423Sadrian	iwn_stats_general_print(is, s);
253262423Sadrian
254262423Sadrian	/* RX */
255262423Sadrian	iwn_stats_phy_print(is, &s->rx.ofdm, "ofdm");
256262423Sadrian	iwn_stats_phy_print(is, &s->rx.cck, "cck");
257262423Sadrian	iwn_stats_ht_phy_print(is, &s->rx.ht);
258262423Sadrian	iwn_stats_rx_general_print(is, &s->rx.general);
259262423Sadrian
260262423Sadrian	/* TX */
261262423Sadrian	iwn_stats_tx_print(is, &s->tx);
262262423Sadrian	printf("--\n");
263262423Sadrian}
264262423Sadrian
265262759Sadrianstatic void
266262759Sadrianusage(void)
267262759Sadrian{
268262759Sadrian	printf("Usage: iwnstats [-h] [-i ifname]\n");
269262759Sadrian	printf("    -h:			Help\n");
270262759Sadrian	printf("    -i <ifname>:	Use ifname (default %s)\n",
271262759Sadrian	    IWN_DEFAULT_IF);
272262759Sadrian}
273262759Sadrian
274262423Sadrianint
275262759Sadrianmain(int argc, char *argv[])
276262423Sadrian{
277262423Sadrian	struct iwnstats *is;
278262759Sadrian	int ch;
279262759Sadrian	char *ifname;
280262766Seadler	bool first;
281262893Seadler	char *sysctlname;
282262893Seadler	size_t len;
283262893Seadler	int ret;
284262423Sadrian
285262759Sadrian	ifname = strdup(IWN_DEFAULT_IF);
286262423Sadrian
287262759Sadrian	/* Parse command line arguments */
288262759Sadrian	while ((ch = getopt(argc, argv,
289262759Sadrian	    "hi:")) != -1) {
290262759Sadrian		switch (ch) {
291262759Sadrian		case 'i':
292262759Sadrian			if (ifname)
293262759Sadrian				free(ifname);
294262759Sadrian			ifname = strdup(optarg);
295262759Sadrian			break;
296262759Sadrian		default:
297262759Sadrian		case '?':
298262759Sadrian		case 'h':
299262759Sadrian			usage();
300262759Sadrian			exit(1);
301262759Sadrian		}
302262759Sadrian	}
303262759Sadrian
304262759Sadrian	is = iwnstats_new(ifname);
305262759Sadrian
306262423Sadrian	if (is == NULL) {
307262423Sadrian		fprintf(stderr, "%s: couldn't allocate new stats structure\n",
308262423Sadrian		    argv[0]);
309262423Sadrian		exit(127);
310262423Sadrian	}
311262423Sadrian
312262423Sadrian	/* begin fetching data */
313262766Seadler	first = true;
314262423Sadrian	while (1) {
315262423Sadrian		if (iwn_collect(is) != 0) {
316262423Sadrian			fprintf(stderr, "%s: fetch failed\n", argv[0]);
317262766Seadler			if (first)
318262766Seadler				return 1;
319262423Sadrian			goto next;
320262423Sadrian		}
321262423Sadrian
322262423Sadrian		iwn_print(is);
323262423Sadrian
324262423Sadrian	next:
325262423Sadrian		usleep(100 * 1000);
326262766Seadler		first = false;
327262423Sadrian	}
328262423Sadrian
329262423Sadrian	exit(0);
330262423Sadrian}
331