/**** Copyright (c) AvantCom Corp 2003 - 2004 ***/
/**** All rights reserved ***/
/*__________________________________________________________________
  |                                                                |
  |         Copyright 2003 - 2004  -  AvantCom Corporation         |
  |                                                                |
  |    This material is the exclusive confidential property of     |
  |    AvantCom Corporation. All rights, including copyrights      |
  |    and patent rights, are reserved. No copies of any portion   |
  |    are to be made by any means without the express written     |
  |    permission of AvantCom Corporation.                         |
  |                                                                |
  |________________________________________________________________|
*/ 
/****************************************************************************
*                                                                           *
*  File Name:           avc802dot11.c                                       *
*  Used By:                                                                 *
*                                                                           *
*  Operating System:                                                        *
*  Purpose:                                                                 *
*                                                                           *
*  Comments:                                                                *
*                                                                           *
*  Author:              Larry Simmons                                       *
*                       lsimmons@avantcom.com                               *
*                       www.avantcom.com                                    *
*                                                                           *
*  Creation Date:       09/24/03                                            *
*                                                                           *
*   Ver    Date   Inits Modification                                        *
*  ----- -------- ----- ------------                                        *
*  0.0.1 09/24/03  LRS  created                                             *
****************************************************************************/
/****************************************************************************
*                               Includes                                    *
****************************************************************************/
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#include "avc802dot11.h"
#include "dirent.h"

/****************************************************************************
*                                Defines                                    *
****************************************************************************/
#define TABLE_SIZE   1
//#define MINLOADFREQ 15                    // min reload frequency in seconds
#define HOSTAPPATH  "/proc/net/hostap/"
#define MADWIFIPATH "/proc/sys/net/"

#define MINLOADFREQ 5                       // min reload frequency in seconds     // for testing
//#define HOSTAPPATH  "/home/lsimmons/proc/net/hostap/"                            // for testing
//#define MADWIFIPATH "/home/lsimmons/proc/sys/net/"                               // for testing

#define EXPECTED_MIB_VERSION   "Version 0.0.2 - 09/25/2003"
#define AGENT_VERSION          "Version 0.0.2 - 09/25/2003"

#ifndef UCHAR
  typedef unsigned char UCHAR;
#endif

/****************************************************************************
*                            Private Functions                              *
****************************************************************************/
static void loadTables();
static void loadHostAPClients();
static void loadMadWiFiStats();

// Linked List Functions
static void addList ( char *, char *, int );
static void initLists();                    // initialize all the linked lists
static void flushLists();                   // flush all the linked lists
static void flushList ( char * );           // flush a single linked list

// Utility Functions
static int isMACAddress ( char * );
static void strtoupper  ( char * );
static unsigned int htoui ( char * );
static char *htob ( char * );

/****************************************************************************
*                            Private Variables                              *
****************************************************************************/
static unsigned long lastLoad = 0;          // ET in secs at last table load

static struct avc802dot11Node *lastNode, *newNode, *np;

/****************************************************************************
*                            External Functions                             *
****************************************************************************/

/****************************************************************************
*   avc802dot11_variables_oid:                                              *
*       this is the top level oid that we want to register under.  This     *
*       is essentially a prefix, with the suffix appearing in the           *
*       variable below.                                                     *
****************************************************************************/
oid avc802dot11_variables_oid[] = { 1,3,6,1,3,14614 };

/****************************************************************************
*   variable7 avc802dot11_variables:                                        *
*     this variable defines function callbacks and type return information  *
*     for the ieee802dot11 mib section                                      *
****************************************************************************/
struct variable7 avc802dot11_variables[] = {
/*  magic number        , variable type , ro/rw , callback fn  , L, oidsuffix */
#define   SYSEXPECTEDMIBVERSION  2
  { SYSEXPECTEDMIBVERSION, ASN_OCTET_STR , RONLY , var_avc802dot11, 2, { 1,2 } },
#define   SYSAGENTVERSION       3
  { SYSAGENTVERSION     , ASN_OCTET_STR , RONLY , var_avc802dot11, 2, { 1,3 } },


#define   PRACMACADDRESS        6
  { PRACMACADDRESS      , ASN_OCTET_STR , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,1 } },
#define   PRACSTATIONTYPE       7
  { PRACSTATIONTYPE     , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,2 } },
#define   PRACLISTENINTERVAL    8
  { PRACLISTENINTERVAL  , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,3 } },
#define   PRACSUPPORTS1MBPS     9
  { PRACSUPPORTS1MBPS   , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,4 } },
#define   PRACSUPPORTS2MBPS     10
  { PRACSUPPORTS2MBPS   , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,5 } },
#define   PRACSUPPORTS5MBPS     11
  { PRACSUPPORTS5MBPS   , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,6 } },
#define   PRACSUPPORTS11MBPS    12
  { PRACSUPPORTS11MBPS  , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,7 } },
#define   PRACJIFFIES           13
  { PRACJIFFIES         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,8 } },
#define   PRACLASTAUTH          14
  { PRACLASTAUTH        , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,9 } },
#define   PRACLASTASSOC         15
  { PRACLASTASSOC       , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,10 } },
#define   PRACLASTRX            16
  { PRACLASTRX          , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,11 } },
#define   PRACLASTTX            17
  { PRACLASTTX          , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,12 } },
#define   PRACRXPACKETS         18
  { PRACRXPACKETS       , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,13 } },
#define   PRACTXPACKETS         19
  { PRACTXPACKETS       , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,14 } },
#define   PRACRXBYTES           20
  { PRACRXBYTES         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,15 } },
#define   PRACTXBYTES           21
  { PRACTXBYTES         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,16 } },
#define   PRACBFRCNT            22
  { PRACBFRCNT          , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,17 } },
#define   PRACLASTRXSILENCE     23
  { PRACLASTRXSILENCE   , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,18 } },
#define   PRACLASTRXSIGNAL      24
  { PRACLASTRXSIGNAL    , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,19 } },
#define   PRACLASTRXRATE        25
  { PRACLASTRXRATE      , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,20 } },
#define   PRACLASTRXFLOW        26
  { PRACLASTRXFLOW      , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,21 } },
#define   PRACTXRATE            27
  { PRACTXRATE          , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,22 } },
#define   PRACTX1MBPS           28
  { PRACTX1MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,23 } },
#define   PRACTX2MBPS           29
  { PRACTX2MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,24 } },
#define   PRACTX5MBPS           30
  { PRACTX5MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,25 } },
#define   PRACTX11MBPS          31
  { PRACTX11MBPS        , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,26 } },
#define   PRACRX1MBPS           32
  { PRACRX1MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,27 } },
#define   PRACRX2MBPS           33
  { PRACRX2MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,28 } },
#define   PRACRX5MBPS           34
  { PRACRX5MBPS         , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,29 } },
#define   PRACRX11MBPS          35
  { PRACRX11MBPS        , ASN_INTEGER   , RONLY , var_prAssociatedClientsTable, 5, { 2,1,1,1,30 } },


