1238635Sadrian/*- 2238635Sadrian * Copyright (c) 2012, Adrian Chadd. 3238635Sadrian * All rights reserved. 4238635Sadrian * 5238635Sadrian * Redistribution and use in source and binary forms, with or without 6238635Sadrian * modification, are permitted provided that the following conditions 7238635Sadrian * are met: 8238635Sadrian * 1. Redistributions of source code must retain the above copyright 9238635Sadrian * notice, this list of conditions and the following disclaimer, 10238635Sadrian * without modification. 11238635Sadrian * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12238635Sadrian * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13238635Sadrian * redistribution must be conditioned upon including a substantially 14238635Sadrian * similar Disclaimer requirement for further binary redistribution. 15238635Sadrian * 16238635Sadrian * NO WARRANTY 17238635Sadrian * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18238635Sadrian * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19238635Sadrian * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20238635Sadrian * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21238635Sadrian * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22238635Sadrian * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23238635Sadrian * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24238635Sadrian * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25238635Sadrian * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26238635Sadrian * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27238635Sadrian * THE POSSIBILITY OF SUCH DAMAGES. 28238635Sadrian * 29238635Sadrian * $FreeBSD: releng/11.0/tools/tools/ath/athratestats/main.c 295363 2016-02-07 04:28:03Z adrian $ 30238635Sadrian */ 31238635Sadrian 32238635Sadrian#include "opt_ah.h" 33238635Sadrian 34238635Sadrian#include <sys/types.h> 35238635Sadrian#include <sys/file.h> 36238635Sadrian#include <sys/sockio.h> 37238635Sadrian#include <sys/socket.h> 38238635Sadrian#include <net/ethernet.h> 39238635Sadrian#include <net/if.h> 40238635Sadrian#include <net/if_media.h> 41238635Sadrian 42238635Sadrian#include <stdio.h> 43238635Sadrian#include <stdlib.h> 44238635Sadrian#include <stdint.h> 45238635Sadrian#include <signal.h> 46238635Sadrian#include <string.h> 47238635Sadrian#include <unistd.h> 48238635Sadrian#include <err.h> 49238635Sadrian 50240223Sadrian#include <curses.h> 51240223Sadrian 52238635Sadrian#include "ah.h" 53238635Sadrian#include "ah_desc.h" 54238635Sadrian#include "net80211/ieee80211_ioctl.h" 55238635Sadrian#include "net80211/ieee80211_radiotap.h" 56238635Sadrian#include "if_athioctl.h" 57238635Sadrian#include "if_athrate.h" 58238635Sadrian 59238635Sadrian#include "ath_rate/sample/sample.h" 60238635Sadrian 61240223Sadrianstatic int do_loop = 0; 62240223Sadrian 63238646Sadrian/* 64238646Sadrian * This needs to be big enough to fit the two TLVs, the rate table 65238646Sadrian * and the rate statistics table for a single node. 66238646Sadrian */ 67238646Sadrian#define STATS_BUF_SIZE 8192 68238646Sadrian 69240223Sadrian#define PRINTMSG(...) do { \ 70240223Sadrian if (do_loop == 0) \ 71240223Sadrian printf(__VA_ARGS__); \ 72240223Sadrian else \ 73240223Sadrian printw(__VA_ARGS__); \ 74240223Sadrian } while (0) 75240223Sadrian 76240298Sadrian#define PRINTATTR_ON(_x) do { \ 77240298Sadrian if (do_loop) \ 78240298Sadrian attron(_x); \ 79240298Sadrian } while(0) 80240298Sadrian 81240298Sadrian 82240298Sadrian#define PRINTATTR_OFF(_x) do { \ 83240298Sadrian if (do_loop) \ 84240298Sadrian attroff(_x); \ 85240298Sadrian } while(0) 86240298Sadrian 87238635Sadrianstruct ath_ratestats { 88238635Sadrian int s; 89238635Sadrian struct ath_rateioctl re; 90238635Sadrian}; 91238635Sadrian 92238639Sadrianstatic inline int 93238639Sadriandot11rate(struct ath_rateioctl_rt *rt, int rix) 94238639Sadrian{ 95238639Sadrian 96238639Sadrian if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 97238639Sadrian return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS); 98238639Sadrian else 99238639Sadrian return (rt->ratecode[rix] / 2); 100238639Sadrian} 101238639Sadrian 102238639Sadrianstatic const char * 103238639Sadriandot11str(struct ath_rateioctl_rt *rt, int rix) 104238639Sadrian{ 105238639Sadrian if (rix == -1) 106238639Sadrian return ""; 107238639Sadrian else if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 108238639Sadrian return "MCS"; 109238639Sadrian else 110238639Sadrian return " Mb"; 111238639Sadrian} 112238639Sadrian 113238635Sadrianstatic void 114238639Sadrianath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt, 115238639Sadrian struct sample_node *sn) 116238635Sadrian{ 117280361Sadrian uint64_t mask; 118238635Sadrian int rix, y; 119238635Sadrian 120244966Sadrian PRINTMSG("static_rix (%d) ratemask 0x%llx\n", 121238635Sadrian sn->static_rix, 122244966Sadrian (long long) sn->ratemask); 123238635Sadrian 124238635Sadrian for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 125240298Sadrian PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 126240223Sadrian PRINTMSG("[%4u] cur rate %d %s since switch: " 127238639Sadrian "packets %d ticks %u\n", 128238635Sadrian bin_to_size(y), 129238639Sadrian dot11rate(rt, sn->current_rix[y]), 130238639Sadrian dot11str(rt, sn->current_rix[y]), 131238635Sadrian sn->packets_since_switch[y], 132238635Sadrian sn->ticks_since_switch[y]); 133238635Sadrian 134240223Sadrian PRINTMSG("[%4u] last sample (%d %s) cur sample (%d %s) " 135238635Sadrian "packets sent %d\n", 136238635Sadrian bin_to_size(y), 137238639Sadrian dot11rate(rt, sn->last_sample_rix[y]), 138238639Sadrian dot11str(rt, sn->last_sample_rix[y]), 139238639Sadrian dot11rate(rt, sn->current_sample_rix[y]), 140238639Sadrian dot11str(rt, sn->current_sample_rix[y]), 141238635Sadrian sn->packets_sent[y]); 142240298Sadrian PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 143240298Sadrian 144240298Sadrian PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 145240223Sadrian PRINTMSG("[%4u] packets since sample %d sample tt %u\n", 146238635Sadrian bin_to_size(y), 147238635Sadrian sn->packets_since_sample[y], 148238635Sadrian sn->sample_tt[y]); 149240298Sadrian PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 150240298Sadrian PRINTMSG("\n"); 151238635Sadrian } 152240223Sadrian PRINTMSG(" TX Rate TXTOTAL:TXOK EWMA T/ F" 153240184Sadrian " avg last xmit\n"); 154238635Sadrian for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { 155238635Sadrian if ((mask & 1) == 0) 156238635Sadrian continue; 157238635Sadrian for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 158238635Sadrian if (sn->stats[y][rix].total_packets == 0) 159238635Sadrian continue; 160240298Sadrian if (rix == sn->current_rix[y]) 161240298Sadrian PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 162240298Sadrian else if (rix == sn->last_sample_rix[y]) 163240298Sadrian PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 164240298Sadrian#if 0 165240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 50) 166240298Sadrian PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD); 167240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 75) 168240298Sadrian PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD); 169240298Sadrian#endif 170240223Sadrian PRINTMSG("[%2u %s:%4u] %8ju:%-8ju " 171240223Sadrian "(%3d.%1d%%) %8ju/%4d %5uuS %u\n", 172238639Sadrian dot11rate(rt, rix), 173238639Sadrian dot11str(rt, rix), 174238635Sadrian bin_to_size(y), 175238635Sadrian (uintmax_t) sn->stats[y][rix].total_packets, 176238635Sadrian (uintmax_t) sn->stats[y][rix].packets_acked, 177238635Sadrian sn->stats[y][rix].ewma_pct / 10, 178238635Sadrian sn->stats[y][rix].ewma_pct % 10, 179238635Sadrian (uintmax_t) sn->stats[y][rix].tries, 180238635Sadrian sn->stats[y][rix].successive_failures, 181238635Sadrian sn->stats[y][rix].average_tx_time, 182238635Sadrian sn->stats[y][rix].last_tx); 183240298Sadrian if (rix == sn->current_rix[y]) 184240298Sadrian PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 185240298Sadrian else if (rix == sn->last_sample_rix[y]) 186240298Sadrian PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 187240298Sadrian#if 0 188240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 50) 189240298Sadrian PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD); 190240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 75) 191240298Sadrian PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD); 192240298Sadrian#endif 193238635Sadrian } 194238635Sadrian } 195238635Sadrian} 196238635Sadrian 197238635Sadrianstatic void 198238635Sadrianath_setifname(struct ath_ratestats *r, const char *ifname) 199238635Sadrian{ 200238635Sadrian 201238635Sadrian strncpy(r->re.if_name, ifname, sizeof (r->re.if_name)); 202238635Sadrian} 203238635Sadrian 204238635Sadrianstatic void 205244967Sadrianath_setsta(struct ath_ratestats *r, uint8_t *mac) 206238635Sadrian{ 207238635Sadrian 208238635Sadrian memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr)); 209238635Sadrian} 210238635Sadrian 211238635Sadrianstatic void 212238635Sadrianath_rate_ioctl(struct ath_ratestats *r) 213238635Sadrian{ 214238646Sadrian 215238635Sadrian if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0) 216238635Sadrian err(1, "ioctl"); 217238635Sadrian} 218238635Sadrian 219238893Sadrianstatic int 220238893Sadrianrate_node_stats(struct ath_ratestats *r, struct ether_addr *e) 221238635Sadrian{ 222238635Sadrian struct ath_rateioctl_tlv *av; 223238639Sadrian struct sample_node *sn = NULL; 224238639Sadrian struct ath_rateioctl_rt *rt = NULL; 225238893Sadrian int error = 0; 226244967Sadrian uint8_t *buf = (uint8_t *) r->re.buf; 227238893Sadrian 228238893Sadrian /* 229238893Sadrian * For now, hard-code the TLV order and contents. Ew! 230238893Sadrian */ 231238893Sadrian av = (struct ath_rateioctl_tlv *) buf; 232238893Sadrian if (av->tlv_id != ATH_RATE_TLV_RATETABLE) { 233238893Sadrian fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 234238893Sadrian "expected 0x%x\n", 235238893Sadrian av->tlv_id, 236238893Sadrian ATH_RATE_TLV_RATETABLE); 237238893Sadrian exit(127); 238238893Sadrian } 239238893Sadrian if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) { 240238893Sadrian fprintf(stderr, "unexpected TLV len (got %d bytes, " 241238893Sadrian "expected %d bytes\n", 242238893Sadrian av->tlv_len, 243244966Sadrian (int) sizeof(struct ath_rateioctl_rt)); 244238893Sadrian exit(127); 245238893Sadrian } 246238893Sadrian rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv)); 247238893Sadrian 248238893Sadrian /* Next */ 249238893Sadrian av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 250238893Sadrian sizeof(struct ath_rateioctl_rt)); 251238893Sadrian if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) { 252238893Sadrian fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 253238893Sadrian "expected 0x%x\n", 254238893Sadrian av->tlv_id, 255238893Sadrian ATH_RATE_TLV_SAMPLENODE); 256238893Sadrian exit(127); 257238893Sadrian } 258238893Sadrian if (av->tlv_len != sizeof(struct sample_node)) { 259238893Sadrian fprintf(stderr, "unexpected TLV len (got %d bytes, " 260238893Sadrian "expected %d bytes\n", 261238893Sadrian av->tlv_len, 262244966Sadrian (int) sizeof(struct sample_node)); 263238893Sadrian exit(127); 264238893Sadrian } 265238893Sadrian sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 266238893Sadrian sizeof(struct ath_rateioctl_rt) + 267238893Sadrian sizeof(struct ath_rateioctl_tlv)); 268238893Sadrian 269238893Sadrian ath_sample_stats(r, rt, sn); 270244966Sadrian 271244966Sadrian return (0); 272238893Sadrian} 273238893Sadrian 274240223Sadrianstatic void 275240223Sadrianfetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e, 276240223Sadrian uint8_t *buf) 277240223Sadrian{ 278238893Sadrian 279240223Sadrian /* Zero the buffer before it's passed in */ 280240223Sadrian memset(buf, '\0', STATS_BUF_SIZE); 281240223Sadrian 282240223Sadrian /* 283240223Sadrian * Set the station address for this lookup. 284240223Sadrian */ 285240223Sadrian ath_setsta(r, e->octet); 286240223Sadrian 287240223Sadrian /* 288240223Sadrian * Fetch the data from the driver. 289240223Sadrian */ 290240223Sadrian ath_rate_ioctl(r); 291240223Sadrian 292240223Sadrian /* 293240223Sadrian * Decode and parse statistics. 294240223Sadrian */ 295240223Sadrian rate_node_stats(r, e); 296240223Sadrian} 297240223Sadrian 298238893Sadrianint 299238893Sadrianmain(int argc, char *argv[]) 300238893Sadrian{ 301238646Sadrian char const *ifname = NULL, *macaddr = NULL; 302238646Sadrian int c; 303238646Sadrian int do_all = 0; 304238893Sadrian struct ether_addr *e; 305238893Sadrian struct ath_ratestats r; 306238893Sadrian uint8_t *buf; 307240223Sadrian useconds_t sleep_period; 308240223Sadrian float f; 309240298Sadrian short cf, cb; 310238635Sadrian 311238646Sadrian ifname = getenv("ATH"); 312238646Sadrian if (ifname == NULL) 313295363Sadrian ifname = ATH_DEFAULT; 314238646Sadrian 315240223Sadrian while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) { 316238646Sadrian switch (c) { 317238646Sadrian case 'a': 318238646Sadrian do_all = 1; 319238646Sadrian break; 320238646Sadrian case 'i': 321238646Sadrian ifname = optarg; 322238646Sadrian break; 323238646Sadrian case 'm': 324238646Sadrian macaddr = optarg; 325238646Sadrian break; 326240223Sadrian case 's': 327240223Sadrian sscanf(optarg, "%f", &f); 328240223Sadrian do_loop = 1; 329240223Sadrian sleep_period = (useconds_t) (f * 1000000.0); 330240223Sadrian break; 331238646Sadrian default: 332238646Sadrian errx(1, 333240223Sadrian "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n", 334238646Sadrian argv[0]); 335238646Sadrian /* NOTREACHED */ 336238646Sadrian } 337238646Sadrian } 338238646Sadrian 339238646Sadrian if (macaddr == NULL) { 340238646Sadrian errx(1, "%s: macaddress wasn't supplied and no -a given\n", 341238646Sadrian argv[0]); 342238646Sadrian /* NOTREACHED */ 343238646Sadrian } 344238646Sadrian e = ether_aton(macaddr); 345238646Sadrian if (e == NULL) 346238646Sadrian err(1, "ether_aton"); 347238635Sadrian 348238893Sadrian bzero(&r, sizeof(r)); 349238893Sadrian 350238893Sadrian /* 351238893Sadrian * Persistent buffer for each lookup 352238893Sadrian */ 353238893Sadrian buf = malloc(STATS_BUF_SIZE); 354238893Sadrian if (buf == NULL) 355238893Sadrian err(1, "calloc"); 356238893Sadrian 357244967Sadrian r.re.buf = (char *) buf; 358238635Sadrian r.re.len = STATS_BUF_SIZE; 359238635Sadrian 360238893Sadrian r.s = socket(AF_INET, SOCK_DGRAM, 0); 361238893Sadrian if (r.s < 0) { 362238893Sadrian err(1, "socket"); 363238893Sadrian } 364240223Sadrian 365238893Sadrian /* XXX error check */ 366238893Sadrian ath_setifname(&r, ifname); 367238893Sadrian 368240223Sadrian if (do_loop) { 369240223Sadrian initscr(); 370240223Sadrian start_color(); 371240223Sadrian use_default_colors(); 372240298Sadrian pair_content(0, &cf, &cb); 373240298Sadrian /* Error - medium */ 374240298Sadrian init_pair(1, COLOR_YELLOW, cb); 375240298Sadrian /* Error - high */ 376240298Sadrian init_pair(2, COLOR_RED, cb); 377240298Sadrian /* Sample */ 378240298Sadrian init_pair(3, COLOR_CYAN, cb); 379240298Sadrian /* 250 byte frames */ 380240298Sadrian init_pair(4, COLOR_BLUE, cb); 381240298Sadrian /* 1600 byte frames */ 382240298Sadrian init_pair(5, COLOR_MAGENTA, cb); 383240223Sadrian cbreak(); 384240223Sadrian noecho(); 385240223Sadrian nonl(); 386240223Sadrian nodelay(stdscr, 1); 387240223Sadrian intrflush(stdscr, FALSE); 388240223Sadrian keypad(stdscr, TRUE); 389238893Sadrian 390240223Sadrian while (1) { 391240223Sadrian clear(); 392240223Sadrian move(0, 0); 393240223Sadrian fetch_and_print_stats(&r, e, buf); 394240223Sadrian refresh(); 395240223Sadrian usleep(sleep_period); 396240223Sadrian } 397240223Sadrian } else 398240223Sadrian fetch_and_print_stats(&r, e, buf); 399238893Sadrian 400240223Sadrian exit(0); 401238635Sadrian} 402