/*      $NetBSD: wiconfig.c,v 1.46 2025/06/15 14:49:32 rillig Exp $     */
/*
* Copyright (c) 1997, 1998, 1999
*      Bill Paul <[email protected]>.  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.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed by Bill Paul.
* 4. Neither the name of the author nor the names of any co-contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 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 DAMAGE.
*
*      From: Id: wicontrol.c,v 1.6 1999/05/22 16:12:49 wpaul Exp $
*/

#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#include <net/if.h>
#ifdef __FreeBSD__
#include <net/if_var.h>
#include <net/ethernet.h>

#include <machine/if_wavelan_ieee.h>
#else
#include <netinet/in.h>
#include <netinet/if_ether.h>
#ifdef __NetBSD__
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#include <dev/ic/wi_ieee.h>
#else
#include <dev/pcmcia/if_wavelan_ieee.h>
#endif
#endif

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>

__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 1999\
Bill Paul.  All rights reserved.");
__RCSID("$NetBSD: wiconfig.c,v 1.46 2025/06/15 14:49:32 rillig Exp $");

struct wi_table {
       int wi_type;
       int wi_code;
#define WI_NONE                 0x00
#define WI_STRING               0x01
#define WI_BOOL                 0x02
#define WI_WORDS                0x03
#define WI_HEXBYTES             0x04
#define WI_KEYSTRUCT            0x05
#define WI_BITS                 0x06
#define WI_VENDOR               0x07
       const char *wi_label;           /* label used to print info */
       int wi_opt;                     /* option character to set this */
       const char *wi_desc;
       char *wi_optval;
};

/* already define in wireg.h XXX */
#define WI_APRATE_0             0x00    /* NONE */
#define WI_APRATE_1             0x0A    /* 1 Mbps */
#define WI_APRATE_2             0x14    /* 2 Mbps */
#define WI_APRATE_5             0x37    /* 5.5 Mbps */
#define WI_APRATE_11            0x6E    /* 11 Mbps */

#ifdef WI_RID_SCAN_APS
static void wi_apscan(char *);
static int  get_if_flags(int, const char *);
static int  set_if_flags(int, const char *, int);
#endif
static int  wi_getval(char *, struct wi_req *);
static void wi_setval(char *, struct wi_req *);
static void wi_printstr(struct wi_req *);
static void wi_setstr(char *, int, char *);
static void wi_setbytes(char *, int, char *, int);
static void wi_setword(char *, int, int);
static void wi_sethex(char *, int, char *);
static void wi_printwords(struct wi_req *);
static void wi_printbool(struct wi_req *);
static void wi_printhex(struct wi_req *);
static void wi_printbits(struct wi_req *);
static void wi_checkwifi(char *);
static void wi_dumpinfo(char *);
static void wi_printkeys(struct wi_req *);
static void wi_printvendor(struct wi_req *);
static void wi_dumpstats(char *);
__dead static void usage(void);
static struct wi_table *wi_optlookup(struct wi_table *, int);

#ifdef WI_RID_SCAN_APS
static int
get_if_flags(int s, const char *name)
{
       struct ifreq    ifreq;
       int             flags;

       strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
       if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifreq) == -1)
                 err(1, "SIOCGIFFLAGS");
       flags = ifreq.ifr_flags;

       return flags;
}

static int
set_if_flags(int s, const char *name, int flags)
{
       struct ifreq ifreq;

       ifreq.ifr_flags = flags;
       strncpy(ifreq.ifr_name, name, sizeof(ifreq.ifr_name));
       if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifreq) == -1)
                 err(1, "SIOCSIFFLAGS");

       return 0;
}