#define   AMCWATCHDOG           38
  { AMCWATCHDOG         , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,1 } },
#define   AMCFATALHWINTERRUPT   39
  { AMCFATALHWINTERRUPT , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,2 } },
#define   AMCMISSEDBEACONS      40
  { AMCMISSEDBEACONS    , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,3 } },
#define   AMCRXOVERRUN          41
  { AMCRXOVERRUN        , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,4 } },
#define   AMCRXEOL              42
  { AMCRXEOL            , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,5 } },
#define   AMCTXUNDERRUN         43
  { AMCTXUNDERRUN       , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,6 } },
#define   AMCTXMGMTFRAMES       44
  { AMCTXMGMTFRAMES     , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,7 } },
#define   AMCTXDISCARDS         45
  { AMCTXDISCARDS       , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,8 } },
#define   AMCTXINVALID          46
  { AMCTXINVALID        , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,9 } },
#define   AMCTXQUEUESTOPPED     47
  { AMCTXQUEUESTOPPED   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,10 } },
#define   AMCTXENCAPFAILED      48
  { AMCTXENCAPFAILED    , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,11 } },
#define   AMCTXNONODE           49
  { AMCTXNONODE         , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,12 } },
#define   AMCTXNODATABUFFER     50
  { AMCTXNODATABUFFER   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,13 } },
#define   AMCTXNOMGMTBUFFER     51
  { AMCTXNOMGMTBUFFER   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,14 } },
#define   AMCTXEXCESSIVERETRIES  52
  { AMCTXEXCESSIVERETRIES, ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,15 } },
#define   AMCTXFIFOUNDERRUN     53
  { AMCTXFIFOUNDERRUN   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,16 } },
#define   AMCTXFILTERED         54
  { AMCTXFILTERED       , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,17 } },
#define   AMCTXSHORTRETRIES     55
  { AMCTXSHORTRETRIES   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,18 } },
#define   AMCTXLONGRETRIES      56
  { AMCTXLONGRETRIES    , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,19 } },
#define   AMCTXBADRATE          57
  { AMCTXBADRATE        , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,20 } },
#define   AMCTXNOACK            58
  { AMCTXNOACK          , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,21 } },
#define   AMCTXRTS              59
  { AMCTXRTS            , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,22 } },
#define   AMCTXCTS              60
  { AMCTXCTS            , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,23 } },
#define   AMCTXSHORTPREAMBLE    61
  { AMCTXSHORTPREAMBLE  , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,24 } },
#define   AMCTXRSSI             62
  { AMCTXRSSI           , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,25 } },
#define   AMCTXRSSIDELTA        63
  { AMCTXRSSIDELTA      , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,26 } },
#define   AMCRXDESCOVERRUN      64
  { AMCRXDESCOVERRUN    , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,27 } },
#define   AMCRXFRAMETOOSHORT    65
  { AMCRXFRAMETOOSHORT  , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,28 } },
#define   AMCRXCRCERROR         66
  { AMCRXCRCERROR       , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,29 } },
#define   AMCRXFIFOOVERRUN      67
  { AMCRXFIFOOVERRUN    , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,30 } },
#define   AMCRXCRYPTFAILURE     68
  { AMCRXCRYPTFAILURE   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,31 } },
#define   AMCRXNOSKBUFFER       69
  { AMCRXNOSKBUFFER     , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,32 } },
#define   AMCRXRSSI             70
  { AMCRXRSSI           , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,33 } },
#define   AMCRXRSSIDELTA        71
  { AMCRXRSSIDELTA      , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,34 } },
#define   AMCNOBEACONBUFFER     72
  { AMCNOBEACONBUFFER   , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,35 } },
#define   AMCCALIBRATIONS       73
  { AMCCALIBRATIONS     , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,36 } },
#define   AMCFAILEDCALIBRATIONS  74
  { AMCFAILEDCALIBRATIONS, ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,37 } },
#define   AMCRFGAINRESETS       75
  { AMCRFGAINRESETS     , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,38 } },
#define   AMCRATECONTROLCHECKS  76
  { AMCRATECONTROLCHECKS, ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,39 } },
#define   AMCRATECONTROLRAISE   77
  { AMCRATECONTROLRAISE , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,40 } },
#define   AMCRATECONTROLDROP    78
  { AMCRATECONTROLDROP  , ASN_INTEGER   , RONLY , var_athMacCountersTable, 5, { 3,1,1,1,41 } },


#define   APCTOTALPHYERRORS     81
  { APCTOTALPHYERRORS   , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,1 } },
#define   APCTXUNDERRUN         82
  { APCTXUNDERRUN       , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,2 } },
#define   APCTIMINGERRORS       83
  { APCTIMINGERRORS     , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,3 } },
#define   APCPARITYERRORS       84
  { APCPARITYERRORS     , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,4 } },
#define   APCINVALIDRATE        85
  { APCINVALIDRATE      , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,5 } },
#define   APCINVALIDLENGTH      86
  { APCINVALIDLENGTH    , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,6 } },
#define   APCRADARDETECTED      87
  { APCRADARDETECTED    , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,7 } },
#define   APCINVALIDSERVICE     88
  { APCINVALIDSERVICE   , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,8 } },
#define   APCTXOVERRIDERX       89
  { APCTXOVERRIDERX     , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,9 } },
#define   APCOFDMTIMING         90
  { APCOFDMTIMING       , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,10 } },
#define   APCOFDMSIGNALPARITY   91
  { APCOFDMSIGNALPARITY , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,11 } },
#define   APCOFDMINVALIDRATE    92
  { APCOFDMINVALIDRATE  , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,12 } },
#define   APCOFDMINVALIDLENGTH  93
  { APCOFDMINVALIDLENGTH, ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,13 } },
#define   APCOFDMPOWERDROP      94
  { APCOFDMPOWERDROP    , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,14 } },
#define   APCOFDMSERVICE        95
  { APCOFDMSERVICE      , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,15 } },
#define   APCOFDMRESTART        96
  { APCOFDMRESTART      , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,16 } },
#define   APCCCKTIMING          97
  { APCCCKTIMING        , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,17 } },
#define   APCCCKHEADERCRC       98
  { APCCCKHEADERCRC     , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,18 } },
#define   APCCCKINVALIDRATE     99
  { APCCCKINVALIDRATE   , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,19 } },
#define   APCCCKSERVICE         100
  { APCCCKSERVICE       , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,20 } },
#define   APCCCKRESTART         101
  { APCCCKRESTART       , ASN_INTEGER   , RONLY , var_athPhyCountersTable, 5, { 3,2,1,1,21 } },
};
/*    (L = length of the oidsuffix) */

/****************************************************************************
*                                                                           *
*          init_avc802dot11() - perform any required initialization         *
*                                                                           *
****************************************************************************/
void init_avc802dot11(void) {

  /* register ourselves with the agent to handle our mib tree */
  REGISTER_MIB("avc802dot11", avc802dot11_variables, variable7,
               avc802dot11_variables_oid);

  initLists();
}


