126005Smsmith/*
226005Smsmith * Copyright (C) 1996
326005Smsmith *      Michael Smith.  All rights reserved.
426005Smsmith *
526005Smsmith * Redistribution and use in source and binary forms, with or without
626005Smsmith * modification, are permitted provided that the following conditions
726005Smsmith * are met:
826005Smsmith * 1. Redistributions of source code must retain the above copyright
926005Smsmith *    notice, this list of conditions and the following disclaimer.
1026005Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1126005Smsmith *    notice, this list of conditions and the following disclaimer in the
1226005Smsmith *    documentation and/or other materials provided with the distribution.
1326005Smsmith *
1426005Smsmith * THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
1526005Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1626005Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1726005Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
1826005Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1926005Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2026005Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2126005Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2226005Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2326005Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2426005Smsmith * SUCH DAMAGE.
2526005Smsmith */
2630774Scharnier
2730774Scharnier#ifndef lint
2830774Scharnierstatic const char rcsid[] =
2950479Speter  "$FreeBSD$";
3030774Scharnier#endif /* not lint */
3130774Scharnier
3226005Smsmith/*
3326005Smsmith * wlconfig.c
3426005Smsmith *
3526005Smsmith * utility to read out and change various WAVELAN parameters.
3626005Smsmith * Currently supports NWID and IRQ values.
3726005Smsmith *
3826005Smsmith * The NWID is used by 2 or more wavelan controllers to determine
3926005Smsmith * if packets should be received or not.  It is a filter that
4026005Smsmith * is roughly analogous to the "channel" setting with a garage
4126005Smsmith * door controller.  Two companies side by side with wavelan devices
4226005Smsmith * that could actually hear each other can use different NWIDs
4326005Smsmith * and ignore packets.  In truth however, the air space is shared,
4426005Smsmith * and the NWID is a virtual filter.
4526005Smsmith *
4626005Smsmith * In the current set of wavelan drivers, ioctls changed only
4726005Smsmith * the runtime radio modem registers which act in a manner analogous
4826005Smsmith * to an ethernet transceiver.  The ioctls do not change the
4926005Smsmith * stored nvram PSA (or parameter storage area).  At boot, the PSA
5026005Smsmith * values are stored in the radio modem.   Thus when the
5126005Smsmith * system reboots it will restore the wavelan NWID to the value
5226005Smsmith * stored in the PSA.  The NCR/ATT dos utilities must be used to
5326005Smsmith * change the initial NWID values in the PSA.  The wlconfig utility
5426005Smsmith * may be used to set a different NWID at runtime; this is only
5526005Smsmith * permitted while the interface is up and running.
5626005Smsmith *
5726005Smsmith * By contrast, the IRQ value can only be changed while the
5826005Smsmith * Wavelan card is down and unconfigured, and it will remain
5926005Smsmith * disabled after an IRQ change until reboot.
6026005Smsmith *
6126005Smsmith */
6226005Smsmith
6326005Smsmith#include <sys/param.h>
6426005Smsmith#include <sys/socket.h>
6526005Smsmith#include <sys/ioctl.h>
6626023Smsmith#include <sys/time.h>
6726005Smsmith#include <machine/if_wl_wavelan.h>
6826005Smsmith
6926005Smsmith#include <net/if.h>
7026005Smsmith#include <netinet/in.h>
7126005Smsmith#include <netinet/if_ether.h>
7226005Smsmith
7326005Smsmith#include <err.h>
7426005Smsmith#include <stdio.h>
7526005Smsmith#include <stdlib.h>
7626005Smsmith#include <string.h>
7726005Smsmith#include <unistd.h>
7826005Smsmith#include <limits.h>
7926005Smsmith
8026005Smsmith/* translate IRQ bit to number */
8126005Smsmith/* array for maping irq numbers to values for the irq parameter register */
8226005Smsmithstatic int irqvals[16] = {
8326005Smsmith    0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80
8426005Smsmith};
8526005Smsmith
8627818Smsmith/* cache */
8727818Smsmithstatic int w_sigitems;	/* count of valid items */
8827818Smsmithstatic struct w_sigcache wsc[MAXCACHEITEMS];
8927818Smsmith
9026005Smsmithint
9126005Smsmithwlirq(int irqval)
9226005Smsmith{
9326005Smsmith    int irq;
9426005Smsmith
9526005Smsmith    for(irq = 0; irq < 16; irq++)
9626005Smsmith	if(irqvals[irq] == irqval)
9726005Smsmith	    return(irq);
9826005Smsmith    return 0;
9926005Smsmith}
10026005Smsmith
10126005Smsmithchar *compat_type[] = {
10226005Smsmith    "PC-AT 915MHz",
10326005Smsmith    "PC-MC 915MHz",
10426005Smsmith    "PC-AT 2.4GHz",
10526005Smsmith    "PC-MC 2.4GHz",
10626005Smsmith    "PCCARD or 1/2 size AT, 915MHz or 2.4GHz"
10726005Smsmith};
10826005Smsmith
10926005Smsmithchar *subband[] = {
11026005Smsmith    "915MHz/see WaveModem",
11126005Smsmith    "2425MHz",
11226005Smsmith    "2460MHz",
11326005Smsmith    "2484MHz",
11426005Smsmith    "2430.5MHz"
11526005Smsmith};
11626005Smsmith
11726005Smsmith
11826005Smsmith/*
11926005Smsmith** print_psa
12026005Smsmith**
12126005Smsmith** Given a pointer to a PSA structure, print it out
12226005Smsmith*/
12326005Smsmithvoid
12426005Smsmithprint_psa(u_char *psa, int currnwid)
12526005Smsmith{
12626005Smsmith    int		nwid;
12726005Smsmith
12826005Smsmith    /*
12926005Smsmith    ** Work out what sort of board we have
13026005Smsmith    */
13126005Smsmith    if (psa[0] == 0x14) {
13226005Smsmith	printf("Board type            : Microchannel\n");
13326005Smsmith    } else {
13426005Smsmith	if (psa[1] == 0) {
13526005Smsmith	    printf("Board type            : PCCARD\n");
13626005Smsmith	} else {
13726005Smsmith	    printf("Board type            : ISA");
13826005Smsmith	    if ((psa[4] == 0) &&
13926005Smsmith		(psa[5] == 0) &&
14026005Smsmith		(psa[6] == 0))
14126005Smsmith		printf(" (DEC OEM)");
14226005Smsmith	    printf("\n");
14326005Smsmith	    printf("Base address options  : 0x300, 0x%02x0, 0x%02x0, 0x%02x0\n",
14426005Smsmith		   (int)psa[1], (int)psa[2], (int)psa[3]);
14526005Smsmith	    printf("Waitstates            : %d\n",psa[7] & 0xf);
14626005Smsmith	    printf("Bus mode              : %s\n",(psa[7] & 0x10) ? "EISA" : "ISA");
14726005Smsmith	    printf("IRQ                   : %d\n",wlirq(psa[8]));
14826005Smsmith	}
14926005Smsmith    }
15026005Smsmith    printf("Default MAC address   : %02x:%02x:%02x:%02x:%02x:%02x\n",
15126005Smsmith	   psa[0x10],psa[0x11],psa[0x12],psa[0x13],psa[0x14],psa[0x15]);
15226005Smsmith    printf("Soft MAC address      : %02x:%02x:%02x:%02x:%02x:%02x\n",
15326005Smsmith	   psa[0x16],psa[0x17],psa[0x18],psa[0x19],psa[0x1a],psa[0x1b]);
15426005Smsmith    printf("Current MAC address   : %s\n",(psa[0x1c] & 0x1) ? "Soft" : "Default");
15569238Sdannyboy    printf("Adapter compatibility : ");
15626005Smsmith    if (psa[0x1d] < 5) {
15726005Smsmith	printf("%s\n",compat_type[psa[0x1d]]);
15826005Smsmith    } else {
15926005Smsmith	printf("unknown\n");
16026005Smsmith    }
16126005Smsmith    printf("Threshold preset      : %d\n",psa[0x1e]);
16226005Smsmith    printf("Call code required    : %s\n",(psa[0x1f] & 0x1) ? "YES" : "NO");
16326005Smsmith    if (psa[0x1f] & 0x1)
16426005Smsmith	printf("Call code             : 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
16526005Smsmith	       psa[0x30],psa[0x31],psa[0x32],psa[0x33],psa[0x34],psa[0x35],psa[0x36],psa[0x37]);
16626005Smsmith    printf("Subband               : %s\n",subband[psa[0x20] & 0xf]);
16726005Smsmith    printf("Quality threshold     : %d\n",psa[0x21]);
16826005Smsmith    printf("Hardware version      : %d (%s)\n",psa[0x22],psa[0x22] ? "Rel3" : "Rel1/Rel2");
16926005Smsmith    printf("Network ID enable     : %s\n",(psa[0x25] & 0x1) ? "YES" : "NO");
17026005Smsmith    if (psa[0x25] & 0x1) {
17126005Smsmith	nwid = (psa[0x23] << 8) + psa[0x24];
17226005Smsmith	printf("NWID                  : 0x%04x\n",nwid);
17326005Smsmith	if (nwid != currnwid) {
17426005Smsmith	    printf("Current NWID          : 0x%04x\n",currnwid);
17526005Smsmith	}
17626005Smsmith    }
17726005Smsmith    printf("Datalink security     : %s\n",(psa[0x26] & 0x1) ? "YES" : "NO");
17826005Smsmith    if (psa[0x26] & 0x1) {
17926005Smsmith	printf("Encryption key        : ");
18026005Smsmith	if (psa[0x27] == 0) {
18126005Smsmith	    printf("DENIED\n");
18226005Smsmith	} else {
18326005Smsmith	    printf("0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
18426005Smsmith		   psa[0x27],psa[0x28],psa[0x29],psa[0x2a],psa[0x2b],psa[0x2c],psa[0x2d],psa[0x2e]);
18526005Smsmith	}
18626005Smsmith    }
18726005Smsmith    printf("Databus width         : %d (%s)\n",
18826005Smsmith	   (psa[0x2f] & 0x1) ? 16 : 8, (psa[0x2f] & 0x80) ? "fixed" : "variable");
18926005Smsmith    printf("Configuration state   : %sconfigured\n",(psa[0x38] & 0x1) ? "" : "un");
19026005Smsmith    printf("CRC-16                : 0x%02x%02x\n",psa[0x3e],psa[0x3d]);
19126005Smsmith    printf("CRC status            : ");
19226005Smsmith    switch(psa[0x3f]) {
19326005Smsmith    case 0xaa:
19426005Smsmith	printf("OK\n");
19526005Smsmith	break;
19626005Smsmith    case 0x55:
19726005Smsmith	printf("BAD\n");
19826005Smsmith	break;
19926005Smsmith    default:
20026005Smsmith	printf("Error\n");
20126005Smsmith	break;
20226005Smsmith    }
20326005Smsmith}
20426005Smsmith
20526005Smsmith
20630774Scharnierstatic void
20730774Scharnierusage()
20826005Smsmith{
20930774Scharnier    fprintf(stderr,"usage: wlconfig ifname [param value ...]\n");
21026005Smsmith    exit(1);
21126005Smsmith}
21226005Smsmith
21327818Smsmith
21426005Smsmithvoid
21527818Smsmithget_cache(int sd, struct ifreq *ifr)
21627818Smsmith{
21727818Smsmith    /* get the cache count */
21830774Scharnier    if (ioctl(sd, SIOCGWLCITEM, (caddr_t)ifr))
21930774Scharnier	err(1, "SIOCGWLCITEM - get cache count");
22027818Smsmith    w_sigitems = (int) ifr->ifr_data;
22127818Smsmith
22227818Smsmith    ifr->ifr_data = (caddr_t) &wsc;
22327818Smsmith    /* get the cache */
22430774Scharnier    if (ioctl(sd, SIOCGWLCACHE, (caddr_t)ifr))
22530774Scharnier	err(1, "SIOCGWLCACHE - get cache count");
22627818Smsmith}
22727818Smsmith
22827818Smsmithstatic int
22927818Smsmithscale_value(int value, int max)
23027818Smsmith{
23127818Smsmith	double dmax = (double) max;
23227818Smsmith	if (value > max)
23327818Smsmith		return(100);
23427818Smsmith	return((value/dmax) * 100);
23527818Smsmith}
23627818Smsmith
23727818Smsmithstatic void
23827818Smsmithdump_cache(int rawFlag)
23927818Smsmith{
24027818Smsmith	int i;
24127818Smsmith	int signal, silence, quality;
24227818Smsmith
24327818Smsmith	if (rawFlag)
24427818Smsmith		printf("signal range 0..63: silence 0..63: quality 0..15\n");
24527818Smsmith	else
24627818Smsmith		printf("signal range 0..100: silence 0..100: quality 0..100\n");
24727818Smsmith
24827818Smsmith	/* after you read it, loop through structure,i.e. wsc
24927818Smsmith         * print each item:
25027818Smsmith	 */
25127818Smsmith	for(i = 0; i < w_sigitems; i++) {
25227818Smsmith		printf("[%d:%d]>\n", i+1, w_sigitems);
25327818Smsmith        	printf("\tip: %d.%d.%d.%d,",((wsc[i].ipsrc >> 0) & 0xff),
25427818Smsmith				        ((wsc[i].ipsrc >> 8) & 0xff),
25527818Smsmith				        ((wsc[i].ipsrc >> 16) & 0xff),
25627818Smsmith				        ((wsc[i].ipsrc >> 24) & 0xff));
25727818Smsmith		printf(" mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
25827818Smsmith		  		    	wsc[i].macsrc[0]&0xff,
25927818Smsmith		  		    	wsc[i].macsrc[1]&0xff,
26027818Smsmith		   		    	wsc[i].macsrc[2]&0xff,
26127818Smsmith		   			wsc[i].macsrc[3]&0xff,
26227818Smsmith		   			wsc[i].macsrc[4]&0xff,
26327818Smsmith		   			wsc[i].macsrc[5]&0xff);
26427818Smsmith		if (rawFlag) {
26527818Smsmith			signal = wsc[i].signal;
26627818Smsmith			silence = wsc[i].silence;
26727818Smsmith			quality = wsc[i].quality;
26827818Smsmith		}
26927818Smsmith		else {
27027818Smsmith			signal = scale_value(wsc[i].signal, 63);
27127818Smsmith			silence = scale_value(wsc[i].silence, 63);
27227818Smsmith			quality = scale_value(wsc[i].quality, 15);
27327818Smsmith		}
27427818Smsmith		printf("\tsignal: %d, silence: %d, quality: %d, ",
27527818Smsmith		   			signal,
27627818Smsmith		   			silence,
27727818Smsmith		   			quality);
27827818Smsmith		printf("snr: %d\n", signal - silence);
27927818Smsmith	}
28027818Smsmith}
28127818Smsmith
28227818Smsmith#define raw_cache()	dump_cache(1)
28327818Smsmith#define scale_cache()	dump_cache(0)
28427818Smsmith
28530774Scharnierint
28626005Smsmithmain(int argc, char *argv[])
28726005Smsmith{
28826005Smsmith    int 		sd;
28926005Smsmith    struct ifreq	ifr;
29026005Smsmith    u_char		psabuf[0x40];
29126005Smsmith    int			val, argind, i;
29226005Smsmith    char		*cp, *param, *value;
29326005Smsmith    struct ether_addr	*ea;
29426005Smsmith    int			work = 0;
29526005Smsmith    int			currnwid;
29626005Smsmith
29726005Smsmith    if ((argc < 2) || (argc % 2))
29830774Scharnier	usage();
29926005Smsmith
30026005Smsmith    /* get a socket */
30126005Smsmith    sd = socket(AF_INET, SOCK_DGRAM, 0);
30226005Smsmith    if (sd < 0)
30326005Smsmith	err(1,"socket");
30426005Smsmith    strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
30526005Smsmith    ifr.ifr_addr.sa_family = AF_INET;
30626005Smsmith
30726005Smsmith    /* get the PSA */
30826005Smsmith    ifr.ifr_data = (caddr_t)psabuf;
30926005Smsmith    if (ioctl(sd, SIOCGWLPSA, (caddr_t)&ifr))
31030774Scharnier	err(1,"get PSA");
31126005Smsmith
31226005Smsmith    /* get the current NWID */
31326005Smsmith    if (ioctl(sd, SIOCGWLCNWID, (caddr_t)&ifr))
31430774Scharnier	err(1,"get NWID");
31526005Smsmith    currnwid = (int)ifr.ifr_data;
31626005Smsmith
31726005Smsmith    /* just dump and exit? */
31826005Smsmith    if (argc == 2) {
31926005Smsmith	print_psa(psabuf, currnwid);
32026005Smsmith	exit(0);
32126005Smsmith    }
32226005Smsmith
32326005Smsmith    /* loop reading arg pairs */
32426005Smsmith    for (argind = 2; argind < argc; argind += 2) {
32526005Smsmith
32626005Smsmith	param = argv[argind];
32726005Smsmith	value = argv[argind+1];
32826005Smsmith
32926005Smsmith	/* What to do? */
33026005Smsmith
33126005Smsmith	if (!strcasecmp(param,"currnwid")) {		/* set current NWID */
33226005Smsmith	    val = strtol(value,&cp,0);
33326005Smsmith	    if ((val < 0) || (val > 0xffff) || (cp == value))
33430774Scharnier		errx(1,"bad NWID '%s'",value);
33526005Smsmith
33626005Smsmith	    ifr.ifr_data = (caddr_t)val;
33726005Smsmith	    if (ioctl(sd, SIOCSWLCNWID, (caddr_t)&ifr))
33830774Scharnier		err(1,"set NWID (interface not up?)");
33926005Smsmith	    continue ;
34026005Smsmith	}
34126005Smsmith
34226005Smsmith	if (!strcasecmp(param,"irq")) {
34326005Smsmith	    val = strtol(value,&cp,0);
34426005Smsmith	    val = irqvals[val];
34526005Smsmith	    if ((val == 0) || (cp == value))
34630774Scharnier		errx(1,"bad IRQ '%s'",value);
34726005Smsmith	    psabuf[WLPSA_IRQNO] = (u_char)val;
34826005Smsmith	    work = 1;
34926005Smsmith	    continue;
35026005Smsmith	}
35126005Smsmith
35226005Smsmith	if (!strcasecmp(param,"mac")) {
35326005Smsmith	    if ((ea = ether_aton(value)) == NULL)
35430774Scharnier		errx(1,"bad ethernet address '%s'",value);
35526005Smsmith	    for (i = 0; i < 6; i++)
35626005Smsmith		psabuf[WLPSA_LOCALMAC + i] = ea->octet[i];
35726005Smsmith	    work = 1;
35826005Smsmith	    continue;
35926005Smsmith	}
36026005Smsmith
36126005Smsmith	if (!strcasecmp(param,"macsel")) {
36226005Smsmith	    if (!strcasecmp(value,"local")) {
36326005Smsmith		psabuf[WLPSA_MACSEL] |= 0x1;
36426005Smsmith		work = 1;
36526005Smsmith		continue;
36626005Smsmith	    }
36726005Smsmith	    if (!strcasecmp(value,"universal")) {
36826005Smsmith		psabuf[WLPSA_MACSEL] &= ~0x1;
36926005Smsmith		work = 1;
37026005Smsmith		continue;
37126005Smsmith	    }
37230774Scharnier	    errx(1,"bad macsel value '%s'",value);
37326005Smsmith	}
37426005Smsmith
37526005Smsmith	if (!strcasecmp(param,"nwid")) {
37626005Smsmith	    val = strtol(value,&cp,0);
37726005Smsmith	    if ((val < 0) || (val > 0xffff) || (cp == value))
37830774Scharnier		errx(1,"bad NWID '%s'",value);
37926005Smsmith	    psabuf[WLPSA_NWID] = (val >> 8) & 0xff;
38026005Smsmith	    psabuf[WLPSA_NWID+1] = val & 0xff;
38126005Smsmith	    work = 1;
38226005Smsmith	    continue;
38326005Smsmith	}
38427818Smsmith	if (!strcasecmp(param,"cache")) {
38527818Smsmith
38627818Smsmith            /* raw cache dump
38727818Smsmith	    */
38827818Smsmith	    if (!strcasecmp(value,"raw")) {
38927818Smsmith	    	get_cache(sd, &ifr);
39027818Smsmith		raw_cache();
39127818Smsmith		continue;
39227818Smsmith	    }
39327818Smsmith            /* scaled cache dump
39427818Smsmith	    */
39527818Smsmith	    else if (!strcasecmp(value,"scale")) {
39627818Smsmith	    	get_cache(sd, &ifr);
39727818Smsmith		scale_cache();
39827818Smsmith		continue;
39927818Smsmith	    }
40027818Smsmith	    /* zero out cache
40127818Smsmith	    */
40227818Smsmith	    else if (!strcasecmp(value,"zero")) {
40327818Smsmith		if (ioctl(sd, SIOCDWLCACHE, (caddr_t)&ifr))
40430774Scharnier		    err(1,"zero cache");
40527818Smsmith		continue;
40627818Smsmith	    }
40730774Scharnier	    errx(1,"unknown value '%s'", value);
40827818Smsmith 	}
40930774Scharnier	errx(1,"unknown parameter '%s'",param);
41026005Smsmith    }
41126005Smsmith    if (work) {
41226005Smsmith	ifr.ifr_data = (caddr_t)psabuf;
41326005Smsmith	if (ioctl(sd, SIOCSWLPSA, (caddr_t)&ifr))
41430774Scharnier	    err(1,"set PSA");
41526005Smsmith    }
41630774Scharnier    return(0);
41726005Smsmith}
418