static void
wi_apscan(char *iface)
{
       struct wi_req           wreq;
       struct ifreq            ifr;
       int                     s;
       int                     naps, rate;
       int                     retries = 10;
       int                     flags;
       struct wi_apinfo        aps[howmany(WI_MAX_DATALEN,
                                   sizeof(struct wi_apinfo))];
       int                     i, j;

       if (iface == NULL)
               errx(1, "must specify interface name");

       s = socket(AF_INET, SOCK_DGRAM, 0);
       if (s == -1)
               err(1, "socket");
       flags = get_if_flags(s, iface);
       if ((flags & IFF_UP) == 0)
               flags = set_if_flags(s, iface, flags | IFF_UP);

       memset(&wreq, 0, sizeof(wreq));

       wreq.wi_type = WI_RID_SCAN_APS;
       wreq.wi_len = 4;
       /* note chan. 1 is the least significant bit */
       wreq.wi_val[0] = htole16(0x3fff);       /* 1 bit per channel, 1-14 */
       wreq.wi_val[1] = htole16(0xf);          /* tx rate */

       /* write the request */
       wi_setval(iface, &wreq);

       /* now poll for a result */
       memset(&wreq, 0, sizeof(wreq));

       wreq.wi_type = WI_RID_READ_APS;
       wreq.wi_len = WI_MAX_DATALEN;

       /* we have to do this ourself as opposed to
        * using getval, because we cannot bail if
        * the ioctl fails
        */
       memset(&ifr, 0, sizeof(ifr));
       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
       ifr.ifr_data = (caddr_t)&wreq;

       printf("scanning ...");
       fflush(stdout);
       while (ioctl(s, SIOCGWAVELAN, &ifr) == -1) {
               retries--;
               if (retries >= 0) {
                       printf(".");
                       fflush(stdout);
                       sleep(1);
               } else
                       break;
               errno = 0;
       }

       if (errno) {
               set_if_flags(s, iface, flags);
               close(s);
               err(1, "ioctl");
       }

       memcpy(&naps, wreq.wi_val, sizeof(int));

       if (naps > 0)
               printf("\nAP Information\n");
       else
               printf("\nNo APs available\n");

       naps = MIN((unsigned)naps,
           howmany(sizeof(wreq.wi_val) - sizeof(int), sizeof(*aps)));
       memcpy(aps, (const char *)wreq.wi_val + sizeof(int),
           (unsigned)naps * sizeof(*aps));

       for (i = 0; i < naps; i++) {
               const struct wi_apinfo *const w = &aps[i];

               printf("ap[%d]:\n", i);
               if (w->scanreason) {
                       static const char *scanm[] = {
                               "Host initiated",
                               "Firmware initiated",
                               "Inquiry request from host"
                       };
                       printf("\tScanReason:\t\t\t[ %s ]\n",
                           scanm[w->scanreason - 1]);
               }
               printf("\tnetname (SSID):\t\t\t[ ");
               for (j = 0; j < w->namelen; j++)
                       printf("%c", w->name[j]);
               printf(" ]\n");
               printf("\tBSSID:\t\t\t\t[ %02x:%02x:%02x:%02x:%02x:%02x ]\n",
                   w->bssid[0] & 0xff, w->bssid[1] & 0xff,
                   w->bssid[2] & 0xff, w->bssid[3] & 0xff,
                   w->bssid[4] & 0xff, w->bssid[5] & 0xff);
               printf("\tChannel:\t\t\t[ %d ]\n", w->channel);
               printf(""
                   "\tQuality/Signal/Noise [signal]:\t[ %d / %d / %d ]\n"
                   "\t                        [dBm]:\t[ %d / %d / %d ]\n",
                   w->quality, w->signal, w->noise,
                   w->quality, w->signal - 149, w->noise - 149);
               printf("\tBSS Beacon Interval [msec]:\t[ %d ]\n", w->interval);
               printf("\tCapinfo:\t\t\t[ ");
               if (w->capinfo & IEEE80211_CAPINFO_ESS)
                       printf("ESS ");
               if (w->capinfo & IEEE80211_CAPINFO_PRIVACY)
                       printf("WEP ");
               printf("]\n");

               switch (w->rate) {
               case WI_APRATE_1:
                       rate = 1;
                       break;
               case WI_APRATE_2:
                       rate = 2;
                       break;
               case WI_APRATE_5:
                       rate = 5;
                       break;
               case WI_APRATE_11:
                       rate = 11;
                       break;
               case WI_APRATE_0:
               default:
                       rate = 0;
                       break;
               }
               if (rate)
                       printf("\tDataRate [Mbps]:\t\t[ %d ]\n", rate);
       }

       set_if_flags(s, iface, flags);
       close(s);
}
#endif