/****************************************************************************
*                                                                           *
*     shutdown_avc802dot11() - perform any required cleanup @ shutdown      *
*                                                                           *
****************************************************************************/
void shutdown_avc802dot11 ( void )
{
  flushLists();
}

/****************************************************************************
*                                                                           *
*   var_avc802dot11() -                                                     *
*                                                                           *
****************************************************************************/
unsigned char *
var_avc802dot11(struct variable *vp, 
                oid     *name, 
                size_t  *length, 
                int     exact, 
                size_t  *var_len, 
                WriteMethod **write_method)
{
  static unsigned char string[SPRINT_MAX_LEN];

  if ( header_generic ( vp, name, length, exact, var_len, write_method )
                                  == MATCH_FAILED )
    return NULL;

  switch ( vp->magic ) {

    case SYSEXPECTEDMIBVERSION:
      strcpy ( string, EXPECTED_MIB_VERSION );
      *var_len = strlen ( string );
      return ( UCHAR * ) string;

    case SYSAGENTVERSION:
      strcpy ( string, AGENT_VERSION );
      *var_len = strlen ( string );
      return ( UCHAR * ) string;

    default:
      ERROR_MSG ( "" );
  }

  return NULL;
}

/****************************************************************************
*                                                                           *
*   var_prAssociatedClientsTable() -                                        *
*                                                                           *
****************************************************************************/
unsigned char *
var_prAssociatedClientsTable ( struct variable *vp,
                                oid     *name,
                                size_t  *length,
                                int     exact,
                                size_t  *var_len,
                                WriteMethod **write_method )
{
  oid rName [ MAX_OID_LEN ];                            // OID to be returned
  int found = FALSE;
  static char MACWork[17];

  loadTables();

  memcpy (( char * ) rName, ( char * ) vp->name, ( int ) vp->namelen * sizeof ( oid ));
  for ( np = LIST_FIRST ( &prAcList ); np != NULL; np = LIST_NEXT ( np, nodes )) {
    prAc = ( struct prAcTbl_data * ) np->data;
    rName[vp->namelen + 0] = prAc->ifIndex;
    rName[vp->namelen + 1] = htoui ( &prAc->macAddress[0] );
    rName[vp->namelen + 2] = htoui ( &prAc->macAddress[3] );
    rName[vp->namelen + 3] = htoui ( &prAc->macAddress[6] );
    rName[vp->namelen + 4] = htoui ( &prAc->macAddress[9] );
    rName[vp->namelen + 5] = htoui ( &prAc->macAddress[12] );
    rName[vp->namelen + 6] = htoui ( &prAc->macAddress[15] );
    if ((  exact && ( snmp_oid_compare ( rName, vp->namelen + 7, name, *length ) == 0 )) ||
        ( !exact && ( snmp_oid_compare ( rName, vp->namelen + 7, name, *length ) >  0 ))) {
      found = TRUE;
      break;
    }
  }

  if ( !found  )
    return NULL;

  memcpy (( char * ) name, ( char * ) rName, ( vp->namelen + 7 ) * sizeof ( oid ));
  *length = vp->namelen + 7;
  *write_method = 0;
  *var_len = sizeof ( long );

  switch ( vp->magic ) {

    case PRACMACADDRESS:
      MACWork[ 0] = toupper ( prAc->macAddress[ 0] );
      MACWork[ 1] = toupper ( prAc->macAddress[ 1] );
      MACWork[ 2] = toupper ( prAc->macAddress[ 3] );
      MACWork[ 3] = toupper ( prAc->macAddress[ 4] );
      MACWork[ 4] = toupper ( prAc->macAddress[ 6] );
      MACWork[ 5] = toupper ( prAc->macAddress[ 7] );
      MACWork[ 6] = toupper ( prAc->macAddress[ 9] );
      MACWork[ 7] = toupper ( prAc->macAddress[10] );
      MACWork[ 8] = toupper ( prAc->macAddress[12] );
      MACWork[ 9] = toupper ( prAc->macAddress[13] );
      MACWork[10] = toupper ( prAc->macAddress[15] );
      MACWork[11] = toupper ( prAc->macAddress[16] );
      MACWork[12] = '\0';
      *var_len = 6;
      return ( UCHAR * ) htob ( MACWork );
        
    case PRACSTATIONTYPE:     return ( UCHAR * ) &prAc->stationType;
    case PRACLISTENINTERVAL:  return ( UCHAR * ) &prAc->listenInterval;
    case PRACSUPPORTS1MBPS:   return ( UCHAR * ) &prAc->supports1Mbps;
    case PRACSUPPORTS2MBPS:   return ( UCHAR * ) &prAc->supports2Mbps;
    case PRACSUPPORTS5MBPS:   return ( UCHAR * ) &prAc->supports5Mbps;
    case PRACSUPPORTS11MBPS:  return ( UCHAR * ) &prAc->supports11Mbps;
    case PRACJIFFIES:         return ( UCHAR * ) &prAc->jiffies;
    case PRACLASTAUTH:        return ( UCHAR * ) &prAc->lastAuth;
    case PRACLASTASSOC:       return ( UCHAR * ) &prAc->lastAssoc;
    case PRACLASTRX:          return ( UCHAR * ) &prAc->lastRx;
    case PRACLASTTX:          return ( UCHAR * ) &prAc->lastTx;
    case PRACRXPACKETS:       return ( UCHAR * ) &prAc->rxPackets;
    case PRACTXPACKETS:       return ( UCHAR * ) &prAc->txPackets;
    case PRACRXBYTES:         return ( UCHAR * ) &prAc->rxBytes;
    case PRACTXBYTES:         return ( UCHAR * ) &prAc->txBytes;
    case PRACBFRCNT:          return ( UCHAR * ) &prAc->bfrCnt;
    case PRACLASTRXSILENCE:   return ( UCHAR * ) &prAc->lastRxSilence;
    case PRACLASTRXSIGNAL:    return ( UCHAR * ) &prAc->lastRxSignal;
    case PRACLASTRXRATE:      return ( UCHAR * ) &prAc->lastRxRate;
    case PRACLASTRXFLOW:      return ( UCHAR * ) &prAc->lastRxFlow;
    case PRACTXRATE:          return ( UCHAR * ) &prAc->txRate;
    case PRACTX1MBPS:         return ( UCHAR * ) &prAc->tx1Mbps;
    case PRACTX2MBPS:         return ( UCHAR * ) &prAc->tx2Mbps;
    case PRACTX5MBPS:         return ( UCHAR * ) &prAc->tx5Mbps;
    case PRACTX11MBPS:        return ( UCHAR * ) &prAc->tx11Mbps;
    case PRACRX1MBPS:         return ( UCHAR * ) &prAc->rx1Mbps;
    case PRACRX2MBPS:         return ( UCHAR * ) &prAc->rx2Mbps;
    case PRACRX5MBPS:         return ( UCHAR * ) &prAc->rx5Mbps;
    case PRACRX11MBPS:        return ( UCHAR * ) &prAc->rx11Mbps;

    default:
      ERROR_MSG ( "" );
  }

  return NULL;
}

