aboutsummaryrefslogblamecommitdiff
path: root/tools/tools/ath/athratestats/main.c
blob: 317b025c18549260c1ea8927298bbe3f73ebcbe3 (plain) (tree)







































                                                                             








                   

                   








                                        

                       



                                                                  
                             
 






                                                 










                                                 




                                




















                                                                 
           

                                                                      
 
                      

                   
                                                     
                           
                                      

                                                    
                                                               
                                                              
                                           
                                   

                                                      


                                                
                                                                  
                                       



                                                             
                                         
                                                                
                

                                                                  

                                                
                                                      
         
                                                                          

                                                                          
                                    
                                                                          
                          




                                                                 
                                                      
                                                                               
                                                               
                                                                     





                                                                      
                                                           
                                                             

                                               


                                                                        





                                                                  
                                                      
                                                                                
                                                               
                                                                      





                                                                      




                                               
                 

                                       










                                                               
                                                 







                                                                     
 



                                                           

                                                              
 
                                     

                                           
                      
                                             















                                                                         
                                                           

















                                                                         
                                                      






                                                               

                   

 






















                                                                    



                            


                                                   


                               

                                
                     


                               
                                     
 
                                                            









                                         




                                                                    

                               
                                                                                               



                                        
 







                                                                           
 








                                            
                                

                                  



                                             
 


                                  



                                     










                                                















                                                          
 
                
 
/*-
 * Copyright (c) 2012, Adrian Chadd.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
 *    redistribution must be conditioned upon including a substantially
 *    similar Disclaimer requirement for further binary redistribution.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGES.
 *
 * $FreeBSD$
 */

#include "opt_ah.h"

#include <sys/types.h>
#include <sys/file.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_media.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <err.h>

#include <curses.h>

#include "ah.h"
#include "ah_desc.h"
#include "net80211/ieee80211_ioctl.h"
#include "net80211/ieee80211_radiotap.h"
#include "if_athioctl.h"
#include "if_athrate.h"

#include "ath_rate/sample/sample.h"

static int do_loop = 0;

/*
 * This needs to be big enough to fit the two TLVs, the rate table
 * and the rate statistics table for a single node.
 */
#define	STATS_BUF_SIZE	65536

#define	PRINTMSG(...) do {			\
	if (do_loop == 0)			\
		printf(__VA_ARGS__);		\
	else					\
		printw(__VA_ARGS__);		\
	} while (0)

#define	PRINTATTR_ON(_x) do {			\
	if (do_loop)				\
		attron(_x);			\
	} while(0)


#define	PRINTATTR_OFF(_x) do {			\
	if (do_loop)				\
		attroff(_x);			\
	} while(0)

struct ath_ratestats {
	int s;
	struct ath_rateioctl re;
};

static inline int
dot11rate(struct ath_rateioctl_rt *rt, int rix)
{

	if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
		return rt->ratecode[rix] & ~(IEEE80211_RATE_MCS);
	else
		return (rt->ratecode[rix] / 2);
}

static const char *
dot11str(struct ath_rateioctl_rt *rt, int rix)
{
	if (rix == -1)
		return "";
	else if (rt->ratecode[rix] & IEEE80211_RATE_MCS)
		return "MCS";
	else
		return " Mb";
}

static void
ath_sample_stats(struct ath_ratestats *r, struct ath_rateioctl_rt *rt,
    struct sample_node *sn)
{
	uint64_t mask;
	int rix, y;

	PRINTMSG("static_rix (%d) ratemask 0x%llx\n",
	    sn->static_rix,
	    (long long) sn->ratemask);

	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
		PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
		PRINTMSG("[%4u] cur rate %d %s since switch: "
		    "packets %d ticks %u ",
		    bin_to_size(y),
		    dot11rate(rt, sn->current_rix[y]),
		    dot11str(rt, sn->current_rix[y]),
		    sn->packets_since_switch[y],
		    sn->ticks_since_switch[y]);

		PRINTMSG("last sample (%d %s) cur sample (%d %s) "
		    "packets sent %d ",
		    dot11rate(rt, sn->last_sample_rix[y]),
		    dot11str(rt, sn->last_sample_rix[y]),
		    dot11rate(rt, sn->current_sample_rix[y]),
		    dot11str(rt, sn->current_sample_rix[y]),
		    sn->packets_sent[y]);
		PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
		
		PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
		PRINTMSG("packets since sample %d sample tt %u\n",
		    sn->packets_since_sample[y],
		    sn->sample_tt[y]);
		PRINTATTR_OFF(COLOR_PAIR(3) | A_BOLD);
	}
	PRINTMSG("   TX Rate      TXTOTAL:TXOK       EWMA          T/   F"
	    "     avg last xmit  ");
	PRINTMSG("   TX Rate      TXTOTAL:TXOK       EWMA          T/   F"
	    "     avg last xmit\n");
	for (mask = sn->ratemask, rix = 0; mask != 0; mask >>= 1, rix++) {
		int c = 0;
		if ((mask & 1) == 0)
				continue;
		for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
			if (sn->stats[y][rix].total_packets == 0)
				continue;
			if (rix == sn->current_rix[y])
				PRINTATTR_ON(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
			else if (rix == sn->last_sample_rix[y])
				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
#if 0
			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
				PRINTATTR_ON(COLOR_PAIR(2) | A_BOLD);
			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
				PRINTATTR_ON(COLOR_PAIR(1) | A_BOLD);
#endif
			PRINTMSG("[%2u %s:%5u] %8ju:%-8ju "
			    "(%3d.%1d%%) %8ju/%4d %5uuS %u ",
			    dot11rate(rt, rix),
			    dot11str(rt, rix),
			    bin_to_size(y),
			    (uintmax_t) sn->stats[y][rix].total_packets,
			    (uintmax_t) sn->stats[y][rix].packets_acked,
			    sn->stats[y][rix].ewma_pct / 10,
			    sn->stats[y][rix].ewma_pct % 10,
			    (uintmax_t) sn->stats[y][rix].tries,
			    sn->stats[y][rix].successive_failures,
			    sn->stats[y][rix].average_tx_time,
			    sn->stats[y][rix].last_tx);
			if (rix == sn->current_rix[y])
				PRINTATTR_OFF(COLOR_PAIR(2 + (y % 4)) | A_BOLD);
			else if (rix == sn->last_sample_rix[y])
				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
#if 0
			else if (sn->stats[y][rix].ewma_pct / 10 < 50)
				PRINTATTR_OFF(COLOR_PAIR(2) | A_BOLD);
			else if (sn->stats[y][rix].ewma_pct / 10 < 75)
				PRINTATTR_OFF(COLOR_PAIR(1) | A_BOLD);
#endif
			c++;
			if (c == 2) {
				PRINTMSG("\n");
				c = 0;
			}
		}
		if (c != 0)
			PRINTMSG("\n");
	}
}

static void
ath_setifname(struct ath_ratestats *r, const char *ifname)
{

	strncpy(r->re.if_name, ifname, sizeof (r->re.if_name));
}

static void
ath_setsta(struct ath_ratestats *r, uint8_t *mac)
{

	memcpy(&r->re.is_u.macaddr, mac, sizeof(r->re.is_u.macaddr));
}

static void
ath_rate_ioctl(struct ath_ratestats *r)
{

	if (ioctl(r->s, SIOCGATHNODERATESTATS, &r->re) < 0)
		err(1, "ioctl");
}

static int
rate_node_stats(struct ath_ratestats *r, struct ether_addr *e)
{
	struct ath_rateioctl_tlv *av;
	struct sample_node *sn = NULL;
	struct ath_rateioctl_rt *rt = NULL;
	int error = 0;
	uint8_t *buf = (uint8_t *) r->re.buf;

	/*
	 * For now, hard-code the TLV order and contents.  Ew!
	 */
	av = (struct ath_rateioctl_tlv *) buf;
	if (av->tlv_id != ATH_RATE_TLV_RATETABLE) {
		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
		    "expected 0x%x\n",
		    av->tlv_id,
		    ATH_RATE_TLV_RATETABLE);
		exit(127);
	}
	if (av->tlv_len != sizeof(struct ath_rateioctl_rt)) {
		fprintf(stderr, "unexpected TLV len (got %d bytes, "
		    "expected %d bytes\n",
		    av->tlv_len,
		    (int) sizeof(struct ath_rateioctl_rt));
		exit(127);
	}
	rt = (void *) (buf + sizeof(struct ath_rateioctl_tlv));

	/* Next */
	av = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
	    sizeof(struct ath_rateioctl_rt));
	if (av->tlv_id != ATH_RATE_TLV_SAMPLENODE) {
		fprintf(stderr, "unexpected rate control TLV (got 0x%x, "
		    "expected 0x%x\n",
		    av->tlv_id,
		    ATH_RATE_TLV_SAMPLENODE);
		exit(127);
	}
	if (av->tlv_len != sizeof(struct sample_node)) {
		fprintf(stderr, "unexpected TLV len (got %d bytes, "
		    "expected %d bytes\n",
		    av->tlv_len,
		    (int) sizeof(struct sample_node));
		exit(127);
	}
	sn = (void *) (buf + sizeof(struct ath_rateioctl_tlv) +
	    sizeof(struct ath_rateioctl_rt) +
	    sizeof(struct ath_rateioctl_tlv));

	ath_sample_stats(r, rt, sn);

	return (0);
}