static int
wi_getval(char *iface, struct wi_req *wreq)
{
       struct ifreq            ifr;
       int                     s, error;

       error = 0;
       memset(&ifr, 0, sizeof(ifr));

       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
       ifr.ifr_data = (caddr_t)wreq;

       s = socket(AF_INET, SOCK_DGRAM, 0);

       if (s == -1)
               err(1, "socket");

       if (ioctl(s, SIOCGWAVELAN, &ifr) == -1) {
               warn("SIOCGWAVELAN(wreq %04x)", wreq->wi_type);
               error = 1;
       }

       close(s);

       return error;
}

static void
wi_setval(char *iface, struct wi_req *wreq)
{
       struct ifreq            ifr;
       int                     s;

       memset(&ifr, 0, sizeof(ifr));

       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
       ifr.ifr_data = (caddr_t)wreq;

       s = socket(AF_INET, SOCK_DGRAM, 0);

       if (s == -1)
               err(1, "socket");

       if (ioctl(s, SIOCSWAVELAN, &ifr) == -1)
               err(1, "SIOCSWAVELAN");

       close(s);
}

static void
wi_printstr(struct wi_req *wreq)
{
       char                    *ptr;
       int                     i;

       if (wreq->wi_type == WI_RID_SERIALNO) {
               ptr = (char *)&wreq->wi_val;
               for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
                       if (ptr[i] == '\0')
                               ptr[i] = ' ';
               }
       } else {
               int len = le16toh(wreq->wi_val[0]);

               ptr = (char *)&wreq->wi_val[1];
               for (i = 0; i < len; i++) {
                       if (ptr[i] == '\0')
                               ptr[i] = ' ';
               }
       }

       ptr[i] = '\0';
       printf("[ %s ]", ptr);
}

static void
wi_setstr(char *iface, int code, char *str)
{
       struct wi_req           wreq;

       memset(&wreq, 0, sizeof(wreq));

       if (strlen(str) > 30)
               errx(1, "string too long");

       wreq.wi_type = code;
       wreq.wi_len = 18;
       wreq.wi_val[0] = htole16(strlen(str));
       bcopy(str, (char *)&wreq.wi_val[1], strlen(str));

       wi_setval(iface, &wreq);
}

static void
wi_setbytes(char *iface, int code, char *bytes, int len)
{
       struct wi_req           wreq;

       memset(&wreq, 0, sizeof(wreq));

       wreq.wi_type = code;
       wreq.wi_len = (len / 2) + 1;
       bcopy(bytes, (char *)&wreq.wi_val[0], len);

       wi_setval(iface, &wreq);
}

static void
wi_setword(char *iface, int code, int word)
{
       struct wi_req           wreq;

       memset(&wreq, 0, sizeof(wreq));

       wreq.wi_type = code;
       wreq.wi_len = 2;
       wreq.wi_val[0] = htole16(word);

       wi_setval(iface, &wreq);
}

static void
wi_sethex(char *iface, int code, char *str)
{
       struct ether_addr       *addr;

       addr = ether_aton(str);
       if (addr == NULL)
               errx(1, "badly formatted address");

       wi_setbytes(iface, code, (char *)addr, ETHER_ADDR_LEN);
}

static void
wi_printkeys(struct wi_req *wreq)
{
       int                     i, j, bn;
       struct wi_key           *k;
       struct wi_ltv_keys      *keys;
       char                    *ptr;

       keys = (struct wi_ltv_keys *)wreq;

       for (i = 0, bn = 0; i < 4; i++, bn = 0) {
               k = &keys->wi_keys[i];
               ptr = (char *)k->wi_keydat;
               for (j = 0; j < le16toh(k->wi_keylen); j++) {
                       if (!isprint((unsigned char)ptr[j])) {
                               bn = 1;
                               break;
                       }
               }

               if (bn) {
                       printf("[ 0x");
                       for (j = 0; j < le16toh(k->wi_keylen); j++)
                               printf("%02x", ((unsigned char *)ptr)[j]);
                       printf(" ]");
               } else {
                       ptr[j] = '\0';
                       printf("[ %s ]", ptr);
               }
       }
}

