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$ 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#include <net/if_var.h> 42238635Sadrian 43238635Sadrian#include <stdio.h> 44238635Sadrian#include <stdlib.h> 45238635Sadrian#include <stdint.h> 46238635Sadrian#include <signal.h> 47238635Sadrian#include <string.h> 48238635Sadrian#include <unistd.h> 49238635Sadrian#include <err.h> 50238635Sadrian 51240223Sadrian#include <curses.h> 52240223Sadrian 53238635Sadrian#include "ah.h" 54238635Sadrian#include "ah_desc.h" 55238635Sadrian#include "net80211/ieee80211_ioctl.h" 56238635Sadrian#include "net80211/ieee80211_radiotap.h" 57238635Sadrian#include "if_athioctl.h" 58238635Sadrian#include "if_athrate.h" 59238635Sadrian 60238635Sadrian#include "ath_rate/sample/sample.h" 61238635Sadrian 62240223Sadrianstatic int do_loop = 0; 63240223Sadrian 64238646Sadrian/* 65238646Sadrian * This needs to be big enough to fit the two TLVs, the rate table 66238646Sadrian * and the rate statistics table for a single node. 67238646Sadrian */ 68238646Sadrian#define STATS_BUF_SIZE 8192 69238646Sadrian 70240223Sadrian#define PRINTMSG(...) do { \ 71240223Sadrian if (do_loop == 0) \ 72240223Sadrian printf(__VA_ARGS__); \ 73240223Sadrian else \ 74240223Sadrian printw(__VA_ARGS__); \ 75240223Sadrian } while (0) 76240223Sadrian 77240298Sadrian#define PRINTATTR_ON(_x) do { \ 78240298Sadrian if (do_loop) \ 79240298Sadrian attron(_x); \ 80240298Sadrian } while(0) 81240298Sadrian 82240298Sadrian 83240298Sadrian#define PRINTATTR_OFF(_x) do { \ 84240298Sadrian if (do_loop) \ 85240298Sadrian attroff(_x); \ 86240298Sadrian } while(0) 87240298Sadrian 88238635Sadrianstruct ath_ratestats { 89238635Sadrian int s; 90238635Sadrian struct ath_rateioctl re; 91238635Sadrian}; 92238635Sadrian 93238639Sadrianstatic inline int 94238639Sadriandot11rate(struct ath_rateioctl_rt *rt, int rix) 95238639Sadrian{ 96238639Sadrian 97238639Sadrian if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 98238639Sadrian return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS); 99238639Sadrian else 100238639Sadrian return (rt->ratecode[rix] / 2); 101238639Sadrian} 102238639Sadrian 103238639Sadrianstatic const char * 104238639Sadriandot11str(struct ath_rateioctl_rt *rt, int rix) 105238639Sadrian{ 106238639Sadrian if (rix == -1) 107238639Sadrian return ""; 108238639Sadrian else if (rt->ratecode[rix] & IEEE80211_RATE_MCS) 109238639Sadrian return "MCS"; 110238639Sadrian else 111238639Sadrian return " Mb"; 112238639Sadrian} 113238639Sadrian 114238635Sadrianstatic void 115238639Sadrianath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt, 116238639Sadrian struct sample_node *sn) 117238635Sadrian{ 118238635Sadrian uint32_t mask; 119238635Sadrian int rix, y; 120238635Sadrian 121244966Sadrian PRINTMSG("static_rix (%d) ratemask 0x%llx\n", 122238635Sadrian sn->static_rix, 123244966Sadrian (long long) sn->ratemask); 124238635Sadrian 125238635Sadrian for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 126240298Sadrian PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 127240223Sadrian PRINTMSG("[%4u] cur rate %d %s since switch: " 128238639Sadrian "packets %d ticks %u\n", 129238635Sadrian bin_to_size(y), 130238639Sadrian dot11rate(rt, sn->current_rix[y]), 131238639Sadrian dot11str(rt, sn->current_rix[y]), 132238635Sadrian sn->packets_since_switch[y], 133238635Sadrian sn->ticks_since_switch[y]); 134238635Sadrian 135240223Sadrian PRINTMSG("[%4u] last sample (%d %s) cur sample (%d %s) " 136238635Sadrian "packets sent %d\n", 137238635Sadrian bin_to_size(y), 138238639Sadrian dot11rate(rt, sn->last_sample_rix[y]), 139238639Sadrian dot11str(rt, sn->last_sample_rix[y]), 140238639Sadrian dot11rate(rt, sn->current_sample_rix[y]), 141238639Sadrian dot11str(rt, sn->current_sample_rix[y]), 142238635Sadrian sn->packets_sent[y]); 143240298Sadrian PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 144240298Sadrian 145240298Sadrian PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 146240223Sadrian PRINTMSG("[%4u] packets since sample %d sample tt %u\n", 147238635Sadrian bin_to_size(y), 148238635Sadrian sn->packets_since_sample[y], 149238635Sadrian sn->sample_tt[y]); 150240298Sadrian PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 151240298Sadrian PRINTMSG("\n"); 152238635Sadrian } 153240223Sadrian PRINTMSG(" TX Rate TXTOTAL:TXOK EWMA T/ F" 154240184Sadrian " avg last xmit\n"); 155238635Sadrian for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) { 156238635Sadrian if ((mask & 1) == 0) 157238635Sadrian continue; 158238635Sadrian for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) { 159238635Sadrian if (sn->stats[y][rix].total_packets == 0) 160238635Sadrian continue; 161240298Sadrian if (rix == sn->current_rix[y]) 162240298Sadrian PRINTATTR_ON(COLOR_PAIR(y+4) | A_BOLD); 163240298Sadrian else if (rix == sn->last_sample_rix[y]) 164240298Sadrian PRINTATTR_ON(COLOR_PAIR(3) | A_BOLD); 165240298Sadrian#if 0 166240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 50) 167240298Sadrian PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD); 168240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 75) 169240298Sadrian PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD); 170240298Sadrian#endif 171240223Sadrian PRINTMSG("[%2u %s:%4u] %8ju:%-8ju " 172240223Sadrian "(%3d.%1d%%) %8ju/%4d %5uuS %u\n", 173238639Sadrian dot11rate(rt, rix), 174238639Sadrian dot11str(rt, rix), 175238635Sadrian bin_to_size(y), 176238635Sadrian (uintmax_t) sn->stats[y][rix].total_packets, 177238635Sadrian (uintmax_t) sn->stats[y][rix].packets_acked, 178238635Sadrian sn->stats[y][rix].ewma_pct / 10, 179238635Sadrian sn->stats[y][rix].ewma_pct % 10, 180238635Sadrian (uintmax_t) sn->stats[y][rix].tries, 181238635Sadrian sn->stats[y][rix].successive_failures, 182238635Sadrian sn->stats[y][rix].average_tx_time, 183238635Sadrian sn->stats[y][rix].last_tx); 184240298Sadrian if (rix == sn->current_rix[y]) 185240298Sadrian PRINTATTR_OFF(COLOR_PAIR(y+4) | A_BOLD); 186240298Sadrian else if (rix == sn->last_sample_rix[y]) 187240298Sadrian PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD); 188240298Sadrian#if 0 189240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 50) 190240298Sadrian PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD); 191240298Sadrian else if (sn->stats[y][rix].ewma_pct / 10 < 75) 192240298Sadrian PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD); 193240298Sadrian#endif 194238635Sadrian } 195238635Sadrian } 196238635Sadrian} 197238635Sadrian 198238635Sadrianstatic void 199238635Sadrianath_setifname(struct ath_ratestats *r, const char *ifname) 200238635Sadrian{ 201238635Sadrian 202238635Sadrian strncpy(r->re.if_name, ifname, sizeof (r->re.if_name)); 203238635Sadrian} 204238635Sadrian 205238635Sadrianstatic void 206244967Sadrianath_setsta(struct ath_ratestats *r, uint8_t *mac) 207238635Sadrian{ 208238635Sadrian 209238635Sadrian memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr)); 210238635Sadrian} 211238635Sadrian 212238635Sadrianstatic void 213238635Sadrianath_rate_ioctl(struct ath_ratestats *r) 214238635Sadrian{ 215238646Sadrian 216238635Sadrian if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0) 217238635Sadrian err(1, "ioctl"); 218238635Sadrian} 219238635Sadrian 220238893Sadrianstatic int 221238893Sadrianrate_node_stats(struct ath_ratestats *r, struct ether_addr *e) 222238635Sadrian{ 223238635Sadrian struct ath_rateioctl_tlv *av; 224238639Sadrian struct sample_node *sn = NULL; 225238639Sadrian struct ath_rateioctl_rt *rt = NULL; 226238893Sadrian int error = 0; 227244967Sadrian uint8_t *buf = (uint8_t *) r->re.buf; 228238893Sadrian 229238893Sadrian /* 230238893Sadrian * For now, hard-code the TLV order and contents. Ew! 231238893Sadrian */ 232238893Sadrian av = (struct ath_rateioctl_tlv *) buf; 233238893Sadrian if (av->tlv_id != ATH_RATE_TLV_RATETABLE) { 234238893Sadrian fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 235238893Sadrian "expected 0x%x\n", 236238893Sadrian av->tlv_id, 237238893Sadrian ATH_RATE_TLV_RATETABLE); 238238893Sadrian exit(127); 239238893Sadrian } 240238893Sadrian if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) { 241238893Sadrian fprintf(stderr, "unexpected TLV len (got %d bytes, " 242238893Sadrian "expected %d bytes\n", 243238893Sadrian av->tlv_len, 244244966Sadrian (int) sizeof(struct ath_rateioctl_rt)); 245238893Sadrian exit(127); 246238893Sadrian } 247238893Sadrian rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv)); 248238893Sadrian 249238893Sadrian /* Next */ 250238893Sadrian av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 251238893Sadrian sizeof(struct ath_rateioctl_rt)); 252238893Sadrian if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) { 253238893Sadrian fprintf(stderr, "unexpected rate control TLV (got 0x%x, " 254238893Sadrian "expected 0x%x\n", 255238893Sadrian av->tlv_id, 256238893Sadrian ATH_RATE_TLV_SAMPLENODE); 257238893Sadrian exit(127); 258238893Sadrian } 259238893Sadrian if (av->tlv_len != sizeof(struct sample_node)) { 260238893Sadrian fprintf(stderr, "unexpected TLV len (got %d bytes, " 261238893Sadrian "expected %d bytes\n", 262238893Sadrian av->tlv_len, 263244966Sadrian (int) sizeof(struct sample_node)); 264238893Sadrian exit(127); 265238893Sadrian } 266238893Sadrian sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) + 267238893Sadrian sizeof(struct ath_rateioctl_rt) + 268238893Sadrian sizeof(struct ath_rateioctl_tlv)); 269238893Sadrian 270238893Sadrian ath_sample_stats(r, rt, sn); 271244966Sadrian 272244966Sadrian return (0); 273238893Sadrian} 274238893Sadrian 275240223Sadrianstatic void 276240223Sadrianfetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e, 277240223Sadrian uint8_t *buf) 278240223Sadrian{ 279238893Sadrian 280240223Sadrian /* Zero the buffer before it's passed in */ 281240223Sadrian memset(buf, '\0', STATS_BUF_SIZE); 282240223Sadrian 283240223Sadrian /* 284240223Sadrian * Set the station address for this lookup. 285240223Sadrian */ 286240223Sadrian ath_setsta(r, e->octet); 287240223Sadrian 288240223Sadrian /* 289240223Sadrian * Fetch the data from the driver. 290240223Sadrian */ 291240223Sadrian ath_rate_ioctl(r); 292240223Sadrian 293240223Sadrian /* 294240223Sadrian * Decode and parse statistics. 295240223Sadrian */ 296240223Sadrian rate_node_stats(r, e); 297240223Sadrian} 298240223Sadrian 299238893Sadrianint 300238893Sadrianmain(int argc, char *argv[]) 301238893Sadrian{ 302238646Sadrian char const *ifname = NULL, *macaddr = NULL; 303238646Sadrian int c; 304238646Sadrian int do_all = 0; 305238893Sadrian struct ether_addr *e; 306238893Sadrian struct ath_ratestats r; 307238893Sadrian uint8_t *buf; 308240223Sadrian useconds_t sleep_period; 309240223Sadrian float f; 310240298Sadrian short cf, cb; 311238635Sadrian 312238646Sadrian ifname = getenv("ATH"); 313238646Sadrian if (ifname == NULL) 314238646Sadrian ifname = "ath0"; 315238646Sadrian 316240223Sadrian while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) { 317238646Sadrian switch (c) { 318238646Sadrian case 'a': 319238646Sadrian do_all = 1; 320238646Sadrian break; 321238646Sadrian case 'i': 322238646Sadrian ifname = optarg; 323238646Sadrian break; 324238646Sadrian case 'm': 325238646Sadrian macaddr = optarg; 326238646Sadrian break; 327240223Sadrian case 's': 328240223Sadrian sscanf(optarg, "%f", &f); 329240223Sadrian do_loop = 1; 330240223Sadrian sleep_period = (useconds_t) (f * 1000000.0); 331240223Sadrian break; 332238646Sadrian default: 333238646Sadrian errx(1, 334240223Sadrian "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n", 335238646Sadrian argv[0]); 336238646Sadrian /* NOTREACHED */ 337238646Sadrian } 338238646Sadrian } 339238646Sadrian 340238646Sadrian if (macaddr == NULL) { 341238646Sadrian errx(1, "%s: macaddress wasn't supplied and no -a given\n", 342238646Sadrian argv[0]); 343238646Sadrian /* NOTREACHED */ 344238646Sadrian } 345238646Sadrian e = ether_aton(macaddr); 346238646Sadrian if (e == NULL) 347238646Sadrian err(1, "ether_aton"); 348238635Sadrian 349238893Sadrian bzero(&r, sizeof(r)); 350238893Sadrian 351238893Sadrian /* 352238893Sadrian * Persistent buffer for each lookup 353238893Sadrian */ 354238893Sadrian buf = malloc(STATS_BUF_SIZE); 355238893Sadrian if (buf == NULL) 356238893Sadrian err(1, "calloc"); 357238893Sadrian 358244967Sadrian r.re.buf = (char *) buf; 359238635Sadrian r.re.len = STATS_BUF_SIZE; 360238635Sadrian 361238893Sadrian r.s = socket(AF_INET, SOCK_DGRAM, 0); 362238893Sadrian if (r.s < 0) { 363238893Sadrian err(1, "socket"); 364238893Sadrian } 365240223Sadrian 366238893Sadrian /* XXX error check */ 367238893Sadrian ath_setifname(&r, ifname); 368238893Sadrian 369240223Sadrian if (do_loop) { 370240223Sadrian initscr(); 371240223Sadrian start_color(); 372240223Sadrian use_default_colors(); 373240298Sadrian pair_content(0, &cf, &cb); 374240298Sadrian /* Error - medium */ 375240298Sadrian init_pair(1, COLOR_YELLOW, cb); 376240298Sadrian /* Error - high */ 377240298Sadrian init_pair(2, COLOR_RED, cb); 378240298Sadrian /* Sample */ 379240298Sadrian init_pair(3, COLOR_CYAN, cb); 380240298Sadrian /* 250 byte frames */ 381240298Sadrian init_pair(4, COLOR_BLUE, cb); 382240298Sadrian /* 1600 byte frames */ 383240298Sadrian init_pair(5, COLOR_MAGENTA, cb); 384240223Sadrian cbreak(); 385240223Sadrian noecho(); 386240223Sadrian nonl(); 387240223Sadrian nodelay(stdscr, 1); 388240223Sadrian intrflush(stdscr, FALSE); 389240223Sadrian keypad(stdscr, TRUE); 390238893Sadrian 391240223Sadrian while (1) { 392240223Sadrian clear(); 393240223Sadrian move(0, 0); 394240223Sadrian fetch_and_print_stats(&r, e, buf); 395240223Sadrian refresh(); 396240223Sadrian usleep(sleep_period); 397240223Sadrian } 398240223Sadrian } else 399240223Sadrian fetch_and_print_stats(&r, e, buf); 400238893Sadrian 401240223Sadrian exit(0); 402238635Sadrian} 403