/****************************************************************************
*                                                                           *
*   var_athMacCountersTable() -                                             *
*                                                                           *
****************************************************************************/
unsigned char *
var_athMacCountersTable ( struct variable *vp,
    	                      oid     *name,
    	                      size_t  *length,
    	                      int     exact,
    	                      size_t  *var_len,
    	                      WriteMethod **write_method)
{
  int found = FALSE;
  oid rName [ MAX_OID_LEN ];                            // OID to be returned

  loadTables();
  memcpy (( char * ) rName, ( char * ) vp->name, ( int ) vp->namelen * sizeof ( oid ));
  for ( np = LIST_FIRST ( &athMacList ); np != NULL; np = LIST_NEXT ( np, nodes )) {
    athMac = ( struct athMacTbl_data * ) np->data;
    rName[vp->namelen] = athMac->ifIndex;
    if ((  exact && ( snmp_oid_compare ( rName, vp->namelen + 1, name, *length ) == 0 )) || 
        ( !exact && ( snmp_oid_compare ( rName, vp->namelen + 1, name, *length ) >  0 ))) {
      found = TRUE;
      break;
    }
  }

  if ( !found ) 
    return NULL;

  memcpy (( char * ) name, ( char * ) rName, ( vp->namelen + 1 ) * sizeof ( oid ));
  *length = vp->namelen + 1;
  *var_len = sizeof ( long );
  *write_method = NULL;

  switch ( vp->magic ) {

    case AMCWATCHDOG:           return ( UCHAR * ) &athMac->watchDog;
    case AMCFATALHWINTERRUPT:   return ( UCHAR * ) &athMac->fatalHwInterrupt;
    case AMCMISSEDBEACONS:      return ( UCHAR * ) &athMac->missedBeacons;
    case AMCRXOVERRUN:          return ( UCHAR * ) &athMac->rxOverRun;
    case AMCRXEOL:              return ( UCHAR * ) &athMac->rxEol;
    case AMCTXUNDERRUN:         return ( UCHAR * ) &athMac->txUnderRun;
    case AMCTXMGMTFRAMES:       return ( UCHAR * ) &athMac->txMgmtFrames;
    case AMCTXDISCARDS:         return ( UCHAR * ) &athMac->txDiscards;
    case AMCTXINVALID:          return ( UCHAR * ) &athMac->txInvalid;
    case AMCTXQUEUESTOPPED:     return ( UCHAR * ) &athMac->txQueueStopped;
    case AMCTXENCAPFAILED:      return ( UCHAR * ) &athMac->txEncapFailed;
    case AMCTXNONODE:           return ( UCHAR * ) &athMac->txNoNode;
    case AMCTXNODATABUFFER:     return ( UCHAR * ) &athMac->txNoDataBuffer;
    case AMCTXNOMGMTBUFFER:     return ( UCHAR * ) &athMac->txNoMgmtBuffer;
    case AMCTXEXCESSIVERETRIES: return ( UCHAR * ) &athMac->txExcessiveRetries;
    case AMCTXFIFOUNDERRUN:     return ( UCHAR * ) &athMac->txFifoUnderRun;
    case AMCTXFILTERED:         return ( UCHAR * ) &athMac->txFiltered;
    case AMCTXSHORTRETRIES:     return ( UCHAR * ) &athMac->txShortRetries;
    case AMCTXLONGRETRIES:      return ( UCHAR * ) &athMac->txLongRetries;
    case AMCTXBADRATE:          return ( UCHAR * ) &athMac->txBadRate;
    case AMCTXNOACK:            return ( UCHAR * ) &athMac->txNoAck;
    case AMCTXRTS:              return ( UCHAR * ) &athMac->txRts;
    case AMCTXCTS:              return ( UCHAR * ) &athMac->txCts;
    case AMCTXSHORTPREAMBLE:    return ( UCHAR * ) &athMac->txShortPreamble;
    case AMCTXRSSI:             return ( UCHAR * ) &athMac->txRssi;
    case AMCTXRSSIDELTA:        return ( UCHAR * ) &athMac->txRssiDelta;
    case AMCRXDESCOVERRUN:      return ( UCHAR * ) &athMac->rxDescOverRun;
    case AMCRXFRAMETOOSHORT:    return ( UCHAR * ) &athMac->rxFrameTooShort;
    case AMCRXCRCERROR:         return ( UCHAR * ) &athMac->rxCrcError;
    case AMCRXFIFOOVERRUN:      return ( UCHAR * ) &athMac->rxFifoOverRun;
    case AMCRXCRYPTFAILURE:     return ( UCHAR * ) &athMac->rxCryptFailure;
    case AMCRXNOSKBUFFER:       return ( UCHAR * ) &athMac->rxNoSkBuffer;
    case AMCRXRSSI:             return ( UCHAR * ) &athMac->rxRssi;
    case AMCRXRSSIDELTA:        return ( UCHAR * ) &athMac->rxRssiDelta;
    case AMCNOBEACONBUFFER:     return ( UCHAR * ) &athMac->noBeaconBuffer;
    case AMCCALIBRATIONS:       return ( UCHAR * ) &athMac->calibrations;
    case AMCFAILEDCALIBRATIONS: return ( UCHAR * ) &athMac->failedCalibrations;
    case AMCRFGAINRESETS:       return ( UCHAR * ) &athMac->rfGainResets;
    case AMCRATECONTROLCHECKS:  return ( UCHAR * ) &athMac->rateControlChecks;
    case AMCRATECONTROLRAISE:   return ( UCHAR * ) &athMac->rateControlRaise;
    case AMCRATECONTROLDROP:    return ( UCHAR * ) &athMac->rateControlDrop;

    default:
      ERROR_MSG ( "" );
  }

  return NULL;
}