static void
wi_printvendor(struct wi_req *wreq)
{
       /* id
        * vendor
        * firmware major
        *          minor
        */
#define WI_RID_STA_IDENTITY_LUCENT      0x1
#define WI_RID_STA_IDENTITY_PRISMII     0x2
#define WI_RID_STA_IDENTITY_SAMSUNG     0x3
#define WI_RID_STA_IDENTITY_DLINK       0x6

       const char *vendor = "Unknown";

       if (wreq->wi_len < 4)
               return;

       switch (le16toh(wreq->wi_val[1])) {
       case WI_RID_STA_IDENTITY_LUCENT:
               vendor = "Lucent";
               break;
       case WI_RID_STA_IDENTITY_PRISMII:
               vendor = "generic PRISM II";
               break;
       case WI_RID_STA_IDENTITY_SAMSUNG:
               vendor = "Samsung";
               break;
       case WI_RID_STA_IDENTITY_DLINK:
               vendor = "D-Link";
               break;
       }
       printf("[ %s ID: %d version: %d.%d ]", vendor, le16toh(wreq->wi_val[0]),
           le16toh(wreq->wi_val[2]), le16toh(wreq->wi_val[3]));
}

static void
wi_printwords(struct wi_req *wreq)
{
       int                     i;

       printf("[ ");
       for (i = 0; i < wreq->wi_len - 1; i++)
               printf("%d ", le16toh(wreq->wi_val[i]));
       printf("]");
}

static void
wi_printbool(struct wi_req *wreq)
{
       if (le16toh(wreq->wi_val[0]))
               printf("[ On ]");
       else
               printf("[ Off ]");
}

static void
wi_printhex(struct wi_req *wreq)
{
       int                     i;
       unsigned char           *c;

       c = (unsigned char *)&wreq->wi_val;

       printf("[ ");
       for (i = 0; i < (wreq->wi_len - 1) * 2; i++) {
               printf("%02x", c[i]);
               if (i < ((wreq->wi_len - 1) * 2) - 1)
                       printf(":");
       }
       printf(" ]");
}

static void
wi_printbits(struct wi_req *wreq)
{
       int                     i;
       int bits = le16toh(wreq->wi_val[0]);

       printf("[");
       for (i = 0; i < 16; i++) {
               if (bits & 0x1)
                       printf(" %d", i + 1);
               bits >>= 1;
       }
       printf(" ]");
}