static void
fetch_and_print_stats(struct ath_ratestats *r, struct ether_addr *e,
    uint8_t *buf)
{

	/* Zero the buffer before it's passed in */
	memset(buf, '\0', STATS_BUF_SIZE);

	/*
	 * Set the station address for this lookup.
	 */
	ath_setsta(r, e->octet);

	/*
	 * Fetch the data from the driver.
	 */
	ath_rate_ioctl(r);

	/*
	 * Decode and parse statistics.
	 */
	rate_node_stats(r, e);
}

int
main(int argc, char *argv[])
{
	char const *ifname = NULL, *macaddr = NULL;
	int c;
	int do_all = 0;
	struct ether_addr *e;
	struct ath_ratestats r;
	uint8_t *buf;
	useconds_t sleep_period;
	float f;
	short cf, cb;

	ifname = getenv("ATH");
	if (ifname == NULL)
		ifname = ATH_DEFAULT;

	while ((c = getopt(argc, argv, "ahi:m:s:")) != -1) {
		switch (c) {
		case 'a':
			do_all = 1;
			break;
		case 'i':
			ifname = optarg;
			break;
		case 'm':
			macaddr = optarg;
			break;
		case 's':
			sscanf(optarg, "%f", &f);
			do_loop = 1;
			sleep_period = (useconds_t) (f * 1000000.0);
			break;
		default:
			errx(1,
			    "usage: %s [-h] [-i ifname] [-a] [-m macaddr] [-s sleep period]\n",
			    argv[0]);
			/* NOTREACHED */
		}
	}

	if (macaddr == NULL) {
		errx(1, "%s: macaddress wasn't supplied and no -a given\n",
		    argv[0]);
		/* NOTREACHED */
	}
	e = ether_aton(macaddr);
	if (e == NULL)
		err(1, "ether_aton");

	bzero(&r, sizeof(r));

	/*
	 * Persistent buffer for each lookup
	 */
	buf = malloc(STATS_BUF_SIZE);
	if (buf == NULL)
		err(1, "calloc");

	r.re.buf = (char *) buf;
	r.re.len = STATS_BUF_SIZE;

	r.s = socket(AF_INET, SOCK_DGRAM, 0);
	if (r.s < 0) {
		err(1, "socket");
	}

	/* XXX error check */
	ath_setifname(&r, ifname);

	if (do_loop) {
		initscr();
		start_color();
		use_default_colors();
		pair_content(0, &cf, &cb);
		/* Error - medium */
		init_pair(1, COLOR_YELLOW, cb);
		/* Error - high */
		init_pair(2, COLOR_RED, cb);
		/* Sample */
		init_pair(3, COLOR_CYAN, cb);
		/* 250 byte frames */
		init_pair(4, COLOR_BLUE, cb);
		/* 1600 byte frames */
		init_pair(5, COLOR_MAGENTA, cb);
		cbreak();
		noecho();
		nonl();
		nodelay(stdscr, 1);
		intrflush(stdscr, FALSE);
		keypad(stdscr, TRUE);

		while (1) {
			clear();
			move(0, 0);
			fetch_and_print_stats(&r, e, buf);
			refresh();
			usleep(sleep_period);
		}
	} else
		fetch_and_print_stats(&r, e, buf);

	exit(0);
}