wlconfig.c revision 27818
1/*
2 * Copyright (C) 1996
3 *      Michael Smith.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: wlconfig.c,v 1.2 1997/05/23 21:46:50 trost Exp $
27 *
28 */
29/*
30 * wlconfig.c
31 *
32 * utility to read out and change various WAVELAN parameters.
33 * Currently supports NWID and IRQ values.
34 *
35 * The NWID is used by 2 or more wavelan controllers to determine
36 * if packets should be received or not.  It is a filter that
37 * is roughly analogous to the "channel" setting with a garage
38 * door controller.  Two companies side by side with wavelan devices
39 * that could actually hear each other can use different NWIDs
40 * and ignore packets.  In truth however, the air space is shared,
41 * and the NWID is a virtual filter.
42 *
43 * In the current set of wavelan drivers, ioctls changed only
44 * the runtime radio modem registers which act in a manner analogous
45 * to an ethernet transceiver.  The ioctls do not change the
46 * stored nvram PSA (or parameter storage area).  At boot, the PSA
47 * values are stored in the radio modem.   Thus when the
48 * system reboots it will restore the wavelan NWID to the value
49 * stored in the PSA.  The NCR/ATT dos utilities must be used to
50 * change the initial NWID values in the PSA.  The wlconfig utility
51 * may be used to set a different NWID at runtime; this is only
52 * permitted while the interface is up and running.
53 *
54 * By contrast, the IRQ value can only be changed while the
55 * Wavelan card is down and unconfigured, and it will remain
56 * disabled after an IRQ change until reboot.
57 *
58 */
59
60#include <sys/param.h>
61#include <sys/socket.h>
62#include <sys/ioctl.h>
63#include <sys/time.h>
64#include <machine/if_wl_wavelan.h>
65
66#include <net/if.h>
67#include <net/if_var.h>
68#include <netinet/in.h>
69#include <netinet/if_ether.h>
70extern struct ether_addr *ether_aton(char *a);
71
72#include <err.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <unistd.h>
77#include <limits.h>
78
79/* translate IRQ bit to number */
80/* array for maping irq numbers to values for the irq parameter register */
81static int irqvals[16] = {
82    0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80
83};
84
85/* cache */
86static int w_sigitems;	/* count of valid items */
87static struct w_sigcache wsc[MAXCACHEITEMS];
88
89int
90wlirq(int irqval)
91{
92    int irq;
93
94    for(irq = 0; irq < 16; irq++)
95	if(irqvals[irq] == irqval)
96	    return(irq);
97    return 0;
98}
99
100char *compat_type[] = {
101    "PC-AT 915MHz",
102    "PC-MC 915MHz",
103    "PC-AT 2.4GHz",
104    "PC-MC 2.4GHz",
105    "PCCARD or 1/2 size AT, 915MHz or 2.4GHz"
106};
107
108char *subband[] = {
109    "915MHz/see WaveModem",
110    "2425MHz",
111    "2460MHz",
112    "2484MHz",
113    "2430.5MHz"
114};
115
116
117/*
118** print_psa
119**
120** Given a pointer to a PSA structure, print it out
121*/
122void
123print_psa(u_char *psa, int currnwid)
124{
125    int		nwid;
126
127    /*
128    ** Work out what sort of board we have
129    */
130    if (psa[0] == 0x14) {
131	printf("Board type            : Microchannel\n");
132    } else {
133	if (psa[1] == 0) {
134	    printf("Board type            : PCCARD\n");
135	} else {
136	    printf("Board type            : ISA");
137	    if ((psa[4] == 0) &&
138		(psa[5] == 0) &&
139		(psa[6] == 0))
140		printf(" (DEC OEM)");
141	    printf("\n");
142	    printf("Base address options  : 0x300, 0x%02x0, 0x%02x0, 0x%02x0\n",
143		   (int)psa[1], (int)psa[2], (int)psa[3]);
144	    printf("Waitstates            : %d\n",psa[7] & 0xf);
145	    printf("Bus mode              : %s\n",(psa[7] & 0x10) ? "EISA" : "ISA");
146	    printf("IRQ                   : %d\n",wlirq(psa[8]));
147	}
148    }
149    printf("Default MAC address   : %02x:%02x:%02x:%02x:%02x:%02x\n",
150	   psa[0x10],psa[0x11],psa[0x12],psa[0x13],psa[0x14],psa[0x15]);
151    printf("Soft MAC address      : %02x:%02x:%02x:%02x:%02x:%02x\n",
152	   psa[0x16],psa[0x17],psa[0x18],psa[0x19],psa[0x1a],psa[0x1b]);
153    printf("Current MAC address   : %s\n",(psa[0x1c] & 0x1) ? "Soft" : "Default");
154    printf("Adapter compatability : ");
155    if (psa[0x1d] < 5) {
156	printf("%s\n",compat_type[psa[0x1d]]);
157    } else {
158	printf("unknown\n");
159    }
160    printf("Threshold preset      : %d\n",psa[0x1e]);
161    printf("Call code required    : %s\n",(psa[0x1f] & 0x1) ? "YES" : "NO");
162    if (psa[0x1f] & 0x1)
163	printf("Call code             : 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
164	       psa[0x30],psa[0x31],psa[0x32],psa[0x33],psa[0x34],psa[0x35],psa[0x36],psa[0x37]);
165    printf("Subband               : %s\n",subband[psa[0x20] & 0xf]);
166    printf("Quality threshold     : %d\n",psa[0x21]);
167    printf("Hardware version      : %d (%s)\n",psa[0x22],psa[0x22] ? "Rel3" : "Rel1/Rel2");
168    printf("Network ID enable     : %s\n",(psa[0x25] & 0x1) ? "YES" : "NO");
169    if (psa[0x25] & 0x1) {
170	nwid = (psa[0x23] << 8) + psa[0x24];
171	printf("NWID                  : 0x%04x\n",nwid);
172	if (nwid != currnwid) {
173	    printf("Current NWID          : 0x%04x\n",currnwid);
174	}
175    }
176    printf("Datalink security     : %s\n",(psa[0x26] & 0x1) ? "YES" : "NO");
177    if (psa[0x26] & 0x1) {
178	printf("Encryption key        : ");
179	if (psa[0x27] == 0) {
180	    printf("DENIED\n");
181	} else {
182	    printf("0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
183		   psa[0x27],psa[0x28],psa[0x29],psa[0x2a],psa[0x2b],psa[0x2c],psa[0x2d],psa[0x2e]);
184	}
185    }
186    printf("Databus width         : %d (%s)\n",
187	   (psa[0x2f] & 0x1) ? 16 : 8, (psa[0x2f] & 0x80) ? "fixed" : "variable");
188    printf("Configuration state   : %sconfigured\n",(psa[0x38] & 0x1) ? "" : "un");
189    printf("CRC-16                : 0x%02x%02x\n",psa[0x3e],psa[0x3d]);
190    printf("CRC status            : ");
191    switch(psa[0x3f]) {
192    case 0xaa:
193	printf("OK\n");
194	break;
195    case 0x55:
196	printf("BAD\n");
197	break;
198    default:
199	printf("Error\n");
200	break;
201    }
202}
203
204
205void
206syntax(char *pname)
207{
208    fprintf(stderr,"Usage: %s <ifname> [<param> <value> ...]\n",pname);
209    fprintf(stderr,"    <ifname>    Wavelan interface name.\n");
210    fprintf(stderr,"    <param>     Parameter name (see below)\n");
211    fprintf(stderr,"    <value>     New value for parameter.\n");
212    fprintf(stderr," Parameter name:        Value:\n");
213    fprintf(stderr,"     irq		3,4,5,6,10,11,12,15\n");
214    fprintf(stderr,"     mac		soft ethernet address\n");
215    fprintf(stderr,"     macsel		soft or default\n");
216    fprintf(stderr,"     nwid		default NWID (0x0-0xffff)\n");
217    fprintf(stderr,"     currnwid       current NWID (0x0-0xffff) or 'get'\n");
218    fprintf(stderr,"     cache          signal strength cache\n");
219    fprintf(stderr,"     cache values = { raw, scale, zero }\n");
220    exit(1);
221}
222
223
224void
225get_cache(int sd, struct ifreq *ifr)
226{
227    /* get the cache count */
228    if (ioctl(sd, SIOCGWLCITEM, (caddr_t)ifr)) {
229	perror("SIOCGWLCITEM - get cache count");
230	exit(1);
231    }
232    w_sigitems = (int) ifr->ifr_data;
233
234    ifr->ifr_data = (caddr_t) &wsc;
235    /* get the cache */
236    if (ioctl(sd, SIOCGWLCACHE, (caddr_t)ifr)) {
237	perror("SIOCGWLCACHE - get cache count");
238	exit(1);
239    }
240}
241
242static int
243scale_value(int value, int max)
244{
245	double dmax = (double) max;
246	if (value > max)
247		return(100);
248	return((value/dmax) * 100);
249}
250
251static void
252dump_cache(int rawFlag)
253{
254	int i;
255	int signal, silence, quality;
256
257	if (rawFlag)
258		printf("signal range 0..63: silence 0..63: quality 0..15\n");
259	else
260		printf("signal range 0..100: silence 0..100: quality 0..100\n");
261
262	/* after you read it, loop through structure,i.e. wsc
263         * print each item:
264	 */
265	for(i = 0; i < w_sigitems; i++) {
266		printf("[%d:%d]>\n", i+1, w_sigitems);
267        	printf("\tip: %d.%d.%d.%d,",((wsc[i].ipsrc >> 0) & 0xff),
268				        ((wsc[i].ipsrc >> 8) & 0xff),
269				        ((wsc[i].ipsrc >> 16) & 0xff),
270				        ((wsc[i].ipsrc >> 24) & 0xff));
271		printf(" mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
272		  		    	wsc[i].macsrc[0]&0xff,
273		  		    	wsc[i].macsrc[1]&0xff,
274		   		    	wsc[i].macsrc[2]&0xff,
275		   			wsc[i].macsrc[3]&0xff,
276		   			wsc[i].macsrc[4]&0xff,
277		   			wsc[i].macsrc[5]&0xff);
278		if (rawFlag) {
279			signal = wsc[i].signal;
280			silence = wsc[i].silence;
281			quality = wsc[i].quality;
282		}
283		else {
284			signal = scale_value(wsc[i].signal, 63);
285			silence = scale_value(wsc[i].silence, 63);
286			quality = scale_value(wsc[i].quality, 15);
287		}
288		printf("\tsignal: %d, silence: %d, quality: %d, ",
289		   			signal,
290		   			silence,
291		   			quality);
292		printf("snr: %d\n", signal - silence);
293	}
294}
295
296#define raw_cache()	dump_cache(1)
297#define scale_cache()	dump_cache(0)
298
299void
300main(int argc, char *argv[])
301{
302    int 		sd;
303    struct ifreq	ifr;
304    u_char		psabuf[0x40];
305    int			val, argind, i;
306    char		*cp, *param, *value;
307    struct ether_addr	*ea;
308    int			work = 0;
309    int			currnwid;
310
311    if ((argc < 2) || (argc % 2))
312	syntax(argv[0]);
313
314    /* get a socket */
315    sd = socket(AF_INET, SOCK_DGRAM, 0);
316    if (sd < 0)
317	err(1,"socket");
318    strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
319    ifr.ifr_addr.sa_family = AF_INET;
320
321    /* get the PSA */
322    ifr.ifr_data = (caddr_t)psabuf;
323    if (ioctl(sd, SIOCGWLPSA, (caddr_t)&ifr))
324	err(1,"Get PSA");
325
326    /* get the current NWID */
327    if (ioctl(sd, SIOCGWLCNWID, (caddr_t)&ifr))
328	err(1,"Get NWID");
329    currnwid = (int)ifr.ifr_data;
330
331    /* just dump and exit? */
332    if (argc == 2) {
333	print_psa(psabuf, currnwid);
334	exit(0);
335    }
336
337    /* loop reading arg pairs */
338    for (argind = 2; argind < argc; argind += 2) {
339
340	param = argv[argind];
341	value = argv[argind+1];
342
343	/* What to do? */
344
345	if (!strcasecmp(param,"currnwid")) {		/* set current NWID */
346	    val = strtol(value,&cp,0);
347	    if ((val < 0) || (val > 0xffff) || (cp == value))
348		errx(1,"Bad NWID '%s'",value);
349
350	    ifr.ifr_data = (caddr_t)val;
351	    if (ioctl(sd, SIOCSWLCNWID, (caddr_t)&ifr))
352		err(1,"Set NWID (interface not up?)");
353	    continue ;
354	}
355
356	if (!strcasecmp(param,"irq")) {
357	    val = strtol(value,&cp,0);
358	    val = irqvals[val];
359	    if ((val == 0) || (cp == value))
360		errx(1,"Bad IRQ '%s'",value);
361	    psabuf[WLPSA_IRQNO] = (u_char)val;
362	    work = 1;
363	    continue;
364	}
365
366	if (!strcasecmp(param,"mac")) {
367	    if ((ea = ether_aton(value)) == NULL)
368		errx(1,"Bad ethernet address '%s'",value);
369	    for (i = 0; i < 6; i++)
370		psabuf[WLPSA_LOCALMAC + i] = ea->octet[i];
371	    work = 1;
372	    continue;
373	}
374
375	if (!strcasecmp(param,"macsel")) {
376	    if (!strcasecmp(value,"local")) {
377		psabuf[WLPSA_MACSEL] |= 0x1;
378		work = 1;
379		continue;
380	    }
381	    if (!strcasecmp(value,"universal")) {
382		psabuf[WLPSA_MACSEL] &= ~0x1;
383		work = 1;
384		continue;
385	    }
386	    errx(1,"Bad macsel value '%s'",value);
387	}
388
389	if (!strcasecmp(param,"nwid")) {
390	    val = strtol(value,&cp,0);
391	    if ((val < 0) || (val > 0xffff) || (cp == value))
392		errx(1,"Bad NWID '%s'",value);
393	    psabuf[WLPSA_NWID] = (val >> 8) & 0xff;
394	    psabuf[WLPSA_NWID+1] = val & 0xff;
395	    work = 1;
396	    continue;
397	}
398	if (!strcasecmp(param,"cache")) {
399
400            /* raw cache dump
401	    */
402	    if (!strcasecmp(value,"raw")) {
403	    	get_cache(sd, &ifr);
404		raw_cache();
405		continue;
406	    }
407            /* scaled cache dump
408	    */
409	    else if (!strcasecmp(value,"scale")) {
410	    	get_cache(sd, &ifr);
411		scale_cache();
412		continue;
413	    }
414	    /* zero out cache
415	    */
416	    else if (!strcasecmp(value,"zero")) {
417		if (ioctl(sd, SIOCDWLCACHE, (caddr_t)&ifr))
418		    err(1,"Zero cache");
419		continue;
420	    }
421	    errx(1,"Unknown value '%s'", value);
422 	}
423	errx(1,"Unknown parameter '%s'",param);
424    }
425    if (work) {
426	ifr.ifr_data = (caddr_t)psabuf;
427	if (ioctl(sd, SIOCSWLPSA, (caddr_t)&ifr))
428	    err(1,"Set PSA");
429    }
430}
431