static struct wi_table wi_table[] = {
       { WI_RID_SERIALNO, WI_STRING, "NIC serial number:\t\t\t", 0, 0, 0 },
       { WI_RID_NODENAME, WI_STRING, "Station name:\t\t\t\t",
           's', "station name", 0, },
       { WI_RID_OWN_SSID, WI_STRING, "SSID for IBSS creation:\t\t\t", 0, 0, 0 },
       { WI_RID_CURRENT_SSID, WI_STRING, "Current netname (SSID):\t\t\t", 0, 0, 0 },
       { WI_RID_DESIRED_SSID, WI_STRING, "Desired netname (SSID):\t\t\t", 0, 0, 0 },
       { WI_RID_CURRENT_BSSID, WI_HEXBYTES, "Current BSSID:\t\t\t\t", 0, 0, 0 },
       { WI_RID_CHANNEL_LIST, WI_BITS, "Channel list:\t\t\t\t", 0, 0, 0 },
       { WI_RID_OWN_CHNL, WI_WORDS, "IBSS channel:\t\t\t\t", 0, 0, 0 },
       { WI_RID_CURRENT_CHAN, WI_WORDS, "Current channel:\t\t\t", 0, 0, 0 },
       { WI_RID_COMMS_QUALITY, WI_WORDS, "Comms quality/signal/noise:\t\t", 0, 0, 0 },
       { WI_RID_PROMISC, WI_BOOL, "Promiscuous mode:\t\t\t", 0, 0, 0 },
       { WI_RID_PORTTYPE, WI_WORDS, "Port type:\t\t\t\t", 0, 0, 0 },
       { WI_RID_MAC_NODE, WI_HEXBYTES, "MAC address:\t\t\t\t",
           'm', "MAC address", 0, },
       { WI_RID_TX_RATE, WI_WORDS, "TX rate (selection):\t\t\t", 0, 0, 0 },
       { WI_RID_CUR_TX_RATE, WI_WORDS, "TX rate (actual speed):\t\t\t", 0, 0, 0 },
       { WI_RID_CUR_BEACON_INT, WI_WORDS, "Beacon Interval (current) [msec]:\t", 0, 0, 0 },
       { WI_RID_MAX_DATALEN, WI_WORDS, "Maximum data length:\t\t\t",
           'd', "maximum data length", 0 },
       { WI_RID_RTS_THRESH, WI_WORDS, "RTS/CTS handshake threshold:\t\t",
           'r', "RTS threshold", 0 },
       { WI_RID_FRAG_THRESH, WI_WORDS, "fragmentation threshold:\t\t",
           'g', "fragmentation threshold", 0, },
       { WI_RID_DBM_ADJUST, WI_WORDS, "RSSI -> dBm adjustment:\t\t\t", 0, 0, 0 },
       { WI_RID_CREATE_IBSS, WI_BOOL, "Create IBSS:\t\t\t\t", 0, 0, 0 },
       { WI_RID_MICROWAVE_OVEN, WI_WORDS, "Microwave oven robustness:\t\t",
           'M', "microwave oven robustness enabled", 0 },
       { WI_RID_ROAMING_MODE, WI_WORDS, "Roaming mode(1:firm,3:disable):\t\t",
           'R', "roaming mode", 0 },
       { WI_RID_SYSTEM_SCALE, WI_WORDS, "Access point density:\t\t\t",
           'a', "system scale", 0 },
       { WI_RID_PM_ENABLED, WI_WORDS, "Power Mgmt (1=on, 0=off):\t\t", 0, 0, 0 },
       { WI_RID_MAX_SLEEP, WI_WORDS, "Max sleep time (msec):\t\t\t", 0, 0, 0 },
       { WI_RID_STA_IDENTITY, WI_VENDOR, "Vendor info:\t\t\t\t", 0, 0, 0 },
       { 0, WI_NONE, 0, 0, 0, 0 }
};

static struct wi_table wi_crypt_table[] = {
       { WI_RID_ENCRYPTION, WI_BOOL, "WEP encryption:\t\t\t\t", 0, 0, 0 },
       { WI_RID_CNFAUTHMODE, WI_WORDS, "Authentication type \n(1=OpenSys, 2=Shared Key):\t\t",
           'A', "authentication type", 0 },
       { WI_RID_TX_CRYPT_KEY, WI_WORDS, "TX encryption key:\t\t\t", 0, 0, 0 },
       { WI_RID_DEFLT_CRYPT_KEYS, WI_KEYSTRUCT, "Encryption keys:\t\t\t", 0, 0, 0 },
       { 0, WI_NONE, 0, 0, 0, 0 }
};

static struct wi_table *wi_tables[] = {
       wi_table,
       wi_crypt_table,
       NULL
};

static struct wi_table *
wi_optlookup(struct wi_table *table, int opt)
{
       struct wi_table *wt;

       for (wt = table; wt->wi_type != 0; wt++)
               if (wt->wi_opt == opt)
                       return wt;
       return NULL;
}

static void
wi_checkwifi(char *iface)
{
       struct ifreq            ifr;
       struct ieee80211_nwid   nwid;
       int                     s;

       memset(&ifr, 0, sizeof(ifr));

       strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
       ifr.ifr_data = (void *)&nwid;

       s = socket(AF_INET, SOCK_DGRAM, 0);

       if (s == -1)
               err(1, "socket");

       /* Choice of ioctl inspired by ifconfig/ieee80211.c */
       if (ioctl(s, SIOCG80211NWID, &ifr) == -1)
               err(1, "SIOCG80211NWID");

       close(s);
}

