main.c revision 240184
1/*-
2 * Copyright (c) 2012, Adrian Chadd.
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: head/tools/tools/ath/athratestats/main.c 240184 2012-09-07 06:02:40Z adrian $
30 */
31
32#include "opt_ah.h"
33
34#include <sys/types.h>
35#include <sys/file.h>
36#include <sys/sockio.h>
37#include <sys/socket.h>
38#include <net/ethernet.h>
39#include <net/if.h>
40#include <net/if_media.h>
41#include <net/if_var.h>
42
43#include <stdio.h>
44#include <stdlib.h>
45#include <stdint.h>
46#include <signal.h>
47#include <string.h>
48#include <unistd.h>
49#include <err.h>
50
51#include "ah.h"
52#include "ah_desc.h"
53#include "net80211/ieee80211_ioctl.h"
54#include "net80211/ieee80211_radiotap.h"
55#include "if_athioctl.h"
56#include "if_athrate.h"
57
58#include "ath_rate/sample/sample.h"
59
60/*
61 * This needs to be big enough to fit the two TLVs, the rate table
62 * and the rate statistics table for a single node.
63 */
64#define	STATS_BUF_SIZE	8192
65
66struct ath_ratestats {
67	int s;
68	struct ath_rateioctl re;
69};
70
71static inline int
72dot11rate(struct ath_rateioctl_rt *rt, int rix)
73{
74
75	if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
76		return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
77	else
78		return (rt->ratecode[rix] / 2);
79}
80
81static const char *
82dot11str(struct ath_rateioctl_rt *rt, int rix)
83{
84	if (rix == -1)
85		return "";
86	else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
87		return "MCS";
88	else
89		return " Mb";
90}
91
92static void
93ath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
94    struct sample_node *sn)
95{
96	uint32_t mask;
97	int rix, y;
98
99	printf("static_rix (%d) ratemask 0x%x\n",
100	    sn->static_rix,
101	    sn->ratemask);
102
103	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
104		printf("[%4u] cur rate %d %s since switch: "
105		    "packets %d ticks %u\n",
106		    bin_to_size(y),
107		    dot11rate(rt, sn->current_rix[y]),
108		    dot11str(rt, sn->current_rix[y]),
109		    sn->packets_since_switch[y],
110		    sn->ticks_since_switch[y]);
111
112		printf("[%4u] last sample (%d %s) cur sample (%d %s) "
113		    "packets sent %d\n",
114		    bin_to_size(y),
115		    dot11rate(rt, sn->last_sample_rix[y]),
116		    dot11str(rt, sn->last_sample_rix[y]),
117		    dot11rate(rt, sn->current_sample_rix[y]),
118		    dot11str(rt, sn->current_sample_rix[y]),
119		    sn->packets_sent[y]);
120
121		printf("[%4u] packets since sample %d sample tt %u\n",
122		    bin_to_size(y),
123		    sn->packets_since_sample[y],
124		    sn->sample_tt[y]);
125	}
126	printf("   TX Rate     TXTOTAL:TXOK       EWMA          T/   F"
127	    "     avg last xmit\n");
128	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
129		if ((mask & 1) == 0)
130				continue;
131		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
132			if (sn->stats[y][rix].total_packets == 0)
133				continue;
134			printf("[%2u %s:%4u] %8ju:%-8ju "
135			    "(%3d.%1d%%) %8ju/%4d %5ums %u\n",
136			    dot11rate(rt, rix),
137			    dot11str(rt, rix),
138			    bin_to_size(y),
139			    (uintmax_t) sn->stats[y][rix].total_packets,
140			    (uintmax_t) sn->stats[y][rix].packets_acked,
141			    sn->stats[y][rix].ewma_pct / 10,
142			    sn->stats[y][rix].ewma_pct % 10,
143			    (uintmax_t) sn->stats[y][rix].tries,
144			    sn->stats[y][rix].successive_failures,
145			    sn->stats[y][rix].average_tx_time,
146			    sn->stats[y][rix].last_tx);
147		}
148	}
149}
150
151static void
152ath_setifname(struct ath_ratestats *r, const char *ifname)
153{
154
155	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
156}
157
158static void
159ath_setsta(struct ath_ratestats *r, const char *mac)
160{
161
162	memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
163}
164
165static void
166ath_rate_ioctl(struct ath_ratestats *r)
167{
168
169	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
170		err(1, "ioctl");
171}
172
173static int
174rate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
175{
176	struct ath_rateioctl_tlv *av;
177	struct sample_node *sn = NULL;
178	struct ath_rateioctl_rt *rt = NULL;
179	int error = 0;
180	uint8_t *buf = r->re.buf;
181
182	/*
183	 * For now, hard-code the TLV order and contents.  Ew!
184	 */
185	av = (struct ath_rateioctl_tlv *) buf;
186	if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
187		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
188		    "expected 0x%x\n",
189		    av->tlv_id,
190		    ATH_RATE_TLV_RATETABLE);
191		exit(127);
192	}
193	if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
194		fprintf(stderr, "unexpected TLV len (got %d bytes, "
195		    "expected %d bytes\n",
196		    av->tlv_len,
197		    sizeof(struct ath_rateioctl_rt));
198		exit(127);
199	}
200	rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));
201
202	/* Next */
203	av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
204	    sizeof(struct ath_rateioctl_rt));
205	if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
206		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
207		    "expected 0x%x\n",
208		    av->tlv_id,
209		    ATH_RATE_TLV_SAMPLENODE);
210		exit(127);
211	}
212	if (av->tlv_len != sizeof(struct sample_node)) {
213		fprintf(stderr, "unexpected TLV len (got %d bytes, "
214		    "expected %d bytes\n",
215		    av->tlv_len,
216		    sizeof(struct sample_node));
217		exit(127);
218	}
219	sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
220	    sizeof(struct ath_rateioctl_rt) +
221	    sizeof(struct ath_rateioctl_tlv));
222
223	ath_sample_stats(r, rt, sn);
224}
225
226
227int
228main(int argc, char *argv[])
229{
230	char const *ifname = NULL, *macaddr = NULL;
231	int c;
232	int do_all = 0;
233	struct ether_addr *e;
234	struct ath_ratestats r;
235	uint8_t *buf;
236
237	ifname = getenv("ATH");
238	if (ifname == NULL)
239		ifname = "ath0";
240
241	while ((c = getopt(argc, argv, "ahi:m:")) != -1) {
242		switch (c) {
243		case 'a':
244			do_all = 1;
245			break;
246		case 'i':
247			ifname = optarg;
248			break;
249		case 'm':
250			macaddr = optarg;
251			break;
252
253		default:
254			errx(1,
255			    "usage: %s [-h] [-i ifname] [-a] [-m macaddr]\n",
256			    argv[0]);
257			/* NOTREACHED */
258		}
259	}
260
261	if (macaddr == NULL) {
262		errx(1, "%s: macaddress wasn't supplied and no -a given\n",
263		    argv[0]);
264		/* NOTREACHED */
265	}
266	e = ether_aton(macaddr);
267	if (e == NULL)
268		err(1, "ether_aton");
269
270	bzero(&r, sizeof(r));
271
272	/*
273	 * Persistent buffer for each lookup
274	 */
275	buf = malloc(STATS_BUF_SIZE);
276	if (buf == NULL)
277		err(1, "calloc");
278
279	r.re.buf = buf;
280	r.re.len = STATS_BUF_SIZE;
281
282	r.s = socket(AF_INET, SOCK_DGRAM, 0);
283	if (r.s < 0) {
284		err(1, "socket");
285	}
286	/* XXX error check */
287	ath_setifname(&r, ifname);
288
289	/* Zero the buffer before it's passed in */
290	memset(buf, '\0', STATS_BUF_SIZE);
291
292	/*
293	 * Set the station address for this lookup.
294	 */
295	ath_setsta(&r, e->octet);
296
297	/*
298	 * Fetch the data from the driver.
299	 */
300	ath_rate_ioctl(&r);
301
302	/*
303	 * Decode and parse statistics.
304	 */
305	rate_node_stats(&r, e);
306}
307