/****************************************************************************
*                                                                           *
*   var_athPhyCountersTable() -                                             *
*                                                                           *
****************************************************************************/
unsigned char *
var_athPhyCountersTable ( struct variable *vp,
                    	    oid     *name,
    	                    size_t  *length,
    	                    int     exact,
    	                    size_t  *var_len,
    	                    WriteMethod **write_method)
{
  int found = FALSE;
  oid rName [ MAX_OID_LEN ];                            // OID to be returned

  loadTables();
  memcpy (( char * ) rName, ( char * ) vp->name, ( int ) vp->namelen * sizeof ( oid ));
  for ( np = LIST_FIRST ( &athPhyList ); np != NULL; np = LIST_NEXT ( np, nodes )) {
    athPhy = ( struct athPhyTbl_data * ) np->data;
    rName[vp->namelen] = athPhy->ifIndex;
    if ((  exact && ( snmp_oid_compare ( rName, vp->namelen + 1, name, *length ) == 0 )) || 
        ( !exact && ( snmp_oid_compare ( rName, vp->namelen + 1, name, *length ) >  0 ))) {
      found = TRUE;
      break;
    }
  }

  if ( !found ) 
    return NULL;

  memcpy (( char * ) name, ( char * ) rName, ( vp->namelen + 1 ) * sizeof ( oid ));
  *length = vp->namelen + 1;
  *var_len = sizeof ( long );
  *write_method = NULL;

  switch ( vp->magic ) {

    case APCTOTALPHYERRORS:     return ( UCHAR * ) &athPhy->totalPhyErrors;
    case APCTXUNDERRUN:         return ( UCHAR * ) &athPhy->txUnderRun;
    case APCTIMINGERRORS:       return ( UCHAR * ) &athPhy->timingErrors;
    case APCPARITYERRORS:       return ( UCHAR * ) &athPhy->parityErrors;
    case APCINVALIDRATE:        return ( UCHAR * ) &athPhy->invalidRate;
    case APCINVALIDLENGTH:      return ( UCHAR * ) &athPhy->invalidLength;
    case APCRADARDETECTED:      return ( UCHAR * ) &athPhy->radarDetected;
    case APCINVALIDSERVICE:     return ( UCHAR * ) &athPhy->invalidService;
    case APCTXOVERRIDERX:       return ( UCHAR * ) &athPhy->txOverrideRx;
    case APCOFDMTIMING:         return ( UCHAR * ) &athPhy->OfdmTiming;
    case APCOFDMSIGNALPARITY:   return ( UCHAR * ) &athPhy->OfdmSignalParity;
    case APCOFDMINVALIDRATE:    return ( UCHAR * ) &athPhy->OfdmInvalidRate;
    case APCOFDMINVALIDLENGTH:  return ( UCHAR * ) &athPhy->OfdmInvalidLength;
    case APCOFDMPOWERDROP:      return ( UCHAR * ) &athPhy->OfdmPowerDrop;
    case APCOFDMSERVICE:        return ( UCHAR * ) &athPhy->OfdmService;
    case APCOFDMRESTART:        return ( UCHAR * ) &athPhy->OfdmRestart;
    case APCCCKTIMING:          return ( UCHAR * ) &athPhy->CckTiming;
    case APCCCKHEADERCRC:       return ( UCHAR * ) &athPhy->CckHeaderCrc;
    case APCCCKINVALIDRATE:     return ( UCHAR * ) &athPhy->CckInvalidRate;
    case APCCCKSERVICE:         return ( UCHAR * ) &athPhy->CckService;
    case APCCCKRESTART:         return ( UCHAR * ) &athPhy->CckRestart;

    default:
      ERROR_MSG ( "" );
  }

  return NULL;
}

/****************************************************************************
*                                                                           *
*                     loadTables() - Load the Tables                        *
*                                                                           *
****************************************************************************/
static void loadTables()
{
  struct timeval et;                              // elapsed time

  gettimeofday ( &et, ( struct timezone * ) 0 );  // get time-of-day
  if ( et.tv_sec < lastLoad + MINLOADFREQ )       // only reload so often
    return;
  lastLoad = et.tv_sec;

  flushLists();
  loadHostAPClients();
  loadMadWiFiStats();
}

/****************************************************************************
*                                                                           *
*              loadHostAPClients() - Load associated clients                *
*                                                                           *
****************************************************************************/
static void loadHostAPClients()
{
  int i;
  DIR *dp;
  FILE *fp;
  char name[512], path[512], bfr[1024];
  struct dirent *dir;

  for ( i = 0; i < MAXRADIOS; i++ ) {
    memset (( char * ) &nPrAc, 0, sizeof ( nPrAc ));
    nPrAc.supports1Mbps = 2;                  // 2 => false
    nPrAc.supports2Mbps = 2;
    nPrAc.supports5Mbps = 2;
    nPrAc.supports11Mbps = 2;
    sprintf ( nPrAc.ifName, "wlan%d\0", i );
    sprintf ( path, "%s/%s/", HOSTAPPATH, nPrAc.ifName );
    dp = opendir ( path );                    // open directory
    if ( dp ) { 
      while  ( dir = readdir ( dp )) {        // loop thru directory entries
        if ( isMACAddress ( dir->d_name )) {  // name look like MAC address?

          sprintf ( name, "%s/%s", path, dir->d_name );
//        printf ( " ===> %s - name: %s <=== \n", "loadTables()", name );

          nPrAc.ifIndex = if_nametoindex ( nPrAc.ifName );
//        nPrAc.ifIndex = i + 1;                              // XXX - for testing //
          strcpy ( nPrAc.macAddress, dir->d_name );
          strtoupper ( nPrAc.macAddress );
          sprintf ( nPrAc.UID, "%04d %s\0", nPrAc.ifIndex, nPrAc.macAddress );

          fp = fopen ( name, "r" );
          if ( fp ) {
            while ( !feof ( fp )) {
              if ( fgets ( bfr, sizeof ( bfr ) - 1, fp )) {
                strtok (( char * ) &bfr, "\n" );          // '\n' => '\0'
//              printf ( "%s\n", bfr );

                if ( strstr ( bfr, MBPS1 ))
                  nPrAc.supports1Mbps = 1;
                if ( strstr ( bfr, MBPS2 ))
                  nPrAc.supports2Mbps = 1;
                if ( strstr ( bfr, MBPS5 ))
                  nPrAc.supports5Mbps = 1;
                if ( strstr ( bfr, MBPS11 ))
                  nPrAc.supports11Mbps = 1;

                if ( strstr ( bfr, SILENCE ))
                  sscanf ( bfr, "last_rx: silence=%d signal=%d rate=%d flow=%d",
                            &nPrAc.lastRxSilence, &nPrAc.lastRxSignal,
                            &nPrAc.lastRxRate,    &nPrAc.lastRxFlow     );

                if ( strstr ( bfr, AP )) 
                  nPrAc.stationType = 2;
                else if ( strstr ( bfr, STA )) 
                  nPrAc.stationType = 1;
                else if ( strstr ( bfr, LISTENINTERVAL ))
                  nPrAc.listenInterval = atol ( &bfr [ strlen ( LISTENINTERVAL )]);
                else if ( strstr ( bfr, JIFFIES ))
                  nPrAc.jiffies= atol ( &bfr [ strlen ( JIFFIES )]);
                else if ( strstr ( bfr, LASTAUTH ))
                  nPrAc.lastAuth= atol ( &bfr [ strlen ( LASTAUTH )]);
                else if ( strstr ( bfr, LASTASSOC ))
                  nPrAc.lastAssoc= atol ( &bfr [ strlen ( LASTASSOC )]);
                else if ( strstr ( bfr, LASTRX ))
                  nPrAc.lastRx = atol ( &bfr [ strlen ( LASTRX )]);
                else if ( strstr ( bfr, LASTTX ))
                  nPrAc.lastTx = atol ( &bfr [ strlen ( LASTTX )]);
                else if ( strstr ( bfr, RXPACKETS ))
                  nPrAc.rxPackets = atol ( &bfr [ strlen ( RXPACKETS )]);
                else if ( strstr ( bfr, TXPACKETS ))
                  nPrAc.txPackets = atol ( &bfr [ strlen ( TXPACKETS )]);
                else if ( strstr ( bfr, RXBYTES ))
                  nPrAc.rxBytes = atol ( &bfr [ strlen ( RXBYTES )]);
                else if ( strstr ( bfr, TXBYTES ))
                  nPrAc.txBytes = atol ( &bfr [ strlen ( TXBYTES )]);
                else if ( strstr ( bfr, BFRCNT ))
                  nPrAc.bfrCnt = atol ( &bfr [ strlen ( BFRCNT )]);
                else if ( strstr ( bfr, TXRATE ))
                  nPrAc.txRate = atol ( &bfr [ strlen ( TXRATE )]);
                else if ( strstr ( bfr, TX1M ))
                  nPrAc.tx1Mbps = atol ( &bfr [ strlen ( TX1M )]);
                else if ( strstr ( bfr, TX2M ))
                  nPrAc.tx2Mbps = atol ( &bfr [ strlen ( TX2M )]);
                else if ( strstr ( bfr, TX5M ))
                  nPrAc.tx5Mbps = atol ( &bfr [ strlen ( TX5M )]);
                else if ( strstr ( bfr, TX11M ))
                  nPrAc.tx11Mbps = atol ( &bfr [ strlen ( TX11M )]);
                else if ( strstr ( bfr, RX1M ))
                  nPrAc.rx1Mbps = atol ( &bfr [ strlen ( RX1M )]);
                else if ( strstr ( bfr, RX2M ))
                  nPrAc.rx2Mbps = atol ( &bfr [ strlen ( RX2M )]);
                else if ( strstr ( bfr, RX5M ))
                  nPrAc.rx5Mbps = atol ( &bfr [ strlen ( RX5M )]);
                else if ( strstr ( bfr, RX11M ))
                  nPrAc.rx11Mbps = atol ( &bfr [ strlen ( RX11M )]);
              }
            }
            addList (( char * ) &prAcList, ( char * ) &nPrAc, sizeof ( nPrAc ));
//          printf ( "ifName: %s ifIndex: %d macAddress: %s stationType: %d UID: %s\n", 
//                      nPrAc.ifName, nPrAc.ifIndex, nPrAc.macAddress, 
//                      nPrAc.stationType, nPrAc.UID );
            fclose ( fp );
          }
        }
      }
      closedir ( dp );                        // close directory file
    }
  }
}