static void
wi_dumpinfo(char *iface)
{
       struct wi_req           wreq;
       int                     i, has_wep;
       struct wi_table         *w;

       memset(&wreq, 0, sizeof(wreq));

       wreq.wi_len = WI_MAX_DATALEN;
       wreq.wi_type = WI_RID_WEP_AVAIL;

       wi_getval(iface, &wreq);
       has_wep = le16toh(wreq.wi_val[0]);

       w = wi_table;

       for (i = 0; w[i].wi_code != WI_NONE; i++) {
               memset(&wreq, 0, sizeof(wreq));

               wreq.wi_len = WI_MAX_DATALEN;
               wreq.wi_type = w[i].wi_type;

               printf("%s", w[i].wi_label);
               if (wi_getval(iface, &wreq)) {
                       printf("[ Unknown ]\n");
                       continue;
               }
               switch (w[i].wi_code) {
               case WI_STRING:
                       wi_printstr(&wreq);
                       break;
               case WI_WORDS:
                       wi_printwords(&wreq);
                       break;
               case WI_BOOL:
                       wi_printbool(&wreq);
                       break;
               case WI_HEXBYTES:
                       wi_printhex(&wreq);
                       break;
               case WI_BITS:
                       wi_printbits(&wreq);
                       break;
               case WI_VENDOR:
                       wi_printvendor(&wreq);
                       break;
               default:
                       break;
               }
               printf("\n");
       }

       if (has_wep) {
               w = wi_crypt_table;
               for (i = 0; w[i].wi_code != WI_NONE; i++) {
                       memset(&wreq, 0, sizeof(wreq));

                       wreq.wi_len = WI_MAX_DATALEN;
                       wreq.wi_type = w[i].wi_type;

                       wi_getval(iface, &wreq);
                       printf("%s", w[i].wi_label);
                       switch (w[i].wi_code) {
                       case WI_STRING:
                               wi_printstr(&wreq);
                               break;
                       case WI_WORDS:
                               if (wreq.wi_type == WI_RID_TX_CRYPT_KEY)
                                       wreq.wi_val[0] =
                                         htole16(le16toh(wreq.wi_val[0]) + 1);
                               wi_printwords(&wreq);
                               break;
                       case WI_BOOL:
                               wi_printbool(&wreq);
                               break;
                       case WI_HEXBYTES:
                               wi_printhex(&wreq);
                               break;
                       case WI_KEYSTRUCT:
                               wi_printkeys(&wreq);
                               break;
                       default:
                               break;
                       }
                       printf("\n");
               }
       }
}

static void
wi_dumpstats(char *iface)
{
       struct wi_req           wreq;
       struct wi_counters      *c;

       memset(&wreq, 0, sizeof(wreq));
       wreq.wi_len = WI_MAX_DATALEN;
       wreq.wi_type = WI_RID_IFACE_STATS;

       wi_getval(iface, &wreq);

       c = (struct wi_counters *)&wreq.wi_val;

       /* XXX native byte order */
       printf("Transmitted unicast frames:\t\t%d\n",
           c->wi_tx_unicast_frames);
       printf("Transmitted multicast frames:\t\t%d\n",
           c->wi_tx_multicast_frames);
       printf("Transmitted fragments:\t\t\t%d\n",
           c->wi_tx_fragments);
       printf("Transmitted unicast octets:\t\t%d\n",
           c->wi_tx_unicast_octets);
       printf("Transmitted multicast octets:\t\t%d\n",
           c->wi_tx_multicast_octets);
       printf("Single transmit retries:\t\t%d\n",
           c->wi_tx_single_retries);
       printf("Multiple transmit retries:\t\t%d\n",
           c->wi_tx_multi_retries);
       printf("Transmit retry limit exceeded:\t\t%d\n",
           c->wi_tx_retry_limit);
       printf("Transmit discards:\t\t\t%d\n",
           c->wi_tx_discards);
       printf("Transmit discards due to wrong SA:\t%d\n",
           c->wi_tx_discards_wrong_sa);
       printf("Received unicast frames:\t\t%d\n",
           c->wi_rx_unicast_frames);
       printf("Received multicast frames:\t\t%d\n",
           c->wi_rx_multicast_frames);
       printf("Received fragments:\t\t\t%d\n",
           c->wi_rx_fragments);
       printf("Received unicast octets:\t\t%d\n",
           c->wi_rx_unicast_octets);
       printf("Received multicast octets:\t\t%d\n",
           c->wi_rx_multicast_octets);
       printf("Receive FCS errors:\t\t\t%d\n",
           c->wi_rx_fcs_errors);
       printf("Receive discards due to no buffer:\t%d\n",
           c->wi_rx_discards_nobuf);
       printf("Can't decrypt WEP frame:\t\t%d\n",
           c->wi_rx_WEP_cant_decrypt);
       printf("Received message fragments:\t\t%d\n",
           c->wi_rx_msg_in_msg_frags);
       printf("Received message bad fragments:\t\t%d\n",
           c->wi_rx_msg_in_bad_msg_frags);
}

