1/*-
2 * Copyright (c) 2014 Adrian Chadd <adrian@FreeBSD.org>
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 <stdbool.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <signal.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <string.h>
37#include <err.h>
38#include <net/if.h>
39#include <sys/endian.h>
40#include <sys/time.h>
41#include <sys/types.h>
42#include <sys/sysctl.h>
43
44#include "net80211/ieee80211_ioctl.h"
45#include "net80211/ieee80211_radiotap.h"
46
47#include "if_iwn_ioctl.h"
48#include "if_iwnreg.h"
49#include "iwnstats.h"
50#include "iwn_ioctl.h"
51
52#define	IWN_DEFAULT_IF		"iwn0"
53
54static struct iwnstats *
55iwnstats_new(const char *ifname)
56{
57	struct iwnstats *is;
58	char buf[128];
59
60	is = calloc(1, sizeof(struct iwnstats));
61	if (is == NULL)
62		return (NULL);
63
64	snprintf(buf, sizeof(buf), "/dev/%s", ifname);
65	is->s = open(buf, O_RDWR);
66	if (is->s < 0)
67		err(1, "open");
68
69	return (is);
70}
71
72static void
73iwn_stats_phy_print(struct iwn_rx_phy_stats *rxphy, const char *prefix)
74{
75
76	printf("%s: %s: ina=%d, fina=%d, bad_plcp=%d, bad_crc32=%d, overrun=%d, eoverrun=%d\n",
77	        __func__,
78		prefix,
79		le32toh(rxphy->ina),
80		le32toh(rxphy->fina),
81		le32toh(rxphy->bad_plcp),
82		le32toh(rxphy->bad_crc32),
83		le32toh(rxphy->overrun),
84		le32toh(rxphy->eoverrun));
85
86	printf("%s: %s: fa=%d, bad_fina_sync=%d, sfd_timeout=%d, fina_timeout=%d, no_rts_ack=%d\n",
87	        __func__,
88		prefix,
89		le32toh(rxphy->fa),
90		le32toh(rxphy->bad_fina_sync),
91		le32toh(rxphy->sfd_timeout),
92		le32toh(rxphy->fina_timeout),
93		le32toh(rxphy->no_rts_ack));
94
95	printf("%s: %s: rxe_limit=%d, ack=%d, cts=%d, ba_resp=%d, dsp_kill=%d, bad_mh=%d, rssi_sum=%d\n",
96	        __func__,
97		prefix,
98		le32toh(rxphy->rxe_limit),
99		le32toh(rxphy->ack),
100		le32toh(rxphy->cts),
101		le32toh(rxphy->ba_resp),
102		le32toh(rxphy->dsp_kill),
103		le32toh(rxphy->bad_mh),
104		le32toh(rxphy->rssi_sum));
105}
106
107static void
108iwn_stats_rx_general_print(struct iwn_rx_general_stats *g)
109{
110
111	printf("%s: bad_cts=%d, bad_ack=%d, not_bss=%d, filtered=%d, bad_chan=%d, beacons=%d\n",
112	    __func__,
113	    le32toh(g->bad_cts),
114	    le32toh(g->bad_ack),
115	    le32toh(g->not_bss),
116	    le32toh(g->filtered),
117	    le32toh(g->bad_chan),
118	    le32toh(g->beacons));
119
120	/* XXX it'd be nice to have adc/ina saturated as a % of time */
121	printf("%s: missed_beacons=%d, adc_saturated=%d, ina_searched=%d\n",
122	    __func__,
123	    le32toh(g->missed_beacons),
124	    le32toh(g->adc_saturated),
125	    le32toh(g->ina_searched));
126
127	printf("%s: noise=[%d, %d, %d] flags=0x%08x, load=%d, fa=%d\n",
128	    __func__,
129	    le32toh(g->noise[0]),
130	    le32toh(g->noise[1]),
131	    le32toh(g->noise[2]),
132	    le32toh(g->flags),
133	    le32toh(g->load),
134	    le32toh(g->fa));
135
136	printf("%s: rssi=[%d, %d, %d] energy=[%d %d %d]\n",
137	    __func__,
138	    le32toh(g->rssi[0]),
139	    le32toh(g->rssi[1]),
140	    le32toh(g->rssi[2]),
141	    le32toh(g->energy[0]),
142	    le32toh(g->energy[1]),
143	    le32toh(g->energy[2]));
144}
145
146static void
147iwn_stats_tx_print(struct iwn_tx_stats *tx)
148{
149
150	printf("%s: preamble=%d, rx_detected=%d, bt_defer=%d, bt_kill=%d, short_len=%d\n",
151	    __func__,
152	    le32toh(tx->preamble),
153	    le32toh(tx->rx_detected),
154	    le32toh(tx->bt_defer),
155	    le32toh(tx->bt_kill),
156	    le32toh(tx->short_len));
157
158	printf("%s: cts_timeout=%d, ack_timeout=%d, exp_ack=%d, ack=%d, msdu=%d\n",
159	    __func__,
160	    le32toh(tx->cts_timeout),
161	    le32toh(tx->ack_timeout),
162	    le32toh(tx->exp_ack),
163	    le32toh(tx->ack),
164	    le32toh(tx->msdu));
165
166	printf("%s: burst_err1=%d, burst_err2=%d, cts_collision=%d, ack_collision=%d\n",
167	    __func__,
168	    le32toh(tx->burst_err1),
169	    le32toh(tx->burst_err2),
170	    le32toh(tx->cts_collision),
171	    le32toh(tx->ack_collision));
172
173	printf("%s: ba_timeout=%d, ba_resched=%d, query_ampdu=%d, query=%d, query_ampdu_frag=%d\n",
174	    __func__,
175	    le32toh(tx->ba_timeout),
176	    le32toh(tx->ba_resched),
177	    le32toh(tx->query_ampdu),
178	    le32toh(tx->query),
179	    le32toh(tx->query_ampdu_frag));
180
181	printf("%s: query_mismatch=%d, not_ready=%d, underrun=%d, bt_ht_kill=%d, rx_ba_resp=%d\n",
182	    __func__,
183	    le32toh(tx->query_mismatch),
184	    le32toh(tx->not_ready),
185	    le32toh(tx->underrun),
186	    le32toh(tx->bt_ht_kill),
187	    le32toh(tx->rx_ba_resp));
188}
189
190static void
191iwn_stats_ht_phy_print(struct iwn_rx_ht_phy_stats *ht)
192{
193
194	printf("%s: bad_plcp=%d, overrun=%d, eoverrun=%d, good_crc32=%d, bad_crc32=%d\n",
195	    __func__,
196	    le32toh(ht->bad_plcp),
197	    le32toh(ht->overrun),
198	    le32toh(ht->eoverrun),
199	    le32toh(ht->good_crc32),
200	    le32toh(ht->bad_crc32));
201
202	printf("%s: bad_mh=%d, good_ampdu_crc32=%d, ampdu=%d, fragment=%d\n",
203	    __func__,
204	    le32toh(ht->bad_plcp),
205	    le32toh(ht->good_ampdu_crc32),
206	    le32toh(ht->ampdu),
207	    le32toh(ht->fragment));
208}
209
210
211static void
212iwn_stats_general_print(struct iwn_stats *stats)
213{
214
215	/* General */
216	printf("%s: temp=%d, temp_m=%d, burst_check=%d, burst=%d, sleep=%d, slot_out=%d, slot_idle=%d\n",
217	        __func__,
218		le32toh(stats->general.temp),
219		le32toh(stats->general.temp_m),
220		le32toh(stats->general.burst_check),
221		le32toh(stats->general.burst),
222		le32toh(stats->general.sleep),
223		le32toh(stats->general.slot_out),
224		le32toh(stats->general.slot_idle));
225	printf("%s: slot_out=%d, ttl_tstamp=0x%08x, tx_ant_a=%d, tx_ant_b=%d, exec=%d, probe=%d\n",
226	        __func__,
227		le32toh(stats->general.slot_out),
228		le32toh(stats->general.ttl_tstamp),
229		le32toh(stats->general.tx_ant_a),
230		le32toh(stats->general.tx_ant_b),
231		le32toh(stats->general.exec),
232		le32toh(stats->general.probe));
233	printf("%s: rx_enabled=%d\n",
234	        __func__,
235		le32toh(stats->general.rx_enabled));
236}
237
238static void
239iwn_print(struct iwnstats *is)
240{
241	struct iwn_stats *s;
242	struct timeval tv;
243
244	s = &is->st;
245
246	gettimeofday(&tv, NULL);
247	printf("time=%ld.%.6ld\n", (long)tv.tv_sec, (long)tv.tv_usec);
248
249	iwn_stats_general_print(s);
250
251	/* RX */
252	iwn_stats_phy_print(&s->rx.ofdm, "ofdm");
253	iwn_stats_phy_print(&s->rx.cck, "cck");
254	iwn_stats_ht_phy_print(&s->rx.ht);
255	iwn_stats_rx_general_print(&s->rx.general);
256
257	/* TX */
258	iwn_stats_tx_print(&s->tx);
259	printf("--\n");
260}
261
262static void
263usage(void)
264{
265	printf("Usage: iwnstats [-h] [-i ifname]\n");
266	printf("    -h:			Help\n");
267	printf("    -i <ifname>:	Use ifname (default %s)\n",
268	    IWN_DEFAULT_IF);
269}
270
271int
272main(int argc, char *argv[])
273{
274	struct iwnstats *is;
275	int ch;
276	char *ifname;
277	bool first;
278
279	ifname = strdup(IWN_DEFAULT_IF);
280
281	/* Parse command line arguments */
282	while ((ch = getopt(argc, argv,
283	    "hi:")) != -1) {
284		switch (ch) {
285		case 'i':
286			if (ifname)
287				free(ifname);
288			ifname = strdup(optarg);
289			break;
290		default:
291		case '?':
292		case 'h':
293			usage();
294			exit(1);
295		}
296	}
297
298	is = iwnstats_new(ifname);
299
300	if (is == NULL) {
301		fprintf(stderr, "%s: couldn't allocate new stats structure\n",
302		    argv[0]);
303		exit(127);
304	}
305
306	/* begin fetching data */
307	first = true;
308	while (1) {
309		if (iwn_collect(is) != 0) {
310			fprintf(stderr, "%s: fetch failed\n", argv[0]);
311			if (first)
312				return 1;
313			goto next;
314		}
315
316		iwn_print(is);
317
318	next:
319		usleep(100 * 1000);
320		first = false;
321	}
322
323	exit(0);
324}
325