/****************************************************************************
*                                                                           *
*              loadMadWiFiStats() - load MadWiFi stats                      *
*                                                                           *
****************************************************************************/
static void loadMadWiFiStats()
{
  int i;
  FILE *fp;
  char name[512], path[512], bfr[1024];

//some day the driver will support more than one radio
//for ( i = 0; i < MAXRADIOS; i++ ) {
    memset (( char * ) &nAthMac, 0, sizeof ( nAthMac ));
    memset (( char * ) &nAthPhy, 0, sizeof ( nAthPhy ));
    sprintf ( nAthMac.ifName, "ath0\0" );
    sprintf ( nAthPhy.ifName, "ath0\0" );
    sprintf ( path, "%s/%s/", MADWIFIPATH, "ath" );
//  sprintf ( nAthMac.ifName, "ath%d\0", i );
//  sprintf ( nAthPhy.ifName, "ath%d\0", i );
//  sprintf ( path, "%s/%s/", MADWIFIPATH, nAthMac.ifName );
    sprintf ( name, "%s/%s", path, "stats" );
//  printf ( "%s - name: %s\n", "loadMadWiFiStats()", name );

    fp = fopen ( name, "r" );
    if ( fp ) {

      nAthMac.ifIndex = if_nametoindex ( nAthMac.ifName );
      nAthPhy.ifIndex = if_nametoindex ( nAthPhy.ifName );
//    nAthMac.ifIndex = i + 1;                              // XXX - for testing //
//    nAthPhy.ifIndex = i + 1;                              // XXX - for testing //
      sprintf ( nAthMac.UID, "%04d\0", nAthMac.ifIndex );
      sprintf ( nAthPhy.UID, "%04d\0", nAthPhy.ifIndex );

      while ( !feof ( fp )) {
        if ( fgets ( bfr, sizeof ( bfr ) - 1, fp )) {
          strtok (( char * ) &bfr, "\n" );                  // '\n' => '\0'
//        printf ( "%s\n", bfr );

          if ( strstr ( bfr, WATCHDOG ))
            nAthMac.watchDog = atol ( &bfr [ strlen ( WATCHDOG )]);
          else if ( strstr ( bfr, HARDWARE ))
            nAthMac.fatalHwInterrupt = atol ( &bfr [ strlen ( HARDWARE )]);
          else if ( strstr ( bfr, BMISS ))
            nAthMac.missedBeacons = atol ( &bfr [ strlen ( BMISS )]);
          else if ( strstr ( bfr, RXORN ))
            nAthMac.rxOverRun = atol ( &bfr [ strlen ( RXORN )]);
          else if ( strstr ( bfr, RXEOL ))
            nAthMac.rxEol = atol ( &bfr [ strlen ( RXEOL )]);
          else if ( strstr ( bfr, TXURN ))
            nAthMac.txUnderRun = atol ( &bfr [ strlen ( TXURN )]);
          else if ( strstr ( bfr, TX_MGMT ))
            nAthMac.txMgmtFrames = atol ( &bfr [ strlen ( TX_MGMT )]);
          else if ( strstr ( bfr, TX_DISCARD ))
            nAthMac.txDiscards = atol ( &bfr [ strlen ( TX_DISCARD )]);
          else if ( strstr ( bfr, TX_INVALID ))
            nAthMac.txInvalid = atol ( &bfr [ strlen ( TX_INVALID )]);
          else if ( strstr ( bfr, TX_QSTOP ))
            nAthMac.txQueueStopped = atol ( &bfr [ strlen ( TX_QSTOP )]);
          else if ( strstr ( bfr, TX_ENCAP ))
            nAthMac.txEncapFailed = atol ( &bfr [ strlen ( TX_ENCAP )]);
          else if ( strstr ( bfr, TX_NONODE ))
            nAthMac.txNoNode = atol ( &bfr [ strlen ( TX_NONODE )]);
          else if ( strstr ( bfr, TX_NOBUF ))
            nAthMac.txNoNode = atol ( &bfr [ strlen ( TX_NOBUF )]);
          else if ( strstr ( bfr, TX_NOBUFMGT ))
            nAthMac.txNoMgmtBuffer = atol ( &bfr [ strlen ( TX_NOBUFMGT )]);
          else if ( strstr ( bfr, TX_XRETRIES ))
            nAthMac.txExcessiveRetries = atol ( &bfr [ strlen ( TX_XRETRIES )]);
          else if ( strstr ( bfr, TX_FIFOERR ))
            nAthMac.txFifoUnderRun = atol ( &bfr [ strlen ( TX_FIFOERR )]);
          else if ( strstr ( bfr, TX_FILTERED ))
            nAthMac.txFiltered = atol ( &bfr [ strlen ( TX_FILTERED )]);
          else if ( strstr ( bfr, TX_SHORTRETRY ))
            nAthMac.txShortRetries = atol ( &bfr [ strlen ( TX_SHORTRETRY )]);
          else if ( strstr ( bfr, TX_LONGRETRY ))
            nAthMac.txLongRetries = atol ( &bfr [ strlen ( TX_LONGRETRY )]);
          else if ( strstr ( bfr, TX_BADRATE ))
            nAthMac.txBadRate = atol ( &bfr [ strlen ( TX_BADRATE )]);
          else if ( strstr ( bfr, TX_NOACK ))
            nAthMac.txNoAck = atol ( &bfr [ strlen ( TX_NOACK )]);
          else if ( strstr ( bfr, TX_RTS ))
            nAthMac.txRts = atol ( &bfr [ strlen ( TX_RTS )]);
          else if ( strstr ( bfr, TX_CTS ))
            nAthMac.txCts = atol ( &bfr [ strlen ( TX_CTS )]);
          else if ( strstr ( bfr, TX_SHORTPRE ))
            nAthMac.txShortPreamble = atol ( &bfr [ strlen ( TX_SHORTPRE )]);
          else if ( strstr ( bfr, TX_RSSI ))
            nAthMac.txRssi = atol ( &bfr [ strlen ( TX_RSSI )]);
          else if ( strstr ( bfr, TX_RSSIDELTA ))
            nAthMac.txRssiDelta = atol ( &bfr [ strlen ( TX_RSSIDELTA )]);
          else if ( strstr ( bfr, RX_ORN ))
            nAthMac.rxOverRun = atol ( &bfr [ strlen ( RX_ORN )]);
          else if ( strstr ( bfr, RX_TOOSHORT ))
            nAthMac.rxFrameTooShort = atol ( &bfr [ strlen ( RX_TOOSHORT )]);
          else if ( strstr ( bfr, RX_CRCERR ))
            nAthMac.rxCrcError = atol ( &bfr [ strlen ( RX_CRCERR )]);
          else if ( strstr ( bfr, RX_FIFOERR ))
            nAthMac.rxFifoOverRun = atol ( &bfr [ strlen ( RX_FIFOERR )]);
          else if ( strstr ( bfr, RX_BADCRYPT ))
            nAthMac.rxCryptFailure = atol ( &bfr [ strlen ( RX_BADCRYPT )]);
          else if ( strstr ( bfr, RX_BADCRYPT ))
            nAthMac.rxCryptFailure = atol ( &bfr [ strlen ( RX_BADCRYPT )]);
          else if ( strstr ( bfr, RX_NOBUF ))
            nAthMac.rxNoSkBuffer = atol ( &bfr [ strlen ( RX_NOBUF )]);
          else if ( strstr ( bfr, RX_RSSI ))
            nAthMac.rxRssi = atol ( &bfr [ strlen ( RX_RSSI )]);
          else if ( strstr ( bfr, RX_RSSIDELTA ))
            nAthMac.rxRssiDelta = atol ( &bfr [ strlen ( RX_RSSIDELTA )]);
          else if ( strstr ( bfr, BE_NOBUF ))
            nAthMac.noBeaconBuffer = atol ( &bfr [ strlen ( BE_NOBUF )]);
          else if ( strstr ( bfr, PER_CAL ))
            nAthMac.calibrations = atol ( &bfr [ strlen ( PER_CAL )]);
          else if ( strstr ( bfr, PER_CALFAIL ))
            nAthMac.failedCalibrations = atol ( &bfr [ strlen ( PER_CALFAIL )]);
          else if ( strstr ( bfr, PER_RFGAIN ))
            nAthMac.rfGainResets = atol ( &bfr [ strlen ( PER_RFGAIN )]);
          else if ( strstr ( bfr, RATE_CALLS ))
            nAthMac.rateControlChecks = atol ( &bfr [ strlen ( RATE_CALLS )]);
          else if ( strstr ( bfr, RATE_RAISE ))
            nAthMac.rateControlRaise = atol ( &bfr [ strlen ( RATE_RAISE )]);
          else if ( strstr ( bfr, RATE_DROP ))
            nAthMac.rateControlDrop = atol ( &bfr [ strlen ( RATE_DROP )]);

          else if ( strstr ( bfr, RX_PHYERR ))
            nAthPhy.totalPhyErrors = atol ( &bfr [ strlen ( RX_PHYERR )]);
          else if ( strstr ( bfr, PHYERR_UNDERRUN ))
            nAthPhy.txUnderRun = atol ( &bfr [ strlen ( PHYERR_UNDERRUN )]);
          else if ( strstr ( bfr, PHYERR_TIMING ))
            nAthPhy.timingErrors = atol ( &bfr [ strlen ( PHYERR_TIMING )]);
          else if ( strstr ( bfr, PHYERR_PARITY ))
            nAthPhy.parityErrors = atol ( &bfr [ strlen ( PHYERR_PARITY )]);
          else if ( strstr ( bfr, PHYERR_RATE ))
            nAthPhy.invalidRate = atol ( &bfr [ strlen ( PHYERR_RATE )]);
          else if ( strstr ( bfr, PHYERR_LENGTH ))
            nAthPhy.invalidLength = atol ( &bfr [ strlen ( PHYERR_LENGTH )]);
          else if ( strstr ( bfr, PHYERR_RADAR ))
            nAthPhy.radarDetected = atol ( &bfr [ strlen ( PHYERR_RADAR )]);
          else if ( strstr ( bfr, PHYERR_SERVICE ))
            nAthPhy.invalidService = atol ( &bfr [ strlen ( PHYERR_SERVICE )]);
          else if ( strstr ( bfr, PHYERR_TOR ))
            nAthPhy.txOverrideRx = atol ( &bfr [ strlen ( PHYERR_TOR )]);
          else if ( strstr ( bfr, PHYERR_OFDM_TIMING )) 
            nAthPhy.OfdmTiming = atol ( &bfr [ strlen ( PHYERR_OFDM_TIMING )]);
          else if ( strstr ( bfr, PHYERR_OFDM_SIGNAL_PARITY ))
            nAthPhy.OfdmSignalParity = atol ( &bfr [ strlen ( PHYERR_OFDM_SIGNAL_PARITY )]);
          else if ( strstr ( bfr, PHYERR_OFDM_RATE_ILLEGAL ))
            nAthPhy.OfdmInvalidRate = atol ( &bfr [ strlen ( PHYERR_OFDM_RATE_ILLEGAL )]);
          else if ( strstr ( bfr, PHYERR_OFDM_LENGTH_ILLEGAL ))
            nAthPhy.OfdmInvalidLength = atol ( &bfr [ strlen ( PHYERR_OFDM_LENGTH_ILLEGAL )]);
          else if ( strstr ( bfr, PHYERR_OFDM_POWER_DROP ))
            nAthPhy.OfdmPowerDrop = atol ( &bfr [ strlen ( PHYERR_OFDM_POWER_DROP )]);
          else if ( strstr ( bfr, PHYERR_OFDM_SERVICE ))
            nAthPhy.OfdmService = atol ( &bfr [ strlen ( PHYERR_OFDM_SERVICE )]);
          else if ( strstr ( bfr, PHYERR_OFDM_RESTART ))
            nAthPhy.OfdmRestart = atol ( &bfr [ strlen ( PHYERR_OFDM_RESTART )]);
          else if ( strstr ( bfr, PHYERR_CCK_TIMING ))
            nAthPhy.CckTiming = atol ( &bfr [ strlen ( PHYERR_CCK_TIMING )]);
          else if ( strstr ( bfr, PHYERR_CCK_HEADER_CRC ))
            nAthPhy.CckHeaderCrc = atol ( &bfr [ strlen ( PHYERR_CCK_HEADER_CRC )]);
          else if ( strstr ( bfr, PHYERR_CCK_RATE_ILLEGAL ))
            nAthPhy.CckInvalidRate = atol ( &bfr [ strlen ( PHYERR_CCK_RATE_ILLEGAL )]);
          else if ( strstr ( bfr, PHYERR_CCK_SERVICE ))
            nAthPhy.CckService = atol ( &bfr [ strlen ( PHYERR_CCK_SERVICE )]);
          else if ( strstr ( bfr, PHYERR_CCK_RESTART ))
            nAthPhy.CckRestart = atol ( &bfr [ strlen ( PHYERR_CCK_RESTART )]);
        }
      }
      fclose ( fp );
      addList (( char * ) &athMacList, ( char * ) &nAthMac, sizeof ( nAthMac ));
      addList (( char * ) &athPhyList, ( char * ) &nAthPhy, sizeof ( nAthPhy ));
    }
//}
}