static void
usage(void)
{

       fprintf(stderr,
           "usage: %s interface [-Dho] [-A 1|2] [-a access point density]\n"
           "                [-d max data length] [-g fragmentation threshold] [-M 0|1]\n"
           "                [-m MAC address] [-R 1|3] [-r RTS threshold] [-s station name]\n"
           ,
           getprogname());
       exit(1);
}

int
main(int argc, char *argv[])
{
       struct wi_table *wt, **table;
       char *iface;
       int ch, dumpinfo, dumpstats, apscan;

#define SET_OPERAND(opr, desc) do {                             \
       if ((opr) == NULL)                                      \
               (opr) = optarg;                                 \
       else                                                    \
               warnx("%s is already specified to %s",          \
                   desc, (opr));                               \
} while (0)

       dumpinfo = 1;
       dumpstats = 0;
       apscan = 0;
       iface = NULL;

       if (argc > 1 && argv[1][0] != '-') {
               iface = argv[1];
               optind++;
       }

       /* LINTED 338 "option should be handled in the switch" */
       while ((ch = getopt(argc, argv,
           "a:d:g:hi:m:or:s:A:M:R:D")) != -1) {
               if (ch != 'i')
                       dumpinfo = 0;
               /*
                * Lookup generic options and remember operand if found.
                */
               wt = NULL;      /* XXXGCC -Wuninitialized */
               for (table = wi_tables; *table != NULL; table++)
                       if ((wt = wi_optlookup(*table, ch)) != NULL) {
                               SET_OPERAND(wt->wi_optval, wt->wi_desc);
                               break;
                       }
               if (wt == NULL)
                       /*
                        * Handle special options.
                        */
                       switch (ch) {
                       case 'o':
                               dumpstats = 1;
                               break;
                       case 'i':
                               SET_OPERAND(iface, "interface");
                               break;
                       case 'D':
                               apscan = 1;
                               break;
                       case 'h':
                       default:
                               usage();
                               break;
                       }
       }

       if (iface == NULL)
               usage();

       /* Check interface is wireless. Will not return on error */
       wi_checkwifi(iface);

       for (table = wi_tables; *table != NULL; table++)
               for (wt = *table; wt->wi_code != WI_NONE; wt++)
                       if (wt->wi_optval != NULL) {
                               switch (wt->wi_code) {
                               case WI_BOOL:
                               case WI_WORDS:
                                       wi_setword(iface, wt->wi_type,
                                           atoi(wt->wi_optval));
                                       break;
                               case WI_STRING:
                                       wi_setstr(iface, wt->wi_type,
                                           wt->wi_optval);
                                       break;
                               case WI_HEXBYTES:
                                       wi_sethex(iface, wt->wi_type,
                                           wt->wi_optval);
                                       break;
                               }
                       }

       if (dumpstats)
               wi_dumpstats(iface);
       if (dumpinfo)
               wi_dumpinfo(iface);

       if (apscan)
#ifdef WI_RID_SCAN_APS
               wi_apscan(iface);
#else
               errx(1, "AP scan mode is not available.");
#endif

       exit(0);
}