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