/****************************************************************************
*                                                                           *
*                        Linked List Functions                              *
*                                                                           *
****************************************************************************/
/****************************************************************************
*                                                                           *
*                addList() - add an entry to a linked list                  *
*                                                                           *
****************************************************************************/
static void 
addList ( char *l, char *data, int len  )
{
  char uid[256];
  LIST_HEAD ( , avc802dot11Node ) *list;       

  // NOTE: this assumes the UID is at the begining of the 
  //       data structure and that UIDs are strings
  
  list = ( LIST_HEAD ( , avc802dot11Node ) * ) l;   // NOTE: don't know how to get 
  strcpy ( uid, data );                             //  rid of compiler warning on
                                                    //  LISTHEAD typecast
  // create a new node and the data that goes in it
  newNode = malloc ( sizeof ( struct avc802dot11Node ));
  newNode->data = malloc ( len );
  memcpy ( newNode->data, data, len );

  // this deals with an empty list
  if ( LIST_EMPTY ( list )) {
    LIST_INSERT_HEAD ( list, newNode, nodes );
    return;
  }

  // this deals with UIDs that match
  for ( np = LIST_FIRST ( list ); np != NULL; np = LIST_NEXT ( np, nodes )) {
    if ( strncmp ( uid, np->data, strlen ( uid )) == 0 ) {                      // found matching UID
      LIST_INSERT_AFTER ( np, newNode, nodes );
      if ( np->data )
        free ( np->data );
      LIST_REMOVE ( np, nodes );
      free ( np );
      return;
    }
  }

  // this deals with inserting a new UID in the list
  for ( np = LIST_FIRST ( list ); np != NULL; np = LIST_NEXT ( np, nodes )) {
    lastNode = np;
    if ( strncmp ( np->data, uid, strlen ( uid )) > 0 ) {                       // old ID > new ID AND
      LIST_INSERT_BEFORE ( np, newNode, nodes );
      return;
    }
  }

  // this deals with a UID that needs to go on the end of the list
  LIST_INSERT_AFTER ( lastNode, newNode, nodes );

  return;
}

