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$
30 */
31#include "diag.h"
32
33#include "ah.h"
34#include "ah_internal.h"
35
36#include <getopt.h>
37#include <errno.h>
38#include <err.h>
39#include <stdlib.h>
40#include <string.h>
41#include <ctype.h>
42#include <unistd.h>
43
44const char *progname;
45
46static void
47usage()
48{
49	fprintf(stderr, "usage: %s [-i ifname]\n", progname);
50	exit(-1);
51}
52
53static int
54get_survey_stats(int s, const char *ifname, HAL_CHANNEL_SURVEY *hs)
55{
56	uint16_t eedata;
57	struct ath_diag atd;
58
59	memset(&atd, '\0', sizeof(atd));
60
61	atd.ad_id = HAL_DIAG_CHANSURVEY | ATH_DIAG_OUT;
62	atd.ad_in_size = 0;
63	atd.ad_in_data = NULL;
64	atd.ad_out_size = sizeof(HAL_CHANNEL_SURVEY);
65	atd.ad_out_data = (caddr_t) hs;
66	strncpy(atd.ad_name, ifname, sizeof(atd.ad_name));
67
68	if (ioctl(s, SIOCGATHDIAG, &atd) < 0) {
69		err(1, "ioctl: %s", atd.ad_name);
70		return (0);
71	}
72	return (1);
73}
74
75static void
76process_survey_stats(HAL_CHANNEL_SURVEY *hs)
77{
78	int i;
79	float tx = 0.0, rx = 0.0, cc = 0.0, cext = 0.0;
80	float max_tx = 0.0, max_rx = 0.0, max_cc = 0.0, max_cext = 0.0;
81	uint64_t avg_tx = 0, avg_rx = 0, avg_cc = 0, avg_cext = 0;
82	float min_tx = 100.0, min_rx = 100.0, min_cc = 100.0, min_cext = 100.0;
83	int n = 0;
84	int cycle_count = 0, max_cycle_count = 0;
85
86	/* Calculate a percentage channel busy */
87	for (i = 0; i < CHANNEL_SURVEY_SAMPLE_COUNT; i++) {
88		/*
89		 * Skip samples with no cycle count
90		 */
91		if (hs->samples[i].cycle_count == 0)
92			continue;
93		n++;
94
95		/*
96		 * Grab cycle count, calculate maximum just for curiousity
97		 */
98		cycle_count = hs->samples[i].cycle_count;
99		if (cycle_count > max_cycle_count)
100			max_cycle_count = cycle_count;
101
102		/*
103		 * Calculate percentage
104		 */
105		tx = (float) hs->samples[i].tx_busy * 100.0 /
106		    hs->samples[i].cycle_count;
107		rx = (float) hs->samples[i].rx_busy * 100.0 /
108		    hs->samples[i].cycle_count;
109		cc = (float) hs->samples[i].chan_busy * 100.0 /
110		    hs->samples[i].cycle_count;
111		cext = (float) hs->samples[i].ext_chan_busy * 100.0 /
112		    hs->samples[i].cycle_count;
113
114		/*
115		 * Update rolling average
116		 * XXX to preserve some accuracy, keep two decimal points
117		 * using "fixed" point math.
118		 */
119		avg_tx += (uint64_t) hs->samples[i].tx_busy * 10000 /
120		    hs->samples[i].cycle_count;
121		avg_rx += (uint64_t) hs->samples[i].rx_busy * 10000 /
122		    hs->samples[i].cycle_count;
123		avg_cc += (uint64_t) hs->samples[i].chan_busy * 10000 /
124		    hs->samples[i].cycle_count;
125		avg_cext += (uint64_t) hs->samples[i].ext_chan_busy * 10000 /
126		    hs->samples[i].cycle_count;
127
128		/*
129		 * Update maximum values
130		 */
131		if (tx > max_tx)
132			max_tx = tx;
133		if (rx > max_rx)
134			max_rx = rx;
135		if (cc > max_cc)
136			max_cc = cc;
137		if (cext > max_cext)
138			max_cext = cext;
139
140		/*
141		 * Update minimum values
142		 */
143		if (tx < min_tx)
144			min_tx = tx;
145		if (rx < min_rx)
146			min_rx = rx;
147		if (cc < min_cc)
148			min_cc = cc;
149		if (cext < min_cext)
150			min_cext = cext;
151	}
152
153	printf("(%4.1f %4.1f %4.1f %4.1f) ",
154	    min_tx, min_rx, min_cc, min_cext);
155	printf("(%4.1f %4.1f %4.1f %4.1f) ",
156	    n == 0 ? 0.0 : (float) (avg_tx / n) / 100.0,
157	    n == 0 ? 0.0 : (float) (avg_rx / n) / 100.0,
158	    n == 0 ? 0.0 : (float) (avg_cc / n) / 100.0,
159	    n == 0 ? 0.0 : (float) (avg_cext / n) / 100.0);
160	printf("(%4.1f %4.1f %4.1f %4.1f)\n",
161	    max_tx, max_rx, max_cc, max_cext);
162}
163
164int
165main(int argc, char *argv[])
166{
167	FILE *fd = NULL;
168	const char *ifname;
169	int c, s;
170	int l = 0;
171
172	s = socket(AF_INET, SOCK_DGRAM, 0);
173	if (s < 0)
174		err(1, "socket");
175	ifname = getenv("ATH");
176	if (!ifname)
177		ifname = ATH_DEFAULT;
178
179	progname = argv[0];
180	while ((c = getopt(argc, argv, "i:")) != -1)
181		switch (c) {
182		case 'i':
183			ifname = optarg;
184			break;
185		default:
186			usage();
187			/*NOTREACHED*/
188		}
189	argc -= optind;
190	argv += optind;
191
192	/* Now, loop over and fetch some statistics .. */
193	while (1) {
194		HAL_CHANNEL_SURVEY hs;
195		memset(&hs, '\0', sizeof(hs));
196		if (get_survey_stats(s, ifname, &hs) == 0)
197			break;
198		/* XXX screen height! */
199		if (l % 23 == 0) {
200			printf("         "
201			    "min                   "
202			    "avg                   "
203			    "max\n");
204			printf("  tx%%  rx%%  bc%%  ec%%  ");
205			printf("  tx%%  rx%%  bc%%  ec%%  ");
206			printf("  tx%%  rx%%  bc%%  ec%%\n");
207		}
208		process_survey_stats(&hs);
209		sleep(1);
210		l++;
211	}
212
213	return (0);
214}
215
216
217