/****************************************************************************
*                                                                           *
*              initLists() - initialize all the linked lists                *
*                                                                           *
****************************************************************************/
static void initLists()
{
  LIST_INIT ( &prAcList   );
  LIST_INIT ( &athMacList );
  LIST_INIT ( &athPhyList );
}

/****************************************************************************
*                                                                           *
*                 flushLists() - flush all linked lists                     *
*                                                                           *
****************************************************************************/
static void flushLists()
{
  flushList (( char * ) &prAcList   );
  flushList (( char * ) &athMacList );
  flushList (( char * ) &athPhyList );
}

/****************************************************************************
*                                                                           *
*                   flushList() - flush a linked list                       *
*                                                                           *
****************************************************************************/
static void flushList ( char *l )
{
  LIST_HEAD ( , avc802dot11Node ) *list;
  
  list = ( LIST_HEAD ( , avc802dot11Node ) * ) l;   // NOTE: don't know how to get 
  while ( !LIST_EMPTY ( list )) {                   //  rid of compiler warning on
    np = LIST_FIRST ( list );                       //  LISTHEAD typecast
    if ( np->data )
      free ( np->data );
    LIST_REMOVE ( np, nodes );
    free ( np );
  }
}

/****************************************************************************
*                                                                           *
*                            Utility Functions                              *
*                                                                           *
****************************************************************************/
/****************************************************************************
*                                                                           *
*        isMACAddress() - this string look kinda like a MAC address?        *
*                                                                           *
****************************************************************************/
static int isMACAddress ( char *s )
{

  if ( strlen ( s ) == 17 &&
       s[2]  == ':'       && s[5]  == ':'       &&
       s[8]  == ':'       && s[11] == ':'       &&
       s[14] == ':'       &&
       isxdigit ( s[0]  ) && isxdigit ( s[1]  ) &&
       isxdigit ( s[3]  ) && isxdigit ( s[4]  ) &&
       isxdigit ( s[6]  ) && isxdigit ( s[7]  ) &&
       isxdigit ( s[9]  ) && isxdigit ( s[10] ) &&
       isxdigit ( s[12] ) && isxdigit ( s[13] ) &&
       isxdigit ( s[15] ) && isxdigit ( s[16] ))
    return ( TRUE );
  else
    return ( FALSE );
}

/****************************************************************************
*                                                                           *
*           strtoupper() - converts a string to upper case characters       *
*                                                                           *
****************************************************************************/
static void strtoupper ( char *s )
{
  while ( *s ) {
    *s = toupper ( *s );
    *s++;
  }
}

/****************************************************************************
*                                                                           *
*                 htob - converts hex string to binary                      *
*                                                                           *
****************************************************************************/
static char *htob ( char *s )
{
    char nibl, *byt;
    static char bin[20];

    byt = bin;

    while ((nibl = *s++) && nibl != ' ') {    /* While not end of string. */
      nibl -= ( nibl > '9') ?  ('A' - 10): '0';
      *byt = nibl << 4;                              /* place high nibble */
      if((nibl = *s++) && nibl != ' ') {
        nibl -= ( nibl > '9') ?  ('A' - 10): '0';
        *byt |= nibl;                                /*  place low nibble */
      }
      else break;
      ++byt;
    }
    *++byt = '\0';
    return ( bin );
}

/****************************************************************************
*                                                                           *
*             htoui - converts hex string to unsigned integer               *
*                                                                           *
****************************************************************************/
static unsigned int htoui ( char *cptr )
{
  unsigned int i, j = 0;

  while (cptr && *cptr && isxdigit(*cptr)) {
    i = *cptr++ - '0';
    if (9 < i)
      i -= 7;
    j <<= 4;
    j |= (i & 0x0f);
  }
  return ( j );
}
