802.11 patch

Andrew Atrens atrens at nortel.com
Thu Nov 24 10:22:07 PST 2005


brings in new 802.11 stack from FreeBSD-current


diff -N -u -r src.preview/sbin/ifconfig/Makefile src/sbin/ifconfig/Makefile
--- src.preview/sbin/ifconfig/Makefile	2005-03-03 21:29:19.000000000 -0500
+++ src/sbin/ifconfig/Makefile	2005-09-27 15:02:10.000000000 -0400
@@ -1,25 +1,31 @@
 #	From: @(#)Makefile	8.1 (Berkeley) 6/5/93
-# $FreeBSD: src/sbin/ifconfig/Makefile,v 1.14.2.7 2002/02/15 03:58:37 luigi Exp $
-# $DragonFly: src/sbin/ifconfig/Makefile,v 1.3 2005/03/04 02:29:19 cpressey Exp $
+# $FreeBSD$
 
 PROG=	ifconfig
 WARNS?=	6
-SRCS=	ifconfig.c
-
-#comment out to exclude SIOC[GS]IFMEDIA support
-SRCS+=	ifmedia.c
-CFLAGS+=-DUSE_IF_MEDIA
-CFLAGS+=-DINET6
-
-#comment out to exclude SIOC[GS]ETVLAN support
-SRCS+=	ifvlan.c
-CFLAGS+=-DUSE_VLANS
-
-#comment out to exclude SIOC[GS]IEEE80211 support
-SRCS+=	ifieee80211.c
-CFLAGS+=-DUSE_IEEE80211
+SRCS=	ifconfig.c		# base support
 
 MAN=	ifconfig.8
+#
+# NB: The order here defines the order in which the constructors
+#     are called.  This in turn defines the default order in which
+#     status is displayed.  Probably should add a priority mechanism
+#     to the registration process so we don't depend on this aspect
+#     of the toolchain.
+#
+SRCS+=	af_link.c		# LLC support
+SRCS+=	af_inet.c		# IPv4 support
+SRCS+=	af_inet6.c		# IPv6 support
+SRCS+=	af_atalk.c		# AppleTalk support
+
+SRCS+=	ifclone.c		# clone device support
+# SRCS+=	ifmac.c			# MAC support
+SRCS+=	ifmedia.c		# SIOC[GS]IFMEDIA support
+SRCS+=	ifvlan.c		# SIOC[GS]ETVLAN support
+SRCS+=	ifieee80211.c		# SIOC[GS]IEEE80211 support
+
+#SRCS+=	ifcarp.c		# SIOC[GS]VH support
+#SRCS+=	ifpfsync.c		# pfsync(4) support
 
 .if defined(RELEASE_CRUNCH)
 CFLAGS+=-DNO_IPX
diff -N -u -r src.preview/sbin/ifconfig/af_atalk.c src/sbin/ifconfig/af_atalk.c
--- src.preview/sbin/ifconfig/af_atalk.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/af_atalk.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>		/* for RTX_IFA */
+
+#include <netatalk/at.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <arpa/inet.h>
+
+#include "ifconfig.h"
+
+static struct netrange at_nr;		/* AppleTalk net range */
+static struct ifaliasreq at_addreq;
+
+/* XXX  FIXME -- should use strtoul for better parsing. */
+static void
+setatrange(const char *range, int dummy __unused, int s, 
+    const struct afswtch *afp)
+{
+	u_int	first = 123, last = 123;
+
+	if (sscanf(range, "%u-%u", &first, &last) != 2
+	    || first == 0 || first > 0xffff
+	    || last == 0 || last > 0xffff || first > last)
+		errx(1, "%s: illegal net range: %u-%u", range, first, last);
+	at_nr.nr_firstnet = htons(first);
+	at_nr.nr_lastnet = htons(last);
+}
+
+static void
+setatphase(const char *phase, int dummy __unused, int s, 
+    const struct afswtch *afp)
+{
+	if (!strcmp(phase, "1"))
+		at_nr.nr_phase = 1;
+	else if (!strcmp(phase, "2"))
+		at_nr.nr_phase = 2;
+	else
+		errx(1, "%s: illegal phase", phase);
+}
+
+static void
+at_status(int s __unused, const struct rt_addrinfo * info)
+{
+	struct sockaddr_at *sat, null_sat;
+	struct netrange *nr;
+
+	memset(&null_sat, 0, sizeof(null_sat));
+
+	sat = (struct sockaddr_at *)info->rti_info[RTAX_IFA];
+	if (sat == NULL)
+		return;
+	nr = &sat->sat_range.r_netrange;
+	printf("\tatalk %d.%d range %d-%d phase %d",
+		ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+		ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase);
+	if (flags & IFF_POINTOPOINT) {
+		/* note RTAX_BRD overlap with IFF_BROADCAST */
+		sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
+		if (!sat)
+			sat = &null_sat;
+		printf("--> %d.%d",
+			ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
+	}
+	if (flags & IFF_BROADCAST) {
+		/* note RTAX_BRD overlap with IFF_POINTOPOINT */
+		sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
+		if (sat)
+			printf(" broadcast %d.%d",
+				ntohs(sat->sat_addr.s_net),
+				sat->sat_addr.s_node);
+	}
+
+	putchar('\n');
+}
+
+static void
+at_getaddr(const char *addr, int which)
+{
+	struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr;
+	u_int net, node;
+
+	sat->sat_family = AF_APPLETALK;
+	sat->sat_len = sizeof(*sat);
+	if (which == MASK)
+		errx(1, "AppleTalk does not use netmasks");
+	if (sscanf(addr, "%u.%u", &net, &node) != 2
+	    || net > 0xffff || node > 0xfe)
+		errx(1, "%s: illegal address", addr);
+	sat->sat_addr.s_net = htons(net);
+	sat->sat_addr.s_node = node;
+}
+
+static void
+at_postproc(int s, const struct afswtch *afp)
+{
+	struct sockaddr_at *sat = (struct sockaddr_at *) &at_addreq.ifra_addr;
+
+	if (at_nr.nr_phase == 0)
+		at_nr.nr_phase = 2;	/* Default phase 2 */
+	if (at_nr.nr_firstnet == 0)
+		at_nr.nr_firstnet =	/* Default range of one */
+		at_nr.nr_lastnet = sat->sat_addr.s_net;
+	printf("\tatalk %d.%d range %d-%d phase %d\n",
+		ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
+		ntohs(at_nr.nr_firstnet), ntohs(at_nr.nr_lastnet),
+		at_nr.nr_phase);
+	if ((u_short) ntohs(at_nr.nr_firstnet) >
+			(u_short) ntohs(sat->sat_addr.s_net)
+		    || (u_short) ntohs(at_nr.nr_lastnet) <
+			(u_short) ntohs(sat->sat_addr.s_net))
+		errx(1, "AppleTalk address is not in range");
+	sat->sat_range.r_netrange = at_nr;
+}
+
+static struct cmd atalk_cmds[] = {
+	DEF_CMD_ARG("range",	setatrange),
+	DEF_CMD_ARG("phase",	setatphase),
+};
+
+static struct afswtch af_atalk = {
+	.af_name	= "atalk",
+	.af_af		= AF_APPLETALK,
+	.af_status	= at_status,
+	.af_getaddr	= at_getaddr,
+	.af_postproc	= at_postproc,
+	.af_difaddr	= SIOCDIFADDR,
+	.af_aifaddr	= SIOCAIFADDR,
+	.af_ridreq	= &at_addreq,
+	.af_addreq	= &at_addreq,
+};
+
+static __constructor void
+atalk_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(atalk_cmds);  i++)
+		cmd_register(&atalk_cmds[i]);
+	af_register(&af_atalk);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/af_inet.c src/sbin/ifconfig/af_inet.c
--- src.preview/sbin/ifconfig/af_inet.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/af_inet.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>		/* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <net/if_var.h>		/* for struct ifaddr */
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "ifconfig.h"
+
+static struct ifaliasreq in_addreq;
+static struct ifreq in_ridreq;
+
+static void
+in_status(int s __unused, const struct rt_addrinfo * info)
+{
+	struct sockaddr_in *sin, null_sin;
+	
+	memset(&null_sin, 0, sizeof(null_sin));
+
+	sin = (struct sockaddr_in *)info->rti_info[RTAX_IFA];
+	if (sin == NULL)
+		return;
+
+	printf("\tinet %s ", inet_ntoa(sin->sin_addr));
+
+	if (flags & IFF_POINTOPOINT) {
+		/* note RTAX_BRD overlap with IFF_BROADCAST */
+		sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
+		if (!sin)
+			sin = &null_sin;
+		printf("--> %s ", inet_ntoa(sin->sin_addr));
+	}
+
+	sin = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
+	if (!sin)
+		sin = &null_sin;
+	printf("netmask 0x%lx ", (unsigned long)ntohl(sin->sin_addr.s_addr));
+
+	if (flags & IFF_BROADCAST) {
+		/* note RTAX_BRD overlap with IFF_POINTOPOINT */
+		sin = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
+		if (sin && sin->sin_addr.s_addr != 0)
+			printf("broadcast %s", inet_ntoa(sin->sin_addr));
+	}
+	putchar('\n');
+}
+
+#define SIN(x) ((struct sockaddr_in *) &(x))
+static struct sockaddr_in *sintab[] = {
+	SIN(in_ridreq.ifr_addr), SIN(in_addreq.ifra_addr),
+	SIN(in_addreq.ifra_mask), SIN(in_addreq.ifra_broadaddr)
+};
+
+static void
+in_getaddr(const char *s, int which)
+{
+#define	MIN(a,b)	((a)<(b)?(a):(b))
+	struct sockaddr_in *sin = sintab[which];
+	struct hostent *hp;
+	struct netent *np;
+
+	sin->sin_len = sizeof(*sin);
+	if (which != MASK)
+		sin->sin_family = AF_INET;
+
+	if (which == ADDR) {
+		char *p = NULL;
+
+		if((p = strrchr(s, '/')) != NULL) {
+			/* address is `name/masklen' */
+			int masklen;
+			int ret;
+			struct sockaddr_in *min = sintab[MASK];
+			*p = '\0';
+			ret = sscanf(p+1, "%u", &masklen);
+			if(ret != 1 || (masklen < 0 || masklen > 32)) {
+				*p = '/';
+				errx(1, "%s: bad value", s);
+			}
+			min->sin_len = sizeof(*min);
+			min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 
+				              0xffffffff);
+		}
+	}
+
+	if (inet_aton(s, &sin->sin_addr))
+		return;
+	if ((hp = gethostbyname(s)) != 0)
+		bcopy(hp->h_addr, (char *)&sin->sin_addr, 
+		    MIN(hp->h_length, sizeof(sin->sin_addr)));
+	else if ((np = getnetbyname(s)) != 0)
+		sin->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
+	else
+		errx(1, "%s: bad value", s);
+#undef MIN
+}
+
+static void
+in_status_tunnel(int s)
+{
+	char src[NI_MAXHOST];
+	char dst[NI_MAXHOST];
+	struct ifreq ifr;
+	const struct sockaddr *sa = (const struct sockaddr *) &ifr.ifr_addr;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, name, IFNAMSIZ);
+
+	if (ioctl(s, SIOCGIFPSRCADDR, (caddr_t)&ifr) < 0)
+		return;
+	if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, NI_NUMERICHOST) != 0)
+		src[0] = '\0';
+
+	if (ioctl(s, SIOCGIFPDSTADDR, (caddr_t)&ifr) < 0)
+		return;
+	if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, NI_NUMERICHOST) != 0)
+		dst[0] = '\0';
+
+	printf("\ttunnel inet %s --> %s\n", src, dst);
+}
+
+static void
+in_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
+{
+	struct ifaliasreq addreq;
+
+	memset(&addreq, 0, sizeof(addreq));
+	strncpy(addreq.ifra_name, name, IFNAMSIZ);
+	memcpy(&addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
+	memcpy(&addreq.ifra_dstaddr, dstres->ai_addr, dstres->ai_addr->sa_len);
+
+	if (ioctl(s, SIOCSIFPHYADDR, &addreq) < 0)
+		warn("SIOCSIFPHYADDR");
+}
+
+static struct afswtch af_inet = {
+	.af_name	= "inet",
+	.af_af		= AF_INET,
+	.af_status	= in_status,
+	.af_getaddr	= in_getaddr,
+	.af_status_tunnel = in_status_tunnel,
+	.af_settunnel	= in_set_tunnel,
+	.af_difaddr	= SIOCDIFADDR,
+	.af_aifaddr	= SIOCAIFADDR,
+	.af_ridreq	= &in_ridreq,
+	.af_addreq	= &in_addreq,
+};
+
+static __constructor void
+inet_ctor(void)
+{
+	af_register(&af_inet);
+}
diff -N -u -r src.preview/sbin/ifconfig/af_inet6.c src/sbin/ifconfig/af_inet6.c
--- src.preview/sbin/ifconfig/af_inet6.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/af_inet6.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>		/* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in.h>
+#include <net/if_var.h>		/* for struct ifaddr */
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <netinet6/nd6.h>	/* Define ND6_INFINITE_LIFETIME */
+
+#include "ifconfig.h"
+
+/* wrapper for KAME-special getnameinfo() */
+#ifndef NI_WITHSCOPEID
+#define	NI_WITHSCOPEID	0
+#endif
+
+static	struct in6_ifreq in6_ridreq;
+static	struct in6_aliasreq in6_addreq = 
+  { { 0 }, 
+    { 0 }, 
+    { 0 }, 
+    { 0 }, 
+    0, 
+    { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } };
+static	int ip6lifetime;
+
+static	void in6_fillscopeid(struct sockaddr_in6 *sin6);
+static	int prefix(void *, int);
+static	char *sec2str(time_t);
+static	int explicit_prefix = 0;
+
+static	char addr_buf[MAXHOSTNAMELEN *2 + 1];	/*for getnameinfo()*/
+
+static void
+setifprefixlen(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+        if (afp->af_getprefix != NULL)
+                afp->af_getprefix(addr, MASK);
+	explicit_prefix = 1;
+}
+
+static void
+setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused,
+    const struct afswtch *afp)
+{
+	if (afp->af_af != AF_INET6)
+		err(1, "address flags can be set only for inet6 addresses");
+
+	if (flag < 0)
+		in6_addreq.ifra_flags &= ~(-flag);
+	else
+		in6_addreq.ifra_flags |= flag;
+}
+
+static void
+setip6lifetime(const char *cmd, const char *val, int s, 
+    const struct afswtch *afp)
+{
+	time_t newval, t;
+	char *ep;
+
+	t = time(NULL);
+	newval = (time_t)strtoul(val, &ep, 0);
+	if (val == ep)
+		errx(1, "invalid %s", cmd);
+	if (afp->af_af != AF_INET6)
+		errx(1, "%s not allowed for the AF", cmd);
+	if (strcmp(cmd, "vltime") == 0) {
+		in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
+		in6_addreq.ifra_lifetime.ia6t_vltime = newval;
+	} else if (strcmp(cmd, "pltime") == 0) {
+		in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
+		in6_addreq.ifra_lifetime.ia6t_pltime = newval;
+	}
+}
+
+static void
+setip6pltime(const char *seconds, int dummy __unused, int s, 
+    const struct afswtch *afp)
+{
+	setip6lifetime("pltime", seconds, s, afp);
+}
+
+static void
+setip6vltime(const char *seconds, int dummy __unused, int s, 
+    const struct afswtch *afp)
+{
+	setip6lifetime("vltime", seconds, s, afp);
+}
+
+static void
+setip6eui64(const char *cmd, int dummy __unused, int s,
+    const struct afswtch *afp)
+{
+	struct ifaddrs *ifap, *ifa;
+	const struct sockaddr_in6 *sin6 = NULL;
+	const struct in6_addr *lladdr = NULL;
+	struct in6_addr *in6;
+
+	if (afp->af_af != AF_INET6)
+		errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
+ 	in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
+	if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
+		errx(EXIT_FAILURE, "interface index is already filled");
+	if (getifaddrs(&ifap) != 0)
+		err(EXIT_FAILURE, "getifaddrs");
+	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+		if (ifa->ifa_addr->sa_family == AF_INET6 &&
+		    strcmp(ifa->ifa_name, name) == 0) {
+			sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
+			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+				lladdr = &sin6->sin6_addr;
+				break;
+			}
+		}
+	}
+	if (!lladdr)
+		errx(EXIT_FAILURE, "could not determine link local address"); 
+
+ 	memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
+
+	freeifaddrs(ifap);
+}
+
+static void
+in6_fillscopeid(struct sockaddr_in6 *sin6)
+{
+#if defined(__KAME__) && defined(KAME_SCOPEID)
+	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+		sin6->sin6_scope_id =
+			ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+		sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
+	}
+#endif
+}
+
+static void
+in6_status(int s __unused, const struct rt_addrinfo * info)
+{
+	struct sockaddr_in6 *sin, null_sin;
+	struct in6_ifreq ifr6;
+	int s6;
+	u_int32_t flags6;
+	struct in6_addrlifetime lifetime;
+	time_t t = time(NULL);
+	int error;
+	u_int32_t scopeid;
+
+	memset(&null_sin, 0, sizeof(null_sin));
+
+	sin = (struct sockaddr_in6 *)info->rti_info[RTAX_IFA];
+	if (sin == NULL)
+		return;
+
+	strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
+	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+		warn("socket(AF_INET6,SOCK_DGRAM)");
+		return;
+	}
+	ifr6.ifr_addr = *sin;
+	if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+		warn("ioctl(SIOCGIFAFLAG_IN6)");
+		close(s6);
+		return;
+	}
+	flags6 = ifr6.ifr_ifru.ifru_flags6;
+	memset(&lifetime, 0, sizeof(lifetime));
+	ifr6.ifr_addr = *sin;
+	if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) {
+		warn("ioctl(SIOCGIFALIFETIME_IN6)");
+		close(s6);
+		return;
+	}
+	lifetime = ifr6.ifr_ifru.ifru_lifetime;
+	close(s6);
+
+	/* XXX: embedded link local addr check */
+	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
+	    *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
+		u_short index;
+
+		index = *(u_short *)&sin->sin6_addr.s6_addr[2];
+		*(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
+		if (sin->sin6_scope_id == 0)
+			sin->sin6_scope_id = ntohs(index);
+	}
+	scopeid = sin->sin6_scope_id;
+
+	error = getnameinfo((struct sockaddr *)sin, sin->sin6_len, addr_buf,
+			    sizeof(addr_buf), NULL, 0,
+			    NI_NUMERICHOST|NI_WITHSCOPEID);
+	if (error != 0)
+		inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
+			  sizeof(addr_buf));
+	printf("\tinet6 %s ", addr_buf);
+
+	if (flags & IFF_POINTOPOINT) {
+		/* note RTAX_BRD overlap with IFF_BROADCAST */
+		sin = (struct sockaddr_in6 *)info->rti_info[RTAX_BRD];
+		/*
+		 * some of the interfaces do not have valid destination
+		 * address.
+		 */
+		if (sin && sin->sin6_family == AF_INET6) {
+			int error;
+
+			/* XXX: embedded link local addr check */
+			if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) &&
+			    *(u_short *)&sin->sin6_addr.s6_addr[2] != 0) {
+				u_short index;
+
+				index = *(u_short *)&sin->sin6_addr.s6_addr[2];
+				*(u_short *)&sin->sin6_addr.s6_addr[2] = 0;
+				if (sin->sin6_scope_id == 0)
+					sin->sin6_scope_id = ntohs(index);
+			}
+
+			error = getnameinfo((struct sockaddr *)sin,
+					    sin->sin6_len, addr_buf,
+					    sizeof(addr_buf), NULL, 0,
+					    NI_NUMERICHOST|NI_WITHSCOPEID);
+			if (error != 0)
+				inet_ntop(AF_INET6, &sin->sin6_addr, addr_buf,
+					  sizeof(addr_buf));
+			printf("--> %s ", addr_buf);
+		}
+	}
+
+	sin = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK];
+	if (!sin)
+		sin = &null_sin;
+	printf("prefixlen %d ", prefix(&sin->sin6_addr,
+		sizeof(struct in6_addr)));
+
+	if ((flags6 & IN6_IFF_ANYCAST) != 0)
+		printf("anycast ");
+	if ((flags6 & IN6_IFF_TENTATIVE) != 0)
+		printf("tentative ");
+	if ((flags6 & IN6_IFF_DUPLICATED) != 0)
+		printf("duplicated ");
+	if ((flags6 & IN6_IFF_DETACHED) != 0)
+		printf("detached ");
+	if ((flags6 & IN6_IFF_DEPRECATED) != 0)
+		printf("deprecated ");
+	if ((flags6 & IN6_IFF_AUTOCONF) != 0)
+		printf("autoconf ");
+	if ((flags6 & IN6_IFF_TEMPORARY) != 0)
+		printf("temporary ");
+
+        if (scopeid)
+		printf("scopeid 0x%x ", scopeid);
+
+	if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) {
+		printf("pltime ");
+		if (lifetime.ia6t_preferred) {
+			printf("%s ", lifetime.ia6t_preferred < t
+				? "0" : sec2str(lifetime.ia6t_preferred - t));
+		} else
+			printf("infty ");
+
+		printf("vltime ");
+		if (lifetime.ia6t_expire) {
+			printf("%s ", lifetime.ia6t_expire < t
+				? "0" : sec2str(lifetime.ia6t_expire - t));
+		} else
+			printf("infty ");
+	}
+
+	putchar('\n');
+}
+
+#define	SIN6(x) ((struct sockaddr_in6 *) &(x))
+static struct	sockaddr_in6 *sin6tab[] = {
+	SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
+	SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)
+};
+
+static void
+in6_getprefix(const char *plen, int which)
+{
+	struct sockaddr_in6 *sin = sin6tab[which];
+	u_char *cp;
+	int len = atoi(plen);
+
+	if ((len < 0) || (len > 128))
+		errx(1, "%s: bad value", plen);
+	sin->sin6_len = sizeof(*sin);
+	if (which != MASK)
+		sin->sin6_family = AF_INET6;
+	if ((len == 0) || (len == 128)) {
+		memset(&sin->sin6_addr, 0xff, sizeof(struct in6_addr));
+		return;
+	}
+	memset((void *)&sin->sin6_addr, 0x00, sizeof(sin->sin6_addr));
+	for (cp = (u_char *)&sin->sin6_addr; len > 7; len -= 8)
+		*cp++ = 0xff;
+	*cp = 0xff << (8 - len);
+}
+
+static void
+in6_getaddr(const char *s, int which)
+{
+	struct sockaddr_in6 *sin = sin6tab[which];
+	struct addrinfo hints, *res;
+	int error = -1;
+
+	newaddr &= 1;
+
+	sin->sin6_len = sizeof(*sin);
+	if (which != MASK)
+		sin->sin6_family = AF_INET6;
+
+	if (which == ADDR) {
+		char *p = NULL;
+		if((p = strrchr(s, '/')) != NULL) {
+			*p = '\0';
+			in6_getprefix(p + 1, MASK);
+			explicit_prefix = 1;
+		}
+	}
+
+	if (sin->sin6_family == AF_INET6) {
+		bzero(&hints, sizeof(struct addrinfo));
+		hints.ai_family = AF_INET6;
+		error = getaddrinfo(s, NULL, &hints, &res);
+	}
+	if (error != 0) {
+		if (inet_pton(AF_INET6, s, &sin->sin6_addr) != 1)
+			errx(1, "%s: bad value", s);
+	} else
+		bcopy(res->ai_addr, sin, res->ai_addrlen);
+}
+
+static int
+prefix(void *val, int size)
+{
+        u_char *name = (u_char *)val;
+        int byte, bit, plen = 0;
+
+        for (byte = 0; byte < size; byte++, plen += 8)
+                if (name[byte] != 0xff)
+                        break;
+	if (byte == size)
+		return (plen);
+	for (bit = 7; bit != 0; bit--, plen++)
+                if (!(name[byte] & (1 << bit)))
+                        break;
+        for (; bit != 0; bit--)
+                if (name[byte] & (1 << bit))
+                        return(0);
+        byte++;
+        for (; byte < size; byte++)
+                if (name[byte])
+                        return(0);
+        return (plen);
+}
+
+static char *
+sec2str(time_t total)
+{
+	static char result[256];
+	int days, hours, mins, secs;
+	int first = 1;
+	char *p = result;
+
+	if (0) {
+		days = total / 3600 / 24;
+		hours = (total / 3600) % 24;
+		mins = (total / 60) % 60;
+		secs = total % 60;
+
+		if (days) {
+			first = 0;
+			p += sprintf(p, "%dd", days);
+		}
+		if (!first || hours) {
+			first = 0;
+			p += sprintf(p, "%dh", hours);
+		}
+		if (!first || mins) {
+			first = 0;
+			p += sprintf(p, "%dm", mins);
+		}
+		sprintf(p, "%ds", secs);
+	} else
+		sprintf(result, "%lu", (unsigned long)total);
+
+	return(result);
+}
+
+static void
+in6_postproc(int s, const struct afswtch *afp)
+{
+	if (explicit_prefix == 0) {
+		/* Aggregatable address architecture defines all prefixes
+		   are 64. So, it is convenient to set prefixlen to 64 if
+		   it is not specified. */
+		setifprefixlen("64", 0, s, afp);
+		/* in6_getprefix("64", MASK) if MASK is available here... */
+	}
+}
+
+static void
+in6_status_tunnel(int s)
+{
+	char src[NI_MAXHOST];
+	char dst[NI_MAXHOST];
+#ifdef NI_WITHSCOPEID
+	const int niflag = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+	const int niflag = NI_NUMERICHOST;
+#endif
+	struct in6_ifreq in6_ifr;
+	const struct sockaddr *sa = (const struct sockaddr *) &in6_ifr.ifr_addr;
+
+	memset(&in6_ifr, 0, sizeof(in6_ifr));
+	strncpy(in6_ifr.ifr_name, name, IFNAMSIZ);
+
+	if (ioctl(s, SIOCGIFPSRCADDR_IN6, (caddr_t)&in6_ifr) < 0)
+		return;
+	if (sa->sa_family == AF_INET6)
+		in6_fillscopeid(&in6_ifr.ifr_addr);
+	if (getnameinfo(sa, sa->sa_len, src, sizeof(src), 0, 0, niflag) != 0)
+		src[0] = '\0';
+
+	if (ioctl(s, SIOCGIFPDSTADDR_IN6, (caddr_t)&in6_ifr) < 0)
+		return;
+	if (sa->sa_family == AF_INET6)
+		in6_fillscopeid(&in6_ifr.ifr_addr);
+	if (getnameinfo(sa, sa->sa_len, dst, sizeof(dst), 0, 0, niflag) != 0)
+		dst[0] = '\0';
+
+	printf("\ttunnel inet6 %s --> %s\n", src, dst);
+}
+
+static void
+in6_set_tunnel(int s, struct addrinfo *srcres, struct addrinfo *dstres)
+{
+	struct in6_aliasreq in6_addreq; 
+
+	memset(&in6_addreq, 0, sizeof(in6_addreq));
+	strncpy(in6_addreq.ifra_name, name, IFNAMSIZ);
+	memcpy(&in6_addreq.ifra_addr, srcres->ai_addr, srcres->ai_addr->sa_len);
+	memcpy(&in6_addreq.ifra_dstaddr, dstres->ai_addr,
+	    dstres->ai_addr->sa_len);
+
+	if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addreq) < 0)
+		warn("SIOCSIFPHYADDR_IN6");
+}
+
+static struct cmd inet6_cmds[] = {
+	DEF_CMD_ARG("prefixlen",			setifprefixlen),
+	DEF_CMD("tentative",	IN6_IFF_TENTATIVE,	setip6flags),
+	DEF_CMD("-tentative",	-IN6_IFF_TENTATIVE,	setip6flags),
+	DEF_CMD("deprecated",	IN6_IFF_DEPRECATED,	setip6flags),
+	DEF_CMD("-deprecated", -IN6_IFF_DEPRECATED,	setip6flags),
+	DEF_CMD("autoconf",	IN6_IFF_AUTOCONF,	setip6flags),
+	DEF_CMD("-autoconf",	-IN6_IFF_AUTOCONF,	setip6flags),
+	DEF_CMD_ARG("pltime",        			setip6pltime),
+	DEF_CMD_ARG("vltime",        			setip6vltime),
+	DEF_CMD("eui64",	0,			setip6eui64),
+};
+
+static struct afswtch af_inet6 = {
+	.af_name	= "inet6",
+	.af_af		= AF_INET6,
+	.af_status	= in6_status,
+	.af_getaddr	= in6_getaddr,
+	.af_getprefix	= in6_getprefix,
+	.af_postproc	= in6_postproc,
+	.af_status_tunnel = in6_status_tunnel,
+	.af_settunnel	= in6_set_tunnel,
+	.af_difaddr	= SIOCDIFADDR_IN6,
+	.af_aifaddr	= SIOCAIFADDR_IN6,
+	.af_ridreq	= &in6_addreq,
+	.af_addreq	= &in6_addreq,
+};
+
+static void
+in6_Lopt_cb(const char *optarg __unused)
+{
+	ip6lifetime++;	/* print IPv6 address lifetime */
+}
+static struct option in6_Lopt = { "L", "[-L]", in6_Lopt_cb };
+
+static __constructor void
+inet6_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(inet6_cmds);  i++)
+		cmd_register(&inet6_cmds[i]);
+	af_register(&af_inet6);
+	opt_register(&in6_Lopt);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/af_ipx.c src/sbin/ifconfig/af_ipx.c
--- src.preview/sbin/ifconfig/af_ipx.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/af_ipx.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/if_var.h>
+#define	IPXIP
+#define IPTUNNEL
+#include <netipx/ipx.h>
+#include <netipx/ipx_if.h>
+
+#include "ifconfig.h"
+
+static struct ifaliasreq ipx_addreq;
+static struct ifreq ipx_ridreq;
+
+static void
+ipx_status(int s __unused, const struct rt_addrinfo * info)
+{
+	struct sockaddr_ipx *sipx, null_sipx;
+
+	sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_IFA];
+	if (sipx == NULL)
+		return;
+
+	printf("\tipx %s ", ipx_ntoa(sipx->sipx_addr));
+
+	if (flags & IFF_POINTOPOINT) {
+		sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_BRD];
+		if (!sipx) {
+			memset(&null_sipx, 0, sizeof(null_sipx));
+			sipx = &null_sipx;
+		}
+		printf("--> %s ", ipx_ntoa(sipx->sipx_addr));
+	}
+	putchar('\n');
+}
+
+#define SIPX(x) ((struct sockaddr_ipx *) &(x))
+struct sockaddr_ipx *sipxtab[] = {
+	SIPX(ipx_ridreq.ifr_addr), SIPX(ipx_addreq.ifra_addr),
+	SIPX(ipx_addreq.ifra_mask), SIPX(ipx_addreq.ifra_broadaddr)
+};
+
+static void
+ipx_getaddr(const char *addr, int which)
+{
+	struct sockaddr_ipx *sipx = sipxtab[which];
+
+	sipx->sipx_family = AF_IPX;
+	sipx->sipx_len = sizeof(*sipx);
+	sipx->sipx_addr = ipx_addr(addr);
+	if (which == MASK)
+		printf("Attempt to set IPX netmask will be ineffectual\n");
+}
+
+static void
+ipx_postproc(int s, const struct afswtch *afp)
+{
+	if (setipdst) {
+		struct ipxip_req rq;
+		int size = sizeof(rq);
+
+		rq.rq_ipx = ipx_addreq.ifra_addr;
+		rq.rq_ip = ipx_addreq.ifra_dstaddr;
+
+		if (setsockopt(s, 0, SO_IPXIP_ROUTE, &rq, size) < 0)
+			Perror("Encapsulation Routing");
+	}
+}
+
+static struct afswtch af_ipx = {
+	.af_name	= "ipx",
+	.af_af		= AF_IPX,
+	.af_status	= ipx_status,
+	.af_getaddr	= ipx_getaddr,
+	.af_postproc	= ipx_postproc,
+	.af_difaddr	= SIOCDIFADDR,
+	.af_aifaddr	= SIOCAIFADDR,
+	.af_ridreq	= &ipx_ridreq,
+	.af_addreq	= &ipx_addreq,
+};
+
+static __constructor void
+ipx_ctor(void)
+{
+	af_register(&af_ipx);
+}
diff -N -u -r src.preview/sbin/ifconfig/af_link.c src/sbin/ifconfig/af_link.c
--- src.preview/sbin/ifconfig/af_link.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/af_link.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>		/* for RTX_IFA */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+
+#include "ifconfig.h"
+
+static struct ifreq link_ridreq;
+
+static void
+link_status(int s __unused, const struct rt_addrinfo *info)
+{
+	const struct sockaddr_dl *sdl =
+		(const struct sockaddr_dl *) info->rti_info[RTAX_IFA];
+
+	if (sdl != NULL && sdl->sdl_alen > 0) {
+		if (sdl->sdl_type == IFT_ETHER &&
+		    sdl->sdl_alen == ETHER_ADDR_LEN)
+			printf("\tether %s\n",
+			    ether_ntoa((const struct ether_addr *)LLADDR(sdl)));
+		else {
+			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
+
+			printf("\tlladdr %s\n", link_ntoa(sdl) + n);
+		}
+	}
+}
+
+static void
+link_getaddr(const char *addr, int which)
+{
+	char *temp;
+	struct sockaddr_dl sdl;
+	struct sockaddr *sa = &link_ridreq.ifr_addr;
+
+	if (which != ADDR)
+		errx(1, "can't set link-level netmask or broadcast");
+	if ((temp = malloc(strlen(addr) + 1)) == NULL)
+		errx(1, "malloc failed");
+	temp[0] = ':';
+	strcpy(temp + 1, addr);
+	sdl.sdl_len = sizeof(sdl);
+	link_addr(temp, &sdl);
+	free(temp);
+	if (sdl.sdl_alen > sizeof(sa->sa_data))
+		errx(1, "malformed link-level address");
+	sa->sa_family = AF_LINK;
+	sa->sa_len = sdl.sdl_alen;
+	bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen);
+}
+
+static struct afswtch af_link = {
+	.af_name	= "link",
+	.af_af		= AF_LINK,
+	.af_status	= link_status,
+	.af_getaddr	= link_getaddr,
+	.af_aifaddr	= SIOCSIFLLADDR,
+	.af_addreq	= &link_ridreq,
+};
+static struct afswtch af_ether = {
+	.af_name	= "ether",
+	.af_af		= AF_LINK,
+	.af_status	= link_status,
+	.af_getaddr	= link_getaddr,
+	.af_aifaddr	= SIOCSIFLLADDR,
+	.af_addreq	= &link_ridreq,
+};
+static struct afswtch af_lladdr = {
+	.af_name	= "lladdr",
+	.af_af		= AF_LINK,
+	.af_status	= link_status,
+	.af_getaddr	= link_getaddr,
+	.af_aifaddr	= SIOCSIFLLADDR,
+	.af_addreq	= &link_ridreq,
+};
+
+static __constructor void
+link_ctor(void)
+{
+	af_register(&af_link);
+	af_register(&af_ether);
+	af_register(&af_lladdr);
+}
diff -N -u -r src.preview/sbin/ifconfig/ifcarp.c src/sbin/ifconfig/ifcarp.c
--- src.preview/sbin/ifconfig/ifcarp.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/ifcarp.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,199 @@
+/*	$FreeBSD$ */
+/*	from $OpenBSD: ifconfig.c,v 1.82 2003/10/19 05:43:35 mcbride Exp $ */
+
+/*
+ * Copyright (c) 2002 Michael Shalayeff. All rights reserved.
+ * Copyright (c) 2003 Ryan McBride. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR OR HIS RELATIVES 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 MIND, 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.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/ip_carp.h>
+#include <net/route.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+
+#include "ifconfig.h"
+
+static const char *carp_states[] = { CARP_STATES };
+
+void carp_status(int s);
+void setcarp_advbase(const char *,int, int, const struct afswtch *rafp);
+void setcarp_advskew(const char *, int, int, const struct afswtch *rafp);
+void setcarp_passwd(const char *, int, int, const struct afswtch *rafp);
+void setcarp_vhid(const char *, int, int, const struct afswtch *rafp);
+
+void
+carp_status(int s)
+{
+	const char *state;
+	struct carpreq carpr;
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data = (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+		return;
+
+	if (carpr.carpr_vhid > 0) {
+		if (carpr.carpr_state > CARP_MAXSTATE)
+			state = "<UNKNOWN>";
+		else
+			state = carp_states[carpr.carpr_state];
+
+		printf("\tcarp: %s vhid %d advbase %d advskew %d\n",
+		    state, carpr.carpr_vhid, carpr.carpr_advbase,
+		    carpr.carpr_advskew);
+	}
+
+	return;
+
+}
+
+void
+setcarp_passwd(const char *val, int d, int s, const struct afswtch *afp)
+{
+	struct carpreq carpr;
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data = (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGVH");
+
+	/* XXX Should hash the password into the key here, perhaps? */
+	strlcpy(carpr.carpr_key, val, CARP_KEY_LEN);
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
+void
+setcarp_vhid(const char *val, int d, int s, const struct afswtch *afp)
+{
+	int vhid;
+	struct carpreq carpr;
+
+	vhid = atoi(val);
+
+	if (vhid <= 0)
+		errx(1, "vhid must be greater than 0");
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data = (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGVH");
+
+	carpr.carpr_vhid = vhid;
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
+void
+setcarp_advskew(const char *val, int d, int s, const struct afswtch *afp)
+{
+	int advskew;
+	struct carpreq carpr;
+
+	advskew = atoi(val);
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data = (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGVH");
+
+	carpr.carpr_advskew = advskew;
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
+void
+setcarp_advbase(const char *val, int d, int s, const struct afswtch *afp)
+{
+	int advbase;
+	struct carpreq carpr;
+
+	advbase = atoi(val);
+
+	memset((char *)&carpr, 0, sizeof(struct carpreq));
+	ifr.ifr_data = (caddr_t)&carpr;
+
+	if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGVH");
+
+	carpr.carpr_advbase = advbase;
+
+	if (ioctl(s, SIOCSVH, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSVH");
+
+	return;
+}
+
+static struct cmd carp_cmds[] = {
+	DEF_CMD_ARG("advbase",	setcarp_advbase),
+	DEF_CMD_ARG("advskew",	setcarp_advskew),
+	DEF_CMD_ARG("pass",	setcarp_passwd),
+	DEF_CMD_ARG("vhid",	setcarp_vhid),
+};
+static struct afswtch af_carp = {
+	.af_name	= "af_carp",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = carp_status,
+};
+
+static __constructor void
+carp_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(carp_cmds);  i++)
+		cmd_register(&carp_cmds[i]);
+	af_register(&af_carp);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifclone.c src/sbin/ifconfig/ifclone.c
--- src.preview/sbin/ifconfig/ifclone.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/ifclone.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1983, 1993
+ *	The Regents of the University of California.  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.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS OR CONTRIBUTORS 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+static void
+list_cloners(void)
+{
+	struct if_clonereq ifcr;
+	char *cp, *buf;
+	int idx;
+	int s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s == -1)
+		err(1, "socket(AF_INET,SOCK_DGRAM)");
+
+	memset(&ifcr, 0, sizeof(ifcr));
+
+	if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+		err(1, "SIOCIFGCLONERS for count");
+
+	buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
+	if (buf == NULL)
+		err(1, "unable to allocate cloner name buffer");
+
+	ifcr.ifcr_count = ifcr.ifcr_total;
+	ifcr.ifcr_buffer = buf;
+
+	if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
+		err(1, "SIOCIFGCLONERS for names");
+
+	/*
+	 * In case some disappeared in the mean time, clamp it down.
+	 */
+	if (ifcr.ifcr_count > ifcr.ifcr_total)
+		ifcr.ifcr_count = ifcr.ifcr_total;
+
+	for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
+		if (idx > 0)
+			putchar(' ');
+		printf("%s", cp);
+	}
+
+	putchar('\n');
+	free(buf);
+}
+
+void
+clone_create(void)
+{
+	int s;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s == -1)
+		err(1, "socket(AF_INET,SOCK_DGRAM)");
+
+	memset(&ifr, 0, sizeof(ifr));
+	(void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCIFCREATE, &ifr) < 0)
+		err(1, "SIOCIFCREATE");
+
+	/*
+	 * If we get a different name back then we put in, we probably
+	 * want to print it out, but we might change our mind later so
+	 * we just signal our intrest and leave the printout for later.
+	 */
+	if (strcmp(name, ifr.ifr_name) != 0) {
+		printname = 1;
+		strlcpy(name, ifr.ifr_name, sizeof(name));
+	}
+
+	close(s);
+}
+
+static void
+clone_destroy(const char *val, int d, int s, const struct afswtch *rafp)
+{
+
+	(void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+	if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
+		err(1, "SIOCIFDESTROY");
+	/*
+	 * If we create and destroy an interface in the same command,
+	 * there isn't any reason to print it's name.
+	 */
+	printname = 0;
+}
+
+static struct cmd clone_cmds[] = {
+	DEF_CMD("destroy",	0,	clone_destroy),
+	DEF_CMD("unplumb",	0,	clone_destroy),
+};
+
+static void
+clone_Copt_cb(const char *optarg __unused)
+{
+	list_cloners();
+	exit(0);
+}
+static struct option clone_Copt = { "C", "[-C]", clone_Copt_cb };
+
+static __constructor void
+clone_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(clone_cmds);  i++)
+		cmd_register(&clone_cmds[i]);
+	opt_register(&clone_Copt);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifconfig.8 src/sbin/ifconfig/ifconfig.8
--- src.preview/sbin/ifconfig/ifconfig.8	2005-04-25 13:33:26.000000000 -0400
+++ src/sbin/ifconfig/ifconfig.8	2005-09-27 15:02:10.000000000 -0400
@@ -26,10 +26,9 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     From: @(#)ifconfig.8	8.3 (Berkeley) 1/5/94
-.\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.85 2004/07/27 09:51:49 yar Exp $
-.\" $DragonFly: src/sbin/ifconfig/ifconfig.8,v 1.7 2005/04/25 17:33:26 swildner Exp $
+.\" $FreeBSD$
 .\"
-.Dd July 26, 2004
+.Dd November 2, 2004
 .Dt IFCONFIG 8
 .Os
 .Sh NAME
@@ -56,6 +55,7 @@
 .Op Fl d
 .Op Fl m
 .Op Fl u
+.Op Fl v
 .Op Ar address_family
 .Nm
 .Fl l
@@ -67,6 +67,7 @@
 .Op Fl d
 .Op Fl m
 .Op Fl u
+.Op Fl v
 .Op Fl C
 .Sh DESCRIPTION
 The
@@ -224,6 +225,14 @@
 .It Fl arp
 Disable the use of the Address Resolution Protocol
 .Pq Xr arp 4 .
+.It Cm staticarp
+If the Address Resolution Protocol is enabled,
+the host will only reply to requests for its addresses,
+and will never send any requests.
+.It Fl staticarp
+If the Address Resolution Protocol is enabled,
+the host will perform normally,
+sending out requests and listening for replies.
 .It Cm broadcast
 (Inet only.)
 Specify the address to use to represent broadcasts to the
@@ -256,15 +265,18 @@
 Fill interface index
 (lowermost 64bit of an IPv6 address)
 automatically.
-.\" .It Cm ipdst
-.\" This is used to specify an Internet host who is willing to receive
-.\" ip packets encapsulating NS packets bound for a remote network.
-.\" An apparent point to point link is constructed, and
-.\" the address specified will be taken as the NS address and network
-.\" of the destination.
-.\" IP encapsulation of
-.\" .Tn CLNP
-.\" packets is done differently.
+.It Cm ipdst
+This is used to specify an Internet host who is willing to receive
+IP packets encapsulating IPX packets bound for a remote network.
+An apparent point to point link is constructed, and
+the address specified will be taken as the IPX address and network
+of the destination.
+.\".It Cm maclabel Ar label
+.\"If Mandatory Access Control support is enabled in the kernel,
+.\"set the MAC label to
+.\".Ar label .
+.\" (see
+.\" .Xr maclabel 7 ) .
 .It Cm media Ar type
 If the driver supports the media selection system, set the media type
 of the interface to
@@ -318,18 +330,18 @@
 of each other, so setting one may also set the other.
 The driver will offload as much checksum work as it can reliably
 support, the exact level of offloading varies between drivers.
-.\".It Fl rxcsum , Fl txcsum
-.\"If the driver supports user-configurable checksum offloading,
-.\"disable receive (or transmit) checksum offloading on the interface.
-.\"These settings may not always be independent of each other.
-.\".It Cm polling
-.\"If the driver has user-configurable
-.\".Xr polling 4
-.\"support, select the polling mode on the interface.
-.\".It Fl polling
-.\"If the driver has user-configurable
-.\".Xr polling 4
-.\"support, select the interrupt mode on the interface.
+.It Fl rxcsum , txcsum
+If the driver supports user-configurable checksum offloading,
+disable receive (or transmit) checksum offloading on the interface.
+These settings may not always be independent of each other.
+.It Cm polling
+If the driver has user-configurable
+.Xr polling 4
+support, select the polling mode on the interface.
+.It Fl polling
+If the driver has user-configurable
+.Xr polling 4
+support, select the interrupt mode on the interface.
 .It Cm tunnel Ar src_addr dest_addr
 (IP tunnel devices only.)
 Configure the physical source and destination address for IP tunnel
@@ -350,7 +362,10 @@
 If the interface is given without a unit number, try to create a new
 device with an arbitrary unit number.
 If creation of an arbitrary device is successful, the new device name is
-printed to standard output.
+printed to standard output unless the interface is renamed or destroyed
+in the same
+.Nm
+invocation.
 .It Cm destroy
 Destroy the specified network pseudo-device.
 .It Cm plumb
@@ -446,7 +461,7 @@
 not on a
 .Xr vlan 4
 interface itself.
-.It Fl vlanmtu , Fl vlanhwtag
+.It Fl vlanmtu , vlanhwtag
 If the driver offers user-configurable VLAN support, disable
 reception of extended frames or tag processing in hardware,
 respectively.
@@ -568,13 +583,13 @@
 .It Fl link Op Cm 0 No - Cm 2
 .Sm on
 Disable special processing at the link level with the specified interface.
-.\".It Cm monitor
-.\"Put the interface in monitor mode.
-.\"No packets are transmitted, and received packets are discarded after
-.\".Xr bpf 4
-.\"processing.
-.\".It Fl monitor
-.\"Take the interface out of monitor mode.
+.It Cm monitor
+Put the interface in monitor mode.
+No packets are transmitted, and received packets are discarded after
+.Xr bpf 4
+processing.
+.It Fl monitor
+Take the interface out of monitor mode.
 .It Cm up
 Mark an interface
 .Dq up .
@@ -583,64 +598,104 @@
 It happens automatically when setting the first address on an interface.
 If the interface was reset when previously marked down,
 the hardware will be re-initialized.
-.It Cm ssid Ar ssid
-For IEEE 802.11 wireless interfaces, set the desired Service Set
-Identifier (aka network name).
-The SSID is a string up to 32 characters
-in length and may be specified as either a normal string or in
-hexadecimal when proceeded by
-.Ql 0x .
-Additionally, the SSID may be cleared by setting it to
-.Ql - .
-.It Cm nwid Ar ssid
-Another name for the
-.Cm ssid
-parameter.
-Included for
-.Nx
-compatibility.
-.It Cm stationname Ar name
-For IEEE 802.11 wireless interfaces, set the name of this station.
-It appears that the station name is not really part of the IEEE 802.11
-protocol though all interfaces seem to support it.
-As such it only
-seems to be meaningful to identical or virtually identical equipment.
-Setting the station name is identical in syntax to setting the SSID.
-.It Cm station Ar name
-Another name for the
-.Cm stationname
-parameter.
-Included for
-.Bsx
-compatibility.
-.It Cm channel Ar number
-For IEEE 802.11 wireless interfaces, set the desired channel.
-Channels range from 1 to 14, but the exact selection available
-depends on the region your adaptor was manufactured for.
-Setting
-the channel to 0 will give you the default for your adaptor.
-Many
-adaptors ignore this setting unless you are in ad-hoc mode.
+.El
+.Pp
+The following parameters are specific to IEEE 802.11 wireless interfaces:
+.Bl -tag -width indent
+.It Cm apbridge
+When operating as an access point, pass packets between
+wireless clients directly (default).
+To instead let them pass up through the
+system and be forwarded using some other mechanism, use
+.Fl apbridge .
+Disabling the internal bridging
+is useful when traffic is to be processed with
+packet filtering.
 .It Cm authmode Ar mode
-For IEEE 802.11 wireless interfaces, set the desired authentication mode
-in infrastructure mode.
+Set the desired authentication mode in infrastructure mode.
 Not all adaptors support all modes.
 The set of
 valid modes is
 .Dq Li none ,
 .Dq Li open ,
+.Dq Li shared
+(shared key),
+.Dq Li 8021x
+(IEEE 802.1x),
+or
+.Dq Li wpa
+(IEEE WPA/WPA2/802.11i).
+The
+.Dq Li 8021x
 and
-.Dq Li shared .
+.Dq Li wpa
+modes are only useful when used an authentication service
+(a supplicant for client operation or an authenticator when
+operating as an access point).
 Modes are case insensitive.
+.It Cm bssid Ar address
+Specify the MAC address of the access point to use when operating
+as a station in a BSS network.
+This overrides any automatic selection done by the system.
+To disable a previously selected access point, supply
+.Dq Li any ,
+.Dq Li none ,
+or
+.Dq Li -
+for the address.
+This option is useful when more than one access points have the same SSID.
+Another name for the
+.Cm bssid
+parameter is
+.Cm ap .
+.It Cm chanlist Ar channels
+Set the desired channels to use when scanning for access
+points, neighbors in an IBSS network, or looking for unoccupied
+channels when operating as an access point.
+The set of channels is specified as a comma-separated list with
+each element in the list representing either a single channel number or a range
+of the form
+.Dq Li a-b .
+Channel numbers must be in the range 1 to 255 and be permissible
+according to the operating characteristics of the device.
+.It Cm channel Ar number
+Set a single desired channel.
+Channels range from 1 to 255, but the exact selection available
+depends on the region your adaptor was manufactured for.
+Setting
+the channel to
+.Dq Li 0 ,
+.Dq Li any ,
+or
+.Dq Li -
+will give you the default for your adaptor.
+Many
+adaptors ignore this setting unless you are in ad-hoc mode.
+Alternatively the frequency, in megahertz, may be specified
+instead of the channel number.
+.It Cm hidessid
+When operating as an access point, do not broadcast the SSID
+in beacon frames.
+By default, the SSID is included in beacon frames.
+To re-enable the broadcast of the SSID, use
+.Fl hidessid .
 .It Cm powersave
-For IEEE 802.11 wireless interfaces, enable powersave mode.
-.It Fl powersave
-For IEEE 802.11 wireless interfaces, disable powersave mode.
+Enable powersave operation.
+When operating as a client, the station will conserve power by
+periodically turning off the radio and listening for
+messages from the access point telling it there are packets waiting.
+The station must then retrieve the packets.
+When operating as an access point, the station must honor power
+save operation of associated clients.
+Not all devices support power save operation, either as a client
+or as an access point.
+Use
+.Fl powersave
+to disable powersave operation.
 .It Cm powersavesleep Ar sleep
-For IEEE 802.11 wireless interfaces, set the desired max powersave sleep
-time in milliseconds.
+Set the desired max powersave sleep time in milliseconds.
 .It Cm protmode Ar technique
-For IEEE 802.11 wireless interfaces operating in 11g, use the specified
+For interfaces operating in 802.11g, use the specified
 .Ar technique
 for protecting OFDM frames in a mixed 11b/11g network.
 The set of valid techniques is
@@ -651,8 +706,26 @@
 .Dq Li rtscts
 (RTS/CTS).
 Technique names are case insensitive.
+.It Cm roaming Ar mode
+When operating as a station, control how the system will
+behave when communication with the current access point
+is broken.
+The
+.Ar mode
+argument may be one of
+.Dq Li device
+(leave it to the hardware device to decide),
+.Dq Li auto
+(handle either in the device or the operating system\[em]as appropriate),
+.Dq Li manual
+(do nothing until explicitly instructed).
+By default, the device is left to handle this if it is
+capable; otherwise, the operating system will automatically
+attempt to reestablish communication.
+Manual mode is mostly useful when an application wants to
+control the selection of an access point.
 .It Cm rtsthreshold Ar length
-For IEEE 802.11 wireless interfaces, set the threshold for which
+Set the threshold for which
 transmitted frames are preceded by transmission of an
 RTS
 control frame.
@@ -661,8 +734,26 @@
 argument
 is the frame size in bytes and must be in the range 1 to 2312.
 Not all adaptors support setting the RTS threshold.
+.It Cm ssid Ar ssid
+Set the desired Service Set Identifier (aka network name).
+The SSID is a string up to 32 characters
+in length and may be specified as either a normal string or in
+hexadecimal when proceeded by
+.Ql 0x .
+Additionally, the SSID may be cleared by setting it to
+.Ql - .
+.It Cm scan
+Display the current set of scanned neighbors and/or trigger a new scan.
+Only the super-user can trigger a scan.
+.It Cm stationname Ar name
+Set the name of this station.
+It appears that the station name is not really part of the IEEE 802.11
+protocol though all interfaces seem to support it.
+As such it only
+seems to be meaningful to identical or virtually identical equipment.
+Setting the station name is identical in syntax to setting the SSID.
 .It Cm txpower Ar power
-For IEEE 802.11 wireless interfaces, set the power used to transmit frames.
+Set the power used to transmit frames.
 The
 .Ar power
 argument
@@ -673,7 +764,7 @@
 the driver will use the setting closest to the specified value.
 Not all adaptors support changing the transmit power.
 .It Cm wepmode Ar mode
-For IEEE 802.11 wireless interfaces, set the desired WEP mode.
+Set the desired WEP mode.
 Not all adaptors support all modes.
 The set of valid modes is
 .Dq Li off ,
@@ -693,10 +784,9 @@
 .Dq Li mixed .
 Modes are case insensitive.
 .It Cm weptxkey Ar index
-For IEEE 802.11 wireless interfaces, set the WEP key to be used for
-transmission.
+Set the WEP key to be used for transmission.
 .It Cm wepkey Ar key Ns | Ns Ar index : Ns Ar key
-For IEEE 802.11 wireless interfaces, set the selected WEP key.
+Set the selected WEP key.
 If an
 .Ar index
 is not given, key 1 is set.
@@ -719,6 +809,31 @@
 If that is the case, then the first four keys
 (1-4) will be the standard temporary keys and any others will be adaptor
 specific keys such as permanent keys stored in NVRAM.
+.It Cm wme
+Enable Wireless Media Extensions (WME) support, if available,
+for the specified interface.
+WME is a subset of the IEEE 802.11e standard to support the
+efficient communication of realtime and multimedia data.
+To disable WME support, use
+.Fl wme .
+.El
+.Pp
+The following parameters are support for compatibility with other systems:
+.Bl -tag -width indent
+.It Cm nwid Ar ssid
+Another name for the
+.Cm ssid
+parameter.
+Included for
+.Nx
+compatibility.
+.It Cm station Ar name
+Another name for the
+.Cm stationname
+parameter.
+Included for
+.Bsx
+compatibility.
 .It Cm wep
 Another way of saying
 .Cm wepmode on .
@@ -733,9 +848,7 @@
 compatibility.
 .It Cm nwkey key
 Another way of saying:
-.Pp
 .Dq Li "wepmode on weptxkey 1 wepkey 1:key wepkey 2:- wepkey 3:- wepkey 4:-" .
-.Pp
 Included for
 .Nx
 compatibility.
@@ -745,16 +858,13 @@
 .Sm on
 .Xc
 Another way of saying
-.Pp
 .Dq Li "wepmode on weptxkey n wepkey 1:k1 wepkey 2:k2 wepkey 3:k3 wepkey 4:k4" .
-.Pp
 Included for
 .Nx
 compatibility.
 .It Fl nwkey
 Another way of saying
 .Cm wepmode off .
-.Pp
 Included for
 .Nx
 compatibility.
@@ -768,9 +878,6 @@
 .Nm
 will report only the details specific to that protocol family.
 .Pp
-If the driver does supports the media selection system, the supported
-media list will be included in the output.
-.Pp
 If the
 .Fl m
 flag is passed before an interface name,
@@ -810,6 +917,10 @@
 (only list interfaces that are up).
 .Pp
 The
+.Fl v
+flag may be used to get more verbose status for an interface.
+.Pp
+The
 .Fl C
 flag may be used to list all of the interface cloners available on
 the system, with no additional information.
@@ -823,22 +934,10 @@
 Messages indicating the specified interface does not exist, the
 requested address is unknown, or the user is not privileged and
 tried to alter an interface's configuration.
-.Sh BUGS
-Basic IPv6 node operation requires a link-local address on each
-interface configured for IPv6.
-Normally, such an address is automatically configured by the
-kernel on each interface added to the system; this behaviour may
-be disabled by setting the sysctl MIB variable
-.Va net.inet6.ip6.auto_linklocal
-to 0.
-.Pp
-If you delete such an address using
-.Nm ,
-the kernel may act very oddly.
-Do this at your own risk.
 .Sh SEE ALSO
 .Xr netstat 1 ,
 .Xr netintro 4 ,
+.Xr polling 4 ,
 .Xr vlan 4 ,
 .\" .Xr eon 5 ,
 .Xr rc 8 ,
@@ -849,3 +948,16 @@
 .Nm
 utility appeared in
 .Bx 4.2 .
+.Sh BUGS
+Basic IPv6 node operation requires a link-local address on each
+interface configured for IPv6.
+Normally, such an address is automatically configured by the
+kernel on each interface added to the system; this behaviour may
+be disabled by setting the sysctl MIB variable
+.Va net.inet6.ip6.auto_linklocal
+to 0.
+.Pp
+If you delete such an address using
+.Nm ,
+the kernel may act very oddly.
+Do this at your own risk.
diff -N -u -r src.preview/sbin/ifconfig/ifconfig.c src/sbin/ifconfig/ifconfig.c
--- src.preview/sbin/ifconfig/ifconfig.c	2005-05-26 05:06:40.000000000 -0400
+++ src/sbin/ifconfig/ifconfig.c	2005-09-27 15:03:42.000000000 -0400
@@ -25,17 +25,25 @@
  * 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.
- *
- * @(#) Copyright (c) 1983, 1993 The Regents of the University of California.  All rights reserved.
- * @(#)ifconfig.c	8.2 (Berkeley) 2/16/94
- * $FreeBSD: src/sbin/ifconfig/ifconfig.c,v 1.96 2004/02/27 06:43:14 kan Exp $
- * $DragonFly: src/sbin/ifconfig/ifconfig.c,v 1.24 2005/05/26 09:06:40 dillon Exp $
  */
 
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+	The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)ifconfig.c	8.2 (Berkeley) 2/16/94";
+#endif
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif /* not lint */
+
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
-#include <sys/sockio.h>
 #include <sys/sysctl.h>
 #include <sys/time.h>
 #include <sys/module.h>
@@ -54,30 +62,6 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 
-#ifdef INET6
-#include <netinet6/nd6.h>	/* Define ND6_INFINITE_LIFETIME */
-#endif
-
-#ifndef NO_IPX
-/* IPX */
-#define	IPXIP
-#define IPTUNNEL
-#include <netipx/ipx.h>
-#include <netipx/ipx_if.h>
-#endif
-
-/* Appletalk */
-#include <netatalk/at.h>
-
-/* XNS */
-#ifdef NS
-#define	NSIP
-#include <netns/ns.h>
-#include <netns/ns_if.h>
-#endif
-
-/* OSI */
-
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -86,34 +70,15 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <ifaddrs.h>
 
 #include "ifconfig.h"
 
-/* wrapper for KAME-special getnameinfo() */
-#ifndef NI_WITHSCOPEID
-#define	NI_WITHSCOPEID	0
-#endif
-
 /*
  * Since "struct ifreq" is composed of various union members, callers
  * should pay special attention to interprete the value.
  * (.e.g. little/big endian difference in the structure.)
  */
-struct	ifreq		ifr, ridreq;
-struct	ifaliasreq	addreq;
-#ifdef INET6
-struct	in6_ifreq	in6_ridreq;
-struct	in6_aliasreq	in6_addreq = 
-  { { 0 }, 
-    { 0, 0, 0, 0, { { { 0 } } }, 0 }, 
-    { 0, 0, 0, 0, { { { 0 } } }, 0 }, 
-    { 0, 0, 0, 0, { { { 0 } } }, 0 }, 
-    0, 
-    { 0, 0, ND6_INFINITE_LIFETIME, ND6_INFINITE_LIFETIME } };
-#endif
-struct	sockaddr_in	netmask;
-struct	netrange	at_nr;		/* AppleTalk net range */
+struct	ifreq ifr;
 
 char	name[IFNAMSIZ];
 int	flags;
@@ -123,326 +88,78 @@
 int	doalias;
 int	clearaddr;
 int	newaddr = 1;
-#ifdef INET6
-static	int ip6lifetime;
-#endif
+int	verbose;
 
-struct	afswtch;
+int	supmedia = 0;
+int	printname = 0;		/* Print the name of the created interface. */
 
-int supmedia = 0;
-int listcloners = 0;
-int printname = 0;		/* Print the name of the created interface. */
-
-#ifdef INET6
-char	addr_buf[MAXHOSTNAMELEN *2 + 1];	/*for getnameinfo()*/
-#endif
-
-void	Perror(const char *cmd);
-void	checkatrange(struct sockaddr_at *);
-int	ifconfig(int argc, char *const *argv, const struct afswtch *afp);
-void	notealias(const char *, int, int, const struct afswtch *afp);
-void	list_cloners(void);
-void	printb(const char *s, unsigned value, const char *bits);
-void	rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
-void	status(const struct afswtch *afp, int addrcount,
+static	int ifconfig(int argc, char *const *argv, const struct afswtch *afp);
+static	void status(const struct afswtch *afp, int addrcount,
 		    struct sockaddr_dl *sdl, struct if_msghdr *ifm,
 		    struct ifa_msghdr *ifam);
-void	tunnel_status(int s);
-void	usage(void);
-void	ifmaybeload(char *if_nm);
-
-#ifdef INET6
-void	in6_fillscopeid(struct sockaddr_in6 *sin6);
-int	prefix(void *, int);
-static	char *sec2str(time_t);
-int	explicit_prefix = 0;
-#endif
-
-typedef	void c_func(const char *cmd, int arg, int s, const struct afswtch *afp);
-typedef	void c_func2(const char *arg, const char *arg2, int s, const struct afswtch *afp);
-c_func	setatphase, setatrange;
-c_func	setifaddr, setifbroadaddr, setifdstaddr, setifnetmask;
-c_func2	settunnel;
-c_func	deletetunnel;
-#ifdef INET6
-c_func	setifprefixlen;
-c_func	setip6flags;
-c_func  setip6pltime;
-c_func  setip6vltime;
-c_func2	setip6lifetime;
-c_func	setip6eui64;
-#endif
-c_func	setifipdst;
-c_func	setifflags, setifmetric, setifmtu, setifcap;
-c_func	clone_destroy;
-c_func	setifname;
-
-void clone_create(void);
-
-
-#define	NEXTARG		0xffffff
-#define	NEXTARG2	0xfffffe
-
-const
-struct	cmd {
-	const	char *c_name;
-	int	c_parameter;		/* NEXTARG means next argv */
-	void	(*c_func)(const char *, int, int, const struct afswtch *afp);
-	void	(*c_func2)(const char *, const char *, int, const struct afswtch *afp);
-} cmds[] = {
-	{ "up",		IFF_UP,		setifflags,	NULL },
-	{ "down",	-IFF_UP,	setifflags,	NULL },
-	{ "polling",	IFF_POLLING,	setifflags,	NULL },
-	{ "-polling",	-IFF_POLLING,	setifflags,	NULL },
-	{ "arp",	-IFF_NOARP,	setifflags,	NULL },
-	{ "-arp",	IFF_NOARP,	setifflags,	NULL },
-	{ "debug",	IFF_DEBUG,	setifflags,	NULL },
-	{ "-debug",	-IFF_DEBUG,	setifflags,	NULL },
-	{ "promisc",	IFF_PPROMISC,	setifflags,	NULL },
-	{ "-promisc",	-IFF_PPROMISC,	setifflags,	NULL },
-	{ "add",	IFF_UP,		notealias,	NULL },
-	{ "alias",	IFF_UP,		notealias,	NULL },
-	{ "-alias",	-IFF_UP,	notealias,	NULL },
-	{ "delete",	-IFF_UP,	notealias,	NULL },
-	{ "remove",	-IFF_UP,	notealias,	NULL },
-#ifdef notdef
-#define	EN_SWABIPS	0x1000
-	{ "swabips",	EN_SWABIPS,	setifflags,	NULL },
-	{ "-swabips",	-EN_SWABIPS,	setifflags,	NULL },
-#endif
-	{ "netmask",	NEXTARG,	setifnetmask,	NULL },
-#ifdef INET6
-	{ "prefixlen",	NEXTARG,	setifprefixlen,	NULL },
-	{ "anycast",	IN6_IFF_ANYCAST, setip6flags,	NULL },
-	{ "tentative",	IN6_IFF_TENTATIVE, setip6flags,	NULL },
-	{ "-tentative",	-IN6_IFF_TENTATIVE, setip6flags, NULL },
-	{ "deprecated",	IN6_IFF_DEPRECATED, setip6flags, NULL },
-	{ "-deprecated", -IN6_IFF_DEPRECATED, setip6flags, NULL },
-	{ "autoconf",	IN6_IFF_AUTOCONF, setip6flags,	NULL },
-	{ "-autoconf",	-IN6_IFF_AUTOCONF, setip6flags,	NULL },
-	{ "pltime",     NEXTARG,        setip6pltime,	NULL },
-	{ "vltime",     NEXTARG,        setip6vltime,	NULL },
-	{ "eui64",	0,		setip6eui64,	NULL },
-#endif
-	{ "range",	NEXTARG,	setatrange,	NULL },
-	{ "phase",	NEXTARG,	setatphase,	NULL },
-	{ "metric",	NEXTARG,	setifmetric,	NULL },
-	{ "broadcast",	NEXTARG,	setifbroadaddr,	NULL },
-	{ "ipdst",	NEXTARG,	setifipdst,	NULL },
-	{ "tunnel",	NEXTARG2,	NULL,		settunnel },
-	{ "deletetunnel", 0,		deletetunnel,	NULL },
-	{ "link0",	IFF_LINK0,	setifflags,	NULL },
-	{ "-link0",	-IFF_LINK0,	setifflags,	NULL },
-	{ "link1",	IFF_LINK1,	setifflags,	NULL },
-	{ "-link1",	-IFF_LINK1,	setifflags,	NULL },
-	{ "link2",	IFF_LINK2,	setifflags,	NULL },
-	{ "-link2",	-IFF_LINK2,	setifflags,	NULL },
-#if notyet
-	{ "monitor",	IFF_MONITOR,	setifflags,	NULL },
-	{ "-monitor",	-IFF_MONITOR,	setifflags,	NULL },
-	{ "staticarp",	IFF_STATICARP,	setifflags,	NULL },
-	{ "-staticarp",	-IFF_STATICARP,	setifflags,	NULL },
-#endif
+static	void tunnel_status(int s);
+static	void usage(void);
 
-#ifdef USE_IF_MEDIA
-	{ "media",	NEXTARG,	setmedia,	NULL },
-	{ "mode",	NEXTARG,	setmediamode,	NULL },
-	{ "mediaopt",	NEXTARG,	setmediaopt,	NULL },
-	{ "-mediaopt",	NEXTARG,	unsetmediaopt,	NULL },
-#endif
-#ifdef USE_VLANS
-	{ "vlan",	NEXTARG,	setvlantag,	NULL },
-	{ "vlandev",	NEXTARG,	setvlandev,	NULL },
-	{ "-vlandev",	NEXTARG,	unsetvlandev,	NULL },
-#endif
-#if 0
-	/* XXX `create' special-cased below */
-	{"create",	0,		clone_create,	NULL },
-	{"plumb",	0,		clone_create,	NULL },
-#endif
-	{"destroy",	0,		clone_destroy,	NULL },
-	{"unplumb",	0,		clone_destroy,	NULL },
-#ifdef USE_IEEE80211
-	{ "ssid",	NEXTARG,	set80211ssid,	NULL },
-	{ "nwid",	NEXTARG,	set80211ssid,	NULL },
-	{ "stationname", NEXTARG,	set80211stationname,	NULL },
-	{ "station",	NEXTARG,	set80211stationname,	NULL },	/* BSD/OS */
-	{ "channel",	NEXTARG,	set80211channel,	NULL },
-	{ "authmode",	NEXTARG,	set80211authmode,	NULL },
-	{ "powersavemode", NEXTARG,	set80211powersavemode,	NULL },
-	{ "powersave",	1,		set80211powersave,	NULL },
-	{ "-powersave",	0,		set80211powersave,	NULL },
-	{ "powersavesleep", NEXTARG,	set80211powersavesleep,	NULL },
-	{ "wepmode",	NEXTARG,	set80211wepmode,	NULL },
-	{ "wep",	1,		set80211wep,	NULL },
-	{ "-wep",	0,		set80211wep,	NULL },
-	{ "weptxkey",	NEXTARG,	set80211weptxkey,	NULL },
-	{ "wepkey",	NEXTARG,	set80211wepkey,	NULL },
-	{ "nwkey",	NEXTARG,	set80211nwkey,	NULL },	/* NetBSD */
-	{ "-nwkey",	0,		set80211wep,	NULL },		/* NetBSD */
-	{ "rtsthreshold",NEXTARG,	set80211rtsthreshold,	NULL },
-	{ "protmode",	NEXTARG,	set80211protmode,	NULL },
-	{ "txpower",	NEXTARG,	set80211txpower,	NULL },
-#endif
-	{ "rxcsum",	IFCAP_RXCSUM,	setifcap,	NULL },
-	{ "-rxcsum",	-IFCAP_RXCSUM,	setifcap,	NULL },
-	{ "txcsum",	IFCAP_TXCSUM,	setifcap,	NULL },
-	{ "-txcsum",	-IFCAP_TXCSUM,	setifcap,	NULL },
-	{ "netcons",	IFCAP_NETCONS,	setifcap,	NULL },
-	{ "-netcons",	-IFCAP_NETCONS,	setifcap,	NULL },
-	{ "vlanmtu",	IFCAP_VLAN_MTU,		setifcap,	NULL },
-	{ "-vlanmtu",	-IFCAP_VLAN_MTU,	setifcap,	NULL },
-	{ "vlanhwtag",	IFCAP_VLAN_HWTAGGING,	setifcap,	NULL },
-	{ "-vlanhwtag",	-IFCAP_VLAN_HWTAGGING,	setifcap,	NULL },
-	{ "normal",	-IFF_LINK0,	setifflags,	NULL },
-	{ "compress",	IFF_LINK0,	setifflags,	NULL },
-	{ "noicmp",	IFF_LINK1,	setifflags,	NULL },
-	{ "mtu",	NEXTARG,	setifmtu,	NULL },
-	{ "name",	NEXTARG,	setifname,	NULL },
-	{ NULL,		0,		setifaddr,	NULL },
-	{ NULL,		0,		setifdstaddr,	NULL },
-};
-
-/*
- * XNS support liberally adapted from code written at the University of
- * Maryland principally by James O'Toole and Chris Torek.
- */
-typedef	void af_status(int, struct rt_addrinfo *);
-typedef	void af_getaddr(const char *, int);
-typedef void af_getprefix(const char *, int);
-
-af_status	in_status, at_status, link_status;
-af_getaddr	in_getaddr, at_getaddr, link_getaddr;
-
-#ifndef NO_IPX
-af_status	ipx_status;
-af_getaddr	ipx_getaddr;
-#endif
+static struct afswtch *af_getbyname(const char *name);
+static struct afswtch *af_getbyfamily(int af);
+static void af_other_status(int);
 
-#ifdef INET6
-af_status	in6_status;
-af_getaddr	in6_getaddr;
-af_getprefix	in6_getprefix;
-#endif /*INET6*/
-#ifdef NS
-af_status	xns_status;
-af_getaddr	xns_getaddr;
-#endif
-
-/* Known address families */
-const
-struct	afswtch {
-	const char *af_name;
-	short af_af;
-	af_status *af_status;
-	af_getaddr *af_getaddr;
-	af_getprefix *af_getprefix;
-	u_long af_difaddr;
-	u_long af_aifaddr;
-	caddr_t af_ridreq;
-	caddr_t af_addreq;
-} afs[] = {
-#define C(x) ((caddr_t) &x)
-	{ "inet", AF_INET, in_status, in_getaddr, NULL,
-	     SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
-#ifdef INET6
-	{ "inet6", AF_INET6, in6_status, in6_getaddr, in6_getprefix,
-	     SIOCDIFADDR_IN6, SIOCAIFADDR_IN6,
-	     C(in6_ridreq), C(in6_addreq) },
-#endif /*INET6*/
-#ifndef NO_IPX
-	{ "ipx", AF_IPX, ipx_status, ipx_getaddr, NULL,
-	     SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
-#endif
-	{ "atalk", AF_APPLETALK, at_status, at_getaddr, NULL,
-	     SIOCDIFADDR, SIOCAIFADDR, C(addreq), C(addreq) },
-#ifdef NS
-	{ "ns", AF_NS, xns_status, xns_getaddr, NULL,
-	     SIOCDIFADDR, SIOCAIFADDR, C(ridreq), C(addreq) },
-#endif
-	{ "link", AF_LINK, link_status, link_getaddr, NULL,
-	     0, SIOCSIFLLADDR, NULL, C(ridreq) },
-	{ "ether", AF_LINK, link_status, link_getaddr, NULL,
-	     0, SIOCSIFLLADDR, NULL, C(ridreq) },
-	{ "lladdr", AF_LINK, link_status, link_getaddr, NULL,
-	     0, SIOCSIFLLADDR, NULL, C(ridreq) },
-	{ NULL,	0,	NULL, NULL, NULL,	0, 0,	NULL, NULL }
-};
-
-/*
- * Expand the compacted form of addresses as returned via the
- * configuration read via sysctl().
- */
-
-#define ROUNDUP(a) \
-        ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
-#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+static struct option *opts = NULL;
 
 void
-rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
+opt_register(struct option *p)
 {
-        struct sockaddr *sa;
-        int i;
-
-        memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
-        for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
-                if ((rtinfo->rti_addrs & (1 << i)) == 0)
-                        continue;
-                rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
-                ADVANCE(cp, sa);
-        }
+	p->next = opts;
+	opts = p;
 }
 
-
-void
+static void
 usage(void)
 {
-#ifndef INET6
-	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
-	"usage: ifconfig interface address_family [address [dest_address]]",
-	"                [parameters]",
-	"       ifconfig -C",
-	"       ifconfig interface create",
-	"       ifconfig -a [-d] [-m] [-u] [address_family]",
-	"       ifconfig -l [-d] [-u] [address_family]",
-	"       ifconfig [-d] [-m] [-u]");
-#else
-	fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
-	"usage: ifconfig [-L] interface address_family [address [dest_address]]",
-	"                [parameters]",
-	"       ifconfig -C",
-	"       ifconfig interface create",
-	"       ifconfig -a [-L] [-d] [-m] [-u] [address_family]",
-	"       ifconfig -l [-d] [-u] [address_family]",
-	"       ifconfig [-L] [-d] [-m] [-u]");
-#endif
+	char options[1024];
+	struct option *p;
+
+	/* XXX not right but close enough for now */
+	options[0] = '\0';
+	for (p = opts; p != NULL; p = p->next) {
+		strlcat(options, p->opt_usage, sizeof(options));
+		strlcat(options, " ", sizeof(options));
+	}
+
+	fprintf(stderr,
+	"usage: ifconfig %sinterface address_family [address [dest_address]]\n"
+	"                [parameters]\n"
+	"       ifconfig interface create\n"
+	"       ifconfig -a %s[-d] [-m] [-u] [-v] [address_family]\n"
+	"       ifconfig -l [-d] [-u] [address_family]\n"
+	"       ifconfig %s[-d] [-m] [-u] [-v]\n",
+		options, options, options);
 	exit(1);
 }
 
 int
-main(int argc, char * const *argv)
+main(int argc, char *argv[])
 {
-	int c;
-	int all, namesonly, downonly, uponly;
+	int c, all, namesonly, downonly, uponly;
 	int need_nl = 0, count = 0;
-	const struct afswtch *afp = 0;
+	const struct afswtch *afp = NULL;
 	int addrcount, ifindex;
-	struct	if_msghdr *ifm, *nextifm;
-	struct	ifa_msghdr *ifam;
-	struct	sockaddr_dl *sdl;
-	char	*buf, *lim, *next;
-
+	struct if_msghdr *ifm, *nextifm;
+	struct ifa_msghdr *ifam;
+	struct sockaddr_dl *sdl;
+	char *buf, *lim, *next;
 	size_t needed;
 	int mib[6];
+	char options[1024];
+	struct option *p;
+
+	all = downonly = uponly = namesonly = verbose = 0;
 
 	/* Parse leading line options */
-	all = downonly = uponly = namesonly = 0;
-	while ((c = getopt(argc, argv, "adlmuC"
-#ifdef INET6
-					"L"
-#endif
-			)) != -1) {
+	strlcpy(options, "adlmuv", sizeof(options));
+	for (p = opts; p != NULL; p = p->next)
+		strlcat(options, p->opt, sizeof(options));
+	while ((c = getopt(argc, argv, options)) != -1) {
 		switch (c) {
 		case 'a':	/* scan all interfaces */
 			all++;
@@ -459,32 +176,23 @@
 		case 'u':	/* restrict scan to "up" interfaces */
 			uponly++;
 			break;
-		case 'C':
-			listcloners = 1;
+		case 'v':
+			verbose++;
 			break;
-#ifdef INET6
-		case 'L':
-			ip6lifetime++;	/* print IPv6 address lifetime */
-			break;
-#endif
 		default:
-			usage();
+			for (p = opts; p != NULL; p = p->next)
+				if (p->opt[0] == c) {
+					p->cb(optarg);
+					break;
+				}
+			if (p == NULL)
+				usage();
 			break;
 		}
 	}
 	argc -= optind;
 	argv += optind;
 
-	if (listcloners) {
-		/* -C must be solitary */
-		if (all || supmedia || uponly || downonly || namesonly ||
-		    argc > 0)
-			usage();
-		
-		list_cloners();
-		exit(0);
-	}
-
 	/* -l cannot be used with -a or -m */
 	if (namesonly && (all || supmedia))
 		usage();
@@ -504,13 +212,11 @@
 
 		ifindex = 0;
 		if (argc == 1) {
-			for (afp = afs; afp->af_name; afp++)
-				if (strcmp(afp->af_name, *argv) == 0) {
-					argc--, argv++;
-					break;
-				}
-			if (afp->af_name == NULL)
+			afp = af_getbyname(*argv);
+			if (afp == NULL)
 				usage();
+			if (afp->af_name != NULL)
+				argc--, argv++;
 			/* leave with afp non-zero */
 		}
 	} else {
@@ -543,13 +249,9 @@
 
 	/* Check for address family */
 	if (argc > 0) {
-		for (afp = afs; afp->af_name; afp++)
-			if (strcmp(afp->af_name, *argv) == 0) {
-				argc--, argv++;
-				break;
-			}
-		if (afp->af_name == NULL)
-			afp = NULL;	/* not a family, NULL */
+		afp = af_getbyname(*argv);
+		if (afp != NULL)
+			argc--, argv++;
 	}
 
 retry:
@@ -561,7 +263,7 @@
 	mib[5] = ifindex;		/* interface index */
 
 	/* if particular family specified, only ask about it */
-	if (afp)
+	if (afp != NULL)
 		mib[3] = afp->af_af;
 
 	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
@@ -586,10 +288,10 @@
 		
 		if (ifm->ifm_type == RTM_IFINFO) {
 #if  notyet
-			if (ifm->ifm_data.ifi_datalen == 0)
-				ifm->ifm_data.ifi_datalen = sizeof(struct if_data);
-			sdl = (struct sockaddr_dl *)((char *)ifm + sizeof(struct if_msghdr) -
-			     sizeof(struct if_data) + ifm->ifm_data.ifi_datalen);
+                        if (ifm->ifm_data.ifi_datalen == 0)
+                                ifm->ifm_data.ifi_datalen = sizeof(struct if_data);
+                        sdl = (struct sockaddr_dl *)((char *)ifm + sizeof(struct if_msghdr) -
+                             sizeof(struct if_data) + ifm->ifm_data.ifi_datalen);
 #else
                        sdl = (struct sockaddr_dl *)(ifm + 1);
 #endif
@@ -627,14 +329,6 @@
 		    sizeof(name)-1 : sdl->sdl_nlen] = '\0';
 
 		if (all || namesonly) {
-			size_t len;
-
-			/* sdl_data may not be terminated, don't use strlcpy */
-			if ((len = sdl->sdl_nlen) > sizeof(name) - 1)
-				len = sizeof(name) - 1;
-			bcopy(sdl->sdl_data, name, len);
-			name[len] = 0;
-
 			if (uponly)
 				if ((flags & IFF_UP) == 0)
 					continue; /* not up */
@@ -642,9 +336,8 @@
 				if (flags & IFF_UP)
 					continue; /* not down */
 			if (namesonly) {
-				if (afp == NULL ||
-					afp->af_status != link_status ||
-					sdl->sdl_type == IFT_ETHER) {
+				if (afp == NULL || afp->af_af != AF_LINK ||
+				    sdl->sdl_type == IFT_ETHER) {
 					if (need_nl)
 						putchar(' ');
 					fputs(name, stdout);
@@ -670,92 +363,192 @@
 	exit (0);
 }
 
+static struct afswtch *afs = NULL;
 
-int
+void
+af_register(struct afswtch *p)
+{
+	p->af_next = afs;
+	afs = p;
+}
+
+static struct afswtch *
+af_getbyname(const char *name)
+{
+	struct afswtch *afp;
+
+	for (afp = afs; afp !=  NULL; afp = afp->af_next)
+		if (strcmp(afp->af_name, name) == 0)
+			return afp;
+	return NULL;
+}
+
+static struct afswtch *
+af_getbyfamily(int af)
+{
+	struct afswtch *afp;
+
+	for (afp = afs; afp != NULL; afp = afp->af_next)
+		if (afp->af_af == af)
+			return afp;
+	return NULL;
+}
+
+static void
+af_other_status(int s)
+{
+	struct afswtch *afp;
+	uint8_t afmask[howmany(AF_MAX, NBBY)];
+
+	memset(afmask, 0, sizeof(afmask));
+	for (afp = afs; afp != NULL; afp = afp->af_next) {
+		if (afp->af_other_status == NULL)
+			continue;
+		if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+			continue;
+		afp->af_other_status(s);
+		setbit(afmask, afp->af_af);
+	}
+}
+
+static void
+af_all_tunnel_status(int s)
+{
+	struct afswtch *afp;
+	uint8_t afmask[howmany(AF_MAX, NBBY)];
+
+	memset(afmask, 0, sizeof(afmask));
+	for (afp = afs; afp != NULL; afp = afp->af_next) {
+		if (afp->af_status_tunnel == NULL)
+			continue;
+		if (afp->af_af != AF_UNSPEC && isset(afmask, afp->af_af))
+			continue;
+		afp->af_status_tunnel(s);
+		setbit(afmask, afp->af_af);
+	}
+}
+
+static struct cmd *cmds = NULL;
+
+void
+cmd_register(struct cmd *p)
+{
+	p->c_next = cmds;
+	cmds = p;
+}
+
+static const struct cmd *
+cmd_lookup(const char *name)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	const struct cmd *p;
+
+	for (p = cmds; p != NULL; p = p->c_next)
+		if (strcmp(name, p->c_name) == 0)
+			return p;
+	return NULL;
+#undef N
+}
+
+struct callback {
+	callback_func *cb_func;
+	void	*cb_arg;
+	struct callback *cb_next;
+};
+static struct callback *callbacks = NULL;
+
+void
+callback_register(callback_func *func, void *arg)
+{
+	struct callback *cb;
+
+	cb = malloc(sizeof(struct callback));
+	if (cb == NULL)
+		errx(1, "unable to allocate memory for callback");
+	cb->cb_func = func;
+	cb->cb_arg = arg;
+	cb->cb_next = callbacks;
+	callbacks = cb;
+}
+
+/* specially-handled commands */
+static void setifaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifaddr_cmd = DEF_CMD("ifaddr", 0, setifaddr);
+
+static void setifdstaddr(const char *, int, int, const struct afswtch *);
+static const struct cmd setifdstaddr_cmd =
+	DEF_CMD("ifdstaddr", 0, setifdstaddr);
+
+static int
 ifconfig(int argc, char *const *argv, const struct afswtch *afp)
 {
+	struct callback *cb;
 	int s;
 
 	if (afp == NULL)
-		afp = &afs[0];
-	ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af;
+		afp = af_getbyname("inet");
+	ifr.ifr_addr.sa_family =
+		afp->af_af == AF_LINK || afp->af_af == AF_UNSPEC ?
+		AF_INET : afp->af_af;
 	strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
 
 	if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
-		err(1, "socket");
+		err(1, "socket(family %u,SOCK_DGRAM", ifr.ifr_addr.sa_family);
 
 	while (argc > 0) {
 		const struct cmd *p;
 
-		for (p = cmds; p->c_name; p++)
-			if (strcmp(*argv, p->c_name) == 0)
-				break;
-		if (p->c_name == 0 && setaddr)
-			p++;	/* got src, do dst */
-		if (p->c_func || p->c_func2) {
+		p = cmd_lookup(*argv);
+		if (p == NULL) {
+			/*
+			 * Not a recognized command, choose between setting
+			 * the interface address and the dst address.
+			 */
+			p = (setaddr ? &setifdstaddr_cmd : &setifaddr_cmd);
+		}
+		if (p->c_u.c_func || p->c_u.c_func2) {
 			if (p->c_parameter == NEXTARG) {
 				if (argv[1] == NULL)
 					errx(1, "'%s' requires argument",
 					    p->c_name);
-				(*p->c_func)(argv[1], 0, s, afp);
+				p->c_u.c_func(argv[1], 0, s, afp);
 				argc--, argv++;
+			} else if (p->c_parameter == OPTARG) {
+				p->c_u.c_func(argv[1], 0, s, afp);
+				if (argv[1] != NULL)
+					argc--, argv++;
 			} else if (p->c_parameter == NEXTARG2) {
 				if (argc < 3)
 					errx(1, "'%s' requires 2 arguments",
 					    p->c_name);
-				(*p->c_func2)(argv[1], argv[2], s, afp);
+				p->c_u.c_func2(argv[1], argv[2], s, afp);
 				argc -= 2, argv += 2;
 			} else
-				(*p->c_func)(*argv, p->c_parameter, s, afp);
+				p->c_u.c_func(*argv, p->c_parameter, s, afp);
 		}
 		argc--, argv++;
 	}
-#ifdef INET6
-	if (ifr.ifr_addr.sa_family == AF_INET6 && explicit_prefix == 0) {
-		/* Aggregatable address architecture defines all prefixes
-		   are 64. So, it is convenient to set prefixlen to 64 if
-		   it is not specified. */
-		setifprefixlen("64", 0, s, afp);
-		/* in6_getprefix("64", MASK) if MASK is available here... */
-	}
-#endif
-#ifndef NO_IPX
-	if (setipdst && ifr.ifr_addr.sa_family == AF_IPX) {
-		struct ipxip_req rq;
-		int size = sizeof(rq);
-
-		rq.rq_ipx = addreq.ifra_addr;
-		rq.rq_ip = addreq.ifra_dstaddr;
-
-		if (setsockopt(s, 0, SO_IPXIP_ROUTE, &rq, size) < 0)
-			Perror("Encapsulation Routing");
-	}
-#endif
-	if (ifr.ifr_addr.sa_family == AF_APPLETALK)
-		checkatrange((struct sockaddr_at *) &addreq.ifra_addr);
-#ifdef NS
-	if (setipdst && ifr.ifr_addr.sa_family == AF_NS) {
-		struct nsip_req rq;
-		int size = sizeof(rq);
 
-		rq.rq_ns = addreq.ifra_addr;
-		rq.rq_ip = addreq.ifra_dstaddr;
-
-		if (setsockopt(s, 0, SO_NSIP_ROUTE, &rq, size) < 0)
-			Perror("Encapsulation Routing");
-	}
-#endif
+	/*
+	 * Do any post argument processing required by the address family.
+	 */
+	if (afp->af_postproc != NULL)
+		afp->af_postproc(s, afp);
+	/*
+	 * Do deferred operations.
+	 */
 	if (clearaddr) {
 		if (afp->af_ridreq == NULL || afp->af_difaddr == 0) {
 			warnx("interface %s cannot change %s addresses!",
 			      name, afp->af_name);
-			clearaddr = NULL;
+			clearaddr = 0;
 		}
 	}
 	if (clearaddr) {
 		int ret;
 		strncpy(afp->af_ridreq, name, sizeof ifr.ifr_name);
-		if ((ret = ioctl(s, afp->af_difaddr, afp->af_ridreq)) < 0) {
+		ret = ioctl(s, afp->af_difaddr, afp->af_ridreq);
+		if (ret < 0) {
 			if (errno == EADDRNOTAVAIL && (doalias >= 0)) {
 				/* means no previous address for interface */
 			} else
@@ -774,20 +567,23 @@
 		if (ioctl(s, afp->af_aifaddr, afp->af_addreq) < 0)
 			Perror("ioctl (SIOCAIFADDR)");
 	}
+
+	/*
+	 * Do deferred callbacks registered while processing
+	 * command-line arguments.
+	 */
+	for (cb = callbacks; cb != NULL; cb = cb->cb_next)
+		cb->cb_func(s, cb->cb_arg);
+
 	close(s);
 	return(0);
 }
-#define RIDADDR 0
-#define ADDR	1
-#define MASK	2
-#define DSTADDR	3
 
 /*ARGSUSED*/
-void
-setifaddr(const char *addr, int param __unused, int s __unused,
-	  const struct afswtch *afp)
+static void
+setifaddr(const char *addr, int param, int s, const struct afswtch *afp)
 {
-	if (*afp->af_getaddr == NULL)
+	if (afp->af_getaddr == NULL)
 		return;
 	/*
 	 * Delay the ioctl to set the interface addr until flags are all set.
@@ -797,21 +593,20 @@
 	setaddr++;
 	if (doalias == 0 && afp->af_af != AF_LINK)
 		clearaddr = 1;
-	(*afp->af_getaddr)(addr, (doalias >= 0 ? ADDR : RIDADDR));
+	afp->af_getaddr(addr, (doalias >= 0 ? ADDR : RIDADDR));
 }
 
-void
+static void
 settunnel(const char *src, const char *dst, int s, const struct afswtch *afp)
 {
-	struct addrinfo hints, *srcres, *dstres;
-	struct ifaliasreq addr_req;
+	struct addrinfo *srcres, *dstres;
 	int ecode;
-#ifdef INET6
-	struct in6_aliasreq in6_addr_req; 
-#endif
 
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = afp->af_af;
+	if (afp->af_settunnel == NULL) {
+		warn("address family %s does not support tunnel setup",
+			afp->af_name);
+		return;
+	}
 
 	if ((ecode = getaddrinfo(src, NULL, NULL, &srcres)) != 0)
 		errx(1, "error in parsing address string: %s",
@@ -825,178 +620,58 @@
 		errx(1,
 		    "source and destination address families do not match");
 
-	switch (srcres->ai_addr->sa_family) {
-	case AF_INET:
-		memset(&addr_req, 0, sizeof(addr_req));
-		strncpy(addr_req.ifra_name, name, IFNAMSIZ);
-		memcpy(&addr_req.ifra_addr, srcres->ai_addr,
-		    srcres->ai_addr->sa_len);
-		memcpy(&addr_req.ifra_dstaddr, dstres->ai_addr,
-		    dstres->ai_addr->sa_len);
-
-		if (ioctl(s, SIOCSIFPHYADDR, &addr_req) < 0)
-			warn("SIOCSIFPHYADDR");
-		break;
-
-#ifdef INET6
-	case AF_INET6:
-		memset(&in6_addr_req, 0, sizeof(in6_addr_req));
-		strncpy(in6_addr_req.ifra_name, name, IFNAMSIZ);
-		memcpy(&in6_addr_req.ifra_addr, srcres->ai_addr,
-		    srcres->ai_addr->sa_len);
-		memcpy(&in6_addr_req.ifra_dstaddr, dstres->ai_addr,
-		    dstres->ai_addr->sa_len);
-
-		if (ioctl(s, SIOCSIFPHYADDR_IN6, &in6_addr_req) < 0)
-			warn("SIOCSIFPHYADDR_IN6");
-		break;
-#endif /* INET6 */
-
-	default:
-		warn("address family not supported");
-	}
+	afp->af_settunnel(s, srcres, dstres);
 
 	freeaddrinfo(srcres);
 	freeaddrinfo(dstres);
 }
 
 /* ARGSUSED */
-void
-deletetunnel(const char *vname __unused, int param __unused, int s,
-	     const struct afswtch *afp __unused)
+static void
+deletetunnel(const char *vname, int param, int s, const struct afswtch *afp)
 {
+
 	if (ioctl(s, SIOCDIFPHYADDR, &ifr) < 0)
 		err(1, "SIOCDIFPHYADDR");
 }
 
-void
-setifnetmask(const char *addr, int dummy __unused, int s __unused,
-             const struct afswtch *afp)
-{
-	if (*afp->af_getaddr == NULL)
-		return;
-	setmask++;
-	(*afp->af_getaddr)(addr, MASK);
-}
-
-#ifdef INET6
-void
-setifprefixlen(const char *addr, int dummy __unused, int s __unused,
-               const struct afswtch *afp)
-{
-        if (*afp->af_getprefix)
-                (*afp->af_getprefix)(addr, MASK);
-	explicit_prefix = 1;
-}
-
-void
-setip6flags(const char *dummyaddr __unused, int flag, int dummysoc __unused,
-            const struct afswtch *afp)
-{
-	if (afp->af_af != AF_INET6)
-		err(1, "address flags can be set only for inet6 addresses");
-
-	if (flag < 0)
-		in6_addreq.ifra_flags &= ~(-flag);
-	else
-		in6_addreq.ifra_flags |= flag;
-}
-
-void
-setip6pltime(const char *seconds, int dummy __unused, int s,
-             const struct afswtch *afp)
+static void
+setifnetmask(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
 {
-	setip6lifetime("pltime", seconds, s, afp);
-}
-
-void
-setip6vltime(const char *seconds, int dummy __unused, int s,
-             const struct afswtch *afp)
-{
-	setip6lifetime("vltime", seconds, s, afp);
-}
-
-void
-setip6lifetime(const char *cmd, const char *val, int s __unused,
-               const struct afswtch *afp)
-{
-	time_t newval, t;
-	char *ep;
-
-	t = time(NULL);
-	newval = (time_t)strtoul(val, &ep, 0);
-	if (val == ep)
-		errx(1, "invalid %s", cmd);
-	if (afp->af_af != AF_INET6)
-		errx(1, "%s not allowed for the AF", cmd);
-	if (strcmp(cmd, "vltime") == 0) {
-		in6_addreq.ifra_lifetime.ia6t_expire = t + newval;
-		in6_addreq.ifra_lifetime.ia6t_vltime = newval;
-	} else if (strcmp(cmd, "pltime") == 0) {
-		in6_addreq.ifra_lifetime.ia6t_preferred = t + newval;
-		in6_addreq.ifra_lifetime.ia6t_pltime = newval;
+	if (afp->af_getaddr != NULL) {
+		setmask++;
+		afp->af_getaddr(addr, MASK);
 	}
 }
 
-void
-setip6eui64(const char *cmd, int dummy __unused, int s __unused,
-            const struct afswtch *afp)
+static void
+setifbroadaddr(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
 {
-	struct ifaddrs *ifap, *ifa;
-	const struct sockaddr_in6 *sin6 = NULL;
-	const struct in6_addr *lladdr = NULL;
-	struct in6_addr *in6;
-
-	if (afp->af_af != AF_INET6)
-		errx(EXIT_FAILURE, "%s not allowed for the AF", cmd);
- 	in6 = (struct in6_addr *)&in6_addreq.ifra_addr.sin6_addr;
-	if (memcmp(&in6addr_any.s6_addr[8], &in6->s6_addr[8], 8) != 0)
-		errx(EXIT_FAILURE, "interface index is already filled");
-	if (getifaddrs(&ifap) != 0)
-		err(EXIT_FAILURE, "getifaddrs");
-	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
-		if (ifa->ifa_addr->sa_family == AF_INET6 &&
-		    strcmp(ifa->ifa_name, name) == 0) {
-			sin6 = (const struct sockaddr_in6 *)ifa->ifa_addr;
-			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
-				lladdr = &sin6->sin6_addr;
-				break;
-			}
-		}
-	}
-	if (!lladdr)
-		errx(EXIT_FAILURE, "could not determine link local address"); 
-
- 	memcpy(&in6->s6_addr[8], &lladdr->s6_addr[8], 8);
-
-	freeifaddrs(ifap);
+	if (afp->af_getaddr != NULL)
+		afp->af_getaddr(addr, DSTADDR);
 }
-#endif
 
-void
-setifbroadaddr(const char *addr, int dummy __unused, int s __unused,
-               const struct afswtch *afp)
+static void
+setifipdst(const char *addr, int dummy __unused, int s,
+    const struct afswtch *afp)
 {
-	if (*afp->af_getaddr == NULL)
-		return;
-	(*afp->af_getaddr)(addr, DSTADDR);
-}
+	const struct afswtch *inet;
 
-void
-setifipdst(const char *addr, int dummy __unused, int s __unused,
-           const struct afswtch *afp __unused)
-{
-	in_getaddr(addr, DSTADDR);
+	inet = af_getbyname("inet");
+	if (inet == NULL)
+		return;
+	inet->af_getaddr(addr, DSTADDR);
 	setipdst++;
 	clearaddr = 0;
 	newaddr = 0;
 }
-#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
 
-void
-notealias(const char *addr __unused, int param, int s __unused,
-	  const struct afswtch *afp)
+static void
+notealias(const char *addr, int param, int s, const struct afswtch *afp)
 {
+#define rqtosa(x) (&(((struct ifreq *)(afp->x))->ifr_addr))
 	if (setaddr && doalias == 0 && param < 0)
 		if (afp->af_addreq != NULL && afp->af_ridreq != NULL)
 			bcopy((caddr_t)rqtosa(af_addreq),
@@ -1008,16 +683,16 @@
 		newaddr = 0;
 	} else
 		clearaddr = 0;
+#undef rqtosa
 }
 
 /*ARGSUSED*/
-void
-setifdstaddr(const char *addr, int param __unused, int s __unused,
-             const struct afswtch *afp)
+static void
+setifdstaddr(const char *addr, int param __unused, int s, 
+    const struct afswtch *afp)
 {
-	if (*afp->af_getaddr == NULL)
-		return;
-	(*afp->af_getaddr)(addr, DSTADDR);
+	if (afp->af_getaddr != NULL)
+		afp->af_getaddr(addr, DSTADDR);
 }
 
 /*
@@ -1025,9 +700,8 @@
  * of the ifreq structure, which may confuse other parts of ifconfig.
  * Make a private copy so we can avoid that.
  */
-void
-setifflags(const char *vname, int value, int s,
-	   const struct afswtch *afp __unused)
+static void
+setifflags(const char *vname, int value, int s, const struct afswtch *afp)
 {
 	struct ifreq		my_ifr;
 
@@ -1052,8 +726,7 @@
 }
 
 void
-setifcap(const char *vname, int value, int s,
-	 const struct afswtch *afp __unused)
+setifcap(const char *vname, int value, int s, const struct afswtch *afp)
 {
 
  	if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) < 0) {
@@ -1071,9 +744,9 @@
 		Perror(vname);
 }
 
-void
-setifmetric(const char *val, int dummy __unused, int s,
-            const struct afswtch *afp __unused)
+static void
+setifmetric(const char *val, int dummy __unused, int s, 
+    const struct afswtch *afp)
 {
 	strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
 	ifr.ifr_metric = atoi(val);
@@ -1081,9 +754,9 @@
 		warn("ioctl (set metric)");
 }
 
-void
-setifmtu(const char *val, int dummy __unused, int s,
-	 const struct afswtch *afp __unused)
+static void
+setifmtu(const char *val, int dummy __unused, int s, 
+    const struct afswtch *afp)
 {
 	strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
 	ifr.ifr_mtu = atoi(val);
@@ -1091,14 +764,17 @@
 		warn("ioctl (set mtu)");
 }
 
-void
+static void
 setifname(const char *val, int dummy __unused, int s, 
-	  const struct afswtch *afp __unused)
+    const struct afswtch *afp)
 {
-	char	*newname;
+	char *newname;
 
 	newname = strdup(val);
-	
+	if (newname == NULL) {
+		warn("no memory to set ifname");
+		return;
+	}
 	ifr.ifr_data = newname;
 	if (ioctl(s, SIOCSIFNAME, (caddr_t)&ifr) < 0) {
 		warn("ioctl (set name)");
@@ -1106,7 +782,6 @@
 		return;
 	}
 	strlcpy(name, newname, sizeof(name));
-
 	free(newname);
 
 	/*
@@ -1116,38 +791,62 @@
 	printname = 0;
 }
 
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+#define SA_SIZE(sa)                                             \
+	(  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?      \
+	sizeof(long)            :                               \
+	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+
+static void
+rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
+{
+	struct sockaddr *sa;
+	int i;
+
+	memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+		if ((rtinfo->rti_addrs & (1 << i)) == 0)
+			continue;
+		rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+		cp += SA_SIZE(sa);
+	}
+}
+
 #define	IFFBITS \
 "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
 "\10NOARP\11PROMISC\12ALLMULTI\13OACTIVE\14SIMPLEX\15LINK0\16LINK1\17LINK2" \
 "\20MULTICAST\21POLLING\23MONITOR\24STATICARP"
 
 #define	IFCAPBITS \
-"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU\7POLLING"
+"\020\1RXCSUM\2TXCSUM\3NETCONS\4VLAN_MTU\5VLAN_HWTAGGING\6JUMBO_MTU"
 
 /*
  * Print the status of the interface.  If an address family was
- * specified, show it and it only; otherwise, show them all.
+ * specified, show only it; otherwise, show them all.
  */
-void
-status(const struct afswtch *afp, int addrcount, struct sockaddr_dl *sdl,
-       struct if_msghdr *ifm, struct ifa_msghdr *ifam)
+static void
+status(const struct afswtch *afp, int addrcount, struct	sockaddr_dl *sdl,
+    struct if_msghdr *ifm, struct ifa_msghdr *ifam)
 {
-	const struct afswtch *p = NULL;
 	struct	rt_addrinfo info;
 	int allfamilies, s;
 	struct ifstat ifs;
 
 	if (afp == NULL) {
 		allfamilies = 1;
-		afp = &afs[0];
+		afp = af_getbyname("inet");
 	} else
 		allfamilies = 0;
 
 	ifr.ifr_addr.sa_family = afp->af_af == AF_LINK ? AF_INET : afp->af_af;
-	strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
+	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
 
-	if ((s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) < 0)
-		err(1, "socket");
+	s = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0);
+	if (s < 0)
+		err(1, "socket(family %u,SOCK_DGRAM)", ifr.ifr_addr.sa_family);
 
 	printf("%s: ", name);
 	printb("flags", flags, IFFBITS);
@@ -1163,8 +862,7 @@
 			putchar('\n');
 		}
 		if (supmedia && ifr.ifr_reqcap != 0) {
-			printf("\tcapability list:\n");
-			printb("\t\t", ifr.ifr_reqcap, IFCAPBITS);
+			printb("\tcapabilities", ifr.ifr_reqcap, IFCAPBITS);
 			putchar('\n');
 		}
 	}
@@ -1172,395 +870,53 @@
 	tunnel_status(s);
 
 	while (addrcount > 0) {
-		
 		info.rti_addrs = ifam->ifam_addrs;
-
 		/* Expand the compacted addresses */
 		rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
 			  &info);
 
-		if (!allfamilies) {
-			if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family) {
-				p = afp;
-				(*p->af_status)(s, &info);
-			}
-		} else for (p = afs; p->af_name; p++) {
-			if (p->af_af == info.rti_info[RTAX_IFA]->sa_family)
-				(*p->af_status)(s, &info);
-		}
+		if (allfamilies) {
+			const struct afswtch *p;
+			p = af_getbyfamily(info.rti_info[RTAX_IFA]->sa_family);
+			if (p != NULL)
+				p->af_status(s, &info);
+		} else if (afp->af_af == info.rti_info[RTAX_IFA]->sa_family)
+			afp->af_status(s, &info);
 		addrcount--;
 		ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
 	}
-	if (allfamilies || afp->af_status == link_status)
-		link_status(s, (struct rt_addrinfo *)sdl);
-#ifdef USE_IF_MEDIA
-	if (allfamilies || afp->af_status == media_status)
-		media_status(s, NULL);
-#endif
-#ifdef USE_VLANS
-	if (allfamilies || afp->af_status == vlan_status)
-		vlan_status(s, NULL);
-#endif
-#ifdef USE_IEEE80211
-	if (allfamilies || afp->af_status == ieee80211_status)
-		ieee80211_status(s, NULL);
-#endif
-	strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name);
-	if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) 
-		printf("%s", ifs.ascii);
-
-	if (!allfamilies && !p &&
-#ifdef USE_IF_MEDIA
-	    afp->af_status != media_status &&
-#endif
-	    afp->af_status != link_status
-#ifdef USE_VLANS
-	    && afp->af_status != vlan_status
-#endif
-		)
-		warnx("%s has no %s interface address!", name, afp->af_name);
-
-	close(s);
-	return;
-}
-
-void
-tunnel_status(int s)
-{
-	char psrcaddr[NI_MAXHOST];
-	char pdstaddr[NI_MAXHOST];
-	u_long srccmd, dstcmd;
-	struct ifreq *ifrp;
-	const char *ver = "";
-#ifdef NI_WITHSCOPEID
-	const int niflag = NI_NUMERICHOST | NI_WITHSCOPEID;
-#else
-	const int niflag = NI_NUMERICHOST;
-#endif
-#ifdef INET6
-	struct in6_ifreq in6_ifr;
-	int s6;
-#endif /* INET6 */
-
-	psrcaddr[0] = pdstaddr[0] = '\0';
-
-#ifdef INET6
-	memset(&in6_ifr, 0, sizeof(in6_ifr));
-	strncpy(in6_ifr.ifr_name, name, IFNAMSIZ);
-	s6 = socket(AF_INET6, SOCK_DGRAM, 0);
-	if (s6 < 0) {
-		srccmd = SIOCGIFPSRCADDR;
-		dstcmd = SIOCGIFPDSTADDR;
-		ifrp = 𝔦
-	} else {
-		close(s6);
-		srccmd = SIOCGIFPSRCADDR_IN6;
-		dstcmd = SIOCGIFPDSTADDR_IN6;
-		ifrp = (struct ifreq *)&in6_ifr;
-	}
-#else /* INET6 */
-	srccmd = SIOCGIFPSRCADDR;
-	dstcmd = SIOCGIFPDSTADDR;
-	ifrp = 𝔦
-#endif /* INET6 */
-
-	if (ioctl(s, srccmd, (caddr_t)ifrp) < 0)
-		return;
-#if defined(INET6) && defined(__KAME__) && defined(KAME_SCOPEID)
-	if (ifrp->ifr_addr.sa_family == AF_INET6)
-		in6_fillscopeid((struct sockaddr_in6 *)&ifrp->ifr_addr);
-#endif
-	getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
-	    psrcaddr, sizeof(psrcaddr), 0, 0, niflag);
-#ifdef INET6
-	if (ifrp->ifr_addr.sa_family == AF_INET6)
-		ver = "6";
-#endif
-
-	if (ioctl(s, dstcmd, (caddr_t)ifrp) < 0)
-		return;
-#if defined(INET6) && defined(__KAME__) && defined(KAME_SCOPEID)
-	if (ifrp->ifr_addr.sa_family == AF_INET6)
-		in6_fillscopeid((struct sockaddr_in6 *)&ifrp->ifr_addr);
-#endif
-	getnameinfo(&ifrp->ifr_addr, ifrp->ifr_addr.sa_len,
-	    pdstaddr, sizeof(pdstaddr), 0, 0, niflag);
-
-	printf("\ttunnel inet%s %s --> %s\n", ver,
-	    psrcaddr, pdstaddr);
-}
-
-void
-in_status(int s __unused, struct rt_addrinfo *info)
-{
-	struct sockaddr_in *addr_in, null_in;
-
-	memset(&null_in, 0, sizeof(null_in));
-
-	addr_in = (struct sockaddr_in *)info->rti_info[RTAX_IFA];
-	printf("\tinet %s ", inet_ntoa(addr_in->sin_addr));
-
-	if (flags & IFF_POINTOPOINT) {
-		/* note RTAX_BRD overlap with IFF_BROADCAST */
-		addr_in = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
-		if (!addr_in)
-			addr_in = &null_in;
-		printf("--> %s ", inet_ntoa(addr_in->sin_addr));
-	}
-
-	addr_in = (struct sockaddr_in *)info->rti_info[RTAX_NETMASK];
-	if (!addr_in)
-		addr_in = &null_in;
-	printf("netmask 0x%lx ",
-	       (unsigned long)ntohl(addr_in->sin_addr.s_addr));
-
-	if (flags & IFF_BROADCAST) {
-		/* note RTAX_BRD overlap with IFF_POINTOPOINT */
-		addr_in = (struct sockaddr_in *)info->rti_info[RTAX_BRD];
-		if (addr_in && addr_in->sin_addr.s_addr != 0)
-			printf("broadcast %s", inet_ntoa(addr_in->sin_addr));
-	}
-	putchar('\n');
-}
-
-#ifdef INET6
-#if defined(__KAME__) && defined(KAME_SCOPEID)
-void
-in6_fillscopeid(struct sockaddr_in6 *sin6 __unused)
-{
-	if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
-		sin6->sin6_scope_id =
-			ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
-		sin6->sin6_addr.s6_addr[2] = sin6->sin6_addr.s6_addr[3] = 0;
-	}
-}
-#endif /* defined(__KAME__) && defined(KAME_SCOPEID) */
-
-void
-in6_status(int s __unused, struct rt_addrinfo *info)
-{
-	struct sockaddr_in6 *addr_in, null_in;
-	struct in6_ifreq ifr6;
-	int s6;
-	u_int32_t flags6;
-	struct in6_addrlifetime lifetime;
-	time_t t = time(NULL);
-	int error;
-	u_int32_t scopeid;
-
-	memset(&null_in, 0, sizeof(null_in));
-
-	addr_in = (struct sockaddr_in6 *)info->rti_info[RTAX_IFA];
-	strncpy(ifr6.ifr_name, ifr.ifr_name, sizeof(ifr.ifr_name));
-	if ((s6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
-		perror("ifconfig: socket");
-		return;
-	}
-	ifr6.ifr_addr = *addr_in;
-	if (ioctl(s6, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
-		perror("ifconfig: ioctl(SIOCGIFAFLAG_IN6)");
-		close(s6);
-		return;
-	}
-	flags6 = ifr6.ifr_ifru.ifru_flags6;
-	memset(&lifetime, 0, sizeof(lifetime));
-	ifr6.ifr_addr = *addr_in;
-	if (ioctl(s6, SIOCGIFALIFETIME_IN6, &ifr6) < 0) {
-		perror("ifconfig: ioctl(SIOCGIFALIFETIME_IN6)");
-		close(s6);
-		return;
-	}
-	lifetime = ifr6.ifr_ifru.ifru_lifetime;
-	close(s6);
+	if (allfamilies || afp->af_af == AF_LINK) {
+		const struct afswtch *lafp;
 
-	/* XXX: embedded link local addr check */
-	if (IN6_IS_ADDR_LINKLOCAL(&addr_in->sin6_addr) &&
-	    *(u_short *)&addr_in->sin6_addr.s6_addr[2] != 0) {
-		u_short idx;
-
-		idx = *(u_short *)&addr_in->sin6_addr.s6_addr[2];
-		*(u_short *)&addr_in->sin6_addr.s6_addr[2] = 0;
-		if (addr_in->sin6_scope_id == 0)
-			addr_in->sin6_scope_id = ntohs(idx);
-	}
-	scopeid = addr_in->sin6_scope_id;
-
-	error = getnameinfo((struct sockaddr *)addr_in, addr_in->sin6_len, addr_buf,
-			    sizeof(addr_buf), NULL, 0,
-			    NI_NUMERICHOST|NI_WITHSCOPEID);
-	if (error != 0)
-		inet_ntop(AF_INET6, &addr_in->sin6_addr, addr_buf,
-			  sizeof(addr_buf));
-	printf("\tinet6 %s ", addr_buf);
-
-	if (flags & IFF_POINTOPOINT) {
-		/* note RTAX_BRD overlap with IFF_BROADCAST */
-		addr_in = (struct sockaddr_in6 *)info->rti_info[RTAX_BRD];
 		/*
-		 * some of the interfaces do not have valid destination
-		 * address.
+		 * Hack; the link level address is received separately
+		 * from the routing information so any address is not
+		 * handled above.  Cobble together an entry and invoke
+		 * the status method specially.
 		 */
-		if (addr_in && addr_in->sin6_family == AF_INET6) {
-			/* XXX: embedded link local addr check */
-			if (IN6_IS_ADDR_LINKLOCAL(&addr_in->sin6_addr) &&
-			    *(u_short *)&addr_in->sin6_addr.s6_addr[2] != 0) {
-				u_short idx;
-
-				idx = *(u_short *)&addr_in->sin6_addr.s6_addr[2];
-				*(u_short *)&addr_in->sin6_addr.s6_addr[2] = 0;
-				if (addr_in->sin6_scope_id == 0)
-					addr_in->sin6_scope_id = ntohs(idx);
-			}
-
-			error = getnameinfo((struct sockaddr *)addr_in,
-					    addr_in->sin6_len, addr_buf,
-					    sizeof(addr_buf), NULL, 0,
-					    NI_NUMERICHOST|NI_WITHSCOPEID);
-			if (error != 0)
-				inet_ntop(AF_INET6, &addr_in->sin6_addr, addr_buf,
-					  sizeof(addr_buf));
-			printf("--> %s ", addr_buf);
+		lafp = af_getbyname("lladdr");
+		if (lafp != NULL) {
+			info.rti_info[RTAX_IFA] = (struct sockaddr *)sdl;
+			lafp->af_status(s, &info);
 		}
 	}
+	if (allfamilies)
+		af_other_status(s);
+		afp->af_other_status(s);
 
-	addr_in = (struct sockaddr_in6 *)info->rti_info[RTAX_NETMASK];
-	if (!addr_in)
-		addr_in = &null_in;
-	printf("prefixlen %d ", prefix(&addr_in->sin6_addr,
-		sizeof(struct in6_addr)));
-
-	if ((flags6 & IN6_IFF_ANYCAST) != 0)
-		printf("anycast ");
-	if ((flags6 & IN6_IFF_TENTATIVE) != 0)
-		printf("tentative ");
-	if ((flags6 & IN6_IFF_DUPLICATED) != 0)
-		printf("duplicated ");
-	if ((flags6 & IN6_IFF_DETACHED) != 0)
-		printf("detached ");
-	if ((flags6 & IN6_IFF_DEPRECATED) != 0)
-		printf("deprecated ");
-	if ((flags6 & IN6_IFF_AUTOCONF) != 0)
-		printf("autoconf ");
-	if ((flags6 & IN6_IFF_TEMPORARY) != 0)
-		printf("temporary ");
-
-        if (scopeid)
-		printf("scopeid 0x%x ", scopeid);
-
-	if (ip6lifetime && (lifetime.ia6t_preferred || lifetime.ia6t_expire)) {
-		printf("pltime ");
-		if (lifetime.ia6t_preferred) {
-			printf("%s ", lifetime.ia6t_preferred < t
-				? "0" : sec2str(lifetime.ia6t_preferred - t));
-		} else
-			printf("infty ");
-
-		printf("vltime ");
-		if (lifetime.ia6t_expire) {
-			printf("%s ", lifetime.ia6t_expire < t
-				? "0" : sec2str(lifetime.ia6t_expire - t));
-		} else
-			printf("infty ");
-	}
-
-	putchar('\n');
-}
-#endif /*INET6*/
-
-#ifndef NO_IPX
-void
-ipx_status(int s __unused, struct rt_addrinfo *info)
-{
-	struct sockaddr_ipx *sipx, null_sipx;
-
-	memset(&null_sipx, 0, sizeof(null_sipx));
-
-	sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_IFA];
-	printf("\tipx %s ", ipx_ntoa(sipx->sipx_addr));
-
-	if (flags & IFF_POINTOPOINT) {
-		sipx = (struct sockaddr_ipx *)info->rti_info[RTAX_BRD];
-		if (!sipx)
-			sipx = &null_sipx;
-		printf("--> %s ", ipx_ntoa(sipx->sipx_addr));
-	}
-	putchar('\n');
-}
-#endif
-
-void
-at_status(int s __unused, struct rt_addrinfo *info)
-{
-	struct sockaddr_at *sat, null_sat;
-	struct netrange *nr;
-
-	memset(&null_sat, 0, sizeof(null_sat));
-
-	sat = (struct sockaddr_at *)info->rti_info[RTAX_IFA];
-	nr = &sat->sat_range.r_netrange;
-	printf("\tatalk %d.%d range %d-%d phase %d",
-		ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
-		ntohs(nr->nr_firstnet), ntohs(nr->nr_lastnet), nr->nr_phase);
-	if (flags & IFF_POINTOPOINT) {
-		/* note RTAX_BRD overlap with IFF_BROADCAST */
-		sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
-		if (!sat)
-			sat = &null_sat;
-		printf("--> %d.%d",
-			ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node);
-	}
-	if (flags & IFF_BROADCAST) {
-		/* note RTAX_BRD overlap with IFF_POINTOPOINT */
-		sat = (struct sockaddr_at *)info->rti_info[RTAX_BRD];
-		if (sat)
-			printf(" broadcast %d.%d",
-				ntohs(sat->sat_addr.s_net),
-				sat->sat_addr.s_node);
-	}
-
-	putchar('\n');
-}
-
-#ifdef NS
-void
-xns_status(int s __unused, struct rt_addrinfo *info)
-{
-	struct sockaddr_ns *sns, null_sns;
-
-	memset(&null_sns, 0, sizeof(null_sns));
-
-	sns = (struct sockaddr_ns *)info->rti_info[RTAX_IFA];
-	printf("\tns %s ", ns_ntoa(sns->sns_addr));
-
-	if (flags & IFF_POINTOPOINT) {
-		sns = (struct sockaddr_ns *)info->rti_info[RTAX_BRD];
-		if (!sns)
-			sns = &null_sns;
-		printf("--> %s ", ns_ntoa(sns->sns_addr));
-	}
+	strncpy(ifs.ifs_name, name, sizeof ifs.ifs_name);
+	if (ioctl(s, SIOCGIFSTATUS, &ifs) == 0) 
+		printf("%s", ifs.ascii);
 
-	putchar('\n');
 	close(s);
+	return;
 }
-#endif
-
 
-void
-link_status(int s __unused, struct rt_addrinfo *info)
+static void
+tunnel_status(int s)
 {
-	struct sockaddr_dl *sdl = (struct sockaddr_dl *)info;
-
-	if (sdl->sdl_alen > 0) {
-		if (sdl->sdl_type == IFT_ETHER &&
-		    sdl->sdl_alen == ETHER_ADDR_LEN)
-			printf("\tether %s\n",
-			    ether_ntoa((struct ether_addr *)LLADDR(sdl)));
-		else {
-			int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
-
-			printf("\tlladdr %s\n", link_ntoa(sdl) + n);
-		}
-	}
+	af_all_tunnel_status(s);
 }
 
 void
@@ -1581,116 +937,6 @@
 	}
 }
 
-#define SIN(x) ((struct sockaddr_in *) &(x))
-struct sockaddr_in *sintab[] = {
-SIN(ridreq.ifr_addr), SIN(addreq.ifra_addr),
-SIN(addreq.ifra_mask), SIN(addreq.ifra_broadaddr)};
-
-void
-in_getaddr(const char *s, int which)
-{
-	struct sockaddr_in *addr_in = sintab[which];
-	struct hostent *hp;
-	struct netent *np;
-
-	addr_in->sin_len = sizeof(*addr_in);
-	if (which != MASK)
-		addr_in->sin_family = AF_INET;
-
-	if (which == ADDR) {
-		char *p = NULL;
-
-		if((p = strrchr(s, '/')) != NULL) {
-			/* address is `name/masklen' */
-			int masklen;
-			int ret;
-			struct sockaddr_in *min = sintab[MASK];
-			*p = '\0';
-			ret = sscanf(p+1, "%u", &masklen);
-			if(ret != 1 || (masklen < 0 || masklen > 32)) {
-				*p = '/';
-				errx(1, "%s: bad value", s);
-			}
-			min->sin_len = sizeof(*min);
-			min->sin_addr.s_addr = htonl(~((1LL << (32 - masklen)) - 1) & 
-				              0xffffffff);
-		}
-	}
-
-	if (inet_aton(s, &addr_in->sin_addr))
-		return;
-	if ((hp = gethostbyname(s)) != 0)
-		bcopy(hp->h_addr, (char *)&addr_in->sin_addr, 
-		    MIN((size_t)hp->h_length, sizeof(addr_in->sin_addr)));
-	else if ((np = getnetbyname(s)) != 0)
-		addr_in->sin_addr = inet_makeaddr(np->n_net, INADDR_ANY);
-	else
-		errx(1, "%s: bad value", s);
-}
-
-#ifdef INET6
-#define	SIN6(x) ((struct sockaddr_in6 *) &(x))
-struct	sockaddr_in6 *sin6tab[] = {
-SIN6(in6_ridreq.ifr_addr), SIN6(in6_addreq.ifra_addr),
-SIN6(in6_addreq.ifra_prefixmask), SIN6(in6_addreq.ifra_dstaddr)};
-
-void
-in6_getaddr(const char *s, int which)
-{
-	struct sockaddr_in6 *addr_in = sin6tab[which];
-	struct addrinfo hints, *res;
-	int error = -1;
-
-	newaddr &= 1;
-
-	addr_in->sin6_len = sizeof(*addr_in);
-	if (which != MASK)
-		addr_in->sin6_family = AF_INET6;
-
-	if (which == ADDR) {
-		char *p = NULL;
-		if((p = strrchr(s, '/')) != NULL) {
-			*p = '\0';
-			in6_getprefix(p + 1, MASK);
-			explicit_prefix = 1;
-		}
-	}
-
-	if (addr_in->sin6_family == AF_INET6) {
-		bzero(&hints, sizeof(struct addrinfo));
-		hints.ai_family = AF_INET6;
-		error = getaddrinfo(s, NULL, &hints, &res);
-	}
-	if (error != 0) {
-		if (inet_pton(AF_INET6, s, &addr_in->sin6_addr) != 1)
-			errx(1, "%s: bad value", s);
-	} else
-		bcopy(res->ai_addr, addr_in, res->ai_addrlen);
-}
-
-void
-in6_getprefix(const char *plen, int which)
-{
-	struct sockaddr_in6 *addr_in = sin6tab[which];
-	u_char *cp;
-	int len = atoi(plen);
-
-	if ((len < 0) || (len > 128))
-		errx(1, "%s: bad value", plen);
-	addr_in->sin6_len = sizeof(*addr_in);
-	if (which != MASK)
-		addr_in->sin6_family = AF_INET6;
-	if ((len == 0) || (len == 128)) {
-		memset(&addr_in->sin6_addr, 0xff, sizeof(struct in6_addr));
-		return;
-	}
-	memset((void *)&addr_in->sin6_addr, 0x00, sizeof(addr_in->sin6_addr));
-	for (cp = (u_char *)&addr_in->sin6_addr; len > 7; len -= 8)
-		*cp++ = 0xff;
-	*cp = 0xff << (8 - len);
-}
-#endif
-
 /*
  * Print a value a la the %b format of the kernel's printf
  */
@@ -1722,200 +968,16 @@
 	}
 }
 
-#ifndef NO_IPX
-#define SIPX(x) ((struct sockaddr_ipx *) &(x))
-struct sockaddr_ipx *sipxtab[] = {
-SIPX(ridreq.ifr_addr), SIPX(addreq.ifra_addr),
-SIPX(addreq.ifra_mask), SIPX(addreq.ifra_broadaddr)};
-
-void
-ipx_getaddr(const char *addr, int which)
-{
-	struct sockaddr_ipx *sipx = sipxtab[which];
-
-	sipx->sipx_family = AF_IPX;
-	sipx->sipx_len = sizeof(*sipx);
-	sipx->sipx_addr = ipx_addr(addr);
-	if (which == MASK)
-		printf("Attempt to set IPX netmask will be ineffectual\n");
-}
-#endif
-
-void
-at_getaddr(const char *addr, int which)
-{
-	struct sockaddr_at *sat = (struct sockaddr_at *) &addreq.ifra_addr;
-	u_int net, node;
-
-	sat->sat_family = AF_APPLETALK;
-	sat->sat_len = sizeof(*sat);
-	if (which == MASK)
-		errx(1, "AppleTalk does not use netmasks");
-	if (sscanf(addr, "%u.%u", &net, &node) != 2
-	    || net > 0xffff || node > 0xfe)
-		errx(1, "%s: illegal address", addr);
-	sat->sat_addr.s_net = htons(net);
-	sat->sat_addr.s_node = node;
-}
-
-void
-link_getaddr(const char *addr, int which)
-{
-	char *temp;
-	struct sockaddr_dl sdl;
-	struct sockaddr *sa = &ridreq.ifr_addr;
-
-	if (which != ADDR)
-		errx(1, "can't set link-level netmask or broadcast");
-	if ((temp = malloc(strlen(addr) + 1)) == NULL)
-		errx(1, "malloc failed");
-	temp[0] = ':';
-	strcpy(temp + 1, addr);
-	sdl.sdl_len = sizeof(sdl);
-	link_addr(temp, &sdl);
-	free(temp);
-	if (sdl.sdl_alen > sizeof(sa->sa_data))
-		errx(1, "malformed link-level address");
-	sa->sa_family = AF_LINK;
-	sa->sa_len = sdl.sdl_alen;
-	bcopy(LLADDR(&sdl), sa->sa_data, sdl.sdl_alen);
-}
-
-/* XXX  FIXME -- should use strtoul for better parsing. */
-void
-setatrange(const char *range, int dummy __unused, int s __unused,
-           const struct afswtch *afp __unused)
-{
-	u_int	first = 123, last = 123;
-
-	if (sscanf(range, "%u-%u", &first, &last) != 2
-	    || first == 0 || first > 0xffff
-	    || last == 0 || last > 0xffff || first > last)
-		errx(1, "%s: illegal net range: %u-%u", range, first, last);
-	at_nr.nr_firstnet = htons(first);
-	at_nr.nr_lastnet = htons(last);
-}
-
-void
-setatphase(const char *phase, int dummy __unused, int s __unused,
-           const struct afswtch *afp __unused)
-{
-	if (!strcmp(phase, "1"))
-		at_nr.nr_phase = 1;
-	else if (!strcmp(phase, "2"))
-		at_nr.nr_phase = 2;
-	else
-		errx(1, "%s: illegal phase", phase);
-}
-
-void
-checkatrange(struct sockaddr_at *sat)
-{
-	if (at_nr.nr_phase == 0)
-		at_nr.nr_phase = 2;	/* Default phase 2 */
-	if (at_nr.nr_firstnet == 0)
-		at_nr.nr_firstnet =	/* Default range of one */
-		at_nr.nr_lastnet = sat->sat_addr.s_net;
-printf("\tatalk %d.%d range %d-%d phase %d\n",
-	ntohs(sat->sat_addr.s_net), sat->sat_addr.s_node,
-	ntohs(at_nr.nr_firstnet), ntohs(at_nr.nr_lastnet), at_nr.nr_phase);
-	if ((u_short) ntohs(at_nr.nr_firstnet) >
-			(u_short) ntohs(sat->sat_addr.s_net)
-		    || (u_short) ntohs(at_nr.nr_lastnet) <
-			(u_short) ntohs(sat->sat_addr.s_net))
-		errx(1, "AppleTalk address is not in range");
-	sat->sat_range.r_netrange = at_nr;
-}
-
-#ifdef NS
-#define SNS(x) ((struct sockaddr_ns *) &(x))
-struct sockaddr_ns *snstab[] = {
-SNS(ridreq.ifr_addr), SNS(addreq.ifra_addr),
-SNS(addreq.ifra_mask), SNS(addreq.ifra_broadaddr)};
-
-void
-xns_getaddr(const char *addr, int which)
-{
-	struct sockaddr_ns *sns = snstab[which];
-
-	sns->sns_family = AF_NS;
-	sns->sns_len = sizeof(*sns);
-	sns->sns_addr = ns_addr(addr);
-	if (which == MASK)
-		printf("Attempt to set XNS netmask will be ineffectual\n");
-}
-#endif
-
-#ifdef INET6
-int
-prefix(void *val, int size)
-{
-	u_char *addr = (u_char *)val;
-	int byte, bit, plen = 0;
-
-        for (byte = 0; byte < size; byte++, plen += 8)
-		if (addr[byte] != 0xff)
-                        break;
-	if (byte == size)
-		return (plen);
-	for (bit = 7; bit != 0; bit--, plen++)
-                if (!(addr[byte] & (1 << bit)))
-                        break;
-        for (; bit != 0; bit--)
-                if (addr[byte] & (1 << bit))
-                        return(0);
-        byte++;
-        for (; byte < size; byte++)
-                if (addr[byte])
-                        return(0);
-        return (plen);
-}
-
-static char *
-sec2str(time_t total)
-{
-	static char result[256];
-	int days, hours, mins, secs;
-	int first = 1;
-	char *p = result;
-
-	if (0) {
-		days = total / 3600 / 24;
-		hours = (total / 3600) % 24;
-		mins = (total / 60) % 60;
-		secs = total % 60;
-
-		if (days) {
-			first = 0;
-			p += sprintf(p, "%dd", days);
-		}
-		if (!first || hours) {
-			first = 0;
-			p += sprintf(p, "%dh", hours);
-		}
-		if (!first || mins) {
-			first = 0;
-			p += sprintf(p, "%dm", mins);
-		}
-		sprintf(p, "%ds", secs);
-	} else
-		sprintf(result, "%lu", (unsigned long)total);
-
-	return(result);
-}
-#endif /*INET6*/
-
 void
-ifmaybeload(char *if_nm)
+ifmaybeload(char *name)
 {
 	struct module_stat mstat;
 	int fileid, modid;
 	char ifkind[35], *cp, *dp;
 
-
 	/* turn interface and unit into module name */
 	strcpy(ifkind, "if_");
-	for (cp = if_nm, dp = ifkind + 3;
+	for (cp = name, dp = ifkind + 3;
 	    (*cp != 0) && !isdigit(*cp); cp++, dp++)
 		*dp = *cp;
 	*dp = 0;
@@ -1935,8 +997,8 @@
 				cp = mstat.name;
 			}
 			/* already loaded? */
-			if (strncmp(if_nm, cp, strlen(cp)) == 0 ||
-				strncmp(ifkind, cp, strlen(cp)) == 0)
+			if (strncmp(name, cp, strlen(cp)) == 0 ||
+			    strncmp(ifkind, cp, strlen(cp)) == 0)
 				return;
 		}
 	}
@@ -1945,87 +1007,61 @@
 	kldload(ifkind);
 }
 
-void
-list_cloners(void)
-{
-	struct if_clonereq ifcr;
-	char *cp, *buf;
-	int idx;
-	int s;
-
-	s = socket(AF_INET, SOCK_DGRAM, 0);
-	if (s == -1)
-		err(1, "socket");
-
-	memset(&ifcr, 0, sizeof(ifcr));
-
-	if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
-		err(1, "SIOCIFGCLONERS for count");
-
-	buf = malloc(ifcr.ifcr_total * IFNAMSIZ);
-	if (buf == NULL)
-		err(1, "unable to allocate cloner name buffer");
-
-	ifcr.ifcr_count = ifcr.ifcr_total;
-	ifcr.ifcr_buffer = buf;
-
-	if (ioctl(s, SIOCIFGCLONERS, &ifcr) < 0)
-		err(1, "SIOCIFGCLONERS for names");
-
-	/*
-	 * In case some disappeared in the mean time, clamp it down.
-	 */
-	if (ifcr.ifcr_count > ifcr.ifcr_total)
-		ifcr.ifcr_count = ifcr.ifcr_total;
-
-	for (cp = buf, idx = 0; idx < ifcr.ifcr_count; idx++, cp += IFNAMSIZ) {
-		if (idx > 0)
-			putchar(' ');
-		printf("%s", cp);
-	}
-
-	putchar('\n');
-	free(buf);
-}
-
-void
-clone_create(void)
-{
-	int s;
-
-	s = socket(AF_INET, SOCK_DGRAM, 0);
-	if (s == -1)
-		err(1, "socket");
-
-	memset(&ifr, 0, sizeof(ifr));
-	(void) strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-	if (ioctl(s, SIOCIFCREATE, &ifr) < 0)
-		err(1, "SIOCIFCREATE");
-
-	/*
-	 * If we get a different name back then we put in, we probably
-	 * want to print it out, but we might change our mind later so
-	 * we just signal our intrest and leave the printout for later.
-	 */
-	if (strcmp(name, ifr.ifr_name) != 0) {
-		printname = 1;
-		strlcpy(name, ifr.ifr_name, sizeof(name));
-	}
-
-	close(s);
-}
+static struct cmd basic_cmds[] = {
+	DEF_CMD("up",		IFF_UP,		setifflags),
+	DEF_CMD("down",		-IFF_UP,	setifflags),
+	DEF_CMD("arp",		-IFF_NOARP,	setifflags),
+	DEF_CMD("-arp",		IFF_NOARP,	setifflags),
+	DEF_CMD("debug",	IFF_DEBUG,	setifflags),
+	DEF_CMD("-debug",	-IFF_DEBUG,	setifflags),
+	DEF_CMD("promisc",	IFF_PPROMISC,	setifflags),
+	DEF_CMD("-promisc",	-IFF_PPROMISC,	setifflags),
+	DEF_CMD("add",		IFF_UP,		notealias),
+	DEF_CMD("alias",	IFF_UP,		notealias),
+	DEF_CMD("-alias",	-IFF_UP,	notealias),
+	DEF_CMD("delete",	-IFF_UP,	notealias),
+	DEF_CMD("remove",	-IFF_UP,	notealias),
+#ifdef notdef
+#define	EN_SWABIPS	0x1000
+	DEF_CMD("swabips",	EN_SWABIPS,	setifflags),
+	DEF_CMD("-swabips",	-EN_SWABIPS,	setifflags),
+#endif
+	DEF_CMD_ARG("netmask",			setifnetmask),
+	DEF_CMD_ARG("metric",			setifmetric),
+	DEF_CMD_ARG("broadcast",		setifbroadaddr),
+	DEF_CMD_ARG("ipdst",			setifipdst),
+	DEF_CMD_ARG2("tunnel",			settunnel),
+	DEF_CMD("deletetunnel", 0,		deletetunnel),
+	DEF_CMD("link0",	IFF_LINK0,	setifflags),
+	DEF_CMD("-link0",	-IFF_LINK0,	setifflags),
+	DEF_CMD("link1",	IFF_LINK1,	setifflags),
+	DEF_CMD("-link1",	-IFF_LINK1,	setifflags),
+	DEF_CMD("link2",	IFF_LINK2,	setifflags),
+	DEF_CMD("-link2",	-IFF_LINK2,	setifflags),
+	DEF_CMD("monitor",	IFF_MONITOR,	setifflags),
+	DEF_CMD("-monitor",	-IFF_MONITOR,	setifflags),
+	DEF_CMD("staticarp",	IFF_STATICARP,	setifflags),
+	DEF_CMD("-staticarp",	-IFF_STATICARP,	setifflags),
+	DEF_CMD("rxcsum",	IFCAP_RXCSUM,	setifcap),
+	DEF_CMD("-rxcsum",	-IFCAP_RXCSUM,	setifcap),
+	DEF_CMD("txcsum",	IFCAP_TXCSUM,	setifcap),
+	DEF_CMD("-txcsum",	-IFCAP_TXCSUM,	setifcap),
+	DEF_CMD("netcons",	IFCAP_NETCONS,	setifcap),
+	DEF_CMD("-netcons",	-IFCAP_NETCONS,	setifcap),
+	DEF_CMD("normal",	-IFF_LINK0,	setifflags),
+	DEF_CMD("compress",	IFF_LINK0,	setifflags),
+	DEF_CMD("noicmp",	IFF_LINK1,	setifflags),
+	DEF_CMD_ARG("mtu",			setifmtu),
+	DEF_CMD_ARG("name",			setifname),
+};
 
-void
-clone_destroy(const char *val __unused, int d __unused, int s,
-	      const struct afswtch *rafp __unused)
+static __constructor void
+ifconfig_ctor(void)
 {
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
 
-	(void) strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-	if (ioctl(s, SIOCIFDESTROY, &ifr) < 0)
-		err(1, "SIOCIFDESTROY");
-	/*
-	 * If we create and destroy an interface in the same command,
-	 * there isn't any reason to print it's name.
-	 */
-	printname = 0;
+	for (i = 0; i < N(basic_cmds);  i++)
+		cmd_register(&basic_cmds[i]);
+#undef N
 }
diff -N -u -r src.preview/sbin/ifconfig/ifconfig.h src/sbin/ifconfig/ifconfig.h
--- src.preview/sbin/ifconfig/ifconfig.h	2005-04-25 13:33:26.000000000 -0400
+++ src/sbin/ifconfig/ifconfig.h	2005-09-27 15:02:10.000000000 -0400
@@ -31,41 +31,112 @@
  *
  * so there!
  *
- * $FreeBSD: src/sbin/ifconfig/ifconfig.h,v 1.12 2004/03/30 22:59:22 sam Exp $
- * $DragonFly: src/sbin/ifconfig/ifconfig.h,v 1.5 2005/04/25 17:33:26 swildner Exp $
+ * $FreeBSD$
  */
 
-extern struct ifreq ifr;
+#define	__constructor	__attribute__((constructor))
 
-extern char name[IFNAMSIZ];	/* name of interface */
-extern int allmedia;
-extern int supmedia;
 struct afswtch;
+struct cmd;
 
-extern void setmedia(const char *, int, int, const struct afswtch *rafp);
-extern void setmediamode(const char *, int, int, const struct afswtch *rafp);
-extern void setmediaopt(const char *, int, int, const struct afswtch *rafp);
-extern void unsetmediaopt(const char *, int, int, const struct afswtch *rafp);
-extern void media_status(int s, struct rt_addrinfo *);
-
-extern void setvlantag(const char *, int, int, const struct afswtch *rafp);
-extern void setvlandev(const char *, int, int, const struct afswtch *rafp);
-extern void unsetvlandev(const char *, int, int, const struct afswtch *rafp);
-extern void vlan_status(int s, struct rt_addrinfo *);
-
-extern void set80211ssid(const char *, int, int, const struct afswtch *rafp);
-extern void set80211stationname(const char *, int, int, const struct afswtch *rafp);
-extern void set80211channel(const char *, int, int, const struct afswtch *rafp);
-extern void set80211authmode(const char *, int, int, const struct afswtch *rafp);
-extern void set80211powersave(const char *, int, int, const struct afswtch *rafp);
-extern void set80211powersavemode(const char *, int, int, const struct afswtch *rafp);
-extern void set80211powersavesleep(const char *, int, int, const struct afswtch *rafp);
-extern void set80211wepmode(const char *, int, int, const struct afswtch *rafp);
-extern void set80211wep(const char *, int, int, const struct afswtch *rafp);
-extern void set80211weptxkey(const char *, int, int, const struct afswtch *rafp);
-extern void set80211wepkey(const char *, int, int, const struct afswtch *rafp);
-extern void set80211nwkey(const char *, int, int, const struct afswtch *rafp);
-extern void set80211rtsthreshold(const char *, int, int, const struct afswtch *rafp);
-extern void set80211protmode(const char *, int, int, const struct afswtch *rafp);
-extern void set80211txpower(const char *, int, int, const struct afswtch *rafp);
-extern void ieee80211_status(int s, struct rt_addrinfo *);
+typedef	void c_func(const char *cmd, int arg, int s, const struct afswtch *afp);
+typedef	void c_func2(const char *arg1, const char *arg2, int s, const struct afswtch *afp);
+
+struct cmd {
+	const char *c_name;
+	int	c_parameter;
+#define	NEXTARG		0xffffff	/* has following arg */
+#define	NEXTARG2	0xfffffe	/* has 2 following args */
+#define	OPTARG		0xfffffd	/* has optional following arg */
+	union {
+		c_func	*c_func;
+		c_func2	*c_func2;
+	} c_u;
+	struct cmd *c_next;
+};
+void	cmd_register(struct cmd *);
+
+typedef	void callback_func(int s, void *);
+void	callback_register(callback_func *, void *);
+
+/*
+ * Macros for declaring command functions and initializing entries.
+ */
+#define	DECL_CMD_FUNC(name, cmd, arg) \
+	void name(const char *cmd, int arg, int s, const struct afswtch *afp)
+#define	DECL_CMD_FUNC2(name, arg1, arg2) \
+	void name(const char *arg1, const char *arg2, int s, const struct afswtch *afp)
+
+#define	DEF_CMD(name, param, func)	{ name, param, { .c_func = func } }
+#define	DEF_CMD_ARG(name, func)		{ name, NEXTARG, { .c_func = func } }
+#define	DEF_CMD_OPTARG(name, func)	{ name, OPTARG, { .c_func = func } }
+#define	DEF_CMD_ARG2(name, func)	{ name, NEXTARG2, { .c_func2 = func } }
+
+struct rt_addrinfo;
+struct addrinfo;
+
+enum {
+	RIDADDR,
+	ADDR,
+	MASK,
+	DSTADDR,
+};
+
+struct afswtch {
+	const char	*af_name;	/* as given on cmd line, e.g. "inet" */
+	short		af_af;		/* AF_* */
+	/*
+	 * Status is handled one of two ways; if there is an
+	 * address associated with the interface then the
+	 * associated address family af_status method is invoked
+	 * with the appropriate addressin info.  Otherwise, if
+	 * all possible info is to be displayed and af_other_status
+	 * is defined then it is invoked after all address status
+	 * is presented.
+	 */
+	void		(*af_status)(int, const struct rt_addrinfo *);
+	void		(*af_other_status)(int);
+					/* parse address method */
+	void		(*af_getaddr)(const char *, int);
+					/* parse prefix method (IPv6) */
+	void		(*af_getprefix)(const char *, int);
+	void		(*af_postproc)(int s, const struct afswtch *);
+	u_long		af_difaddr;	/* set dst if address ioctl */
+	u_long		af_aifaddr;	/* set if address ioctl */
+	void		*af_ridreq;	/* */
+	void		*af_addreq;	/* */
+	struct afswtch	*af_next;
+
+	/* XXX doesn't fit model */
+	void		(*af_status_tunnel)(int);
+	void		(*af_settunnel)(int s, struct addrinfo *srcres,
+				struct addrinfo *dstres);
+};
+void	af_register(struct afswtch *);
+
+struct option {
+	const char *opt;
+	const char *opt_usage;
+	void	(*cb)(const char *arg);
+	struct option *next;
+};
+void	opt_register(struct option *);
+
+extern	struct ifreq ifr;
+extern	char name[IFNAMSIZ];	/* name of interface */
+extern	int allmedia;
+extern	int supmedia;
+extern	int printname;
+extern	int flags;
+extern	int newaddr;
+extern	int verbose;
+extern	int setipdst;
+
+void	setifcap(const char *, int value, int s, const struct afswtch *);
+
+void	Perror(const char *cmd);
+void	printb(const char *s, unsigned value, const char *bits);
+
+void	ifmaybeload(char *name);
+
+void	clone_create(void);
diff -N -u -r src.preview/sbin/ifconfig/ifieee80211.c src/sbin/ifconfig/ifieee80211.c
--- src.preview/sbin/ifconfig/ifieee80211.c	2005-03-06 00:01:59.000000000 -0500
+++ src/sbin/ifconfig/ifieee80211.c	2005-09-27 15:02:10.000000000 -0400
@@ -24,8 +24,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $FreeBSD: src/sbin/ifconfig/ifieee80211.c,v 1.1.2.3 2002/02/07 15:12:37 ambrisko Exp $
- * $DragonFly: src/sbin/ifconfig/ifieee80211.c,v 1.8 2005/03/06 05:01:59 dillon Exp $
+ * $FreeBSD$
  */
 
 /*-
@@ -75,7 +74,10 @@
 #include <net/if.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
+#include <net/if_media.h>
 #include <net/route.h>
+
+#include <netproto/802_11/ieee80211_dragonfly.h>
 #include <netproto/802_11/ieee80211.h>
 #include <netproto/802_11/ieee80211_crypto.h>
 #include <netproto/802_11/ieee80211_ioctl.h>
@@ -96,9 +98,15 @@
     u_int8_t *buf, int *lenp);
 static void print_string(const u_int8_t *buf, int len);
 
-void
-set80211ssid(const char *val, int d __unused, int s,
-	     const struct afswtch *rafp __unused)
+static int
+isanyarg(const char *arg)
+{
+	return (strcmp(arg, "-") == 0 ||
+	    strcasecmp(arg, "any") == 0 || strcasecmp(arg, "off") == 0);
+}
+
+static void
+set80211ssid(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int		ssid;
 	int		len;
@@ -118,9 +126,8 @@
 	set80211(s, IEEE80211_IOC_SSID, ssid, len, data);
 }
 
-void
-set80211stationname(const char *val, int d __unused, int s,
-		    const struct afswtch *rafp __unused)
+static void
+set80211stationname(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int			len;
 	u_int8_t		data[33];
@@ -132,19 +139,50 @@
 	set80211(s, IEEE80211_IOC_STATIONNAME, 0, len, data);
 }
 
-void
-set80211channel(const char *val, int d __unused, int s,
-		const struct afswtch *rafp __unused)
+/*
+ * Convert IEEE channel number to MHz frequency.
+ */
+static u_int
+ieee80211_ieee2mhz(u_int chan)
+{
+	if (chan == 14)
+		return 2484;
+	if (chan < 14)			/* 0-13 */
+		return 2407 + chan*5;
+	if (chan < 27)			/* 15-26 */
+		return 2512 + ((chan-15)*20);
+	return 5000 + (chan*5);
+}
+
+/*
+ * Convert MHz frequency to IEEE channel number.
+ */
+static u_int
+ieee80211_mhz2ieee(u_int freq)
+{
+	if (freq == 2484)
+		return 14;
+	if (freq < 2484)
+		return (freq - 2407) / 5;
+	if (freq < 5000)
+		return 15 + ((freq - 2512) / 20);
+	return (freq - 5000) / 5;
+}
+
+static void
+set80211channel(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	if (strcmp(val, "-") == 0)
+	if (!isanyarg(val)) {
+		int v = atoi(val);
+		if (v > 255)		/* treat as frequency */
+			v = ieee80211_mhz2ieee(v);
+		set80211(s, IEEE80211_IOC_CHANNEL, v, 0, NULL);
+	} else
 		set80211(s, IEEE80211_IOC_CHANNEL, IEEE80211_CHAN_ANY, 0, NULL);
-	else
-		set80211(s, IEEE80211_IOC_CHANNEL, atoi(val), 0, NULL);
 }
 
-void
-set80211authmode(const char *val, int d __unused, int s,
-		 const struct afswtch *rafp __unused)
+static void
+set80211authmode(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int	mode;
 
@@ -154,6 +192,10 @@
 		mode = IEEE80211_AUTH_OPEN;
 	} else if (strcasecmp(val, "shared") == 0) {
 		mode = IEEE80211_AUTH_SHARED;
+	} else if (strcasecmp(val, "8021x") == 0) {
+		mode = IEEE80211_AUTH_8021X;
+	} else if (strcasecmp(val, "wpa") == 0) {
+		mode = IEEE80211_AUTH_WPA;
 	} else {
 		err(1, "unknown authmode");
 	}
@@ -161,9 +203,8 @@
 	set80211(s, IEEE80211_IOC_AUTHMODE, mode, 0, NULL);
 }
 
-void
-set80211powersavemode(const char *val, int d __unused, int s,
-		      const struct afswtch *rafp __unused)
+static void
+set80211powersavemode(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int	mode;
 
@@ -184,9 +225,8 @@
 	set80211(s, IEEE80211_IOC_POWERSAVE, mode, 0, NULL);
 }
 
-void
-set80211powersave(const char *val __unused, int d, int s,
-		  const struct afswtch *rafp __unused)
+static void
+set80211powersave(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	if (d == 0)
 		set80211(s, IEEE80211_IOC_POWERSAVE, IEEE80211_POWERSAVE_OFF,
@@ -196,16 +236,14 @@
 		    0, NULL);
 }
 
-void
-set80211powersavesleep(const char *val, int d __unused, int s,
-		       const struct afswtch *rafp __unused)
+static void
+set80211powersavesleep(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	set80211(s, IEEE80211_IOC_POWERSAVESLEEP, atoi(val), 0, NULL);
 }
 
-void
-set80211wepmode(const char *val, int d __unused, int s,
-		const struct afswtch *rafp __unused)
+static void
+set80211wepmode(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int	mode;
 
@@ -222,23 +260,29 @@
 	set80211(s, IEEE80211_IOC_WEP, mode, 0, NULL);
 }
 
-void
-set80211wep(const char *val __unused, int d, int s,
-	    const struct afswtch *rafp __unused)
+static void
+set80211wep(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	set80211(s, IEEE80211_IOC_WEP, d, 0, NULL);
 }
 
-void
-set80211weptxkey(const char *val, int d __unused, int s,
-		 const struct afswtch *rafp __unused)
+static int
+isundefarg(const char *arg)
 {
-	set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
+	return (strcmp(arg, "-") == 0 || strncasecmp(arg, "undef", 5) == 0);
 }
 
-void
-set80211wepkey(const char *val, int d __unused, int s,
-	       const struct afswtch *rafp __unused)
+static void
+set80211weptxkey(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	if (isundefarg(val))
+		set80211(s, IEEE80211_IOC_WEPTXKEY, IEEE80211_KEYIX_NONE, 0, NULL);
+	else
+		set80211(s, IEEE80211_IOC_WEPTXKEY, atoi(val)-1, 0, NULL);
+}
+
+static void
+set80211wepkey(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int		key = 0;
 	int		len;
@@ -261,9 +305,8 @@
  * iterface is too inflexable, but it's there so we'll support it since
  * it's not all that hard.
  */
-void
-set80211nwkey(const char *val, int d __unused, int s,
-	      const struct afswtch *rafp __unused)
+static void
+set80211nwkey(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int		txkey;
 	int		i, len;
@@ -298,16 +341,14 @@
 	set80211(s, IEEE80211_IOC_WEPTXKEY, txkey, 0, NULL);
 }
 
-void
-set80211rtsthreshold(const char *val, int d __unused, int s,
-	const struct afswtch *rafp __unused)
+static void
+set80211rtsthreshold(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	set80211(s, IEEE80211_IOC_RTSTHRESHOLD, atoi(val), 0, NULL);
 }
 
-void
-set80211protmode(const char *val, int d __unused, int s,
-	const struct afswtch *rafp __unused)
+static void
+set80211protmode(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	int	mode;
 
@@ -324,160 +365,1049 @@
 	set80211(s, IEEE80211_IOC_PROTMODE, mode, 0, NULL);
 }
 
-void
-set80211txpower(const char *val, int d __unused, int s,
-	const struct afswtch *rafp __unused)
+static void
+set80211txpower(const char *val, int d, int s, const struct afswtch *rafp)
 {
 	set80211(s, IEEE80211_IOC_TXPOWER, atoi(val), 0, NULL);
 }
 
-void
-ieee80211_status (int s, struct rt_addrinfo *info __unused)
+#define	IEEE80211_ROAMING_DEVICE	0
+#define	IEEE80211_ROAMING_AUTO		1
+#define	IEEE80211_ROAMING_MANUAL	2
+
+static void
+set80211roaming(const char *val, int d, int s, const struct afswtch *rafp)
 {
-	int			i;
-	int			num;
-	struct ieee80211req	ireq;
-	u_int8_t		data[32];
-	char			spacer;
+	int mode;
+
+	if (strcasecmp(val, "device") == 0) {
+		mode = IEEE80211_ROAMING_DEVICE;
+	} else if (strcasecmp(val, "auto") == 0) {
+		mode = IEEE80211_ROAMING_AUTO;
+	} else if (strcasecmp(val, "manual") == 0) {
+		mode = IEEE80211_ROAMING_MANUAL;
+	} else {
+		err(1, "unknown roaming mode");
+	}
+	set80211(s, IEEE80211_IOC_ROAMING, mode, 0, NULL);
+}
+
+static void
+set80211wme(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	set80211(s, IEEE80211_IOC_WME, d, 0, NULL);
+}
+
+static void
+set80211hidessid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	set80211(s, IEEE80211_IOC_HIDESSID, d, 0, NULL);
+}
+
+static void
+set80211apbridge(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	set80211(s, IEEE80211_IOC_APBRIDGE, d, 0, NULL);
+}
+
+static void
+set80211chanlist(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	struct ieee80211req_chanlist chanlist;
+#define	MAXCHAN	(sizeof(chanlist.ic_channels)*NBBY)
+	char *temp, *cp, *tp;
+
+	temp = malloc(strlen(val) + 1);
+	if (temp == NULL)
+		errx(1, "malloc failed");
+	strcpy(temp, val);
+	memset(&chanlist, 0, sizeof(chanlist));
+	cp = temp;
+	for (;;) {
+		int first, last, f;
+
+		tp = strchr(cp, ',');
+		if (tp != NULL)
+			*tp++ = '\0';
+		switch (sscanf(cp, "%u-%u", &first, &last)) {
+		case 1:
+			if (first > MAXCHAN)
+				errx(-1, "channel %u out of range, max %u",
+					first, MAXCHAN);
+			setbit(chanlist.ic_channels, first);
+			break;
+		case 2:
+			if (first > MAXCHAN)
+				errx(-1, "channel %u out of range, max %u",
+					first, MAXCHAN);
+			if (last > MAXCHAN)
+				errx(-1, "channel %u out of range, max %u",
+					last, MAXCHAN);
+			if (first > last)
+				errx(-1, "void channel range, %u > %u",
+					first, last);
+			for (f = first; f <= last; f++)
+				setbit(chanlist.ic_channels, f);
+			break;
+		}
+		if (tp == NULL)
+			break;
+		while (isspace(*tp))
+			tp++;
+		if (!isdigit(*tp))
+			break;
+		cp = tp;
+	}
+	set80211(s, IEEE80211_IOC_CHANLIST, 0,
+		sizeof(chanlist), (uint8_t *) &chanlist);
+#undef MAXCHAN
+}
+
+static void
+set80211bssid(const char *val, int d, int s, const struct afswtch *rafp)
+{
+
+	if (!isanyarg(val)) {
+		char *temp;
+		struct sockaddr_dl sdl;
+
+		temp = malloc(strlen(val) + 1);
+		if (temp == NULL)
+			errx(1, "malloc failed");
+		temp[0] = ':';
+		strcpy(temp + 1, val);
+		sdl.sdl_len = sizeof(sdl);
+		link_addr(temp, &sdl);
+		free(temp);
+		if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+			errx(1, "malformed link-level address");
+		set80211(s, IEEE80211_IOC_BSSID, 0,
+			IEEE80211_ADDR_LEN, LLADDR(&sdl));
+	} else {
+		uint8_t zerobssid[IEEE80211_ADDR_LEN];
+		memset(zerobssid, 0, sizeof(zerobssid));
+		set80211(s, IEEE80211_IOC_BSSID, 0,
+			IEEE80211_ADDR_LEN, zerobssid);
+	}
+}
+
+static int
+getac(const char *ac)
+{
+	if (strcasecmp(ac, "ac_be") == 0 || strcasecmp(ac, "be") == 0)
+		return WME_AC_BE;
+	if (strcasecmp(ac, "ac_bk") == 0 || strcasecmp(ac, "bk") == 0)
+		return WME_AC_BK;
+	if (strcasecmp(ac, "ac_vi") == 0 || strcasecmp(ac, "vi") == 0)
+		return WME_AC_VI;
+	if (strcasecmp(ac, "ac_vo") == 0 || strcasecmp(ac, "vo") == 0)
+		return WME_AC_VO;
+	errx(1, "unknown wme access class %s", ac);
+}
+
+static
+DECL_CMD_FUNC2(set80211cwmin, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211cwmax, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211aifs, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211txoplimit, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val), getac(ac), NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211acm, val, d)
+{
+	set80211(s, IEEE80211_IOC_WME_ACM, d, WME_AC_BE, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211ackpolicy, val, d)
+{
+	set80211(s, IEEE80211_IOC_WME_ACKPOLICY, d, WME_AC_BE, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsscwmin, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_CWMIN, atoi(val),
+		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsscwmax, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_CWMAX, atoi(val),
+		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bssaifs, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_AIFS, atoi(val),
+		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC2(set80211bsstxoplimit, ac, val)
+{
+	set80211(s, IEEE80211_IOC_WME_TXOPLIMIT, atoi(val),
+		getac(ac)|IEEE80211_WMEPARAM_BSS, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211dtimperiod, val, d)
+{
+	set80211(s, IEEE80211_IOC_DTIM_PERIOD, atoi(val), 0, NULL);
+}
+
+static
+DECL_CMD_FUNC(set80211bintval, val, d)
+{
+	set80211(s, IEEE80211_IOC_BEACON_INTERVAL, atoi(val), 0, NULL);
+}
+
+static void
+set80211macmac(int s, int op, const char *val)
+{
+	char *temp;
+	struct sockaddr_dl sdl;
+
+	temp = malloc(strlen(val) + 1);
+	if (temp == NULL)
+		errx(1, "malloc failed");
+	temp[0] = ':';
+	strcpy(temp + 1, val);
+	sdl.sdl_len = sizeof(sdl);
+	link_addr(temp, &sdl);
+	free(temp);
+	if (sdl.sdl_alen != IEEE80211_ADDR_LEN)
+		errx(1, "malformed link-level address");
+	set80211(s, op, 0, IEEE80211_ADDR_LEN, LLADDR(&sdl));
+}
+
+static
+DECL_CMD_FUNC(set80211addmac, val, d)
+{
+	set80211macmac(s, IEEE80211_IOC_ADDMAC, val);
+}
+
+static
+DECL_CMD_FUNC(set80211delmac, val, d)
+{
+	set80211macmac(s, IEEE80211_IOC_DELMAC, val);
+}
+
+static
+DECL_CMD_FUNC(set80211maccmd, val, d)
+{
+	set80211(s, IEEE80211_IOC_MACCMD, d, 0, NULL);
+}
+
+static int
+getmaxrate(uint8_t rates[15], uint8_t nrates)
+{
+	int i, maxrate = -1;
+
+	for (i = 0; i < nrates; i++) {
+		int rate = rates[i] & IEEE80211_RATE_VAL;
+		if (rate > maxrate)
+			maxrate = rate;
+	}
+	return maxrate / 2;
+}
+
+static const char *
+getcaps(int capinfo)
+{
+	static char capstring[32];
+	char *cp = capstring;
+
+	if (capinfo & IEEE80211_CAPINFO_ESS)
+		*cp++ = 'E';
+	if (capinfo & IEEE80211_CAPINFO_IBSS)
+		*cp++ = 'I';
+	if (capinfo & IEEE80211_CAPINFO_CF_POLLABLE)
+		*cp++ = 'c';
+	if (capinfo & IEEE80211_CAPINFO_CF_POLLREQ)
+		*cp++ = 'C';
+	if (capinfo & IEEE80211_CAPINFO_PRIVACY)
+		*cp++ = 'P';
+	if (capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)
+		*cp++ = 'S';
+	if (capinfo & IEEE80211_CAPINFO_PBCC)
+		*cp++ = 'B';
+	if (capinfo & IEEE80211_CAPINFO_CHNL_AGILITY)
+		*cp++ = 'A';
+	if (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
+		*cp++ = 's';
+	if (capinfo & IEEE80211_CAPINFO_RSN)
+		*cp++ = 'R';
+	if (capinfo & IEEE80211_CAPINFO_DSSSOFDM)
+		*cp++ = 'D';
+	*cp = '\0';
+	return capstring;
+}
+
+static void
+printie(const char* tag, const uint8_t *ie, size_t ielen, int maxlen)
+{
+	printf("%s", tag);
+	if (verbose) {
+		maxlen -= strlen(tag)+2;
+		if (2*ielen > maxlen)
+			maxlen--;
+		printf("<");
+		for (; ielen > 0; ie++, ielen--) {
+			if (maxlen-- <= 0)
+				break;
+			printf("%02x", *ie);
+		}
+		if (ielen != 0)
+			printf("-");
+		printf(">");
+	}
+}
+
+/*
+ * Copy the ssid string contents into buf, truncating to fit.  If the
+ * ssid is entirely printable then just copy intact.  Otherwise convert
+ * to hexadecimal.  If the result is truncated then replace the last
+ * three characters with "...".
+ */
+static size_t
+copy_essid(char buf[], size_t bufsize, const u_int8_t *essid, size_t essid_len)
+{
+	const u_int8_t *p; 
+	size_t maxlen;
+	int i;
+
+	if (essid_len > bufsize)
+		maxlen = bufsize;
+	else
+		maxlen = essid_len;
+	/* determine printable or not */
+	for (i = 0, p = essid; i < maxlen; i++, p++) {
+		if (*p < ' ' || *p > 0x7e)
+			break;
+	}
+	if (i != maxlen) {		/* not printable, print as hex */
+		if (bufsize < 3)
+			return 0;
+		strlcpy(buf, "0x", bufsize);
+		bufsize -= 2;
+		p = essid;
+		for (i = 0; i < maxlen && bufsize >= 2; i++) {
+			sprintf(&buf[2+2*i], "%02x", *p++);
+			bufsize -= 2;
+		}
+		maxlen = 2+2*i;
+	} else {			/* printable, truncate as needed */
+		memcpy(buf, essid, maxlen);
+	}
+	if (maxlen != essid_len)
+		memcpy(buf+maxlen-3, "...", 3);
+	return maxlen;
+}
+
+/* unalligned little endian access */     
+#define LE_READ_4(p)					\
+	((u_int32_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8) |		\
+	  (((const u_int8_t *)(p))[2] << 16) |		\
+	  (((const u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static void
+printies(const u_int8_t *vp, int ielen, int maxcols)
+{
+	while (ielen > 0) {
+		switch (vp[0]) {
+		case IEEE80211_ELEMID_VENDOR:
+			if (iswpaoui(vp))
+				printie(" WPA", vp, 2+vp[1], maxcols);
+			else if (iswmeoui(vp))
+				printie(" WME", vp, 2+vp[1], maxcols);
+			else if (isatherosoui(vp))
+				printie(" ATH", vp, 2+vp[1], maxcols);
+			else
+				printie(" VEN", vp, 2+vp[1], maxcols);
+			break;
+		case IEEE80211_ELEMID_RSN:
+			printie(" RSN", vp, 2+vp[1], maxcols);
+			break;
+		default:
+			printie(" ???", vp, 2+vp[1], maxcols);
+			break;
+		}
+		ielen -= 2+vp[1];
+		vp += 2+vp[1];
+	}
+}
+
+static void
+list_scan(int s)
+{
+	uint8_t buf[24*1024];
+	struct ieee80211req ireq;
+	char ssid[14];
+	uint8_t *cp;
+	int len;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_SCAN_RESULTS;
+	ireq.i_data = buf;
+	ireq.i_len = sizeof(buf);
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		errx(1, "unable to get scan results");
+	len = ireq.i_len;
+	if (len < sizeof(struct ieee80211req_scan_result))
+		return;
+
+	printf("%-14.14s  %-17.17s  %4s %4s  %-5s %3s %4s\n"
+		, "SSID"
+		, "BSSID"
+		, "CHAN"
+		, "RATE"
+		, "S:N"
+		, "INT"
+		, "CAPS"
+	);
+	cp = buf;
+	do {
+		struct ieee80211req_scan_result *sr;
+		uint8_t *vp;
+
+		sr = (struct ieee80211req_scan_result *) cp;
+		vp = (u_int8_t *)(sr+1);
+		printf("%-14.*s  %s  %3d  %3dM %2d:%-2d  %3d %-4.4s"
+			, copy_essid(ssid, sizeof(ssid), vp, sr->isr_ssid_len)
+				, ssid
+			, ether_ntoa((const struct ether_addr *) sr->isr_bssid)
+			, ieee80211_mhz2ieee(sr->isr_freq)
+			, getmaxrate(sr->isr_rates, sr->isr_nrates)
+			, sr->isr_rssi, sr->isr_noise
+			, sr->isr_intval
+			, getcaps(sr->isr_capinfo)
+		);
+		printies(vp + sr->isr_ssid_len, sr->isr_ie_len, 24);;
+		printf("\n");
+		cp += sr->isr_len, len -= sr->isr_len;
+	} while (len >= sizeof(struct ieee80211req_scan_result));
+}
+
+static void
+scan_and_wait(int s)
+{
+	struct ieee80211req ireq;
+	int sroute;
+
+	sroute = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (sroute < 0) {
+		perror("socket(PF_ROUTE,SOCK_RAW)");
+		return;
+	}
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_SCAN_REQ;
+	/* NB: only root can trigger a scan so ignore errors */
+	if (ioctl(s, SIOCS80211, &ireq) >= 0) {
+		char buf[2048];
+		struct if_announcemsghdr *ifan;
+		struct rt_msghdr *rtm;
+
+		do {
+			if (read(sroute, buf, sizeof(buf)) < 0) {
+				perror("read(PF_ROUTE)");
+				break;
+			}
+			rtm = (struct rt_msghdr *) buf;
+			if (rtm->rtm_version != RTM_VERSION)
+				break;
+			ifan = (struct if_announcemsghdr *) rtm;
+		} while (rtm->rtm_type != RTM_IEEE80211 ||
+		    ifan->ifan_what != RTM_IEEE80211_SCAN);
+	}
+	close(sroute);
+}
+
+static
+DECL_CMD_FUNC(set80211scan, val, d)
+{
+	scan_and_wait(s);
+	list_scan(s);
+}
+
+static void
+list_stations(int s)
+{
+	uint8_t buf[24*1024];
+	struct ieee80211req ireq;
+	uint8_t *cp;
+	int len;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_STA_INFO;
+	ireq.i_data = buf;
+	ireq.i_len = sizeof(buf);
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		errx(1, "unable to get station information");
+	len = ireq.i_len;
+	if (len < sizeof(struct ieee80211req_sta_info))
+		return;
+
+	printf("%-17.17s %4s %4s %4s %4s %4s %6s %6s %4s %3s\n"
+		, "ADDR"
+		, "AID"
+		, "CHAN"
+		, "RATE"
+		, "RSSI"
+		, "IDLE"
+		, "TXSEQ"
+		, "RXSEQ"
+		, "CAPS"
+		, "ERP"
+	cp = buf;
+	do {
+		struct ieee80211req_sta_info *si;
+		uint8_t *vp;
+
+		si = (struct ieee80211req_sta_info *) cp;
+		vp = (u_int8_t *)(si+1);
+		printf("%s %4u %4d %3dM %4d %4d %6d %6d %-4.4s %3x"
+			, ether_ntoa((const struct ether_addr*) si->isi_macaddr)
+			, IEEE80211_AID(si->isi_associd)
+			, ieee80211_mhz2ieee(si->isi_freq)
+			, (si->isi_rates[si->isi_txrate] & IEEE80211_RATE_VAL)/2
+			, si->isi_rssi
+			, si->isi_inact
+			, si->isi_txseqs[0]
+			, si->isi_rxseqs[0]
+			, getcaps(si->isi_capinfo)
+			, si->isi_erp
+		);
+		printies(vp, si->isi_ie_len, 24);
+		printf("\n");
+		cp += si->isi_len, len -= si->isi_len;
+	} while (len >= sizeof(struct ieee80211req_sta_info));
+}
+
+static void
+print_chaninfo(const struct ieee80211_channel *c)
+{
+#define	IEEE80211_IS_CHAN_PASSIVE(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PASSIVE))
+	char buf[14];
+
+	buf[0] = '\0';
+	if (IEEE80211_IS_CHAN_FHSS(c))
+		strlcat(buf, " FHSS", sizeof(buf));
+	if (IEEE80211_IS_CHAN_A(c))
+		strlcat(buf, " 11a", sizeof(buf));
+	/* XXX 11g schizophrenia */
+	if (IEEE80211_IS_CHAN_G(c) ||
+	    IEEE80211_IS_CHAN_PUREG(c))
+		strlcat(buf, " 11g", sizeof(buf));
+	else if (IEEE80211_IS_CHAN_B(c))
+		strlcat(buf, " 11b", sizeof(buf));
+	if (IEEE80211_IS_CHAN_T(c))
+		strlcat(buf, " Turbo", sizeof(buf));
+	printf("Channel %3u : %u%c Mhz%-14.14s",
+		ieee80211_mhz2ieee(c->ic_freq), c->ic_freq,
+		IEEE80211_IS_CHAN_PASSIVE(c) ? '*' : ' ', buf);
+#undef IEEE80211_IS_CHAN_PASSIVE
+}
+
+static void
+list_channels(int s, int allchans)
+{
+	struct ieee80211req ireq;
+	struct ieee80211req_chaninfo chans;
+	struct ieee80211req_chaninfo achans;
+	const struct ieee80211_channel *c;
+	int i, half;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_CHANINFO;
+	ireq.i_data = &chans;
+	ireq.i_len = sizeof(chans);
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		errx(1, "unable to get channel information");
+	if (!allchans) {
+		struct ieee80211req_chanlist active;
+
+		ireq.i_type = IEEE80211_IOC_CHANLIST;
+		ireq.i_data = &active;
+		ireq.i_len = sizeof(active);
+		if (ioctl(s, SIOCG80211, &ireq) < 0)
+			errx(1, "unable to get active channel list");
+		memset(&achans, 0, sizeof(achans));
+		for (i = 0; i < chans.ic_nchans; i++) {
+			c = &chans.ic_chans[i];
+			if (isset(active.ic_channels, ieee80211_mhz2ieee(c->ic_freq)) || allchans)
+				achans.ic_chans[achans.ic_nchans++] = *c;
+		}
+	} else
+		achans = chans;
+	half = achans.ic_nchans / 2;
+	if (achans.ic_nchans % 2)
+		half++;
+	for (i = 0; i < achans.ic_nchans / 2; i++) {
+		print_chaninfo(&achans.ic_chans[i]);
+		print_chaninfo(&achans.ic_chans[half+i]);
+		printf("\n");
+	}
+	if (achans.ic_nchans % 2) {
+		print_chaninfo(&achans.ic_chans[i]);
+		printf("\n");
+	}
+}
+
+static void
+list_keys(int s)
+{
+}
+
+#define	IEEE80211_C_BITS \
+"\020\1WEP\2TKIP\3AES\4AES_CCM\6CKIP\11IBSS\12PMGT\13HOSTAP\14AHDEMO" \
+"\15SWRETRY\16TXPMGT\17SHSLOT\20SHPREAMBLE\21MONITOR\22TKIPMIC\30WPA1" \
+"\31WPA2\32BURST\33WME"
+
+static void
+list_capabilities(int s)
+{
+	struct ieee80211req ireq;
+	u_int32_t caps;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_DRIVER_CAPS;
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		errx(1, "unable to get driver capabilities");
+	caps = (((u_int16_t) ireq.i_val) << 16) | ((u_int16_t) ireq.i_len);
+	printb(name, caps, IEEE80211_C_BITS);
+	putchar('\n');
+}
+
+static void
+list_wme(int s)
+{
+	static const char *acnames[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
+	struct ieee80211req ireq;
+	int ac;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_len = 0;
+	for (ac = WME_AC_BE; ac <= WME_AC_VO; ac++) {
+again:
+		if (ireq.i_len & IEEE80211_WMEPARAM_BSS)
+			printf("\t%s", "     ");
+		else
+			printf("\t%s", acnames[ac]);
+
+		ireq.i_len = (ireq.i_len & IEEE80211_WMEPARAM_BSS) | ac;
+
+		/* show WME BSS parameters */
+		ireq.i_type = IEEE80211_IOC_WME_CWMIN;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			printf(" cwmin %2u", ireq.i_val);
+		ireq.i_type = IEEE80211_IOC_WME_CWMAX;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			printf(" cwmax %2u", ireq.i_val);
+		ireq.i_type = IEEE80211_IOC_WME_AIFS;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			printf(" aifs %2u", ireq.i_val);
+		ireq.i_type = IEEE80211_IOC_WME_TXOPLIMIT;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			printf(" txopLimit %3u", ireq.i_val);
+		ireq.i_type = IEEE80211_IOC_WME_ACM;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (ireq.i_val)
+				printf(" acm");
+			else if (verbose)
+				printf(" -acm");
+		}
+		/* !BSS only */
+		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
+			ireq.i_type = IEEE80211_IOC_WME_ACKPOLICY;
+			if (ioctl(s, SIOCG80211, &ireq) != -1) {
+				if (!ireq.i_val)
+					printf(" -ack");
+				else if (verbose)
+					printf(" ack");
+			}
+		}
+		printf("\n");
+		if ((ireq.i_len & IEEE80211_WMEPARAM_BSS) == 0) {
+			ireq.i_len |= IEEE80211_WMEPARAM_BSS;
+			goto again;
+		} else
+			ireq.i_len &= ~IEEE80211_WMEPARAM_BSS;
+	}
+}
+
+static
+DECL_CMD_FUNC(set80211list, arg, d)
+{
+#define	iseq(a,b)	(strncasecmp(a,b,sizeof(b)-1) == 0)
+
+	if (iseq(arg, "sta"))
+		list_stations(s);
+	else if (iseq(arg, "scan") || iseq(arg, "ap"))
+		list_scan(s);
+	else if (iseq(arg, "chan") || iseq(arg, "freq"))
+		list_channels(s, 1);
+	else if (iseq(arg, "active"))
+		list_channels(s, 0);
+	else if (iseq(arg, "keys"))
+		list_keys(s);
+	else if (iseq(arg, "caps"))
+		list_capabilities(s);
+	else if (iseq(arg, "wme"))
+		list_wme(s);
+	else
+		errx(1, "Don't know how to list %s for %s", arg, name);
+#undef iseq
+}
+
+static enum ieee80211_opmode
+get80211opmode(int s)
+{
+	struct ifmediareq ifmr;
+
+	(void) memset(&ifmr, 0, sizeof(ifmr));
+	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC)
+			return IEEE80211_M_IBSS;	/* XXX ahdemo */
+		if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+			return IEEE80211_M_HOSTAP;
+		if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+			return IEEE80211_M_MONITOR;
+	}
+	return IEEE80211_M_STA;
+}
+
+static const struct ieee80211_channel *
+getchaninfo(int s, int chan)
+{
+	struct ieee80211req ireq;
+	static struct ieee80211req_chaninfo chans;
+	static struct ieee80211_channel undef;
+	const struct ieee80211_channel *c;
+	int i, freq;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_CHANINFO;
+	ireq.i_data = &chans;
+	ireq.i_len = sizeof(chans);
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		errx(1, "unable to get channel information");
+	freq = ieee80211_ieee2mhz(chan);
+	for (i = 0; i < chans.ic_nchans; i++) {
+		c = &chans.ic_chans[i];
+		if (c->ic_freq == freq)
+			return c;
+	}
+	return &undef;
+}
+
+#if 0
+static void
+printcipher(int s, struct ieee80211req *ireq, int keylenop)
+{
+	switch (ireq->i_val) {
+	case IEEE80211_CIPHER_WEP:
+		ireq->i_type = keylenop;
+		if (ioctl(s, SIOCG80211, ireq) != -1)
+			printf("WEP-%s", 
+			    ireq->i_len <= 5 ? "40" :
+			    ireq->i_len <= 13 ? "104" : "128");
+		else
+			printf("WEP");
+		break;
+	case IEEE80211_CIPHER_TKIP:
+		printf("TKIP");
+		break;
+	case IEEE80211_CIPHER_AES_OCB:
+		printf("AES-OCB");
+		break;
+	case IEEE80211_CIPHER_AES_CCM:
+		printf("AES-CCM");
+		break;
+	case IEEE80211_CIPHER_CKIP:
+		printf("CKIP");
+		break;
+	case IEEE80211_CIPHER_NONE:
+		printf("NONE");
+		break;
+	default:
+		printf("UNKNOWN (0x%x)", ireq->i_val);
+		break;
+	}
+}
+#endif
+
+#define	MAXCOL	78
+int	col;
+char	spacer;
+
+#define	LINE_BREAK() do {			\
+	if (spacer != '\t') {			\
+		printf("\n");			\
+		spacer = '\t';			\
+	}					\
+	col = 8;	/* 8-col tab */		\
+} while (0)
+#define	LINE_CHECK(fmt, ...) do {		\
+	col += sizeof(fmt)-2;			\
+	if (col > MAXCOL) {			\
+		LINE_BREAK();			\
+		col += sizeof(fmt)-2;		\
+	}					\
+	printf(fmt, __VA_ARGS__);		\
+	spacer = ' ';				\
+} while (0)
+
+static void
+printkey(const struct ieee80211req_key *ik)
+{
+	static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE];
+	int keylen = ik->ik_keylen;
+	int printcontents;
+
+	printcontents =
+		(memcmp(ik->ik_keydata, zerodata, keylen) != 0 || verbose);
+	if (printcontents)
+		LINE_BREAK();
+	switch (ik->ik_type) {
+	case IEEE80211_CIPHER_WEP:
+		/* compatibility */
+		LINE_CHECK("%cwepkey %u:%s", spacer, ik->ik_keyix+1,
+		    keylen <= 5 ? "40-bit" :
+		    keylen <= 13 ? "104-bit" : "128-bit");
+		break;
+	case IEEE80211_CIPHER_TKIP:
+		if (keylen > 128/8)
+			keylen -= 128/8;	/* ignore MIC for now */
+		LINE_CHECK("%cTKIP %u:%u-bit",
+			spacer, ik->ik_keyix+1, 8*keylen);
+		break;
+	case IEEE80211_CIPHER_AES_OCB:
+		LINE_CHECK("%cAES-OCB %u:%u-bit",
+			spacer, ik->ik_keyix+1, 8*keylen);
+		break;
+	case IEEE80211_CIPHER_AES_CCM:
+		LINE_CHECK("%cAES-CCM %u:%u-bit",
+			spacer, ik->ik_keyix+1, 8*keylen);
+		break;
+	case IEEE80211_CIPHER_CKIP:
+		LINE_CHECK("%cCKIP %u:%u-bit",
+			spacer, ik->ik_keyix+1, 8*keylen);
+		break;
+	case IEEE80211_CIPHER_NONE:
+		LINE_CHECK("%cNULL %u:%u-bit",
+			spacer, ik->ik_keyix+1, 8*keylen);
+		break;
+	default:
+		LINE_CHECK("%cUNKNOWN (0x%x) %u:%u-bit", spacer,
+			ik->ik_type, ik->ik_keyix+1, 8*keylen);
+		break;
+	}
+	if (printcontents) {
+		int i;
+
+		printf(" <");
+		for (i = 0; i < keylen; i++)
+			printf("%02x", ik->ik_keydata[i]);
+		printf(">");
+		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
+		    (ik->ik_keyrsc != 0 || verbose))
+			printf(" rsc %llu", ik->ik_keyrsc);
+		if (ik->ik_type != IEEE80211_CIPHER_WEP &&
+		    (ik->ik_keytsc != 0 || verbose))
+			printf(" tsc %llu", ik->ik_keytsc);
+		if (ik->ik_flags != 0 && verbose) {
+			const char *sep = " ";
+
+			if (ik->ik_flags & IEEE80211_KEY_XMIT)
+				printf("%stx", sep), sep = "+";
+			if (ik->ik_flags & IEEE80211_KEY_RECV)
+				printf("%srx", sep), sep = "+";
+			if (ik->ik_flags & IEEE80211_KEY_DEFAULT)
+				printf("%sdef", sep), sep = "+";
+		}
+		LINE_BREAK();
+	}
+}
+
+static void
+ieee80211_status(int s)
+{
+	static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
+	enum ieee80211_opmode opmode = get80211opmode(s);
+	int i, num, wpa, wme;
+	struct ieee80211req ireq;
+	u_int8_t data[32];
+	const struct ieee80211_channel *c;
 
 	(void) memset(&ireq, 0, sizeof(ireq));
 	(void) strncpy(ireq.i_name, name, sizeof(ireq.i_name));
 	ireq.i_data = &data;
 
+	wpa = 0;		/* unknown/not set */
+
 	ireq.i_type = IEEE80211_IOC_SSID;
 	ireq.i_val = -1;
 	if (ioctl(s, SIOCG80211, &ireq) < 0) {
 		/* If we can't get the SSID, the this isn't an 802.11 device. */
 		return;
 	}
-	printf("\tssid ");
-	print_string(data, ireq.i_len);
 	num = 0;
 	ireq.i_type = IEEE80211_IOC_NUMSSIDS;
-	if (ioctl(s, SIOCG80211, &ireq) >= 0) {
+	if (ioctl(s, SIOCG80211, &ireq) >= 0)
 		num = ireq.i_val;
-	}
-	ireq.i_type = IEEE80211_IOC_SSID;
-	for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
-		if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
-			printf(" %d:", ireq.i_val + 1);
-			print_string(data, ireq.i_len);
+	printf("\tssid ");
+	if (num > 1) {
+		ireq.i_type = IEEE80211_IOC_SSID;
+		for (ireq.i_val = 0; ireq.i_val < num; ireq.i_val++) {
+			if (ioctl(s, SIOCG80211, &ireq) >= 0 && ireq.i_len > 0) {
+				printf(" %d:", ireq.i_val + 1);
+				print_string(data, ireq.i_len);
+			}
 		}
-	}
-	printf("\n");
+	} else
+		print_string(data, ireq.i_len);
+
+	ireq.i_type = IEEE80211_IOC_CHANNEL;
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		goto end;
+	c = getchaninfo(s, ireq.i_val);
+	if (ireq.i_val != -1) {
+		printf(" channel %d", ireq.i_val);
+		if (verbose)
+			printf(" (%u)", c->ic_freq);
+	} else if (verbose)
+		printf(" channel UNDEF");
+
+	ireq.i_type = IEEE80211_IOC_BSSID;
+	ireq.i_len = IEEE80211_ADDR_LEN;
+	if (ioctl(s, SIOCG80211, &ireq) >= 0 &&
+	    memcmp(ireq.i_data, zerobssid, sizeof(zerobssid)) != 0)
+		printf(" bssid %s", ether_ntoa(ireq.i_data));
 
 	ireq.i_type = IEEE80211_IOC_STATIONNAME;
 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
-		printf("\tstationname ");
+		printf("\n\tstationname ");
 		print_string(data, ireq.i_len);
-		printf("\n");
 	}
 
-	ireq.i_type = IEEE80211_IOC_CHANNEL;
-	if (ioctl(s, SIOCG80211, &ireq) < 0) {
-		goto end;
-	}
-	printf("\tchannel %d", ireq.i_val);
+	spacer = ' ';		/* force first break */
+	LINE_BREAK();
 
 	ireq.i_type = IEEE80211_IOC_AUTHMODE;
 	if (ioctl(s, SIOCG80211, &ireq) != -1) {
-		printf(" authmode");
 		switch (ireq.i_val) {
 			case IEEE80211_AUTH_NONE:
-				printf(" NONE");
+				LINE_CHECK("%cauthmode NONE", spacer);
 				break;
 			case IEEE80211_AUTH_OPEN:
-				printf(" OPEN");
+				LINE_CHECK("%cauthmode OPEN", spacer);
 				break;
 			case IEEE80211_AUTH_SHARED:
-				printf(" SHARED");
+				LINE_CHECK("%cauthmode SHARED", spacer);
 				break;
-			default:
-				printf(" UNKNOWN");
+			case IEEE80211_AUTH_8021X:
+				LINE_CHECK("%cauthmode 802.1x", spacer);
 				break;
-		}
-	}
-
-	ireq.i_type = IEEE80211_IOC_POWERSAVE;
-	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
-	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
-		printf(" powersavemode");
-		switch (ireq.i_val) {
-			case IEEE80211_POWERSAVE_OFF:
-				printf(" OFF");
+			case IEEE80211_AUTH_WPA:
+				ireq.i_type = IEEE80211_IOC_WPA;
+				if (ioctl(s, SIOCG80211, &ireq) != -1)
+					wpa = ireq.i_val;
+				if (!wpa)
+					wpa = 1;	/* default to WPA1 */
+				switch (wpa) {
+				case 2:
+					LINE_CHECK("%cauthmode WPA2/802.11i",
+						spacer);
+					break;
+				case 3:
+					LINE_CHECK("%cauthmode WPA1+WPA2/802.11i",
+						spacer);
+					break;
+				default:
+					LINE_CHECK("%cauthmode WPA", spacer);
+					break;
+				}
 				break;
-			case IEEE80211_POWERSAVE_CAM:
-				printf(" CAM");
-				break;
-			case IEEE80211_POWERSAVE_PSP:
-				printf(" PSP");
-				break;
-			case IEEE80211_POWERSAVE_PSP_CAM:
-				printf(" PSP-CAM");
-				break;
-		}
-
-		ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
-		if (ioctl(s, SIOCG80211, &ireq) != -1) {
-			if (ireq.i_val)
-				printf(" powersavesleep %d", ireq.i_val);
-		}
-	}
-
-	printf("\n");
-
-	spacer = '\t';
-	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
-	if (ioctl(s, SIOCG80211, &ireq) != -1) {
-		printf("%crtsthreshold %d", spacer, ireq.i_val);
-		spacer = ' ';
-	}
-
-	ireq.i_type = IEEE80211_IOC_PROTMODE;
-	if (ioctl(s, SIOCG80211, &ireq) != -1) {
-		printf("%cprotmode", spacer);
-		switch (ireq.i_val) {
-			case IEEE80211_PROTMODE_OFF:
-				printf(" OFF");
-				break;
-			case IEEE80211_PROTMODE_CTS:
-				printf(" CTS");
-				break;
-			case IEEE80211_PROTMODE_RTSCTS:
-				printf(" RTSCTS");
+			case IEEE80211_AUTH_AUTO:
+				LINE_CHECK("%cauthmode AUTO", spacer);
 				break;
 			default:
-				printf(" UNKNOWN");
+				LINE_CHECK("%cauthmode UNKNOWN (0x%x)",
+					spacer, ireq.i_val);
 				break;
 		}
-		spacer = ' ';
 	}
 
-	ireq.i_type = IEEE80211_IOC_TXPOWER;
-	if (ioctl(s, SIOCG80211, &ireq) != -1) {
-		printf("%ctxpower %d", spacer, ireq.i_val);
-		spacer = ' ';
-	}
-
-	if (spacer != '\t')
-		printf("\n");
-
 	ireq.i_type = IEEE80211_IOC_WEP;
 	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
 	    ireq.i_val != IEEE80211_WEP_NOSUP) {
-		printf("\twepmode");
-		switch (ireq.i_val) {
+		int firstkey, wepmode;
+
+		wepmode = ireq.i_val;
+		switch (wepmode) {
 			case IEEE80211_WEP_OFF:
-				printf(" OFF");
+				LINE_CHECK("%cprivacy OFF", spacer);
 				break;
 			case IEEE80211_WEP_ON:
-				printf(" ON");
+				LINE_CHECK("%cprivacy ON", spacer);
 				break;
 			case IEEE80211_WEP_MIXED:
-				printf(" MIXED");
+				LINE_CHECK("%cprivacy MIXED", spacer);
 				break;
 			default:
-				printf(" UNKNOWN");
+				LINE_CHECK("%cprivacy UNKNOWN (0x%x)",
+					spacer, wepmode);
 				break;
 		}
 
@@ -491,7 +1421,10 @@
 			warn("WEP support, but no tx key!");
 			goto end;
 		}
-		printf(" weptxkey %d", ireq.i_val+1);
+		if (ireq.i_val != -1)
+			LINE_CHECK("%cdeftxkey %d", spacer, ireq.i_val+1);
+		else if (wepmode != IEEE80211_WEP_OFF || verbose)
+			LINE_CHECK("%cdeftxkey UNDEF", spacer);
 
 		ireq.i_type = IEEE80211_IOC_NUMWEPKEYS;
 		if (ioctl(s, SIOCG80211, &ireq) < 0) {
@@ -500,28 +1433,201 @@
 		}
 		num = ireq.i_val;
 
-		printf("\n");
-
-		ireq.i_type = IEEE80211_IOC_WEPKEY;
-		spacer = '\t';
+		firstkey = 1;
 		for (i = 0; i < num; i++) {
-			ireq.i_val = i;
+			struct ieee80211req_key ik;
+
+			memset(&ik, 0, sizeof(ik));
+			ik.ik_keyix = i;
+			ireq.i_type = IEEE80211_IOC_WPAKEY;
+			ireq.i_data = &ik;
+			ireq.i_len = sizeof(ik);
 			if (ioctl(s, SIOCG80211, &ireq) < 0) {
 				warn("WEP support, but can get keys!");
 				goto end;
 			}
-			if (ireq.i_len == 0 ||
-			    ireq.i_len > IEEE80211_KEYBUF_SIZE)
-				continue;
-			printf("%cwepkey %d:%s", spacer, i+1,
-			    ireq.i_len <= 5 ? "40-bit" :
-			    ireq.i_len <= 13 ? "104-bit" : "128-bit");
-			if (spacer == '\t')
+			if (ik.ik_keylen != 0) {
+				if (verbose)
+					LINE_BREAK();
+				printkey(&ik);
+				firstkey = 0;
+			}
+		}
+	}
+
+	ireq.i_type = IEEE80211_IOC_POWERSAVE;
+	if (ioctl(s, SIOCG80211, &ireq) != -1 &&
+	    ireq.i_val != IEEE80211_POWERSAVE_NOSUP ) {
+		if (ireq.i_val != IEEE80211_POWERSAVE_OFF || verbose) {
+			switch (ireq.i_val) {
+				case IEEE80211_POWERSAVE_OFF:
+					LINE_CHECK("%cpowersavemode OFF",
+						spacer);
+					break;
+				case IEEE80211_POWERSAVE_CAM:
+					LINE_CHECK("%cpowersavemode CAM",
+						spacer);
+					break;
+				case IEEE80211_POWERSAVE_PSP:
+					LINE_CHECK("%cpowersavemode PSP",
+						spacer);
+					break;
+				case IEEE80211_POWERSAVE_PSP_CAM:
+					LINE_CHECK("%cpowersavemode PSP-CAM",
+						spacer);
+					break;
+			}
+			ireq.i_type = IEEE80211_IOC_POWERSAVESLEEP;
+			if (ioctl(s, SIOCG80211, &ireq) != -1)
+				LINE_CHECK("%cpowersavesleep %d",
+					spacer, ireq.i_val);
+		}
+	}
+
+	ireq.i_type = IEEE80211_IOC_TXPOWMAX;
+	if (ioctl(s, SIOCG80211, &ireq) != -1)
+		LINE_CHECK("%ctxpowmax %d", spacer, ireq.i_val);
+
+	if (verbose) {
+		ireq.i_type = IEEE80211_IOC_TXPOWER;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			LINE_CHECK("%ctxpower %d", spacer, ireq.i_val);
+	}
+
+	ireq.i_type = IEEE80211_IOC_RTSTHRESHOLD;
+	if (ioctl(s, SIOCG80211, &ireq) != -1) {
+		if (ireq.i_val != IEEE80211_RTS_MAX || verbose)
+			LINE_CHECK("%crtsthreshold %d", spacer, ireq.i_val);
+	}
+
+	if (IEEE80211_IS_CHAN_G(c) || IEEE80211_IS_CHAN_PUREG(c) || verbose) {
+		ireq.i_type = IEEE80211_IOC_PROTMODE;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			switch (ireq.i_val) {
+				case IEEE80211_PROTMODE_OFF:
+					LINE_CHECK("%cprotmode OFF", spacer);
+					break;
+				case IEEE80211_PROTMODE_CTS:
+					LINE_CHECK("%cprotmode CTS", spacer);
+					break;
+				case IEEE80211_PROTMODE_RTSCTS:
+					LINE_CHECK("%cprotmode RTSCTS", spacer);
+					break;
+				default:
+					LINE_CHECK("%cprotmode UNKNOWN (0x%x)",
+						spacer, ireq.i_val);
+					break;
+			}
+		}
+	}
+
+	ireq.i_type = IEEE80211_IOC_WME;
+	if (ioctl(s, SIOCG80211, &ireq) != -1) {
+		wme = ireq.i_val;
+		if (wme)
+			LINE_CHECK("%cwme", spacer);
+		else if (verbose)
+			LINE_CHECK("%c-wme", spacer);
+	} else
+		wme = 0;
+
+	if (opmode == IEEE80211_M_HOSTAP) {
+		ireq.i_type = IEEE80211_IOC_HIDESSID;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (ireq.i_val)
+				LINE_CHECK("%cssid HIDE", spacer);
+			else if (verbose)
+				LINE_CHECK("%cssid SHOW", spacer);
+		}
+
+		ireq.i_type = IEEE80211_IOC_APBRIDGE;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (!ireq.i_val)
+				LINE_CHECK("%c-apbridge", spacer);
+			else if (verbose)
+				LINE_CHECK("%capbridge", spacer);
+		}
+
+		ireq.i_type = IEEE80211_IOC_DTIM_PERIOD;
+		if (ioctl(s, SIOCG80211, &ireq) != -1)
+			LINE_CHECK("%cdtimperiod %u", spacer, ireq.i_val);
+	} else {
+		ireq.i_type = IEEE80211_IOC_ROAMING;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (ireq.i_val != IEEE80211_ROAMING_AUTO || verbose) {
+				switch (ireq.i_val) {
+				case IEEE80211_ROAMING_DEVICE:
+					LINE_CHECK("%croaming DEVICE", spacer);
+					break;
+				case IEEE80211_ROAMING_AUTO:
+					LINE_CHECK("%croaming AUTO", spacer);
+					break;
+				case IEEE80211_ROAMING_MANUAL:
+					LINE_CHECK("%croaming MANUAL", spacer);
+					break;
+				default:
+					LINE_CHECK("%croaming UNKNOWN (0x%x)",
+						spacer, ireq.i_val);
+					break;
+				}
+			}
+		}
+	}
+	ireq.i_type = IEEE80211_IOC_BEACON_INTERVAL;
+	if (ioctl(s, SIOCG80211, &ireq) != -1) {
+		if (ireq.i_val)
+			LINE_CHECK("%cbintval %u", spacer, ireq.i_val);
+		else if (verbose)
+			LINE_CHECK("%cbintval %u", spacer, ireq.i_val);
+	}
+
+	if (wme && verbose) {
+		LINE_BREAK();
+		list_wme(s);
+	}
+
+	if (wpa) {
+		ireq.i_type = IEEE80211_IOC_COUNTERMEASURES;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			if (ireq.i_val)
+				LINE_CHECK("%ccountermeasures", spacer);
+			else if (verbose)
+				LINE_CHECK("%c-countermeasures", spacer);
+		}
+#if 0
+		/* XXX not interesting with WPA done in user space */
+		ireq.i_type = IEEE80211_IOC_KEYMGTALGS;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+		}
+
+		ireq.i_type = IEEE80211_IOC_MCASTCIPHER;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			printf("%cmcastcipher ", spacer);
+			printcipher(s, &ireq, IEEE80211_IOC_MCASTKEYLEN);
+			spacer = ' ';
+		}
+
+		ireq.i_type = IEEE80211_IOC_UCASTCIPHER;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
+			printf("%cucastcipher ", spacer);
+			printcipher(s, &ireq, IEEE80211_IOC_UCASTKEYLEN);
+		}
+
+		if (wpa & 2) {
+			ireq.i_type = IEEE80211_IOC_RSNCAPS;
+			if (ioctl(s, SIOCG80211, &ireq) != -1) {
+				printf("%cRSN caps 0x%x", spacer, ireq.i_val);
 				spacer = ' ';
+			}
+		}
+
+		ireq.i_type = IEEE80211_IOC_UCASTCIPHERS;
+		if (ioctl(s, SIOCG80211, &ireq) != -1) {
 		}
-		if (spacer == ' ')
-			printf("\n");
+#endif
+		LINE_BREAK();
 	}
+	LINE_BREAK();
 
 end:
 	return;
@@ -623,3 +1729,79 @@
 	}
 }
 
+static struct cmd ieee80211_cmds[] = {
+	DEF_CMD_ARG("ssid",		set80211ssid),
+	DEF_CMD_ARG("nwid",		set80211ssid),
+	DEF_CMD_ARG("stationname",	set80211stationname),
+	DEF_CMD_ARG("station",		set80211stationname),	/* BSD/OS */
+	DEF_CMD_ARG("channel",		set80211channel),
+	DEF_CMD_ARG("authmode",		set80211authmode),
+	DEF_CMD_ARG("powersavemode",	set80211powersavemode),
+	DEF_CMD("powersave",	1,	set80211powersave),
+	DEF_CMD("-powersave",	0,	set80211powersave),
+	DEF_CMD_ARG("powersavesleep", 	set80211powersavesleep),
+	DEF_CMD_ARG("wepmode",		set80211wepmode),
+	DEF_CMD("wep",		1,	set80211wep),
+	DEF_CMD("-wep",		0,	set80211wep),
+	DEF_CMD_ARG("deftxkey",		set80211weptxkey),
+	DEF_CMD_ARG("weptxkey",		set80211weptxkey),
+	DEF_CMD_ARG("wepkey",		set80211wepkey),
+	DEF_CMD_ARG("nwkey",		set80211nwkey),		/* NetBSD */
+	DEF_CMD("-nwkey",	0,	set80211wep),		/* NetBSD */
+	DEF_CMD_ARG("rtsthreshold",	set80211rtsthreshold),
+	DEF_CMD_ARG("protmode",		set80211protmode),
+	DEF_CMD_ARG("txpower",		set80211txpower),
+	DEF_CMD_ARG("roaming",		set80211roaming),
+	DEF_CMD("wme",		1,	set80211wme),
+	DEF_CMD("-wme",		0,	set80211wme),
+	DEF_CMD("hidessid",	1,	set80211hidessid),
+	DEF_CMD("-hidessid",	0,	set80211hidessid),
+	DEF_CMD("apbridge",	1,	set80211apbridge),
+	DEF_CMD("-apbridge",	0,	set80211apbridge),
+	DEF_CMD_ARG("chanlist",		set80211chanlist),
+	DEF_CMD_ARG("bssid",		set80211bssid),
+	DEF_CMD_ARG("ap",		set80211bssid),
+	DEF_CMD("scan",	0,		set80211scan),
+	DEF_CMD_ARG("list",		set80211list),
+	DEF_CMD_ARG2("cwmin",		set80211cwmin),
+	DEF_CMD_ARG2("cwmax",		set80211cwmax),
+	DEF_CMD_ARG2("aifs",		set80211aifs),
+	DEF_CMD_ARG2("txoplimit",	set80211txoplimit),
+	DEF_CMD("acm",		1,	set80211acm),
+	DEF_CMD("-acm",		0,	set80211acm),
+	DEF_CMD("ack",		1,	set80211ackpolicy),
+	DEF_CMD("-ack",		0,	set80211ackpolicy),
+	DEF_CMD_ARG2("bss:cwmin",	set80211bsscwmin),
+	DEF_CMD_ARG2("bss:cwmax",	set80211bsscwmax),
+	DEF_CMD_ARG2("bss:aifs",	set80211bssaifs),
+	DEF_CMD_ARG2("bss:txoplimit",	set80211bsstxoplimit),
+	DEF_CMD_ARG("dtimperiod",	set80211dtimperiod),
+	DEF_CMD_ARG("bintval",		set80211bintval),
+	DEF_CMD("mac:open",	IEEE80211_MACCMD_POLICY_OPEN,	set80211maccmd),
+	DEF_CMD("mac:allow",	IEEE80211_MACCMD_POLICY_ALLOW,	set80211maccmd),
+	DEF_CMD("mac:deny",	IEEE80211_MACCMD_POLICY_DENY,	set80211maccmd),
+	DEF_CMD("mac:flush",	IEEE80211_MACCMD_FLUSH,		set80211maccmd),
+	DEF_CMD("mac:detach",	IEEE80211_MACCMD_DETACH,	set80211maccmd),
+	DEF_CMD_ARG("mac:add",		set80211addmac),
+	DEF_CMD_ARG("mac:del",		set80211delmac),
+#if 0
+	DEF_CMD_ARG("mac:kick",		set80211kickmac),
+#endif
+};
+static struct afswtch af_ieee80211 = {
+	.af_name	= "af_ieee80211",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = ieee80211_status,
+};
+
+static __constructor void
+ieee80211_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(ieee80211_cmds);  i++)
+		cmd_register(&ieee80211_cmds[i]);
+	af_register(&af_ieee80211);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifmac.c src/sbin/ifconfig/ifmac.c
--- src.preview/sbin/ifconfig/ifmac.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/ifmac.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2001 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by NAI Labs, the
+ * Security Research Division of Network Associates, Inc. under
+ * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
+ * CHATS research program.
+ *
+ * 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. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ifconfig.h"
+
+static void
+maclabel_status(int s)
+{
+	struct ifreq ifr;
+	mac_t label;
+	char *label_text;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+	if (mac_prepare_ifnet_label(&label) == -1)
+		return;
+	ifr.ifr_ifru.ifru_data = (void *)label;
+	if (ioctl(s, SIOCGIFMAC, &ifr) == -1)
+		goto mac_free;
+
+	
+	if (mac_to_text(label, &label_text) == -1)
+		goto mac_free;
+
+	if (strlen(label_text) != 0)
+		printf("\tmaclabel %s\n", label_text);
+	free(label_text);
+
+mac_free:
+	mac_free(label);
+}
+
+static void
+setifmaclabel(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	struct ifreq ifr;
+	mac_t label;
+	int error;
+
+	if (mac_from_text(&label, val) == -1) {
+		perror(val);
+		return;
+	}
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+	ifr.ifr_ifru.ifru_data = (void *)label;
+
+	error = ioctl(s, SIOCSIFMAC, &ifr);
+	mac_free(label);
+	if (error == -1)
+		perror("setifmac");
+}
+
+static struct cmd mac_cmds[] = {
+	DEF_CMD_ARG("maclabel",	setifmaclabel),
+};
+static struct afswtch af_mac = {
+	.af_name	= "af_maclabel",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = maclabel_status,
+};
+
+static __constructor void
+mac_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(mac_cmds);  i++)
+		cmd_register(&mac_cmds[i]);
+	af_register(&af_mac);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifmedia.c src/sbin/ifconfig/ifmedia.c
--- src.preview/sbin/ifconfig/ifmedia.c	2005-03-06 00:01:59.000000000 -0500
+++ src/sbin/ifconfig/ifmedia.c	2005-09-27 15:02:10.000000000 -0400
@@ -1,6 +1,5 @@
 /*	$NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $	*/
-/* $FreeBSD: src/sbin/ifconfig/ifmedia.c,v 1.6.2.3 2001/11/14 04:35:07 yar Exp $ */
-/* $DragonFly: src/sbin/ifconfig/ifmedia.c,v 1.8 2005/03/06 05:01:59 dillon Exp $ */
+/* $FreeBSD$ */
 
 /*
  * Copyright (c) 1997 Jason R. Thorpe.
@@ -90,24 +89,6 @@
 
 #include "ifconfig.h"
 
-#ifndef IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS
-  #define IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS {                       \
-        { IFM_AUTO, "autoselect" },                                     \
-        { IFM_IEEE80211_11A, "11a" },                                   \
-        { IFM_IEEE80211_11B, "11b" },                                   \
-        { IFM_IEEE80211_11G, "11g" },                                   \
-        { IFM_IEEE80211_FH, "fh" },                                     \
-        { 0, NULL },                                                    \
-}
-#endif
-
-#ifndef IFM_SUBTYPE_IEEE80211_MODE_ALIASES
-  #define IFM_SUBTYPE_IEEE80211_MODE_ALIASES {                            \
-        { IFM_AUTO, "auto" },                                           \
-        { 0, NULL },                                                    \
-}
-#endif
-
 static void	domediaopt(const char *, int, int);
 static int	get_media_subtype(int, const char *);
 static int	get_media_mode(int, const char *);
@@ -121,8 +102,8 @@
 static struct ifmedia_description *get_subtype_desc(int,
     struct ifmedia_type_to_subtype *ttos);
 
-void
-media_status(int s, struct rt_addrinfo *info __unused)
+static void
+media_status(int s)
 {
 	struct ifmediareq ifmr;
 	int *media_list, i;
@@ -179,14 +160,12 @@
 				printf("no ring");
 			break;
 
-#if notyet
 		case IFM_ATM:
 			if (ifmr.ifm_status & IFM_ACTIVE)
 				printf("active");
 			else
 				printf("no carrier");
 			break;
-#endif
 
 		case IFM_IEEE80211:
 			/* XXX: Different value for adhoc? */
@@ -211,29 +190,72 @@
 	free(media_list);
 }
 
-void
-setmedia(const char *val, int d __unused, int s,
-	 const struct afswtch *afp __unused)
+static struct ifmediareq *
+getifmediastate(int s)
 {
-	struct ifmediareq ifmr;
-	int first_type, subtype;
+	static struct ifmediareq *ifmr = NULL;
+	int *mwords;
 
-	(void) memset(&ifmr, 0, sizeof(ifmr));
-	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+	if (ifmr == NULL) {
+		ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq));
+		if (ifmr == NULL)
+			err(1, "malloc");
+
+		(void) memset(ifmr, 0, sizeof(struct ifmediareq));
+		(void) strncpy(ifmr->ifm_name, name,
+		    sizeof(ifmr->ifm_name));
+
+		ifmr->ifm_count = 0;
+		ifmr->ifm_ulist = NULL;
 
-	ifmr.ifm_count = 1;
-	ifmr.ifm_ulist = &first_type;
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
 		/*
-		 * If we get E2BIG, the kernel is telling us
-		 * that there are more, so we can ignore it.
+		 * We must go through the motions of reading all
+		 * supported media because we need to know both
+		 * the current media type and the top-level type.
 		 */
-		if (errno != E2BIG)
+
+		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) {
+			err(1, "SIOCGIFMEDIA");
+		}
+
+		if (ifmr->ifm_count == 0)
+			errx(1, "%s: no media types?", name);
+
+		mwords = (int *)malloc(ifmr->ifm_count * sizeof(int));
+		if (mwords == NULL)
+			err(1, "malloc");
+  
+		ifmr->ifm_ulist = mwords;
+		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0)
 			err(1, "SIOCGIFMEDIA");
 	}
 
-	if (ifmr.ifm_count == 0)
-		errx(1, "%s: no media types?", name);
+	return ifmr;
+}
+
+static void
+setifmediacallback(int s, void *arg)
+{
+	struct ifmediareq *ifmr = (struct ifmediareq *)arg;
+	static int did_it = 0;
+
+	if (!did_it) {
+		if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
+			err(1, "SIOCSIFMEDIA (media)");
+		free(ifmr->ifm_ulist);
+		free(ifmr);
+		did_it = 1;
+	}
+}
+
+static void
+setmedia(const char *val, int d, int s, const struct afswtch *afp)
+{
+	struct ifmediareq *ifmr;
+	int subtype;
+	
+
+	ifmr = getifmediastate(s);
 
 	/*
 	 * We are primarily concerned with the top-level type.
@@ -244,27 +266,29 @@
 	 * (I'm assuming that all supported media types for a given
 	 * interface will be the same top-level type..)
 	 */
-	subtype = get_media_subtype(IFM_TYPE(first_type), val);
+	subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val);
 
 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-	ifr.ifr_media = (ifmr.ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
-	    IFM_TYPE(first_type) | subtype;
+	ifr.ifr_media = (ifmr->ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
+	    IFM_TYPE(ifmr->ifm_ulist[0]) | subtype;
+
+	if ((ifr.ifr_media & IFM_TMASK) == 0) {
+		ifr.ifr_media &= ~IFM_GMASK;
+	}
 
-	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
-		err(1, "SIOCSIFMEDIA (media)");
+	ifmr->ifm_current = ifr.ifr_media;
+	callback_register(setifmediacallback, (void *)ifmr);
 }
 
-void
-setmediaopt(const char *val, int d __unused, int s,
-	    const struct afswtch *afp __unused)
+static void
+setmediaopt(const char *val, int d, int s, const struct afswtch *afp)
 {
 
 	domediaopt(val, 0, s);
 }
 
-void
-unsetmediaopt(const char *val, int d __unused, int s,
-	      const struct afswtch *afp __unused)
+static void
+unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp)
 {
 
 	domediaopt(val, 1, s);
@@ -273,87 +297,40 @@
 static void
 domediaopt(const char *val, int clear, int s)
 {
-	struct ifmediareq ifmr;
-	int *mwords, options;
+	struct ifmediareq *ifmr;
+	int options;
 
-	(void) memset(&ifmr, 0, sizeof(ifmr));
-	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+	ifmr = getifmediastate(s);
 
-	/*
-	 * We must go through the motions of reading all
-	 * supported media because we need to know both
-	 * the current media type and the top-level type.
-	 */
-
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
-		err(1, "SIOCGIFMEDIA");
-
-	if (ifmr.ifm_count == 0)
-		errx(1, "%s: no media types?", name);
-
-	mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
-	if (mwords == NULL)
-		err(1, "malloc");
-
-	ifmr.ifm_ulist = mwords;
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
-		err(1, "SIOCGIFMEDIA");
-
-	options = get_media_options(IFM_TYPE(mwords[0]), val);
-
-	free(mwords);
+	options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val);
 
 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-	ifr.ifr_media = ifmr.ifm_current;
+	ifr.ifr_media = ifmr->ifm_current;
 	if (clear)
 		ifr.ifr_media &= ~options;
 	else
 		ifr.ifr_media |= options;
 
-	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
-		err(1, "SIOCSIFMEDIA (mediaopt)");
+	ifmr->ifm_current = ifr.ifr_media;
+	callback_register(setifmediacallback, (void *)ifmr);
 }
 
 
-void
-setmediamode(const char *val, int d __unused, int s,
-	const struct afswtch *afp __unused)
+static void
+setmediamode(const char *val, int d, int s, const struct afswtch *afp)
 {
-	struct ifmediareq ifmr;
-	int *mwords, mode;
-
-	(void) memset(&ifmr, 0, sizeof(ifmr));
-	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
-
-	/*
-	 * We must go through the motions of reading all
-	 * supported media because we need to know both
-	 * the current media type and the top-level type.
-	 */
+	struct ifmediareq *ifmr;
+	int mode;
 
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
-		err(1, "SIOCGIFMEDIA");
-
-	if (ifmr.ifm_count == 0)
-		errx(1, "%s: no media types?", name);
-
-	mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
-	if (mwords == NULL)
-		err(1, "malloc");
-
-	ifmr.ifm_ulist = mwords;
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
-		err(1, "SIOCGIFMEDIA");
-
-	mode = get_media_mode(IFM_TYPE(mwords[0]), val);
+	ifmr = getifmediastate(s);
 
-	free(mwords);
+	mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val);
 
 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
-	ifr.ifr_media = (ifmr.ifm_current & ~IFM_MMASK) | mode;
+	ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode;
 
-	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
-		err(1, "SIOCSIFMEDIA (mode)");
+	ifmr->ifm_current = ifr.ifr_media;
+	callback_register(setifmediacallback, (void *)ifmr);
 }
 
 /**********************************************************************
@@ -405,7 +382,6 @@
 struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] =
     IFM_SUBTYPE_IEEE80211_MODE_ALIASES;
 
-#if notyet
 static struct ifmedia_description ifm_subtype_atm_descriptions[] =
     IFM_SUBTYPE_ATM_DESCRIPTIONS;
 
@@ -414,7 +390,6 @@
 
 static struct ifmedia_description ifm_subtype_atm_option_descriptions[] =
     IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS;
-#endif
 
 static struct ifmedia_description ifm_subtype_shared_descriptions[] =
     IFM_SUBTYPE_SHARED_DESCRIPTIONS;
@@ -516,17 +491,13 @@
 		{
 			{ &ifm_subtype_shared_descriptions[0], 0 },
 			{ &ifm_subtype_shared_aliases[0], 1 },
-#if notyet
 			{ &ifm_subtype_atm_descriptions[0], 0 },
 			{ &ifm_subtype_atm_aliases[0], 1 },
-#endif
 			{ NULL, 0 },
 		},
 		{
 			{ &ifm_shared_option_descriptions[0], 0 },
-#if notyet
 			{ &ifm_subtype_atm_option_descriptions[0], 0 },
-#endif
 			{ NULL, 0 },
 		},
 		{
@@ -808,3 +779,27 @@
 /**********************************************************************
  * ...until here.
  **********************************************************************/
+
+static struct cmd media_cmds[] = {
+	DEF_CMD_ARG("media",	setmedia),
+	DEF_CMD_ARG("mode",	setmediamode),
+	DEF_CMD_ARG("mediaopt",	setmediaopt),
+	DEF_CMD_ARG("-mediaopt",unsetmediaopt),
+};
+static struct afswtch af_media = {
+	.af_name	= "af_media",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = media_status,
+};
+
+static __constructor void
+ifmedia_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(media_cmds);  i++)
+		cmd_register(&media_cmds[i]);
+	af_register(&af_media);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifpfsync.c src/sbin/ifconfig/ifpfsync.c
--- src.preview/sbin/ifconfig/ifpfsync.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sbin/ifconfig/ifpfsync.c	2005-09-27 15:02:10.000000000 -0400
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2003 Ryan McBride. All rights reserved.
+ * Copyright (c) 2004 Max Laier. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <net/pfvar.h>
+#include <net/if_pfsync.h>
+#include <net/route.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ifconfig.h"
+
+void setpfsync_syncif(const char *, int, int, const struct afswtch *rafp);
+void unsetpfsync_syncif(const char *, int, int, const struct afswtch *rafp);
+void setpfsync_maxupd(const char *, int, int, const struct afswtch *rafp);
+void pfsync_status(int);
+
+void
+setpfsync_syncif(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	struct pfsyncreq preq;
+
+	bzero((char *)&preq, sizeof(struct pfsyncreq));
+	ifr.ifr_data = (caddr_t)&preq;
+
+	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGETPFSYNC");
+
+	strlcpy(preq.pfsyncr_syncif, val, sizeof(preq.pfsyncr_syncif));
+
+	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSETPFSYNC");
+}
+
+void
+unsetpfsync_syncif(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	struct pfsyncreq preq;
+
+	bzero((char *)&preq, sizeof(struct pfsyncreq));
+	ifr.ifr_data = (caddr_t)&preq;
+
+	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGETPFSYNC");
+
+	bzero((char *)&preq.pfsyncr_syncif, sizeof(preq.pfsyncr_syncif));
+
+	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSETPFSYNC");
+}
+
+void
+setpfsync_maxupd(const char *val, int d, int s, const struct afswtch *rafp)
+{
+	int maxupdates;
+	struct pfsyncreq preq;
+
+	maxupdates = atoi(val);
+
+	memset((char *)&preq, 0, sizeof(struct pfsyncreq));
+	ifr.ifr_data = (caddr_t)&preq;
+
+	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCGETPFSYNC");
+
+	preq.pfsyncr_maxupdates = maxupdates;
+
+	if (ioctl(s, SIOCSETPFSYNC, (caddr_t)&ifr) == -1)
+		err(1, "SIOCSETPFSYNC");
+}
+
+void
+pfsync_status(int s)
+{
+	struct pfsyncreq preq;
+
+	bzero((char *)&preq, sizeof(struct pfsyncreq));
+	ifr.ifr_data = (caddr_t)&preq;
+
+	if (ioctl(s, SIOCGETPFSYNC, (caddr_t)&ifr) == -1)
+		return;
+
+	if (preq.pfsyncr_syncif[0] != '\0') {
+		printf("\tpfsync: syncif: %s maxupd: %d\n",
+		    preq.pfsyncr_syncif, preq.pfsyncr_maxupdates);
+	}
+}
+
+static struct cmd pfsync_cmds[] = {
+	DEF_CMD_ARG("syncif",		setpfsync_syncif),
+	DEF_CMD_ARG("maxupd",		setpfsync_maxupd),
+	DEF_CMD("-syncif",	1,	unsetpfsync_syncif),
+};
+static struct afswtch af_pfsync = {
+	.af_name	= "af_pfsync",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = pfsync_status,
+};
+
+static __constructor void
+pfsync_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(pfsync_cmds);  i++)
+	af_register(&af_pfsync);
+#undef N
+}
diff -N -u -r src.preview/sbin/ifconfig/ifvlan.c src/sbin/ifconfig/ifvlan.c
--- src.preview/sbin/ifconfig/ifvlan.c	2005-03-06 00:01:59.000000000 -0500
+++ src/sbin/ifconfig/ifvlan.c	2005-09-27 15:02:10.000000000 -0400
@@ -28,9 +28,6 @@
  * 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.
- *
- * $FreeBSD: src/sbin/ifconfig/ifvlan.c,v 1.2 1999/08/28 00:13:09 peter Exp $
- * $DragonFly: src/sbin/ifconfig/ifvlan.c,v 1.6 2005/03/06 05:01:59 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -61,7 +58,8 @@
 static int			__tag = 0;
 static int			__have_tag = 0;
 
-void vlan_status(int s, struct rt_addrinfo *info __unused)
+static void
+vlan_status(int s)
 {
 	struct vlanreq		vreq;
 
@@ -78,8 +76,8 @@
 	return;
 }
 
-void setvlantag(const char *val, int d __unused, int s,
-		const struct afswtch *afp __unused)
+static void
+setvlantag(const char *val, int d, int s, const struct afswtch	*afp)
 {
 	u_int16_t		tag;
 	struct vlanreq		vreq;
@@ -101,8 +99,8 @@
 	return;
 }
 
-void setvlandev(const char *val, int d __unused, int s,
-		const struct afswtch *afp __unused)
+static void
+setvlandev(const char *val, int d, int s, const struct afswtch	*afp)
 {
 	struct vlanreq		vreq;
 
@@ -124,8 +122,8 @@
 	return;
 }
 
-void unsetvlandev(const char *val __unused, int d __unused, int s,
-		  const struct afswtch *afp __unused)
+static void
+unsetvlandev(const char *val, int d, int s, const struct afswtch *afp)
 {
 	struct vlanreq		vreq;
 
@@ -143,3 +141,30 @@
 
 	return;
 }
+
+static struct cmd vlan_cmds[] = {
+	DEF_CMD_ARG("vlan",				setvlantag),
+	DEF_CMD_ARG("vlandev",				setvlandev),
+	DEF_CMD_ARG("-vlandev",				unsetvlandev),
+	DEF_CMD("vlanmtu",	IFCAP_VLAN_MTU,		setifcap),
+	DEF_CMD("-vlanmtu",	-IFCAP_VLAN_MTU,	setifcap),
+	DEF_CMD("vlanhwtag",	IFCAP_VLAN_HWTAGGING,	setifcap),
+	DEF_CMD("-vlanhwtag",	-IFCAP_VLAN_HWTAGGING,	setifcap),
+};
+static struct afswtch af_vlan = {
+	.af_name	= "af_vlan",
+	.af_af		= AF_UNSPEC,
+	.af_other_status = vlan_status,
+};
+
+static __constructor void
+vlan_ctor(void)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	int i;
+
+	for (i = 0; i < N(vlan_cmds);  i++)
+		cmd_register(&vlan_cmds[i]);
+	af_register(&af_vlan);
+#undef N
+}
diff -N -u -r src.preview/sys/bus/cam/scsi/scsi_da.c src/sys/bus/cam/scsi/scsi_da.c
--- src.preview/sys/bus/cam/scsi/scsi_da.c	2005-06-02 17:12:45.000000000 -0400
+++ src/sys/bus/cam/scsi/scsi_da.c	2005-09-23 11:24:22.000000000 -0400
@@ -345,14 +345,6 @@
  	},
 	{
 		/*
-		 * iRiver iFP MP3 player (with UMS Firmware)
-		 * PR: kern/54881, i386/63941, kern/66124
-		 */
-		{T_DIRECT, SIP_MEDIA_REMOVABLE, "iRiver", "iFP*", "*"},
-		/*quirks*/ DA_Q_NO_SYNC_CACHE
- 	},
-	{
-		/*
 		 * Frontier Labs NEX IA+ Digital Audio Player, rev 1.10/0.01
 		 * PR: kern/70158
 		 */
@@ -401,6 +393,14 @@
 #endif /* DA_OLD_QUIRKS */
 	{
 		/*
+		 * iRiver iFP MP3 player (with UMS Firmware)
+		 * PR: kern/54881, i386/63941, kern/66124
+		 */
+		{T_DIRECT, SIP_MEDIA_REMOVABLE, "iRiver", "iFP*", "*"},
+		/*quirks*/ DA_Q_NO_SYNC_CACHE
+ 	},
+	{
+		/*
 		 * EXATELECOM (Sigmatel) i-Bead 100/105 USB Flash MP3 Player
 		 * PR: kern/51675
 		 */
diff -N -u -r src.preview/sys/conf/Makefile.i386 src/sys/conf/Makefile.i386
--- src.preview/sys/conf/Makefile.i386	2005-08-16 22:51:45.000000000 -0400
+++ src/sys/conf/Makefile.i386	2005-09-23 11:24:22.000000000 -0400
@@ -68,6 +68,10 @@
 # include path into 100+ source files.
 INCLUDES+= -I$S/contrib/ipfilter
 
+# This hack lets us use the ath_hal code without spamming a new
+# include path into source files.
+INCLUDES+= -I$S/contrib/dev/ath -I$S/contrib/dev/ath/freebsd
+
 COPTS=	${INCLUDES} ${IDENT} -D_KERNEL -include opt_global.h
 CFLAGS=	${COPTFLAGS} ${CWARNFLAGS} ${DEBUG} ${COPTS}
 
diff -N -u -r src.preview/sys/conf/files src/sys/conf/files
--- src.preview/sys/conf/files	2005-09-03 21:28:57.000000000 -0400
+++ src/sys/conf/files	2005-09-23 11:24:22.000000000 -0400
@@ -65,6 +65,7 @@
 bus/cam/scsi/scsi_target.c		optional targ
 bus/cam/scsi/scsi_targ_bh.c		optional targbh
 
+contrib/dev/ath/freebsd/ah_osdep.c      optional ath_hal
 contrib/ipfilter/netinet/fil.c		optional ipfilter inet
 contrib/ipfilter/netinet/ip_auth.c	optional ipfilter inet
 contrib/ipfilter/netinet/ip_fil.c	optional ipfilter inet
@@ -144,6 +145,11 @@
 dev/disk/ata/atapi-fd.c			optional atapifd
 dev/disk/ata/atapi-tape.c		optional atapist
 dev/disk/ata/atapi-cam.c		optional atapicam
+dev/netif/ath/if_ath.c                  optional ath
+dev/netif/ath/if_ath_pci.c              optional ath pci
+dev/netif/ath/ath_rate/amrr/amrr.c	optional ath_rate_amrr
+dev/netif/ath/ath_rate/onoe/onoe.c	optional ath_rate_onoe
+dev/netif/ath/ath_rate/sample/sample.c	optional ath_rate_sample
 dev/raid/amr/amr_cam.c			optional amr
 dev/raid/amr/amr_disk.c			optional amr
 dev/raid/amr/amr_pci.c			optional amr
@@ -700,12 +706,18 @@
 net/zlib.c		optional ppp_deflate
 net/zlib.c		optional ipsec
 netproto/802_11/ieee80211.c		optional wlan
+netproto/802_11/ieee80211_acl.c		optional wlan
 netproto/802_11/ieee80211_crypto.c	optional wlan
+netproto/802_11/ieee80211_crypto_none.c	optional wlan
+netproto/802_11/ieee80211_crypto_tkip.c	optional wlan
+netproto/802_11/ieee80211_crypto_wep.c	optional wlan
+netproto/802_11/ieee80211_dragonfly.c	optional wlan
 netproto/802_11/ieee80211_input.c	optional wlan
 netproto/802_11/ieee80211_ioctl.c	optional wlan
 netproto/802_11/ieee80211_node.c	optional wlan
 netproto/802_11/ieee80211_output.c	optional wlan
 netproto/802_11/ieee80211_proto.c	optional wlan
+netproto/802_11/ieee80211_xauth.c	optional wlan
 netproto/atalk/aarp.c		optional netatalk
 netproto/atalk/at_control.c	optional netatalk
 netproto/atalk/at_proto.c	optional netatalk
diff -N -u -r src.preview/sys/conf/files.i386 src/sys/conf/files.i386
--- src.preview/sys/conf/files.i386	2005-08-16 06:31:35.000000000 -0400
+++ src/sys/conf/files.i386	2005-09-23 11:24:22.000000000 -0400
@@ -47,6 +47,16 @@
 	no-obj no-implicit-rule before-depend				\
 	clean		"ukbdmap.h"
 #
+hal.o                           optional        ath_hal                 \
+        dependency      "$S/contrib/dev/ath/public/i386-elf.hal.o.uu"   \
+        compile-with    "uudecode < $S/contrib/dev/ath/public/i386-elf.hal.o.uu" \
+        no-implicit-rule
+opt_ah.h                        optional        ath_hal                 \
+        dependency      "$S/contrib/dev/ath/public/i386-elf.opt_ah.h"   \
+        compile-with    "cp $S/contrib/dev/ath/public/i386-elf.opt_ah.h opt_ah.h" \
+        no-obj no-implicit-rule before-depend                           \
+        clean           "opt_ah.h"
+#
 emulation/linux/linux_file.c	optional	compat_linux
 emulation/linux/linux_getcwd.c	optional	compat_linux
 emulation/linux/linux_ioctl.c	optional	compat_linux
diff -N -u -r src.preview/sys/contrib/dev/ath/COPYRIGHT src/sys/contrib/dev/ath/COPYRIGHT
--- src.preview/sys/contrib/dev/ath/COPYRIGHT	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/COPYRIGHT	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,42 @@
+All files contained in this distribution are covered by the following
+copyright unless explicitly identified otherwise.  Note that this
+copyright does _NOT_ contain a "or GPL" clause and does _NOT_ permit
+redistribution with changes.
+
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: COPYRIGHT,v 1.3 2004/01/07 23:09:27 sam Exp $
+ */
diff -N -u -r src.preview/sys/contrib/dev/ath/README src/sys/contrib/dev/ath/README
--- src.preview/sys/contrib/dev/ath/README	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/README	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,64 @@
+$Id: README,v 1.3 2004/01/07 23:09:27 sam Exp $
+
+
+WARNING: THIS IS A BETA DISTRIBUTION.  THIS SOFTWARE HAS KNOWN PROBLEMS AND
+WARNING: LIMITATIONS THAT WILL BE CORRECTED BEFORE A PRODUCTION RELEASE.
+WARNING: USE AT YOUR OWN RISK!
+
+
+Atheros Hardware Access Layer (HAL)
+===================================
+
+* Copyright (c) 2002-2004 Sam Leffler.
+* Copyright (c) 2002-2004 Atheros Communications, Inc.
+* All rights reserved.
+
+Read the file COPYRIGHT for the complete copyright.
+
+This code manages much of the chip-specific operation of the Atheros driver.
+The HAL is provided in a binary-only form in order to comply with FCC
+regulations.  In particular, a radio transmitter can only be operated at
+power levels and on frequency channels for which it is approved.  The FCC
+requires that a software-defined radio cannot be configured by a user
+to operate outside the approved power levels and frequency channels.
+This makes it difficult to open-source code that enforces limits on
+the power levels, frequency channels and other parameters of the radio
+transmitter.  See
+
+http://ftp.fcc.gov/Bureaus/Engineering_Technology/Orders/2001/fcc01264.pdf
+
+for the specific FCC regulation.  Because the module is provided in a
+binary-only form it is marked "Proprietary"; this means when you load
+it you will see messages that your system is now "tainted".
+
+If you wish to use this driver on a platform for which an ath_hal
+module is not already provided please contact the author.  Note that
+this is only necessary for new _architectures_; the HAL is not tied to
+any specific version of your operating system.
+
+
+Atheros Hardware
+================
+There are currently 3 generations of Atheros 802.11 wireless devices:
+
+5210	supports 11a only
+5211	supports both 11a and 11b
+5212	supports 11a, 11b, and 11g
+
+These parts have been incorporated in a variety of retail products
+including cardbus cards from DLink, Linksys, Netgear, and Proxim; and
+mini-pci cards from some of these same vendors.  In addition many
+laptop vendors use Atheros mini-pci cards for their builtin wireless
+support.  An (incomplete) list of products that use Atheros parts is:
+
+Netgear WAG511		D-Link DWL-AG520	Linksys WPC55AG
+Netgear WAB501		D-Link DWL-AG650	Linksys WMP55AG
+			D-Link DWL-AB650	Linksys WPC51AB
+
+In general, if a device is identified as ``11a only'' it is almost
+certain to contain an Atheros 5210 part in it.  All retail a+b
+products use the 5211.  The latest generation of universal a+b+g
+combo products use the 5212.  When in doubt check the PCI vendor
+id with a tool like lspci, the Atheros vendor id is 0x168c; e.g.
+
+   00:13.0 Ethernet controller: Unknown device 168c:0012 (rev 01)
diff -N -u -r src.preview/sys/contrib/dev/ath/ah.h src/sys/contrib/dev/ath/ah.h
--- src.preview/sys/contrib/dev/ath/ah.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/ah.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,691 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah.h,v 1.1.1.6 2004/12/08 18:04:16 sam Exp $
+ */
+
+#ifndef _ATH_AH_H_
+#define _ATH_AH_H_
+/*
+ * Atheros Hardware Access Layer
+ *
+ * Clients of the HAL call ath_hal_attach to obtain a reference to an ath_hal
+ * structure for use with the device.  Hardware-related operations that
+ * follow must call back into the HAL through interface, supplying the
+ * reference as the first parameter.
+ */
+#include "ah_osdep.h"
+
+/*
+ * __ahdecl is analogous to _cdecl; it defines the calling
+ * convention used within the HAL.  For most systems this
+ * can just default to be empty and the compiler will (should)
+ * use _cdecl.  For systems where _cdecl is not compatible this
+ * must be defined.  See linux/ah_osdep.h for an example.
+ */
+#ifndef __ahdecl
+#define __ahdecl
+#endif
+
+/*
+ * Status codes that may be returned by the HAL.  Note that
+ * interfaces that return a status code set it only when an
+ * error occurs--i.e. you cannot check it for success.
+ */
+typedef enum {
+	HAL_OK		= 0,	/* No error */
+	HAL_ENXIO	= 1,	/* No hardware present */
+	HAL_ENOMEM	= 2,	/* Memory allocation failed */
+	HAL_EIO		= 3,	/* Hardware didn't respond as expected */
+	HAL_EEMAGIC	= 4,	/* EEPROM magic number invalid */
+	HAL_EEVERSION	= 5,	/* EEPROM version invalid */
+	HAL_EELOCKED	= 6,	/* EEPROM unreadable */
+	HAL_EEBADSUM	= 7,	/* EEPROM checksum invalid */
+	HAL_EEREAD	= 8,	/* EEPROM read problem */
+	HAL_EEBADMAC	= 9,	/* EEPROM mac address invalid */
+	HAL_EESIZE	= 10,	/* EEPROM size not supported */
+	HAL_EEWRITE	= 11,	/* Attempt to change write-locked EEPROM */
+	HAL_EINVAL	= 12,	/* Invalid parameter to function */
+	HAL_ENOTSUPP	= 13,	/* Hardware revision not supported */
+	HAL_ESELFTEST	= 14,	/* Hardware self-test failed */
+	HAL_EINPROGRESS	= 15,	/* Operation incomplete */
+} HAL_STATUS;
+
+typedef enum {
+	AH_FALSE = 0,		/* NB: lots of code assumes false is zero */
+	AH_TRUE  = 1,
+} HAL_BOOL;
+
+typedef enum {
+	HAL_CAP_REG_DMN		= 0,	/* current regulatory domain */
+	HAL_CAP_CIPHER		= 1,	/* hardware supports cipher */
+	HAL_CAP_TKIP_MIC	= 2,	/* handle TKIP MIC in hardware */
+	HAL_CAP_TKIP_SPLIT	= 3,	/* hardware TKIP uses split keys */
+	HAL_CAP_PHYCOUNTERS	= 4,	/* hardware PHY error counters */
+	HAL_CAP_DIVERSITY	= 5,	/* hardware supports fast diversity */
+	HAL_CAP_KEYCACHE_SIZE	= 6,	/* number of entries in key cache */
+	HAL_CAP_NUM_TXQUEUES	= 7,	/* number of hardware xmit queues */
+	HAL_CAP_VEOL		= 9,	/* hardware supports virtual EOL */
+	HAL_CAP_PSPOLL		= 10,	/* hardware has working PS-Poll support */
+	HAL_CAP_DIAG		= 11,	/* hardware diagnostic support */
+	HAL_CAP_COMPRESSION	= 12,	/* hardware supports compression */
+	HAL_CAP_BURST		= 13,	/* hardware supports packet bursting */
+	HAL_CAP_FASTFRAME	= 14,	/* hardware supoprts fast frames */
+	HAL_CAP_TXPOW		= 15,	/* global tx power limit  */
+	HAL_CAP_TPC		= 16,	/* per-packet tx power control  */
+} HAL_CAPABILITY_TYPE;
+
+/* 
+ * "States" for setting the LED.  These correspond to
+ * the possible 802.11 operational states and there may
+ * be a many-to-one mapping between these states and the
+ * actual hardware states for the LED's (i.e. the hardware
+ * may have fewer states).
+ */
+typedef enum {
+	HAL_LED_INIT	= 0,
+	HAL_LED_SCAN	= 1,
+	HAL_LED_AUTH	= 2,
+	HAL_LED_ASSOC	= 3,
+	HAL_LED_RUN	= 4
+} HAL_LED_STATE;
+
+/*
+ * Transmit queue types/numbers.  These are used to tag
+ * each transmit queue in the hardware and to identify a set
+ * of transmit queues for operations such as start/stop dma.
+ */
+typedef enum {
+	HAL_TX_QUEUE_INACTIVE	= 0,		/* queue is inactive/unused */
+	HAL_TX_QUEUE_DATA	= 1,		/* data xmit q's */
+	HAL_TX_QUEUE_BEACON	= 2,		/* beacon xmit q */
+	HAL_TX_QUEUE_CAB	= 3,		/* "crap after beacon" xmit q */
+	HAL_TX_QUEUE_PSPOLL	= 4,		/* power-save poll xmit q */
+} HAL_TX_QUEUE;
+
+#define	HAL_NUM_TX_QUEUES	10		/* max possible # of queues */
+
+/*
+ * Transmit queue subtype.  These map directly to
+ * WME Access Categories (except for UPSD).  Refer
+ * to Table 5 of the WME spec.
+ */
+typedef enum {
+	HAL_WME_AC_BK	= 0,			/* background access category */
+	HAL_WME_AC_BE	= 1, 			/* best effort access category*/
+	HAL_WME_AC_VI	= 2,			/* video access category */
+	HAL_WME_AC_VO	= 3,			/* voice access category */
+	HAL_WME_UPSD	= 4,			/* uplink power save */
+} HAL_TX_QUEUE_SUBTYPE;
+
+/*
+ * Transmit queue flags that control various
+ * operational parameters.
+ */
+typedef enum {
+	TXQ_FLAG_TXOKINT_ENABLE	    = 0x0001,    /* enable TXOK interrupt */
+	TXQ_FLAG_TXERRINT_ENABLE    = 0x0001,    /* enable TXERR interrupt */
+	TXQ_FLAG_TXDESCINT_ENABLE   = 0x0002,    /* enable TXDESC interrupt */
+	TXQ_FLAG_TXEOLINT_ENABLE    = 0x0004,    /* enable TXEOL interrupt */
+	TXQ_FLAG_TXURNINT_ENABLE    = 0x0008,    /* enable TXURN interrupt */
+	TXQ_FLAG_BACKOFF_DISABLE    = 0x0010,    /* disable Post Backoff  */
+	TXQ_FLAG_COMPRESSION_ENABLE = 0x0020,    /* compression enabled */
+	TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE = 0x0040, /* enable ready time
+							expiry policy */
+	TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE = 0x0080, /* enable backoff while
+							sending fragment burst*/
+} HAL_TX_QUEUE_FLAGS;
+
+typedef struct {
+	u_int32_t	tqi_ver;		/* hal TXQ version */
+	HAL_TX_QUEUE_SUBTYPE tqi_subtype;	/* subtype if applicable */
+	HAL_TX_QUEUE_FLAGS tqi_qflags;		/* flags (see above) */
+	u_int32_t	tqi_priority;		/* (not used) */
+	u_int32_t	tqi_aifs;		/* aifs */
+	u_int32_t	tqi_cwmin;		/* cwMin */
+	u_int32_t	tqi_cwmax;		/* cwMax */
+	u_int16_t	tqi_shretry;		/* rts retry limit */
+	u_int16_t	tqi_lgretry;		/* long retry limit (not used)*/
+	u_int32_t	tqi_cbrPeriod;
+	u_int32_t	tqi_cbrOverflowLimit;
+	u_int32_t	tqi_burstTime;
+	u_int32_t	tqi_readyTime;
+} HAL_TXQ_INFO;
+
+/* token to use for aifs, cwmin, cwmax */
+#define	HAL_TXQ_USEDEFAULT	((u_int32_t) -1)
+
+/*
+ * Transmit packet types.  This belongs in ah_desc.h, but
+ * is here so we can give a proper type to various parameters
+ * (and not require everyone include the file).
+ *
+ * NB: These values are intentionally assigned for
+ *     direct use when setting up h/w descriptors.
+ */
+typedef enum {
+	HAL_PKT_TYPE_NORMAL	= 0,
+	HAL_PKT_TYPE_ATIM	= 1,
+	HAL_PKT_TYPE_PSPOLL	= 2,
+	HAL_PKT_TYPE_BEACON	= 3,
+	HAL_PKT_TYPE_PROBE_RESP	= 4,
+} HAL_PKT_TYPE;
+
+/* Rx Filter Frame Types */
+typedef enum {
+	HAL_RX_FILTER_UCAST	= 0x00000001,	/* Allow unicast frames */
+	HAL_RX_FILTER_MCAST	= 0x00000002,	/* Allow multicast frames */
+	HAL_RX_FILTER_BCAST	= 0x00000004,	/* Allow broadcast frames */
+	HAL_RX_FILTER_CONTROL	= 0x00000008,	/* Allow control frames */
+	HAL_RX_FILTER_BEACON	= 0x00000010,	/* Allow beacon frames */
+	HAL_RX_FILTER_PROM	= 0x00000020,	/* Promiscuous mode */
+	HAL_RX_FILTER_PROBEREQ	= 0x00000080,	/* Allow probe request frames */
+	HAL_RX_FILTER_PHYERR	= 0x00000100,	/* Allow phy errors */
+	HAL_RX_FILTER_PHYRADAR	= 0x00000200,	/* Allow phy radar errors*/
+} HAL_RX_FILTER;
+
+typedef enum {
+	HAL_PM_UNDEFINED	= 0,
+	HAL_PM_AUTO		= 1,
+	HAL_PM_AWAKE		= 2,
+	HAL_PM_FULL_SLEEP	= 3,
+	HAL_PM_NETWORK_SLEEP	= 4
+} HAL_POWER_MODE;
+
+/*
+ * NOTE WELL:
+ * These are mapped to take advantage of the common locations for many of
+ * the bits on all of the currently supported MAC chips. This is to make
+ * the ISR as efficient as possible, while still abstracting HW differences.
+ * When new hardware breaks this commonality this enumerated type, as well
+ * as the HAL functions using it, must be modified. All values are directly
+ * mapped unless commented otherwise.
+ */
+typedef enum {
+	HAL_INT_RX	= 0x00000001,	/* Non-common mapping */
+	HAL_INT_RXDESC	= 0x00000002,
+	HAL_INT_RXNOFRM	= 0x00000008,
+	HAL_INT_RXEOL	= 0x00000010,
+	HAL_INT_RXORN	= 0x00000020,
+	HAL_INT_TX	= 0x00000040,	/* Non-common mapping */
+	HAL_INT_TXDESC	= 0x00000080,
+	HAL_INT_TXURN	= 0x00000800,
+	HAL_INT_MIB	= 0x00001000,
+	HAL_INT_RXPHY	= 0x00004000,
+	HAL_INT_RXKCM	= 0x00008000,
+	HAL_INT_SWBA	= 0x00010000,
+	HAL_INT_BMISS	= 0x00040000,
+	HAL_INT_BNR	= 0x00100000,	/* Non-common mapping */
+	HAL_INT_GPIO	= 0x01000000,
+	HAL_INT_FATAL	= 0x40000000,	/* Non-common mapping */
+	HAL_INT_GLOBAL	= 0x80000000,	/* Set/clear IER */
+
+	/* Interrupt bits that map directly to ISR/IMR bits */
+	HAL_INT_COMMON  = HAL_INT_RXNOFRM
+			| HAL_INT_RXDESC
+			| HAL_INT_RXEOL
+			| HAL_INT_RXORN
+			| HAL_INT_TXURN
+			| HAL_INT_TXDESC
+			| HAL_INT_MIB
+			| HAL_INT_RXPHY
+			| HAL_INT_RXKCM
+			| HAL_INT_SWBA
+			| HAL_INT_BMISS
+			| HAL_INT_GPIO,
+	HAL_INT_NOCARD	= 0xffffffff	/* To signal the card was removed */
+} HAL_INT;
+
+typedef enum {
+	HAL_RFGAIN_INACTIVE		= 0,
+	HAL_RFGAIN_READ_REQUESTED	= 1,
+	HAL_RFGAIN_NEED_CHANGE		= 2
+} HAL_RFGAIN;
+
+/*
+ * Channels are specified by frequency.
+ */
+typedef struct {
+	u_int16_t	channel;	/* setting in Mhz */
+	u_int16_t	channelFlags;	/* see below */
+} HAL_CHANNEL;
+
+#define	CHANNEL_RAD_INT	0x0001	/* Radar interference detected on channel */
+#define	CHANNEL_CW_INT	0x0002	/* CW interference detected on channel */
+#define	CHANNEL_BUSY	0x0004	/* Busy, occupied or overlap with adjoin chan */
+#define	CHANNEL_TURBO	0x0010	/* Turbo Channel */
+#define	CHANNEL_CCK	0x0020	/* CCK channel */
+#define	CHANNEL_OFDM	0x0040	/* OFDM channel */
+#define	CHANNEL_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	CHANNEL_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	CHANNEL_PASSIVE	0x0200	/* Only passive scan allowed in the channel */
+#define	CHANNEL_DYN	0x0400	/* dynamic CCK-OFDM channel */
+#define	CHANNEL_XR	0x0800	/* XR channel */
+#define CHANNEL_AR      0x8000  /* Software use: radar detected */ 
+
+
+#define	CHANNEL_A	(CHANNEL_5GHZ|CHANNEL_OFDM)
+#define	CHANNEL_B	(CHANNEL_2GHZ|CHANNEL_CCK)
+#define	CHANNEL_PUREG	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#ifdef notdef
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_DYN)
+#else
+#define	CHANNEL_G	(CHANNEL_2GHZ|CHANNEL_OFDM)
+#endif
+#define	CHANNEL_T	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_108G	(CHANNEL_2GHZ|CHANNEL_OFDM|CHANNEL_TURBO)
+#define	CHANNEL_X	(CHANNEL_5GHZ|CHANNEL_OFDM|CHANNEL_XR)
+#define	CHANNEL_ALL \
+	(CHANNEL_OFDM|CHANNEL_CCK|CHANNEL_5GHZ|CHANNEL_2GHZ|CHANNEL_TURBO)
+#define	CHANNEL_ALL_NOTURBO 	(CHANNEL_ALL &~ CHANNEL_TURBO)
+
+typedef struct {
+	u_int32_t	ackrcv_bad;
+	u_int32_t	rts_bad;
+	u_int32_t	rts_good;
+	u_int32_t	fcs_bad;
+	u_int32_t	beacons;
+} HAL_MIB_STATS;
+
+typedef u_int16_t HAL_CTRY_CODE;		/* country code */
+typedef u_int16_t HAL_REG_DOMAIN;		/* regulatory domain code */
+
+enum {
+	CTRY_DEBUG	= 0x1ff,		/* debug country code */
+	CTRY_DEFAULT	= 0			/* default country code */
+};
+
+enum {
+	HAL_MODE_11A	= 0x001,
+	HAL_MODE_TURBO	= 0x002,
+	HAL_MODE_11B	= 0x004,
+	HAL_MODE_PUREG	= 0x008,
+#ifdef notdef
+	HAL_MODE_11G	= 0x010,
+#else
+	HAL_MODE_11G	= 0x008,
+#endif
+	HAL_MODE_108G	= 0x020,
+	HAL_MODE_ALL	= 0xfff
+};
+
+typedef struct {
+	int		rateCount;		/* NB: for proper padding */
+	u_int8_t	rateCodeToIndex[32];	/* back mapping */
+	struct {
+		u_int8_t	valid;		/* valid for rate control use */
+		u_int8_t	phy;		/* CCK/OFDM/XR */
+		u_int16_t	rateKbps;	/* transfer rate in kbs */
+		u_int8_t	rateCode;	/* rate for h/w descriptors */
+		u_int8_t	shortPreamble;	/* mask for enabling short
+						 * preamble in CCK rate code */
+		u_int8_t	dot11Rate;	/* value for supported rates
+						 * info element of MLME */
+		u_int8_t	controlRate;	/* index of next lower basic
+						 * rate; used for dur. calcs */
+		u_int16_t	lpAckDuration;	/* long preamble ACK duration */
+		u_int16_t	spAckDuration;	/* short preamble ACK duration*/
+	} info[32];
+} HAL_RATE_TABLE;
+
+typedef struct {
+	u_int		rs_count;		/* number of valid entries */
+	u_int8_t	rs_rates[32];		/* rates */
+} HAL_RATE_SET;
+
+typedef enum {
+	HAL_ANT_VARIABLE = 0,			/* variable by programming */
+	HAL_ANT_FIXED_A	 = 1,			/* fixed to 11a frequencies */
+	HAL_ANT_FIXED_B	 = 2,			/* fixed to 11b frequencies */
+} HAL_ANT_SETTING;
+
+typedef enum {
+	HAL_M_STA	= 1,			/* infrastructure station */
+	HAL_M_IBSS	= 0,			/* IBSS (adhoc) station */
+	HAL_M_HOSTAP	= 6,			/* Software Access Point */
+	HAL_M_MONITOR	= 8			/* Monitor mode */
+} HAL_OPMODE;
+
+typedef struct {
+	u_int8_t	kv_type;		/* one of HAL_CIPHER */
+	u_int8_t	kv_pad;
+	u_int16_t	kv_len;			/* length in bits */
+	u_int8_t	kv_val[16];		/* enough for 128-bit keys */
+	u_int8_t	kv_mic[8];		/* TKIP MIC key */
+} HAL_KEYVAL;
+
+typedef enum {
+	HAL_CIPHER_WEP		= 0,
+	HAL_CIPHER_AES_OCB	= 1,
+	HAL_CIPHER_AES_CCM	= 2,
+	HAL_CIPHER_CKIP		= 3,
+	HAL_CIPHER_TKIP		= 4,
+	HAL_CIPHER_CLR		= 5,		/* no encryption */
+
+	HAL_CIPHER_MIC		= 127		/* TKIP-MIC, not a cipher */
+} HAL_CIPHER;
+
+enum {
+	HAL_SLOT_TIME_9	 = 9,
+	HAL_SLOT_TIME_20 = 20,
+};
+
+/*
+ * Per-station beacon timer state.  Note that the specified
+ * beacon interval (given in TU's) can also include flags
+ * to force a TSF reset and to enable the beacon xmit logic.
+ * If bs_cfpmaxduration is non-zero the hardware is setup to
+ * coexist with a PCF-capable AP.
+ */
+typedef struct {
+	u_int32_t	bs_nexttbtt;		/* next beacon in TU */
+	u_int32_t	bs_nextdtim;		/* next DTIM in TU */
+	u_int32_t	bs_intval;		/* beacon interval+flags */
+#define	HAL_BEACON_PERIOD	0x0000ffff	/* beacon interval period */
+#define	HAL_BEACON_ENA		0x00800000	/* beacon xmit enable */
+#define	HAL_BEACON_RESET_TSF	0x01000000	/* clear TSF */
+	u_int32_t	bs_dtimperiod;
+	u_int16_t	bs_cfpperiod;		/* CFP period in TU */
+	u_int16_t	bs_cfpmaxduration;	/* max CFP duration in TU */
+	u_int32_t	bs_cfpnext;		/* next CFP in TU */
+	u_int16_t	bs_timoffset;		/* byte offset to TIM bitmap */
+	u_int16_t	bs_bmissthreshold;	/* beacon miss threshold */
+	u_int32_t	bs_sleepduration;	/* max sleep duration */
+} HAL_BEACON_STATE;
+
+/*
+ * Per-node statistics maintained by the driver for use in
+ * optimizing signal quality and other operational aspects.
+ */
+typedef struct {
+	u_int32_t	ns_avgbrssi;	/* average beacon rssi */
+	u_int32_t	ns_avgrssi;	/* average data rssi */
+	u_int32_t	ns_avgtxrssi;	/* average tx rssi */
+} HAL_NODE_STATS;
+
+#define	HAL_RSSI_EP_MULTIPLIER	(1<<7)	/* pow2 to optimize out * and / */
+
+struct ath_desc;
+
+/*
+ * Hardware Access Layer (HAL) API.
+ *
+ * Clients of the HAL call ath_hal_attach to obtain a reference to an
+ * ath_hal structure for use with the device.  Hardware-related operations
+ * that follow must call back into the HAL through interface, supplying
+ * the reference as the first parameter.  Note that before using the
+ * reference returned by ath_hal_attach the caller should verify the
+ * ABI version number.
+ */
+struct ath_hal {
+	u_int32_t	ah_magic;	/* consistency check magic number */
+	u_int32_t	ah_abi;		/* HAL ABI version */
+#define	HAL_ABI_VERSION	0x04112900	/* YYMMDDnn */
+	u_int16_t	ah_devid;	/* PCI device ID */
+	u_int16_t	ah_subvendorid;	/* PCI subvendor ID */
+	HAL_SOFTC	ah_sc;		/* back pointer to driver/os state */
+	HAL_BUS_TAG	ah_st;		/* params for register r+w */
+	HAL_BUS_HANDLE	ah_sh;
+	HAL_CTRY_CODE	ah_countryCode;
+
+	u_int32_t	ah_macVersion;	/* MAC version id */
+	u_int16_t	ah_macRev;	/* MAC revision */
+	u_int16_t	ah_phyRev;	/* PHY revision */
+	/* NB: when only one radio is present the rev is in 5Ghz */
+	u_int16_t	ah_analog5GhzRev;/* 5GHz radio revision */
+	u_int16_t	ah_analog2GhzRev;/* 2GHz radio revision */
+
+	const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *,
+				u_int mode);
+	void	  __ahdecl(*ah_detach)(struct ath_hal*);
+
+	/* Reset functions */
+	HAL_BOOL  __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE,
+				HAL_CHANNEL *, HAL_BOOL bChannelChange,
+				HAL_STATUS *status);
+	HAL_BOOL  __ahdecl(*ah_phyDisable)(struct ath_hal *);
+	void	  __ahdecl(*ah_setPCUConfig)(struct ath_hal *);
+	HAL_BOOL  __ahdecl(*ah_perCalibration)(struct ath_hal*, HAL_CHANNEL *);
+	HAL_BOOL  __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, u_int32_t);
+
+	/* Transmit functions */
+	HAL_BOOL  __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*,
+				HAL_BOOL incTrigLevel);
+	int	  __ahdecl(*ah_setupTxQueue)(struct ath_hal *, HAL_TX_QUEUE,
+				const HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_setTxQueueProps)(struct ath_hal *, int q, 
+				const HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_getTxQueueProps)(struct ath_hal *, int q, 
+				HAL_TXQ_INFO *qInfo);
+	HAL_BOOL  __ahdecl(*ah_releaseTxQueue)(struct ath_hal *ah, u_int q);
+	HAL_BOOL  __ahdecl(*ah_resetTxQueue)(struct ath_hal *ah, u_int q);
+	u_int32_t __ahdecl(*ah_getTxDP)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_setTxDP)(struct ath_hal*, u_int, u_int32_t txdp);
+	u_int32_t __ahdecl(*ah_numTxPending)(struct ath_hal *, u_int q);
+	HAL_BOOL  __ahdecl(*ah_startTxDma)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_stopTxDma)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_updateCTSForBursting)(struct ath_hal *,
+				struct ath_desc *, struct ath_desc *,
+				struct ath_desc *, struct ath_desc *,
+				u_int32_t, u_int32_t);
+	HAL_BOOL  __ahdecl(*ah_setupTxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int pktLen, u_int hdrLen,
+				HAL_PKT_TYPE type, u_int txPower,
+				u_int txRate0, u_int txTries0,
+				u_int keyIx, u_int antMode, u_int flags,
+				u_int rtsctsRate, u_int rtsctsDuration);
+	HAL_BOOL  __ahdecl(*ah_setupXTxDesc)(struct ath_hal *, struct ath_desc*,
+				u_int txRate1, u_int txTries1,
+				u_int txRate2, u_int txTries2,
+				u_int txRate3, u_int txTries3);
+	HAL_BOOL  __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int segLen, HAL_BOOL firstSeg,
+				HAL_BOOL lastSeg, const struct ath_desc *);
+	HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc*);
+	void	   __ahdecl(*ah_getTxIntrQueue)(struct ath_hal *, u_int32_t *);
+
+	/* Receive Functions */
+	u_int32_t __ahdecl(*ah_getRxDP)(struct ath_hal*);
+	void	  __ahdecl(*ah_setRxDP)(struct ath_hal*, u_int32_t rxdp);
+	void	  __ahdecl(*ah_enableReceive)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_stopDmaReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_startPcuReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_stopPcuReceive)(struct ath_hal*);
+	void	  __ahdecl(*ah_setMulticastFilter)(struct ath_hal*,
+				u_int32_t filter0, u_int32_t filter1);
+	HAL_BOOL  __ahdecl(*ah_setMulticastFilterIndex)(struct ath_hal*,
+				u_int32_t ndx);
+	HAL_BOOL  __ahdecl(*ah_clrMulticastFilterIndex)(struct ath_hal*,
+				u_int32_t ndx);
+	u_int32_t __ahdecl(*ah_getRxFilter)(struct ath_hal*);
+	void	  __ahdecl(*ah_setRxFilter)(struct ath_hal*, u_int32_t);
+	HAL_BOOL  __ahdecl(*ah_setupRxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int32_t size, u_int flags);
+	HAL_STATUS __ahdecl(*ah_procRxDesc)(struct ath_hal *, struct ath_desc *,
+				u_int32_t phyAddr, struct ath_desc *next);
+	void	  __ahdecl(*ah_rxMonitor)(struct ath_hal *,
+				const HAL_NODE_STATS *);
+	void	  __ahdecl(*ah_procMibEvent)(struct ath_hal *,
+				const HAL_NODE_STATS *);
+
+	/* Misc Functions */
+	HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *,
+				HAL_CAPABILITY_TYPE, u_int32_t capability,
+				u_int32_t *result);
+	HAL_BOOL   __ahdecl(*ah_setCapability)(struct ath_hal *,
+				HAL_CAPABILITY_TYPE, u_int32_t capability,
+				u_int32_t setting, HAL_STATUS *);
+	HAL_BOOL   __ahdecl(*ah_getDiagState)(struct ath_hal *, int request,
+				const void *args, u_int32_t argsize,
+				void **result, u_int32_t *resultsize);
+	void	  __ahdecl(*ah_getMacAddress)(struct ath_hal *, u_int8_t *);
+	HAL_BOOL  __ahdecl(*ah_setMacAddress)(struct ath_hal *, const u_int8_t*);
+	HAL_BOOL  __ahdecl(*ah_setRegulatoryDomain)(struct ath_hal*,
+				u_int16_t, HAL_STATUS *);
+	void	  __ahdecl(*ah_setLedState)(struct ath_hal*, HAL_LED_STATE);
+	void	  __ahdecl(*ah_writeAssocid)(struct ath_hal*,
+				const u_int8_t *bssid, u_int16_t assocId);
+	HAL_BOOL  __ahdecl(*ah_gpioCfgOutput)(struct ath_hal *, u_int32_t gpio);
+	HAL_BOOL  __ahdecl(*ah_gpioCfgInput)(struct ath_hal *, u_int32_t gpio);
+	u_int32_t __ahdecl(*ah_gpioGet)(struct ath_hal *, u_int32_t gpio);
+	HAL_BOOL  __ahdecl(*ah_gpioSet)(struct ath_hal *,
+				u_int32_t gpio, u_int32_t val);
+	void	  __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, u_int32_t);
+	u_int32_t __ahdecl(*ah_getTsf32)(struct ath_hal*);
+	u_int64_t __ahdecl(*ah_getTsf64)(struct ath_hal*);
+	void	  __ahdecl(*ah_resetTsf)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_detectCardPresent)(struct ath_hal*);
+	void	  __ahdecl(*ah_updateMibCounters)(struct ath_hal*,
+				HAL_MIB_STATS*);
+	HAL_RFGAIN __ahdecl(*ah_getRfGain)(struct ath_hal*);
+	u_int	  __ahdecl(*ah_getDefAntenna)(struct ath_hal*);
+	void	  __ahdecl(*ah_setDefAntenna)(struct ath_hal*, u_int);
+	HAL_BOOL  __ahdecl(*ah_setSlotTime)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getSlotTime)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_setAckTimeout)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getAckTimeout)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_setCTSTimeout)(struct ath_hal*, u_int);
+	u_int	  __ahdecl(*ah_getCTSTimeout)(struct ath_hal*);
+
+	/* Key Cache Functions */
+	u_int32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_isKeyCacheEntryValid)(struct ath_hal *,
+				u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_setKeyCacheEntry)(struct ath_hal*,
+				u_int16_t, const HAL_KEYVAL *,
+				const u_int8_t *, int);
+	HAL_BOOL  __ahdecl(*ah_setKeyCacheEntryMac)(struct ath_hal*,
+				u_int16_t, const u_int8_t *);
+
+	/* Power Management Functions */
+	HAL_BOOL  __ahdecl(*ah_setPowerMode)(struct ath_hal*,
+				HAL_POWER_MODE mode, int setChip,
+				u_int16_t sleepDuration);
+	HAL_POWER_MODE __ahdecl(*ah_getPowerMode)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_initPSPoll)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_enablePSPoll)(struct ath_hal *,
+				u_int8_t *, u_int16_t);
+	HAL_BOOL  __ahdecl(*ah_disablePSPoll)(struct ath_hal *);
+
+	/* Beacon Management Functions */
+	void	  __ahdecl(*ah_beaconInit)(struct ath_hal *,
+				u_int32_t nexttbtt, u_int32_t intval);
+	void	  __ahdecl(*ah_setStationBeaconTimers)(struct ath_hal*,
+				const HAL_BEACON_STATE *);
+	void	  __ahdecl(*ah_resetStationBeaconTimers)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_waitForBeaconDone)(struct ath_hal *,
+				HAL_BUS_ADDR);
+
+	/* Interrupt functions */
+	HAL_BOOL  __ahdecl(*ah_isInterruptPending)(struct ath_hal*);
+	HAL_BOOL  __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*);
+	HAL_INT	  __ahdecl(*ah_getInterrupts)(struct ath_hal*);
+	HAL_INT	  __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT);
+};
+
+/* 
+ * Check the PCI vendor ID and device ID against Atheros' values
+ * and return a printable description for any Atheros hardware.
+ * AH_NULL is returned if the ID's do not describe Atheros hardware.
+ */
+extern	const char *__ahdecl ath_hal_probe(u_int16_t vendorid, u_int16_t devid);
+
+/*
+ * Attach the HAL for use with the specified device.  The device is
+ * defined by the PCI device ID.  The caller provides an opaque pointer
+ * to an upper-layer data structure (HAL_SOFTC) that is stored in the
+ * HAL state block for later use.  Hardware register accesses are done
+ * using the specified bus tag and handle.  On successful return a
+ * reference to a state block is returned that must be supplied in all
+ * subsequent HAL calls.  Storage associated with this reference is
+ * dynamically allocated and must be freed by calling the ah_detach
+ * method when the client is done.  If the attach operation fails a
+ * null (AH_NULL) reference will be returned and a status code will
+ * be returned if the status parameter is non-zero.
+ */
+extern	struct ath_hal * __ahdecl ath_hal_attach(u_int16_t devid, HAL_SOFTC,
+		HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS* status);
+
+/*
+ * Return a list of channels available for use with the hardware.
+ * The list is based on what the hardware is capable of, the specified
+ * country code, the modeSelect mask, and whether or not outdoor
+ * channels are to be permitted.
+ *
+ * The channel list is returned in the supplied array.  maxchans
+ * defines the maximum size of this array.  nchans contains the actual
+ * number of channels returned.  If a problem occurred or there were
+ * no channels that met the criteria then AH_FALSE is returned.
+ */
+extern	HAL_BOOL __ahdecl ath_hal_init_channels(struct ath_hal *,
+		HAL_CHANNEL *chans, u_int maxchans, u_int *nchans,
+		HAL_CTRY_CODE cc, u_int16_t modeSelect,
+		HAL_BOOL enableOutdoor, HAL_BOOL enableExtendedChannels);
+
+/*
+ * Return bit mask of wireless modes supported by the hardware.
+ */
+extern	u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*, HAL_CTRY_CODE);
+
+/*
+ * Return rate table for specified mode (11a, 11b, 11g, etc).
+ */
+extern	const HAL_RATE_TABLE * __ahdecl ath_hal_getratetable(struct ath_hal *,
+		u_int mode);
+
+/*
+ * Calculate the transmit duration of a frame.
+ */
+extern u_int16_t __ahdecl ath_hal_computetxtime(struct ath_hal *,
+		const HAL_RATE_TABLE *rates, u_int32_t frameLen,
+		u_int16_t rateix, HAL_BOOL shortPreamble);
+
+/*
+ * Convert between IEEE channel number and channel frequency
+ * using the specified channel flags; e.g. CHANNEL_2GHZ.
+ */
+extern	u_int __ahdecl ath_hal_mhz2ieee(u_int mhz, u_int flags);
+extern	u_int __ahdecl ath_hal_ieee2mhz(u_int ieee, u_int flags);
+
+/*
+ * Return a version string for the HAL release.
+ */
+extern	char ath_hal_version[];
+/*
+ * Return a NULL-terminated array of build/configuration options.
+ */
+extern	const char* ath_hal_buildopts[];
+#endif /* _ATH_AH_H_ */
diff -N -u -r src.preview/sys/contrib/dev/ath/ah_desc.h src/sys/contrib/dev/ath/ah_desc.h
--- src.preview/sys/contrib/dev/ath/ah_desc.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/ah_desc.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,180 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_desc.h,v 1.1.1.2 2004/12/08 18:04:16 sam Exp $
+ */
+
+#ifndef _DEV_ATH_DESC_H
+#define _DEV_ATH_DESC_H
+
+/*
+ * Transmit descriptor status.  This structure is filled
+ * in only after the tx descriptor process method finds a
+ * ``done'' descriptor; at which point it returns something
+ * other than HAL_EINPROGRESS.
+ *
+ * Note that ts_antenna may not be valid for all h/w.  It
+ * should be used only if non-zero.
+ */
+struct ath_tx_status {
+	u_int16_t	ts_seqnum;	/* h/w assigned sequence number */
+	u_int16_t	ts_tstamp;	/* h/w assigned timestamp */
+	u_int8_t	ts_status;	/* frame status, 0 => xmit ok */
+	u_int8_t	ts_rate;	/* h/w transmit rate index */
+#define	HAL_TXSTAT_ALTRATE	0x80	/* alternate xmit rate used */
+	int8_t		ts_rssi;	/* tx ack RSSI */
+	u_int8_t	ts_shortretry;	/* # short retries */
+	u_int8_t	ts_longretry;	/* # long retries */
+	u_int8_t	ts_virtcol;	/* virtual collision count */
+	u_int8_t	ts_antenna;	/* antenna information */
+};
+
+#define	HAL_TXERR_XRETRY	0x01	/* excessive retries */
+#define	HAL_TXERR_FILT		0x02	/* blocked by tx filtering */
+#define	HAL_TXERR_FIFO		0x04	/* fifo underrun */
+
+/*
+ * Receive descriptor status.  This structure is filled
+ * in only after the rx descriptor process method finds a
+ * ``done'' descriptor; at which point it returns something
+ * other than HAL_EINPROGRESS.
+ *
+ * If rx_status is zero, then the frame was received ok;
+ * otherwise the error information is indicated and rs_phyerr
+ * contains a phy error code if HAL_RXERR_PHY is set.  In general
+ * the frame contents is undefined when an error occurred thought
+ * for some errors (e.g. a decryption error), it may be meaningful.
+ *
+ * Note that the receive timestamp is expanded using the TSF to
+ * a full 16 bits (regardless of what the h/w provides directly).
+ *
+ * rx_rssi is in units of dbm above the noise floor.  This value
+ * is measured during the preamble and PLCP; i.e. with the initial
+ * 4us of detection.  The noise floor is typically a consistent
+ * -96dBm absolute power in a 20MHz channel.
+ */
+struct ath_rx_status {
+	u_int16_t	rs_datalen;	/* rx frame length */
+	u_int16_t	rs_tstamp;	/* h/w assigned timestamp */
+	u_int8_t	rs_status;	/* rx status, 0 => recv ok */
+	u_int8_t	rs_phyerr;	/* phy error code */
+	int8_t		rs_rssi;	/* rx frame RSSI */
+	u_int8_t	rs_keyix;	/* key cache index */
+	u_int8_t	rs_rate;	/* h/w receive rate index */
+	u_int8_t	rs_antenna;	/* antenna information */
+	u_int8_t	rs_more;	/* more descriptors follow */
+};
+
+#define	HAL_RXERR_CRC		0x01	/* CRC error on frame */
+#define	HAL_RXERR_PHY		0x02	/* PHY error, rs_phyerr is valid */
+#define	HAL_RXERR_FIFO		0x04	/* fifo overrun */
+#define	HAL_RXERR_DECRYPT	0x08	/* non-Michael decrypt error */
+#define	HAL_RXERR_MIC		0x10	/* Michael MIC decrypt error */
+
+enum {
+	HAL_PHYERR_UNDERRUN		= 0,	/* Transmit underrun */
+	HAL_PHYERR_TIMING		= 1,	/* Timing error */
+	HAL_PHYERR_PARITY		= 2,	/* Illegal parity */
+	HAL_PHYERR_RATE			= 3,	/* Illegal rate */
+	HAL_PHYERR_LENGTH		= 4,	/* Illegal length */
+	HAL_PHYERR_RADAR		= 5,	/* Radar detect */
+	HAL_PHYERR_SERVICE		= 6,	/* Illegal service */
+	HAL_PHYERR_TOR			= 7,	/* Transmit override receive */
+	/* NB: these are specific to the 5212 */
+	HAL_PHYERR_OFDM_TIMING		= 17,	/* */
+	HAL_PHYERR_OFDM_SIGNAL_PARITY	= 18,	/* */
+	HAL_PHYERR_OFDM_RATE_ILLEGAL	= 19,	/* */
+	HAL_PHYERR_OFDM_LENGTH_ILLEGAL	= 20,	/* */
+	HAL_PHYERR_OFDM_POWER_DROP	= 21,	/* */
+	HAL_PHYERR_OFDM_SERVICE		= 22,	/* */
+	HAL_PHYERR_OFDM_RESTART		= 23,	/* */
+	HAL_PHYERR_CCK_TIMING		= 25,	/* */
+	HAL_PHYERR_CCK_HEADER_CRC	= 26,	/* */
+	HAL_PHYERR_CCK_RATE_ILLEGAL	= 27,	/* */
+	HAL_PHYERR_CCK_SERVICE		= 30,	/* */
+	HAL_PHYERR_CCK_RESTART		= 31,	/* */
+};
+
+/* value found in rs_keyix to mark invalid entries */
+#define	HAL_RXKEYIX_INVALID	((u_int8_t) -1)
+/* value used to specify no encryption key for xmit */
+#define	HAL_TXKEYIX_INVALID	((u_int) -1)
+
+/* XXX rs_antenna definitions */
+
+/*
+ * Definitions for the software frame/packet descriptors used by
+ * the Atheros HAL.  This definition obscures hardware-specific
+ * details from the driver.  Drivers are expected to fillin the
+ * portions of a descriptor that are not opaque then use HAL calls
+ * to complete the work.  Status for completed frames is returned
+ * in a device-independent format.
+ */
+struct ath_desc {
+	/*
+	 * The following definitions are passed directly
+	 * the hardware and managed by the HAL.  Drivers
+	 * should not touch those elements marked opaque.
+	 */
+	u_int32_t	ds_link;	/* phys address of next descriptor */
+	u_int32_t	ds_data;	/* phys address of data buffer */
+	u_int32_t	ds_ctl0;	/* opaque DMA control 0 */
+	u_int32_t	ds_ctl1;	/* opaque DMA control 1 */
+	u_int32_t	ds_hw[4];	/* opaque h/w region */
+	/*
+	 * The remaining definitions are managed by software;
+	 * these are valid only after the rx/tx process descriptor
+	 * methods return a non-EINPROGRESS  code.
+	 */
+	union {
+		struct ath_tx_status tx;/* xmit status */
+		struct ath_rx_status rx;/* recv status */
+	} ds_us;
+} __packed;
+
+#define	ds_txstat	ds_us.tx
+#define	ds_rxstat	ds_us.rx
+
+/* flags passed to tx descriptor setup methods */
+#define	HAL_TXDESC_CLRDMASK	0x0001	/* clear destination filter mask */
+#define	HAL_TXDESC_NOACK	0x0002	/* don't wait for ACK */
+#define	HAL_TXDESC_RTSENA	0x0004	/* enable RTS */
+#define	HAL_TXDESC_CTSENA	0x0008	/* enable CTS */
+#define	HAL_TXDESC_INTREQ	0x0010	/* enable per-descriptor interrupt */
+#define	HAL_TXDESC_VEOL		0x0020	/* mark virtual EOL */
+
+/* flags passed to rx descriptor setup methods */
+#define	HAL_RXDESC_INTREQ	0x0020	/* enable per-descriptor interrupt */
+#endif /* _DEV_ATH_AR521XDMA_H */
diff -N -u -r src.preview/sys/contrib/dev/ath/ah_devid.h src/sys/contrib/dev/ath/ah_devid.h
--- src.preview/sys/contrib/dev/ath/ah_devid.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/ah_devid.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_devid.h,v 1.1.1.3 2004/12/08 18:04:16 sam Exp $
+ */
+
+#ifndef _DEV_ATH_DEVID_H_
+#define _DEV_ATH_DEVID_H_
+
+#define ATHEROS_VENDOR_ID	0x168c		/* Atheros PCI vendor ID */
+/*
+ * NB: all Atheros-based devices should have a PCI vendor ID
+ *     of 0x168c, but some vendors, in their infinite wisdom
+ *     do not follow this so we must handle them specially.
+ */
+#define	ATHEROS_3COM_VENDOR_ID	0xa727		/* 3Com 3CRPAG175 vendor ID */
+#define	ATHEROS_3COM2_VENDOR_ID	0x10b7		/* 3Com 3CRDAG675 vendor ID */
+
+/* AR5210 (for reference) */
+#define AR5210_DEFAULT          0x1107          /* No eeprom HW default */
+#define AR5210_PROD             0x0007          /* Final device ID */
+#define AR5210_AP               0x0207          /* Early AP11s */
+
+/* AR5211 */
+#define AR5211_DEFAULT          0x1112          /* No eeprom HW default */
+#define AR5311_DEVID            0x0011          /* Final ar5311 devid */
+#define AR5211_DEVID            0x0012          /* Final ar5211 devid */
+#define AR5211_LEGACY           0xff12          /* Original emulation board */
+#define AR5211_FPGA11B          0xf11b          /* 11b emulation board */
+
+/* AR5212 */
+#define AR5212_DEFAULT          0x1113          /* No eeprom HW default */
+#define AR5212_DEVID            0x0013          /* Final ar5212 devid */
+#define AR5212_FPGA             0xf013          /* Emulation board */
+#define	AR5212_DEVID_IBM	0x1014          /* IBM minipci ID */
+#define AR5212_AR5312_REV2      0x0052          /* AR5312 WMAC (AP31) */
+#define AR5212_AR5312_REV7      0x0057          /* AR5312 WMAC (AP30-040) */
+#define AR5212_AR2313_REV8      0x0058          /* AR2313 WMAC (AP43-030) */
+
+/* AR5212 compatible devid's also attach to 5212 */
+#define	AR5212_DEVID_0014	0x0014
+#define	AR5212_DEVID_0015	0x0015
+#define	AR5212_DEVID_0016	0x0016
+#define	AR5212_DEVID_0017	0x0017
+#define	AR5212_DEVID_0018	0x0018
+#define	AR5212_DEVID_0019	0x0019
+#define AR5212_AR2413      	0x001a          /* AR2413 aka Griffin-lite */
+
+/* AR5213 */
+#define	AR5213_SREV_1_0		0x0055
+#define	AR5213_SREV_REG		0x4020
+
+#define	AR_SUBVENDOR_ID_NOG	0x0e11		/* No 11G subvendor ID */
+#define AR_SUBVENDOR_ID_NEW_A	0x7065		/* Update device to new RD */
+#endif /* _DEV_ATH_DEVID_H */
diff -N -u -r src.preview/sys/contrib/dev/ath/freebsd/ah_if.m src/sys/contrib/dev/ath/freebsd/ah_if.m
--- src.preview/sys/contrib/dev/ath/freebsd/ah_if.m	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/freebsd/ah_if.m	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,90 @@
+#
+# Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+# Communications, Inc.  All rights reserved.
+#
+# Redistribution and use in source and binary forms are permitted
+# provided that the following conditions are met:
+# 1. The materials contained herein are unmodified and are used
+#    unmodified.
+# 2. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following NO
+#    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+#    modification.
+# 3. Redistributions in binary form must reproduce at minimum a
+#    disclaimer similar to the Disclaimer below and any redistribution
+#    must be conditioned upon including a substantially similar
+#    Disclaimer requirement for further binary redistribution.
+# 4. Neither the names of the above-listed copyright holders nor the
+#    names of any contributors may be used to endorse or promote
+#    product derived from this software without specific prior written
+#    permission.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+# FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGES.
+#
+# $Id: ah_if.m,v 1.1.1.2 2004/12/08 18:04:16 sam Exp $
+#
+
+
+METHOD	const char* ath_hal_probe {
+	u_int16_t	vendorID;
+	u_int16_	deviceID;
+};
+
+METHOD	struct ath_hal* ath_hal_attach {
+	u_int16_t	deviceID;
+	HAL_SOFTC	sc;
+	HAL_BUS_TAG	st;
+	HAL_BUS_HANDLE	sh;
+	HAL_STATUS*	error;
+};
+
+METHOD u_int ath_hal_init_channels {
+	struct ath_hal*	ah;
+	HAL_CHANNEL*	chans;
+	u_int		maxchans;
+	u_int*		nchans;
+	HAL_CTRY_CODE	cc;
+	u_int16_t	modeSelect;
+	int		enableOutdoor;
+};
+
+METHOD u_int ath_hal_getwirelessmodes {
+	struct ath_hal*	ah;
+	HAL_CTRY_CODE	cc;
+};
+
+METHOD const HAL_RATE_TABLE* ath_hal_getratetable {
+	struct ath_hal*	ah;
+	u_int		mode;
+};
+
+METHOD u_int16_t ath_hal_computetxtime {
+	struct ath_hal*	ah;
+	const HAL_RATE_TABLE* rates;
+	u_int32_t	frameLength;
+	u_int16_t	rateIndex;
+	HAL_BOOL	shortPreamble;
+};
+
+METHOD u_int ath_hal_mhz2ieee {
+	u_int		mhz;
+	u_int		flags;
+};
+
+METHOD u_int ath_hal_ieee2mhz {
+	u_int		ieee;
+	u_int		flags;
+};
diff -N -u -r src.preview/sys/contrib/dev/ath/freebsd/ah_osdep.c src/sys/contrib/dev/ath/freebsd/ah_osdep.c
--- src.preview/sys/contrib/dev/ath/freebsd/ah_osdep.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/freebsd/ah_osdep.c	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,419 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * DragonFly Port - Copyright (c) 2005 Andrew Atrens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_osdep.c,v 1.3 2004/12/08 18:18:39 sam Exp $
+ */
+#include "opt_ah.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/stdarg.h>
+
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <contrib/dev/ath/ah.h>
+
+extern	void ath_hal_printf(struct ath_hal *, const char*, ...)
+		__printflike(2,3);
+extern	void ath_hal_vprintf(struct ath_hal *, const char*, __va_list)
+		__printflike(2, 0);
+extern	const char* ath_hal_ether_sprintf(const u_int8_t *mac);
+extern	void *ath_hal_malloc(size_t);
+extern	void ath_hal_free(void *);
+#ifdef AH_ASSERT
+extern	void ath_hal_assert_failed(const char* filename,
+		int lineno, const char* msg);
+#endif
+#ifdef AH_DEBUG
+extern	void HALDEBUG(struct ath_hal *ah, const char* fmt, ...);
+extern	void HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...);
+#endif /* AH_DEBUG */
+
+/* NB: put this here instead of the driver to avoid circular references */
+SYSCTL_NODE(_hw, OID_AUTO, ath, CTLFLAG_RD, 0, "Atheros driver parameters");
+SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD, 0, "Atheros HAL parameters");
+
+#ifdef AH_DEBUG
+static	int ath_hal_debug = 0;
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RW, &ath_hal_debug,
+	    0, "Atheros HAL debugging printfs");
+TUNABLE_INT("hw.ath.hal.debug", &ath_hal_debug);
+#endif /* AH_DEBUG */
+
+SYSCTL_STRING(_hw_ath_hal, OID_AUTO, version, CTLFLAG_RD, ath_hal_version, 0,
+	"Atheros HAL version");
+
+int	ath_hal_dma_beacon_response_time = 2;	/* in TU's */
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, dma_brt, CTLFLAG_RW,
+	   &ath_hal_dma_beacon_response_time, 0,
+	   "Atheros HAL DMA beacon response time");
+int	ath_hal_sw_beacon_response_time = 10;	/* in TU's */
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, sw_brt, CTLFLAG_RW,
+	   &ath_hal_sw_beacon_response_time, 0,
+	   "Atheros HAL software beacon response time");
+int	ath_hal_additional_swba_backoff = 0;	/* in TU's */
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, swba_backoff, CTLFLAG_RW,
+	   &ath_hal_additional_swba_backoff, 0,
+	   "Atheros HAL additional SWBA backoff time");
+
+void*
+ath_hal_malloc(size_t size)
+{
+	return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+}
+
+void
+ath_hal_free(void* p)
+{
+	return free(p, M_DEVBUF);
+}
+
+void
+ath_hal_vprintf(struct ath_hal *ah, const char* fmt, __va_list ap)
+{
+	vprintf(fmt, ap);
+}
+
+void
+ath_hal_printf(struct ath_hal *ah, const char* fmt, ...)
+{
+	__va_list ap;
+	__va_start(ap, fmt);
+	ath_hal_vprintf(ah, fmt, ap);
+	__va_end(ap);
+}
+
+const char*
+ath_hal_ether_sprintf(const u_int8_t *mac)
+{
+	static char buffer[256];	
+	sprintf(buffer,"%6D",mac,":");
+	return &buffer[0];
+}
+
+#ifdef AH_DEBUG
+void
+HALDEBUG(struct ath_hal *ah, const char* fmt, ...)
+{
+	if (ath_hal_debug) {
+		__va_list ap;
+		__va_start(ap, fmt);
+		ath_hal_vprintf(ah, fmt, ap);
+		__va_end(ap);
+	}
+}
+
+void
+HALDEBUGn(struct ath_hal *ah, u_int level, const char* fmt, ...)
+{
+	if (ath_hal_debug >= level) {
+		__va_list ap;
+		__va_start(ap, fmt);
+		ath_hal_vprintf(ah, fmt, ap);
+		__va_end(ap);
+	}
+}
+#endif /* AH_DEBUG */
+
+#ifdef AH_DEBUG_ALQ
+/*
+ * ALQ register tracing support.
+ *
+ * Setting hw.ath.hal.alq=1 enables tracing of all register reads and
+ * writes to the file /tmp/ath_hal.log.  The file format is a simple
+ * fixed-size array of records.  When done logging set hw.ath.hal.alq=0
+ * and then decode the file with the arcode program (that is part of the
+ * HAL).  If you start+stop tracing the data will be appended to an
+ * existing file.
+ *
+ * NB: doesn't handle multiple devices properly; only one DEVICE record
+ *     is emitted and the different devices are not identified.
+ */
+#include <sys/alq.h>
+#include <sys/pcpu.h>
+#include <contrib/dev/ath/ah_decode.h>
+
+static	struct alq *ath_hal_alq;
+static	int ath_hal_alq_emitdev;	/* need to emit DEVICE record */
+static	u_int ath_hal_alq_lost;		/* count of lost records */
+static	const char *ath_hal_logfile = "/tmp/ath_hal.log";
+static	u_int ath_hal_alq_qsize = 64*1024;
+
+static int
+ath_hal_setlogging(int enable)
+{
+	int error;
+
+	if (enable) {
+		error = suser(curthread);
+		if (error == 0) {
+			error = alq_open(&ath_hal_alq, ath_hal_logfile,
+				curthread->td_ucred,
+				sizeof (struct athregrec), ath_hal_alq_qsize);
+			ath_hal_alq_lost = 0;
+			ath_hal_alq_emitdev = 1;
+			printf("ath_hal: logging to %s enabled\n",
+				ath_hal_logfile);
+		}
+	} else {
+		if (ath_hal_alq)
+			alq_close(ath_hal_alq);
+		ath_hal_alq = NULL;
+		printf("ath_hal: logging disabled\n");
+		error = 0;
+	}
+	return (error);
+}
+
+static int
+sysctl_hw_ath_hal_log(SYSCTL_HANDLER_ARGS)
+{
+	int error, enable;
+
+	enable = (ath_hal_alq != NULL);
+        error = sysctl_handle_int(oidp, &enable, 0, req);
+        if (error || !req->newptr)
+                return (error);
+	else
+		return (ath_hal_setlogging(enable));
+}
+SYSCTL_PROC(_hw_ath_hal, OID_AUTO, alq, CTLTYPE_INT|CTLFLAG_RW,
+	0, 0, sysctl_hw_ath_hal_log, "I", "Enable HAL register logging");
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_size, CTLFLAG_RW,
+	&ath_hal_alq_qsize, 0, "In-memory log size (#records)");
+SYSCTL_INT(_hw_ath_hal, OID_AUTO, alq_lost, CTLFLAG_RW,
+	&ath_hal_alq_lost, 0, "Register operations not logged");
+
+static struct ale *
+ath_hal_alq_get(struct ath_hal *ah)
+{
+	struct ale *ale;
+
+	if (ath_hal_alq_emitdev) {
+		ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
+		if (ale) {
+			struct athregrec *r =
+				(struct athregrec *) ale->ae_data;
+			r->op = OP_DEVICE;
+			r->reg = 0;
+			r->val = ah->ah_devid;
+			alq_post(ath_hal_alq, ale);
+			ath_hal_alq_emitdev = 0;
+		} else
+			ath_hal_alq_lost++;
+	}
+	ale = alq_get(ath_hal_alq, ALQ_NOWAIT);
+	if (!ale)
+		ath_hal_alq_lost++;
+	return ale;
+}
+
+void
+ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
+{
+	if (ath_hal_alq) {
+		struct ale *ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_WRITE;
+			r->reg = reg;
+			r->val = val;
+			alq_post(ath_hal_alq, ale);
+		}
+	}
+#if _BYTE_ORDER == _BIG_ENDIAN
+	if (reg >= 0x4000 && reg < 0x5000)
+		bus_space_write_4(ah->ah_st, ah->ah_sh, reg, htole32(val));
+	else
+#endif
+		bus_space_write_4(ah->ah_st, ah->ah_sh, reg, val);
+}
+
+u_int32_t
+ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
+{
+	u_int32_t val;
+
+	val = bus_space_read_4(ah->ah_st, ah->ah_sh, reg);
+#if _BYTE_ORDER == _BIG_ENDIAN
+	if (reg >= 0x4000 && reg < 0x5000)
+		val = le32toh(val);
+#endif
+	if (ath_hal_alq) {
+		struct ale *ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_READ;
+			r->reg = reg;
+			r->val = val;
+			alq_post(ath_hal_alq, ale);
+		}
+	}
+	return val;
+}
+
+void
+OS_MARK(struct ath_hal *ah, u_int id, u_int32_t v)
+{
+	if (ath_hal_alq) {
+		struct ale *ale = ath_hal_alq_get(ah);
+		if (ale) {
+			struct athregrec *r = (struct athregrec *) ale->ae_data;
+			r->op = OP_MARK;
+			r->reg = id;
+			r->val = v;
+			alq_post(ath_hal_alq, ale);
+		}
+	}
+}
+#elif defined(AH_DEBUG) || defined(AH_REGOPS_FUNC)
+/*
+ * Memory-mapped device register read/write.  These are here
+ * as routines when debugging support is enabled and/or when
+ * explicitly configured to use function calls.  The latter is
+ * for architectures that might need to do something before
+ * referencing memory (e.g. remap an i/o window).
+ *
+ * NB: see the comments in ah_osdep.h about byte-swapping register
+ *     reads and writes to understand what's going on below.
+ */
+
+void
+ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val)
+{
+#if _BYTE_ORDER == _BIG_ENDIAN
+	if (reg >= 0x4000 && reg < 0x5000)
+		bus_space_write_4(ah->ah_st, ah->ah_sh, reg, htole32(val));
+	else
+#endif
+		bus_space_write_4(ah->ah_st, ah->ah_sh, reg, val);
+}
+
+u_int32_t
+ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg)
+{
+	u_int32_t val;
+
+	val = bus_space_read_4(ah->ah_st, ah->ah_sh, reg);
+#if _BYTE_ORDER == _BIG_ENDIAN
+	if (reg >= 0x4000 && reg < 0x5000)
+		val = le32toh(val);
+#endif
+	return val;
+}
+#endif /* AH_DEBUG || AH_REGOPS_FUNC */
+
+#ifdef AH_ASSERT
+void
+ath_hal_assert_failed(const char* filename, int lineno, const char *msg)
+{
+	printf("Atheros HAL assertion failure: %s: line %u: %s\n",
+		filename, lineno, msg);
+	panic("ath_hal_assert");
+}
+#endif /* AH_ASSERT */
+
+/*
+ * Delay n microseconds.
+ */
+void
+ath_hal_delay(int n)
+{
+	DELAY(n);
+}
+
+u_int32_t
+ath_hal_getuptime(struct ath_hal *ah)
+{
+	struct timeval bt;
+	getmicrouptime(&bt);
+	return (bt.tv_sec * 1000) + (bt.tv_usec / 1000);
+}
+
+void
+ath_hal_memzero(void *dst, size_t n)
+{
+	bzero(dst, n);
+}
+
+void *
+ath_hal_memcpy(void *dst, const void *src, size_t n)
+{
+	return memcpy(dst, src, n);
+}
+
+/*
+ * Module glue.
+ */
+
+static int
+ath_hal_modevent(module_t mod, int type, void *unused)
+{
+	const char *sep;
+	int i;
+
+	switch (type) {
+	case MOD_LOAD:
+		printf("ath_hal: %s (", ath_hal_version);
+		sep = "";
+		for (i = 0; ath_hal_buildopts[i] != NULL; i++) {
+			printf("%s%s", sep, ath_hal_buildopts[i]);
+			sep = ", ";
+		}
+		printf(")\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t ath_hal_mod = {
+	"ath_hal",
+	ath_hal_modevent,
+	0
+};
+DECLARE_MODULE(ath_hal, ath_hal_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
+MODULE_VERSION(ath_hal, 1);
diff -N -u -r src.preview/sys/contrib/dev/ath/freebsd/ah_osdep.h src/sys/contrib/dev/ath/freebsd/ah_osdep.h
--- src.preview/sys/contrib/dev/ath/freebsd/ah_osdep.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/freebsd/ah_osdep.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ * DragonFly Port - Copyright (c) 2005 Andrew Atrens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: ah_osdep.h,v 1.1.1.4 2004/12/08 18:04:17 sam Exp $
+ */
+#ifndef _ATH_AH_OSDEP_H_
+#define _ATH_AH_OSDEP_H_
+/*
+ * Atheros Hardware Access Layer (HAL) OS Dependent Definitions.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+
+#include <machine/bus.h>
+
+typedef void* HAL_SOFTC;
+typedef bus_space_tag_t HAL_BUS_TAG;
+typedef bus_space_handle_t HAL_BUS_HANDLE;
+typedef bus_addr_t HAL_BUS_ADDR;
+
+/*
+ * Delay n microseconds.
+ */
+extern	void ath_hal_delay(int);
+#define	OS_DELAY(_n)	ath_hal_delay(_n)
+
+#define	OS_INLINE	__inline
+#define	OS_MEMZERO(_a, _n)	ath_hal_memzero((_a), (_n))
+extern void ath_hal_memzero(void *, size_t);
+#define	OS_MEMCPY(_d, _s, _n)	ath_hal_memcpy(_d,_s,_n)
+extern void *ath_hal_memcpy(void *, const void *, size_t);
+
+#define	abs(_a)		__builtin_abs(_a)
+
+struct ath_hal;
+extern	u_int32_t ath_hal_getuptime(struct ath_hal *);
+#define	OS_GETUPTIME(_ah)	ath_hal_getuptime(_ah)
+
+/*
+ * Register read/write; we assume the registers will always
+ * be memory-mapped.  Note that register accesses are done
+ * using target-specific functions when debugging is enabled
+ * (AH_DEBUG) or we are explicitly configured this way.  The
+ * latter is used on some platforms where the full i/o space
+ * cannot be directly mapped.
+ */
+#if defined(AH_DEBUG) || defined(AH_REGOPS_FUNC) || defined(AH_DEBUG_ALQ)
+#define	OS_REG_WRITE(_ah, _reg, _val)	ath_hal_reg_write(_ah, _reg, _val)
+#define	OS_REG_READ(_ah, _reg)		ath_hal_reg_read(_ah, _reg)
+
+extern	void ath_hal_reg_write(struct ath_hal *ah, u_int reg, u_int32_t val);
+extern	u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int reg);
+#else
+/*
+ * The hardware registers are native little-endian byte order.
+ * Big-endian hosts are handled by enabling hardware byte-swap
+ * of register reads and writes at reset.  But the PCI clock
+ * domain registers are not byte swapped!  Thus, on big-endian
+ * platforms we have to byte-swap thoese registers specifically.
+ * Most of this code is collapsed at compile time because the
+ * register values are constants.
+ */
+#define	AH_LITTLE_ENDIAN	1234
+#define	AH_BIG_ENDIAN		4321
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define OS_REG_WRITE(_ah, _reg, _val) do {				\
+	if ( (_reg) >= 0x4000 && (_reg) < 0x5000)			\
+		bus_space_write_4((_ah)->ah_st, (_ah)->ah_sh,		\
+			(_reg), htole32(_val));			\
+	else								\
+		bus_space_write_4((_ah)->ah_st, (_ah)->ah_sh,		\
+			(_reg), (_val));				\
+} while (0)
+#define OS_REG_READ(_ah, _reg)						\
+	(((_reg) >= 0x4000 && (_reg) < 0x5000) ?			\
+		le32toh(bus_space_read_4((_ah)->ah_st, (_ah)->ah_sh,	\
+			(_reg))) :					\
+		bus_space_read_4((_ah)->ah_st, (_ah)->ah_sh, (_reg)))
+#else /* _BYTE_ORDER == _LITTLE_ENDIAN */
+#define	OS_REG_WRITE(_ah, _reg, _val)					\
+	bus_space_write_4((_ah)->ah_st, (_ah)->ah_sh, (_reg), (_val))
+#define	OS_REG_READ(_ah, _reg)						\
+	((u_int32_t) bus_space_read_4((_ah)->ah_st, (_ah)->ah_sh, (_reg)))
+#endif /* _BYTE_ORDER */
+#endif /* AH_DEBUG || AH_REGFUNC || AH_DEBUG_ALQ */
+
+#ifdef AH_DEBUG_ALQ
+extern	void OS_MARK(struct ath_hal *, u_int id, u_int32_t value);
+#else
+#define	OS_MARK(_ah, _id, _v)
+#endif
+
+#endif /* _ATH_AH_OSDEP_H_ */
diff -N -u -r src.preview/sys/contrib/dev/ath/public/i386-elf.hal.o.uu src/sys/contrib/dev/ath/public/i386-elf.hal.o.uu
--- src.preview/sys/contrib/dev/ath/public/i386-elf.hal.o.uu	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/public/i386-elf.hal.o.uu	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,3518 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting, Atheros
+ * Communications, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the following conditions are met:
+ * 1. The materials contained herein are unmodified and are used
+ *    unmodified.
+ * 2. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following NO
+ *    ''WARRANTY'' disclaimer below (''Disclaimer''), without
+ *    modification.
+ * 3. Redistributions in binary form must reproduce at minimum a
+ *    disclaimer similar to the Disclaimer below and any redistribution
+ *    must be conditioned upon including a substantially similar
+ *    Disclaimer requirement for further binary redistribution.
+ * 4. Neither the names of the above-listed copyright holders nor the
+ *    names of any contributors may be used to endorse or promote
+ *    product derived from this software without specific prior written
+ *    permission.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT,
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ *
+ * $Id: i386-elf.hal.o.uu,v 1.1.1.1 2004/12/08 18:04:43 sam Exp $
+ */
+#define	ATH_HAL_VERSION	"0.9.14.9"
+begin 644 hal.o
+M?T5,1@$!`0D```````````$``P`!```````````````4"`(``````#0`````
+M`"@`#@`+``^W5"0$@_I7#X2^````@_I7?T6#^A)_&(/Z$0^-HP```(/Z!P^$
+ME````.FM````D(/Z&@^$FP```(/Z&G\. at _H3#X2-````Z9````"#^E)T>^F&
+M````B?:!^A(1``!T9('Z$A$``'\H@?H4$```=&2!^A00``!_#+ at J````@_I8
+M=%_K6('Z!Q$``'0RZTZ)]K@]````@?H3\```=$2!^A/P``!_"H'Z$Q$``'0G
+MZRVX40```('Z&_$``'0EZQZX90```,.X<@```,.)]KA_````PXGVN)(```##
+MB?:X`````,.)]HM4)`2+1"0(9H'ZC!9T$V:!^B>G=`RY`````&:!^K<0=0X/
+MM\!0Z/'^__^#Q`2)P8G(PY!75E.+?"04BW0D&(M<)!R+3"0 at BT0D$`^WT('Z
+M!P(```^$L@```('Z!P(``'\N at _H2?QF#^A$/C80```"#^@</A),```#II@``
+M`(GV at _H3=%B#^AIT3NF5````D('Z$A$``'1<@?H2$0``?Q2!^A00``!T,('Z
+M!Q$``'1<ZW*)]H'Z$_```'0A@?H3\```?PJ!^A,1``!T$>M6@?H;\0``=![K
+M3+ at 3````@^P,45-65P^WP%#H_/___XG"@\0 at ZSN#[`Q14U97#[?`4.C\____
+MB<*#Q"#K))"#[`Q14U97#[?`4.C\____B<*#Q"#K#)"Z`````,<!`0```(72
+M=$MFBX*@`0``9HE""&:+ at J(!``!FB4(*BX*D`0``B4(<9HN"J`$``&:)0B!F
+MBX*J`0``9HE"(F:+ at JP!``!FB4(D9HN"K@$``&:)0B:)T%M>7\-55U93@^P,
+MBVPD)(M\)"B+="0LNP````"+5"0 at BT(4BP0H(?@Y\'4(N`$```#K')"#[`QJ
+M"NC\____@\000X'[YP,``'[2N`````"#Q`Q;7E]=PY!64XM,)`R+="00NP``
+M``"X`````#GP<Q&-%`")R(/@`0G0T>E#.?-R[UM>PU=64XM,)!2+="08BU0D
+M'`^WPHT$0(U$@2`/MU@&#[9`!8/X`G1F at _@"?PF%P'09Z>@```"#^`-T?X/X
+M!`^$GP```.G5````D+_`````@WPD(`!T%`^WPHT$0(!\@2D`=`=FOV``C78`
+MC03U`````(T$@(T$@(T$@,'@`[H`````]_.-1#@*Z98```"0C12=`````+C3
+M36(0]^+!Z@:-3/(5B<B)T[H`````]_.-!(4D````ZVR-=@"-%-T`````N---
+M8A#WXL'J!HU,\A6)R(G3N@````#W\XT$A18```#K0(T4G0````"XTTUB$/?B
+MP>H&C4SR%8G(B=:Z`````/?VC02%`````('[YP,``'<'!;T```#K"H/`7.L%
+MN``````/M\!;7E_#BTPD"+H"````]D$"('5)#[=!`B70````N@,````]P```
+M`'0T#[=!`B70````N at 0````]T````'0?N@$```#V00(0=10/MT$"P>@+@^`!
+M at _@!&=+WTH/B!8G0PY"+5"0$BT0D"*F`````=$6X#@```('ZM`D```^$I```
+M`('ZLPD``'<4@>IG"0``N,W,S,SWXHG0P>@"PY"!ZM`)``"XS<S,S/?BB=#!
+MZ`2#P`_#B?;VQ`%T$X'JB!,``+C-S,S,]^*)T,'H`L.X#@```('ZM`D``'1+
+M@?JS"0``=Q.!ZF<)``"XS<S,S/?BB=#!Z`+#@?J'$P``=Q:!ZM`)``"XS<S,
+MS/?BB=#!Z`2#P`_#@>J($P``N,W,S,SWXHG0P>@"PXUV`(M4)`2+1"0(J8``
+M``!T);BT"0``@_H.=%N#^@UW"HT$D at 5G"0``PY"-!)*-!(6D"```PY#VQ`%T
+M"XT$D at 6($P``PXGVN+0)``"#^@YT)H/Z#7<)C022!6<)``##@_H:=PN-!)*-
+M!(6D"```PXT$D at 6($P``PXUV`(M,)`B+1"0$BY#\!P``A=)T0O9"`A!T"(T$
+MB<'@!,.0#[="`B5``0``/4`!``!U"(T$B<'@`\.0#[="`B70````/<````!U
+M#(T$B8T$0<'@`L.)]HT$B8T$0='@PXUV`(M,)`B+1"0$BY#\!P``A=)T5O9"
+M`A!T$+C-S,S,]^&)T,'H!L.-=@`/MT(")4`!```]0`$``'40N,W,S,SWX8G0
+MP>@%PXUV``^W0@(ET````#W`````=1"XHXLNNO?AB=#!Z`7#C78`N*.++KKW
+MX8G0P>@$PXUV`%575E.#[`2+?"0<@'\$`'5QNP````")]L9$'P3_0X/['W[U
+MNP````"#/P!^58UV`(T$6XUTAR"*1 at B*3 at L/MM"(7!<$"D8)#[;`B%P'!`^V
+MZ6H`56H.5_]T)"CH_/___X/$%&:)1 at QJ`55J#E?_="0HZ/S___^#Q!1FB48.
+M0SD??ZZ#Q`1;7E]=PXGV5E.+3"0,BU0D$(M<)!2+="08C8&T`0``@_H/#X<?
+M`0``_R25``````^W@=0!``")!K@`````Z0T!``"X#0```.D#`0``N`T```#I
+M^0```+ at -````Z>\````/MT`*B0:X`````.G?````#[=`"(D&N`````#ISP``
+M``^V0`*#X`&#^`$9P(/@#>F[````BD`!P.@'#[;`@_@!&<#WT(/@#>FC````
+MB@#`Z`*#X`&#^`$9P(/@#>F.````B@#`Z`.#X`&#^`$9P(/@#>M\B@#`Z`2#
+MX`&#^`$9P(/@#>MJBX'(`0``B0:X`````.M;@_L!=!:X`````(/[`7), at _L"
+M=!>#^P-T(NLO#[^!S`$``(D&N`````#K,`^W@<X!``")!K@`````ZR"+@=`!
+M``")!K@`````ZQ&X#0```.L*B?:X#````(UV`%M>PY"+5"00BT0D%(-\)`@/
+M=1V#?"0,`W46 at _H$=Q&+1"0$B9#0`0``N`$```##D(7`=`;'``P```"X````
+M`,-55U93BVPD%(M<)""+3"0<OP````"#^P=V/HGVBT0D&`^W%+@/MW2X`HG0
+MP>`0"?")`8/!!(/K!(M%%(L$$(D!@\$$@\($@^L$.?)W!8/[`W?F1X/[!W?$
+MB<@K1"0<6UY?7<.04XM$)`B+5"0,BTPD&(M<)!R%TG0'@_H-=!;K,`6@`0``
+MB0''`Q````"X`0```.LA_S/_,?]T)!A0Z%[___^)`[@!````@\00ZP>)]K@`
+M````6\.04XM<)`R+3"00N`````"#>P0`#X0M`0``BP&)`XM!!(E#"(M!"(E#
+M#(M!#(E#$(-Y$/]T%(M!$#W_````=@6X_P```(E#%.L(QT,4`@```)"#>13_
+M=#"+412!^@`$``!V!;H`!```QT,8`0```#E3&',:N`$```"0T>"#R`$YT'+W
+MB4,8ZP:+012)0QB#>1C_=#"+41B!^@`$``!V!;H`!```QT,<`0```#E3'',>
+MN`$```"0T>"#R`$YT'+WB4,<ZPK'0QS_`P``C78`9H-Y'`!T%6:+01QF at _@/
+M=@6X#P```&:)0R#K!F;'0R`*`&:#>1X`=!=FBT$>9H/X#W8%N`\```!FB4,B
+MZPB)]F;'0R(*`(M!((E#)(M!)(E#*(M!*(E#+(M!+(E#,(-Y!`1U$(-[!`%U
+M"L=#.`$```"-=@"X`0```%O#D(M,)`B+5"0,N`````"#>@0`=%N+0 at R)00B+
+M`HD!BT((B4$$BT(,B4$(BT(0B4$,BT(4B4$0BT(8B4$4BT(<B4$89HM"(&:)
+M01QFBT(B9HE!'HM")(E!((M"*(E!)(M"+(E!*(M",(E!++@!````PY!55U93
+M@^P4BUPD-(M,)"R+5"0PQT0D#$````"+1"0H9H$X`3!V",=$)`Q6````O@``
+M```/M^H/M_F-=@"+5"0,#[<$<HG!#Z_-NF0````IPHG0#Z_'`<&X'X7K4??I
+MB00DB50D!(M$)`3!^`7!^1\IR&:)`X/#`D:#_ at I^OX/$%%M>7UW#C78`BU0D
+M"`^WPF:!^O\`=$2+1"0$9H$X`C!W*V:#^CYV$0^WPHT$@`4B%0``#[?`PXGV
+M#[?"C02`C80`[!,```^WP,.-=@`/M\*-!(`%P!(```^WP,.0BU0D"`^WPF:!
+M^O\`=!^+1"0$9H$X`C!W"XV"8`D```^WP,.0C8+\"```#[?`PXGV55=64X/L
+M#(ML)""+="0D9H$^_S]V&F:#?BP`=!.Z`0```&:#?A8`#X13`@``C78`9H$^
+M`C!V9[]0`0``NP````!F@[YV`0````^$+`(``)"#[`2-1"0.4`^WQU!'5?^5
+MA`$``(/$$(7`#X2]`0``9@^V1"0+9HF$7F(!``!F#[9$)`IFB81>9`$``(/#
+M`@^WAG8!```YV'^WZ:4!``"#[`2-1"0.4&@``0``OP$!``!5_Y6$`0``@\00
+MN@````"%P`^$N0$``&:+1"0*B<)FP>H)9HF68@$``&;!Z`*#X']FB89D`0``
+M#[=$)`K!X`6#X']FB89F`0``@^P$C40D#E`/M\=01U7_E80!``"#Q!"Z````
+M`(7`#X1C`0``9HM$)`J)PF;!Z at MF"99F`0``9L'H!(/@?V:)AF@!```/MT0D
+M"L'@`X/@?V:)AFH!``"#[`2-1"0.4`^WQU!'5?^5A`$``(/$$+H`````A<`/
+MA`T!``!FBT0D"HG"9L'J#68)EFH!``!FP>@&@^!_9HF&;`$```^W1"0*T>"#
+MX']FB89N`0``@^P$C40D#E`/M\=01U7_E80!``"#Q!"Z`````(7`#X2X````
+M9HM4)`J)T&;!Z`]F"89N`0``B=!FP>@(@^!_9HF&<`$``&;1ZH/B?V:)EG(!
+M```/MT0D"L'@!H/@?V:)AG0!``"#[`2-1"0.4`^WQU!5_Y6$`0``@\00N@``
+M``"%P'18ZPF)]KH`````ZTUFBT0D"F;!Z`IF"89T`0``NP````!F@[YV`0``
+M`'0I#[>^=@$``(UV``^WA%YB`0``4%;H$?W__X/$"&:)A%YB`0``0SG??^&Z
+M`0```(G0@\0,6UY?7<.)]E575E.#[!B+1"0PBWPD-&:+4!1FB50D$HE$)!0/
+MM\*-%("-%)"-1``#P>@"`<+!X@)2Z/S___^)P8/$$+@`````A<D/A*4```")
+M#P^W5"0&C402`R7\_P<``<B)1PAFBVPD!F:);P2^`````(/Z`'YXC78`BQ^+
+M3PB-!+:-!(:+;"0(9HM4=0!FB12!9HD4<[D`````C02VC02&C1R%`````(GV
+MC02)C02#B<(#5PAFB4H$`T<(9L=`!@``08/Y`W[BBU<(C02VC02&P>`"9L=$
+M$`8$`(M7"&;'1!!"`P!&#[=$)`8Y\'^+N`$```"#Q`Q;7E]=PXGV55=64X/L
+M!(ML)""+5"0<9HM"%F:)109FQP0DK=YFQT0D`JW>N@````"Y``````^WV(GF
+MD(G8T_BH`70*@_H!?REFB0Q60D&#^0-^Z(M4)!QFBT(49HE%!+@`````9H-]
+M!``/A-4!``#K"K@`````Z<D!``"_`````&:#?00`#X2T`0``C11_C127BT0D
+M'(T44(U:&(MU"(T$OXT$A\'@`F:+4AAFB10PBW4(9HM3$F:)5#`"N0````"-
+M!+^-!(>-%(4`````B?:-!(F-!((#10AFQT`&``!!@_D#?NMF at 7PD`JW>=68/
+MMPPDC12_C127C02)P>`"C120`U4(@\($9L="`@0`9HM#`F:)0 at 1F`T,&9HE"
+M!F8#0PIFB4((9 at -##F:)0 at IFBT,$9HE"#&:+0PAFB4(.9HM##&:)0A!FBT,0
+M9HE"$NGA````B?:-!+^-!(>--(4`````#[<$)(T$@(T$A at -%"&:+4P)FB5`(
+M#[=$)`*-!("-!(8#10AFQT`(%``/MT0D`HT$@(T$A at -%"&;'0`HC``^W1"0"
+MC02`C02&`T4(9L=`##\`#[<,)(T$B8T4A at -5"(/"!&;'0@($`&:+0 at 1F`T,&
+M9HE"!F8#0PIFB4((9 at -##F:)0 at IFBT,$9HE"#&:+0PAFB4(.9HM##&:)0A!F
+MBT,09HE"$@^W3"0"C02)C12&`U4(@\($9L="`@,`9HM#%&:)0 at QFBT,69HE"
+M#F:+0QAFB4(01P^W100Y^`^/3/[__[@!````@\0$6UY?7<.-=@!55U93@>Q<
+M`0``OU`!``#'1"0(`````,=$)`0`````@WPD"`$/A-X```"#?"0(`7\/@WPD
+M"`!T&.EC`0``C78`@WPD"`(/A`D!``#I4`$``(N$)'0!``!F at W@6``^$/@0`
+M`(-\)`0)#X\_`0``@^P$C40D$E!71_^T)'P!``"+E"2``0``_Y*$`0``@\00
+MA<`/A!X$``"`?"0.``^$"P$``(M<)`0/MD0D#O]$)`10_[0D>`$``.CU^/__
+M@\0(9HF$7#`!``!F#[9$)`]FA<`/A-<```"+7"0$#[;`_T0D!%#_M"1X`0``
+MZ,/X__^#Q`AFB81<,`$``(-\)`0)#XYJ____Z:0```"+C"1T`0``9H-Y&``/
+MA(8#``"[`````)"+A"1T`0``9H&\6%0!``#_`'099HN$6%0!``"+3"0$9HF$
+M3#`!``!!B4PD!$.#^P)^SNM8BX0D=`$``&:#>!H`#X0Z`P``NP````"0BY0D
+M=`$``&:!O%I.`0``_P!T&6:+A%I.`0``BTPD!&:)A$PP`0``08E,)`1#@_L"
+M?L[K#+@`````Z0H#``")]H/L"&@<`0``C40D'%#H_/___XM$)!1FB40D-+L`
+M````@\00.UPD!`^-7`(``(UL)`Z-=@!FBX1<,`$``&:)1%P0C11;C1239HE$
+M5"B#[`155T?_M"1\`0``BY0D@`$``/^2A`$``(/$$(7`#X27`@``C0Q;C0R+
+MC4Q,(&:+1"0.#[;09L'H!X/@`<'@"&8IPF:)40QFBT0D#HG"9L'J"&;!Z`_!
+MX`AF*<)FB5$0@^P$55='_[0D?`$``(N,)(`!``#_D80!``"#Q!"%P`^$-@(`
+M`(T,6XT,BXV,3%`!``!FBT0D#@^VT&;!Z`>#X`'!X`AF*<)FB9'D_O__9HM$
+M)`Z)PF;!Z at AFP>@/P>`(9BG"9HF1Z/[__X/L!%571_^T)'P!``"+E"2``0``
+M_Y*$`0``@\00A<`/A,P!``"-%%N-%).-5%0 at 9HM$)`Z#X!]FB4(.9HM$)`YF
+MP>@%@^`?9HE"$F:+1"0.9L'H"H/@'V:)0A:#[`155T?_M"1\`0``BXPD@`$`
+M`/^1A`$``(/$$(7`#X1Q`0``C0Q;C0R+C4Q,,&:+1"0.#[;09L'H!X/@`<'@
+M"&8IPF:)40QFBT0D#HG"9L'J"&;!Z`_!X`AF*<)FB5$.@^P$55='_[0D?`$`
+M`(N4)(`!``#_DH0!``"#Q!"%P`^$$`$``(T$6XT$@XVT1%`!``"-CN#^__]F
+MBT0D#@^VT&;!Z`>#X`'!X`AF*<)FB5$0BX0D=`$``&:!.`)`=AIFBT$(9HE!
+M"F8/MD0D#X/@/V:)AMK^___K,HT$6XT$@XV$1%`!``!FBU0D#HG19L'I"&;!
+MZ at _!X at AF*=%FB8CJ_O__9L>`VO[__P$`0SM<)`0/C*O]__^+5"0(BXPD=`$`
+M`&:+1%%F9HE$)":#[`2-!%*-A(%0"0``4(U$)!A0_[0D?`$``.A7^/__@\00
+MA<!T1(M4)`B-!%*+C"1T`0``C82!4`D``%"-1"044/^T)'@!``#H(?G__X/$
+M#(7`=!:)]O]$)`B#?"0(`@^.:/O__[@!````@<1<`0``6UY?7<.055=64XM\
+M)!2+1"08B<5FB4<(#[=7"KX`````]L(!=`6^`0```(G0T>B#X`&#^`%F@][_
+MB=#!Z`*#X`&#^`%F@][_B=#!Z`.#X`&#^`%F@][_NP````!F.>MS*I`/M\.+
+M5"0<9HL,0F:)#$>-%("-%)*-%%"-%%=FB4H,9HER#D-F.>MRUUM>7UW#B?95
+M5U93@^P\BVPD4(M\)%R^`````(-\)&``=&^0@^P$C40D$E`/M\=01U7_E80!
+M``"#Q!"%P`^$/04``(!\)`X`=$@/M]X/MD0D#D90_W0D6.AC]/__@\0(9HE$
+M7!!F#[9$)`]FA<!T(@^WW@^VP$90_W0D6. at _]/__@\0(9HE$7!`/M\8[1"1@
+M<I*-1"004`^WQE#_="1 at Z-C^__^+?"1LT>\#?"1H9L=$)!@``(/$#(M$)%AF
+M at W@(``^&M`0``(UT)`Z0#[=4)`R-!)*-!("-!$*+5"18C5Q"#&:#>P(`#X2_
+M````@^P$5@^WQU!'5?^5A`$``(/$$(7`#X1X!```9HM$)`Z#X!]FB4,,9HM$
+M)`YFP>@%@^!_9HE#!&:+1"0.9L'H#&:)0SR#[`16#[?'4$=5_Y6$`0``@\00
+MA<`/A#0$``!FBT0D#H/@/V:)0Q1FBT0D#F;!Z`:#X`]FB4-$9HM$)`YFP>@*
+M9HE#'(/L!%8/M\=01U7_E80!``"#Q!"%P`^$\`,``&:+1"0.@^`/9HE#3&:+
+M1"0.9L'H!(/@/V:)0R1F at WL"`0^&Y0```&:+1"0.9L'H"H/@'V:)0PYFBT0D
+M#F;!Z`]FB4,&@^P$5@^WQU!'5?^5A`$``(/$$(7`#X21`P``#[=$)`Z#X#_1
+MX&8)0P9FBT0D#F;!Z`:#X`]FB4,^9HM$)`YFP>@*9HE#%H/L!%8/M\=01U7_
+ME80!``"#Q!"%P`^$2P,``&:+1"0.@^`/9HE#1F:+1"0.9L'H!(/@/V:)0QYF
+MBT0D#F;!Z`J#X`]FB4-.9HM$)`YFP>@.9HE#)H/L!%8/M\=01U7_E80!``"#
+MQ!"%P`^$]P(```^W1"0.@^`/P>`"9 at E#)NM2B?9F at WL"`75)9HM$)`YFP>@*
+M@^`/9HE#5&:+1"0.9L'H#F:)0RR#[`16#[?'4$=5_Y6$`0``@\00A<`/A*4"
+M```/MT0D#H/@#\'@`F8)0RR)]F:#>P("#X:]````9HM$)`YFP>@$@^`?9HE#
+M$&:+1"0.9L'H"6:)0PB#[`16#[?'4$=5_Y6$`0``@\00A<`/A%$"``!FBT0D
+M#H/@#V:)0T!FBT0D#F;!Z`2#X#]FB4,89HM$)`YFP>@*@^`/9HE#2&:+1"0.
+M9L'H#F:)0R"#[`16#[?'4$=5_Y6$`0``@\00A<`/A/T!```/MT0D#H/@#\'@
+M`F8)0R!FBT0D#F;!Z`2#X`]FB4-09@^V1"0/@^`_9HE#*.LGC78`9H-[`@)U
+M'6:+1"0.9L'H!(/@#V:)0U9F#[9$)`^#X#]FB4,N9H-[`@,/AC4!``!FBT0D
+M#F;!Z`YFB4,2@^P$5@^WQU!'5?^5A`$``(/$$(7`#X1U`0``#[=$)`Z#X`?!
+MX`)F"4,29HM$)`YFP>@#@^!_9HE#"F:+1"0.9L'H"H/@#V:)0T)FBT0D#F;!
+MZ`YFB4,:@^P$5@^WQU!'5?^5A`$``(/$$(7`#X0>`0``#[=$)`Z#X`_!X`)F
+M"4,:9HM$)`YFP>@$@^`/9HE#2F8/MD0D#X/@/V:)0R)FBT0D#F;!Z`YFB4-2
+M@^P$5@^WQU!'5?^5A`$``(/$$(7`#X3*````#[=$)`Z#X`/!X`)F"4-29HM$
+M)`YFP>@"@^`_9HE#*F8/MD0D#X/@#V:)0UIFBT0D#F;!Z`QFB4,R@^P$5@^W
+MQU!'5?^5A`$``(/$$(7`='H/MT0D#H/@`\'@!&8)0S+K39!F at WL"`W5%9HM$
+M)`YFP>@.9HE#6(/L!%8/M\=01U7_E80!``"#Q!"%P'0]#[=$)`Z#X`/!X`)F
+M"4-89HM$)`YFP>@"@^`_9HE#,(GV9O]$)`R+5"0,BT0D6&8Y4`@/AU'[__^X
+M`0```(/$/%M>7UW#55=64X/L#(M4)"1FBT((BVPD(&:)10AFBT4*9HE$)`AF
+MQT0D!@``J`%T!V;'1"0&`0"+1"0(9M'H@^`!@_@!9H-<)`;_BT0D"&;!Z`*#
+MX`&#^`%F at UPD!O^+1"0(9L'H`X/@`8/X`6:#7"0&_V;'1"0*``"+5"0D9H-Z
+M"``/AL<```"0#[=4)`J+;"0D9HM,50"+1"0 at 9HD,4(T$DHT$@(T$0HM4)""-
+M!$)FB4@,9HML)`9FB6 at 0OP````"[``````^W="0*C02VC02`C01&T>")!"0/
+MM\N-!$F+%"2-!,*+;"0 at C10H9HE:$@^W1"0(T_BH`7069L="%`0`1V:#_P%U
+M*&;'0A0%`.L at D`^WTXT44HT$MHT$@(T$1M'@C130BT0D(&;'1`(4``!#9H/[
+M`W:B9O]$)`IFBVPD"HM4)"1F.6H(#X<Z____@\0,6UY?7<.)]E575E.#["1F
+MQT0D&*W>C40D&&;'0`*MWF;'0`2MWF;'0`:MWL=$)!``````9L=$)!0``(M$
+M)$`/MU`*NP,```"-="08#[=$)!2)V2G!B=#3^*@!=!2)V&8K1"04BTPD$&:)
+M!$Y!B4PD$&;_1"049H-\)!0#=LZ+5"0\9HM""(M,)$!FB4$(9L=$)!8``&:#
+M>0@`#X;_````#[=4)!:-!)*-!("-!$+1X(M,)#R-%`B-:@QFBU(,BTPD0&:)
+M5`@,9L=$)!0``(-\)!``#X2N````#[=4)!:-!)*-!("-!$+1X(E$)`R-=@`/
+MMU0D%`^W1%08C01`BTPD#(T$P8M,)$"-=`@2#[]$50S!X`)FB48.9HM$501F
+MB48$OP$```!F at WX"`79&#[=$)!2)1"0(9HM6`F:)5"0&D`^WSXM$)`B-'(@/
+MOT1=-&:+5$X,C01"9HE$3 at YFBT1.`F8#1%T,9HE$3 at 1'9CE\)`9WS6;_1"04
+M#[=$)!0[1"00#X)I____9O]$)!9FBTPD%HM4)$!F.4H(#X<!____N`$```"#
+MQ"1;7E]=PU575E.![`P%``"+M"0D!0``9L=$)`@$`(U$)`AFQT`"!@!FQT`$
+M"0!FQT`&#`"#[`AH[`0``(U$)!Q0Z/S___\/M[[0````@\009H-^&`!T?XVL
+M)+0!``!FBT9H9HF$)+X!``"#[`QJ!%=55O^T)#P%``#H$/?__X/$(+H`````
+MA<`/A+D```"-GO0*``!FBT9H9HE#"E53Z&O\__]35?^T)#`%``#HL?W__X/$
+M%+H`````A<`/A(8````/MT4.#[=41`8/MT4(#Z_"C7P'`I!F at WX:`'1EC:PD
+M6`,``&:+1FIFB80D8@,``(/L#&H$5U56_[0D/`4``.B*]O__@\0 at N@````"%
+MP'0WC9Z8#```9HM&:F:)0PI54^CI^___4U7_M"0P!0``Z"_]__^#Q!2Z````
+M`(7`=`B-=@"Z`0```(G0@<0,!0``6UY?7<.)]E575E.#[!R+?"0PBT0D-&:!
+M./\_=AIF at W@L`743@^P(4%?HQ?#__X/$$.DJ`P``D(M4)#1F at 3K_3W9-9H-Z
+M+`)U1H/L"%)7Z&3^__^#Q!#I!0,``+@`````Z?L"``"X`````.GQ`@``N```
+M``#IYP(``+@`````Z=T"``"X`````.G3`@``B?;'1"00`````(M,)#1F at 3D#
+M,!GV@^:P@<90`0``@WPD$`%T3H-\)!`!?PZ#?"00`'03Z9\```")]H-\)!`"
+M=&3ID0```(/&!8M$)#1FBX!V`0``9HE$)!B+7"0T@<-X`0``BU0D-('"8@$`
+M`(E4)`SK;XM,)#1F at WD8``^$/0(``(/&-V:+ at 8`#``!FB40D&(G+@<,J!```
+M@<&(`P``B4PD#.L]BU0D-&:#>AH`#X0+`@``@\9&9HN*@`,``&:)3"08B=.!
+MPXX#``"!PH(#``")5"0,ZPNX`````.GR`0``D,=$)!0`````9H-\)!@`#X3)
+M`0``C6PD&HM4)!2+3"0,9HL$46:)`X/L!%561E?_EX0!``"#Q!"%P`^$K?[_
+M_V:+1"0:9L'H"F:)0P1FBT0D&F;!Z`2#X#]FB4,"#[=$)!K!X`*#X#]FB4,>
+M@^P$559&5_^7A`$``(/$$(7`#X1S_O__9HM$)!IFP>@.9 at E#'F8/MD0D&X/@
+M/V:)0R!FBT0D&F;!Z`*#X#]FB4,B#[=$)!K!X`2#X#]FB4,D@^P$559&5_^7
+MA`$``(/$$(7`#X0L_O__9HM$)!IFP>@,9 at E#)&:+1"0:9L'H!H/@/V:)0R9F
+MBT0D&H/@/V:)0RB#[`155D97_Y>$`0``@\00A<`/A/7]__]FBT0D&F;!Z`IF
+MB4,J9HM$)!IFP>@$@^`_9HE#+`^W1"0:P>`"@^`_9HE#+H/L!%561E?_EX0!
+M``"#Q!"%P`^$N_W__V:+1"0:9L'H#F8)0RYF#[9$)!N#X#]FB4,P9HM$)!IF
+MP>@"@^`_9HE#,HU#"%`/MT,$4`^W0P)0_W0D0. at 2Y___N@````"#Q!`/MT,&
+MB<&#^`!^&HUV``^_1%,>C02`C02`T>!FB413'D(YT7_I@\,T_T0D%`^W1"08
+M.T0D%`^/._[___]$)!"#?"00`@^.//W__[@!````@\0<6UY?7<.-=@!55U93
+M@^P<BWPD-+T`````9H$__S]V"0^W1R*-<*OK$&:!/P,P&?:#YK"!QE`!``"#
+M_0%T,X/]`7\&A>UT"NMX at _T"=$OK<9"#QE7'1"00"````(V?R`0``(V'Q at 0`
+M`(E$)`SK7XUV`&:#?Q@`#X31`0``@\9EQT0D$`(```"-GSH%``"-ES@%``")
+M5"0,ZS1F at W\:``^$J0$``(/&:<=$)!`#````C9\:!0``C8<8!0``B40D#.L,
+MN`````#ID0$``(GVBU0D#&;'`@``QT0D%`````"+1"00.40D%`^-80$``)"#
+M[`2-1"0>4%9&_W0D/(M4)$#_DH0!``"#Q!"%P`^$3`$``&:!/P(P=@YF#[9$
+M)!MFB4,(ZP^)]F:+1"0:9L'H"6:)0PAF at WL(``^$^@```(7M=10/MT,(4%?H
+M]>7__X/$"&:)0PCK$@^W0PA05^@UYO__@\0(9HE#"&:!/P(P=B-FBT0D&F;!
+MZ`*#X#]FB4,&#[=$)!K!X`2#X#]FB4,$ZR&)]F:+1"0:9L'H`X/@/V:)0P8/
+MMT0D&L'@`X/@/V:)0P2#[`2-1"0>4%9&_W0D/(M4)$#_DH0!``"#Q!"%P`^$
+MB0```&:!/P(P=BMFBT0D&F;!Z`QF"4,$9HM$)!IFP>@&@^`_9HE#`F:+1"0:
+M@^`_9HD#ZRR09HM$)!IFP>@-9 at E#!&:+1"0:9L'H!X/@/V:)0P)FBT0D&F;1
+MZ(/@/V:)`XM$)`QF_P"#PPK_1"04BU0D$#E4)!0/C*'^__^018/]`@^.POW_
+M_[@!````@\0<6UY?7<.055=64X/L#(ML)""+="0D@<90!0``BT0D)&:!./\_
+M=@D/MW at B@\<:ZW>_OP$``(M4)"1F at 3H",'=GZUVX`````.FY`P``N`````#I
+MKP,``+@`````Z:4#``"X`````.F;`P``N`````#ID0,``+@`````Z8<#``"X
+M`````.E]`P``N`````#I<P,``+@`````Z6D#``"-=@"_;P$``(UV`,=$)`0`
+M````BTPD)&:#N00!````#X0_`P``B?:+1"0$BU0D)&:#O$(&`0```'499H$Z
+M`C!V!H/'".L$D(/'!X/&0.GY`@``D(M,)"1F at 3D",`^&K0```+L`````@^P$
+MC40D#E!71U7_E80!``"#Q!"%P`^$(____V:+1"0*B<)FP>H(9HD4WB7_````
+M9HE$W at B#PP*#^P=^P[L`````B?:#[`2-5"0.4E='5?^5A`$``(/$$(7`#X3I
+M_O__9@^V1"0+@^`_9HE$W@)FBT0D"F;!Z`Z#X`&)1-X$9HM$)`J#X#]FB43>
+M"F:+1"0*9L'H!H/@`8E$W at R#PP*#^P=^H>G9`0``@^P$C4PD#E%71U7_E80!
+M``"#Q!"%P`^$C_[__V:+1"0*B<)FP>H)9HD69L'H`H/@?V:)1@@/MT0D"L'@
+M!8/@?V:)1A"#[`2-1"0.4%='5?^5A`$``(/$$(7`#X15_O__9HM$)`J)PF;!
+MZ at MF"5809L'H!(/@?V:)1A@/MT0D"L'@`X/@?V:)1B"#[`2-5"0.4E='5?^5
+MA`$``(/$$(7`#X0:_O__9HM$)`J)PF;!Z at UF"58 at 9L'H!H/@?V:)1B@/MT0D
+M"M'@@^!_9HE&,(/L!(U,)`Y15T=5_Y6$`0``@\00A<`/A.#]__]FBU0D"HG0
+M9L'H#V8)1C")T&;!Z`B#X']FB48X9L'J`H/B/V:)5@(/MT0D"L'@!(/@/V:)
+M1 at J#[`2-1"0.4%='5?^5A`$``(/$$(7`#X28_?__9HM4)`J)T&;!Z`QF"48*
+MB=!FP>@&@^`_9HE&$H/B/V:)5AJ#[`2-5"0.4E='5?^5A`$``(/$$(7`#X1C
+M_?__9HM$)`J)PF;!Z at IFB58B9L'H!(/@/V:)1BH/MT0D"L'@`H/@/V:)1C*#
+M[`2-3"0.45='5?^5A`$``(/$$(7`#X0H_?__9HM$)`J)PF;!Z at YF"58R9L'H
+M"(/@/V:)1CJ[`````(GV at SS>`'1*BU0D!(M,)"0/MX11!@$``(/@!W0%@_@#
+M=1@/MP3>4/]T)"CH*N'__X/$"&:)!-[K&9`/MP3>4/]T)"CH9N'__X/$"&:)
+M!-Z-=@!#@_L'?JJ#QD"-=@#_1"0$BU0D)`^W@@0!```[1"0$#X_#_/__N`$`
+M``"#Q`Q;7E]=PXGV55=64X/L#(ML)""+="0D9L=&*`\`9L=&*@\`9H$^`C!V
+M$L<$)(0```!FQX8$`0``(`#K$,<$)&P```!FQX8$`0``$`"#[`2-1"0.4(M$
+M)`C_,%7_E80!``"#Q!#'1"0$`````(7`#X0T#0``9HM4)`J)T&;!Z`]FB48&
+MB=!FP>@.@^`!9HE&"HG09L'H"X/@!V:)1 at R)T&;!Z`2#X']FB48.9H$^_S]V
+M#XG09L'H`X/@`6:)1 at CK!F;'1@@!`&:+5"0*B=!FP>@"@^`!9HE&&HG09M'H
+M@^`!9HE&&(/B`6:)5A:+!"2+6`2#[`2-1"0.4%-#5?^5A`$``(/$$,=$)`0`
+M````A<`/A),,``!FBU0D"HG09L'H"(A&'(A6'6:!/O\_#X;7````@^P$C40D
+M#E!30U7_E80!``"#Q!#'1"0$`````(7`#X13#```9HM4)`J)T&;!Z`YFB48L
+MB=!FP>@-@^`!B8;(````B=!FP>@,@^`!B8;,````@>+_#P``9HE6+H/L!(U$
+M)`Y04T-5_Y6$`0``@\00QT0D!`````"%P`^$^`L``&:+1"0*B<*!XO\/``!F
+MB58B9L'H#H/@`8A&(6:!/O]/=C:#PP*#[`2-1"0.4%-5_Y6$`0``@\00QT0D
+M!`````"%P`^$L`L``&:+1"0*9L'H!&:)AM````"_`````(L$)(M<N`B#[`2-
+M1"0.4%-#5?^5A`$``(/$$(7`#X13"0``9@^V1"0+@^!_9HE$?C!FBT0D"F;!
+MZ`*#X#]FB41^-@^W1"0*P>`$@^`_9HE$?G*#[`2-1"0.4%-#5?^5A`$``(/$
+M$(7`#X05"0``9HM$)`IFP>@,9 at E$?G)FBT0D"F;!Z`:#X#]FB41^>&:+1"0*
+M@^`_9HE$?GZ#[`2-1"0.4%-#5?^5A`$``(/$$(7`#X3:"```9HM$)`IFP>@*
+M9HF$?H0```!FBT0D"F;!Z`2#X#]FB81^B@````^W1"0*P>`"@^`_9HF$?I``
+M``"#[`2-1"0.4%-#5?^5A`$``(/$$(7`#X23"```9HM$)`IFP>@.9 at F$?I``
+M``!F#[9$)`N#X#]FB81^E@```&:+1"0*9L'H`H/@/V:)A'Z<````#[=$)`K!
+MX`2#X#]FB81^H@```(/L!(U$)`Y04T-5_Y6$`0``@\00A<`/A#L(``!FBT0D
+M"F;!Z`QF"81^H@```&:+1"0*9L'H!H/@/V:)A'ZH````9HM$)`J#X#]FB81^
+MK@```(/L!(U$)`Y04T-5_Y6$`0``@\00A<`/A/<'``!F#[9$)`N(1#=4 at _\!
+M=%"#_P%_"X7_=`[I_@```(GV at _\"=%SK>6:+1"0*B<)FP>H%@^('9HF6\```
+M`&;!Z`*#X`=FB8;R````#[=$)`K1X(/@!V:)ANP```#K0XUV`&:+5"0*B=!F
+MP>@$@^`'9HF&]````(/B!V:)EO8```#K'V:+5"0*B=!FP>@$@^`'9HF&^```
+M`(/B!V:)EOH```"%_W5X@^P$C40D#E!30U7_E80!``"#Q!"%P`^$10<``&:+
+M5"0*B=!FP>@/9 at F&[````(G09L'H#(/@!V:)ANX```")T&;!Z`F#X`=FB8;H
+M````B=!FP>@&@^`'9HF&Z@```(G09L'H`X/@!V:)AN0```"#X@=FB9;F````
+M@^P$C40D#E!30U7_E80!``"#Q!"%P`^$V at 8``&8/MD0D"V:)1'X\9@^V1"0*
+M9HE$?D*#[`2-1"0.4%-#5?^5A`$``(/$$(7`#X2U!@``9@^V1"0+9HE$?DAF
+M#[9$)`IFB41^3H/L!(U$)`Y04T-5_Y6$`0``@\00A<`/A)`&``!F#[9$)`N(
+M1#=79@^V1"0*9HE$?EKV1'Y:@'0*#0#___]FB41^6H/L!(U$)`Y04T-5_Y6$
+M`0``@\00A<`/A%L&``!FBT0D"F;!Z`4E_P```&:)1'Y at 9HM$)`IFT>B#X`]F
+MB41^9F:+1"0*@^`!9HE$?FQF at 3[_/W8OA?]T!X/_`G04ZR1FBT0D"F;!Z`V#
+MX`%FB48DZQ)FBT0D"F;!Z`V#X`%FB48FB?9F at 3X",`^&E0```(/L!(U$)`Y0
+M4T-5_Y6$`0``@\00A<`/A-L%``!FBT0D"F;!Z`:#X']FB81^M````(/_`702
+M at _\!?P:%_W1+ZU6#_P)T(^M.9HM$)`J)PH/B!V:)EOP```!FP>@#@^`'9HF&
+M``$``.LM9HM$)`J)PH/B!V:)EOX```!FP>@#@^`'9HF&`@$``.L,9HM$)`J#
+MX#]FB4829H$^`S`/AID```!FBT0D"F;!Z`UFB81^N@```(/L!(U$)`Y04T-5
+M_Y6$`0``@\00A<`/A#<%```/MT0D"L'@`X/@.&8)A'ZZ````@_\"=2!FBU0D
+M"HG09L'H`XA&(&:!/@5`=@N)T&;!Z`MFB48JD(7_=3YF at 3[_/P^&S0,``&:+
+M5"0*B=!FP>@(@^`_9HF&1@$``&;!Z at .#XA]FB99*`0``ZPYFQX1^N@````H`
+MQD8@#V:!/O\_#X:/`P``@_\!=""#_P%_#87_#X25`@``Z7@#``"#_P(/A)L`
+M``#I:@,``(/L!(U$)`Y04T-5_Y6$`0``@\00A<`/A'X$```/MD0D"E!6Z';9
+M__^#Q`AFB894`0``#[9$)`M05NA at V?__@\0$9HF&5@$``(U$)`Y04U7_E80!
+M``"#Q!"%P`^$1P0```^V1"0*4%;H,MG__X/$"&:)AE@!``!F at 3X`0`^&Z0(`
+M`&8/MD0D"X/@/V:)AL(```#IU`(``(/L!(U$)`Y04T-5_Y6$`0``@\00A<`/
+MA`($```/MD0D"E!6Z.#8__^#Q`AFB89.`0``#[9$)`M05NC*V/__@\0$9HF&
+M4`$``(U$)`Y04T-5_Y6$`0``@\00A<`/A,H#``!FBT0D"HG"@^)_9HE6$&;!
+MZ`>#X#]FB484@^P$C40D#E!30U7_E80!``"#Q!"%P`^$H@,```^V1"0*4%;H
+M9MC__X/$"&:)AE(!``!F at 3X`0'809@^V1"0+@^`_9HF&Q````(/L!(U$)`Y0
+M4T-5_Y6$`0``@\00A<`/A&8#``!FBU0D"HG09L'H"(/@/V:)AD@!``!FP>H#
+M@^(?9HF63`$``&:!/@%`#X;'`0``@^P$C40D#E!30U7_E80!``"#Q!"%P`^$
+M*0,``&:+5"0*9@^VPF:)1BAF at 3[_3P^&DP$``(G09L'H"(/@?V:)AM8```")
+MT&;!Z`]FB8;:````@^P$C40D#E!30U7_E80!``"#Q!"%P`^$Y0(```^W1"0*
+M@^`?T>!F"8;:````9HM4)`J)T&;!Z`6#X#]FB8;B````9L'J"XB6W0```(/L
+M!(U$)`Y04U7_E80!``"#Q!"%P`^$IP(```^W1"0*@^`'P>`%"(;=````9HM$
+M)`IFP>@#B(;?````Z>H```")]F:!/@!`#X;=````@^P$C40D#E!30U7_E80!
+M``"#Q!"%P`^$9@(``&:+5"0*B="#X#]FB8;`````9H$^_T\/AJ4```")T&;!
+MZ`:#X']FB8;4````B=!FP>@-9HF&V````(/L!(U$)`Y04T-5_Y6$`0``@\00
+MA<`/A!X"```/MT0D"H/@!\'@`V8)AM@```!FBU0D"HG09L'H`X/@/V:)AN``
+M``!FP>H)B);<````@^P$C40D#E!35?^5A`$``(/$$(7`#X3?`0``#[=$)`J#
+MX`'!X`<(AMP```!FBT0D"F;1Z(B&W@```)!'@_\"#XX']___9H$^`C`/A^,!
+M``"#[`2-1"0.4&CL````5?^5A`$``(/$$,=$)`0`````A<`/A'`"``!FBT0D
+M"HG"@^('9HF6_````&;!Z`.#X`=FB88``0``@^P$C40D#E!H[0```%7_E80!
+M``"#Q!#'1"0$`````(7`#X0J`@``Z5(!``#'1"0$`````.D8`@``QT0D!```
+M``#I"P(``,=$)`0`````Z?X!``#'1"0$`````.GQ`0``QT0D!`````#IY`$`
+M`,=$)`0`````Z=<!``#'1"0$`````.G*`0``QT0D!`````#IO0$``,=$)`0`
+M````Z;`!``#'1"0$`````.FC`0``QT0D!`````#IE@$``,=$)`0`````Z8D!
+M``#'1"0$`````.E\`0``QT0D!`````#I;P$``,=$)`0`````Z6(!``#'1"0$
+M`````.E5`0``QT0D!`````#I2`$``,=$)`0`````Z3L!``#'1"0$`````.DN
+M`0``QT0D!`````#I(0$``,=$)`0`````Z10!``#'1"0$`````.D'`0``QT0D
+M!`````#I^@```,=$)`0`````Z>T```#'1"0$`````.G@````QT0D!`````#I
+MTP```&:+1"0*B<*#X@=FB9;^````9L'H`X/@!V:)A@(!``!FQX9@`0``!`!F
+MQX9>`0```0!FQX9<`0```0!FQX9:`0````"+!"2+6!2_`````&:#O at 0!````
+M=$6-=@"#[`2-1"0.4%-#5?^5A`$``(/$$(7`=(!F#[9$)`MFB81^!@$``&8/
+MMD0D"F:)A'X(`0``@\<"#[>&!`$``#GX?[YF at 3X",'<F9L=&6LK_9L=&7/__
+M9L=&7O__=Q)FQT9"#P!FQT9$'`!FQT9&'`#'1"0$`0```)"+1"0$@\0,6UY?
+M7<-64X/L#(MT)!B+7"0<4U;H0/+__X/$$+H`````A<!T5(/L"%-6Z(+3__^#
+MQ!"Z`````(7`=#Z#[`A35NA,Z/__@\00N@````"%P'0H@^P(4U;HFNO__X/$
+M$+H`````A<!T$H/L"%-6Z.#M__^)PH/$$(UV`(G0@\0$6U[#BT0D!`^W at -0!
+M```E_[\``,.-=@!3BUPD#+D`````B?8/O]&-!%)F.9P`(`L``'4$B=#K#$%F
+M at _DJ=N6X_____UO#B?93BUPD#+D`````B?8/O]&-!%)F.1S%X````'4$B=#K
+M#$%F at _EL=N6X_____UO#B?975E.+7"00BW0D%(M\)!A3Z'?___^#Q`2)P;@`
+M````9H7)>&B!X?\/``!T-KH`````C78`C035`````"G09CD,Q>`0``!U%87V
+M=`-FB0Z%_W0"B1>X`0```.LQD$*#^@IVTK@`````@[NP`0``!G4<A?9T"6:A
+MX!```&:)!H7_=`;'!P````"X`0```%M>7\-75E.+="00BT0D%(M\)!AFA<!U
+M,&H05NCT_O__B<-6Z-C^__^#Q`QFA<!T.@^WP%!6Z-K^__^#Q`AF at _C_=">)
+MP^LCD`^WP%!6Z/+^__^8C01`#[<$Q>(```!05NBO_O__B<.#Q!#WQX````!T
+M$@^_PXT$0`^WA``D"P``ZQ")]@^_PXT$0`^WA``B"P``6UY?PXGVBU0D"+ at H
+M````]L(0=1*X!0```/?"@````'4%N!0```##C78`BU0D"/;&`707B=#!Z`2#
+MX`&#^`$9P(/@#H/``\.-=@"X`````/?"@````'02B=#!Z`2#X`&#^`$9P/?0
+M@\`(PXM4)`CVQ@%T$[A`#@``]L(0=26X0`P``,.-=@"X`````/?"@````'0/
+MN.`.``#VPA!U!;C`#P``PXUV`%575E.#[""+1"0X9HE$)!Z+5"0\9HE4)!S'
+M1"04!!8``(%\)!08%@``#X,P`0``9HM$)!Z+3"049H4!#X0+`0``#[=9`E/_
+M="0XZ'7___^#Q`B)1"084_]T)#CH)/___X/$"(E$)`Q3_W0D..CO_O__@\0(
+M9HE$)!*]`````#ML)`P/C<0```"^`````(U$+0")P@'HC02`BTPD&&:#?$$"
+M``^$E@````^W1"02B40D"(E4)`2)%"2-!"J-!("-%':-!!"+5"08C01"9HM(
+M!F8Y2`AR3HM\)`B+1"0$`>B-!("-%':-!!"+5"08C1Q"D&8[3"0<=0V+3"04
+M#[=!`NMCC78`#[?1C8$P]O__9CW<`'8%C00ZZP.-0A2)P68Y0PASS4:+%"2-
+M!"J-!("+3"08#[=$00(Y\`^/>O___T4[;"0,#XP_____C78`@T0D%`2!?"04
+M&!8```^"T/[__[@`````@\0 at 6UY?7<.+1"0(@_@@=R?_)(48%@``N$`!``##
+MN*````##N,````##N%`!``##N-````##B?:X`````,.)]E575E.+="04#[=<
+M)!Q35N@,_O__@\0(B<535NC`_?__@\0(B<=3#[=$)!Q05NCV_/__B<&Z````
+M`(/$##GZ?1F-!%*-!(#1X&8Y#"AU!@'HZPR)]D(Y^GSGN+`0``!;7E]=PY`/
+MMT0D#%`/MT0D#%#_="0,Z(?___\/MP"#Q`S#4_]T)`CHFOO__X/$!(G!9H7`
+M>22)PX'C_W\``+H`````C78`C0129CD<Q>````!T0T*#^FQV[9`/M\&#Z$"#
+M^`MW.?\DA9P6``"XB`$``.LPN(@!``#K*;B+`0``ZR*XB@$``.L;N(P!``#K
+M%+B-`0``ZPT/M\/K"(UV`+@`````6\.0:@!J`/]T)`SHA_O__X/$#,.-=@`/
+MMT0D!(/X"'\. at _@'?1&#^`-T#.L2B?8]F`$``'4)D+@!````PXGVN`````##
+MB?:+3"0(N@````"-=@"-!%+!X`-F.8C@````=0F+ at .P```##B?9"@_IL=N*X
+M`````,-H0`$```^W1"0,4/]T)`SHG/O__P^WP%#H at ____X/$$(7`#Y3`#[;`
+MPXUV`%575E.#[`2+="085NA/____@\0$A<!T=&:+KM0!``"Z`````(T$U0``
+M``")P2G0P>`#9CFHX!```'5!NP````!F@[CL$````'0RO_`0``")SHUV`(G(
+M*="-!$.#/(<`=`B+!(?V``)U:$.)\8GP*=`/MP3%[!```#G8?]A"@_H*=J*X
+M`````.F\````5NCN^?__@\0$B<-FA<!Y2HG!@>'_?P``N@````"-=@"-!%+!
+MX`-F.8C@````=0F#N/``````=11"@_IL=N*X`````.MWN`$```#K<+@!````
+MZVFX`0```.MB:%`!``!6Z(O[__^#Q`B)!"1H4`$``%;H.OO__X/$"(G'#[?#
+M4%;H at _G__YB-!$!FBZP`(@L``+H`````@\0(.?I]%XUV`(T$4HT$@(L,)&8Y
+M+$%TI$(Y^GSLN`````"-=@"#Q`1;7E]=PXM,)`BZ`````&:%R746_W0D!.B9
+M_O__@\0$PXN`\````,.)]HT$4L'@`V8YB.````!TZ$*#^FQVZ[@`````PY!3
+MBUPD#+D`````B?:-!$F-%,4`````9CF:X````'4-N/````"+1`($ZPV)]D&#
+M^6QVVK@`````6\.-=@!55U93@^P$BWPD',<$)`````"+1"0 at 9HMH`H'E\`$`
+M`+X`````C78`C01VC1R%`````&8YNR`7``!U-?]T)!CH</C__X/$!&8Y at R(7
+M``!T"F:#NR(7````=1:-!'9F.2R%)!<``'4)QP0D`0```.L&1H/^$':R at SPD
+M`'1:C01VP>`"@[@H%P```'1+BX at H%P``QP0D`````+X`````9H-Y`@!T,@^W
+M60*+5+$$9H,Z`'0?B?9FBP*+?"0 at 9CL'=0BX`0```.L2D(/"`F:#.@!UXT8Y
+M\W_2BP0D@\0$6UY?7<.-=@!75E.+?"04_W0D$.C`]___@\0$B<:Z`````(GV
+MC035`````"G09CDTA0`"``!U,;D`````C035`````"G0C1R%`````(T$RV8Y
+MN`0"``!U"(N`"`(``.L308/Y`G;FB?9"@_H&=K:X`````%M>7\.0BU0D!+@!
+M````]H+5`0``0'4>N`````"#NK`!```&=!!J`&H`4NBJ]___@\0,C78`PXUV
+M`%.+7"0,N`$```!FA=MT;?]T)`CH%/?__X/$!(G!9H7`>14/M\.)RH'B_W\`
+M`#G0#Y3`#[;`ZT5F/?\!=`JZ`````&:%P'41N`$```#K+K@!````ZR>-=@"-
+M!%+!X`-F.9C@````=0EF.8CB````=-Y"@_IL=N*X`````)!;PXGV_W0D!.BC
+M]O__@\0$B<&Z`````&:%P'DL@>'_?P``B?:-!%)F.0S%X````'0(0H/Z;';M
+MZR*X`0```,.X`0```,.-=@"-!%)F.8P`(`L``'3J0H/Z*G;MN`````##C78`
+M55=64X/L/(M$)&!FB40D-(U$)#A0C40D.E#_="18Z)WV__^#Q`RZ`````(7`
+M#X2K`@``BT0D.(T4Q0`````IPL'B`XN"Y!```(M,)%")@=@!``"#?"1D`'4,
+M@<)@$P``B50D,.L8BU0D.(T$U0`````IT(T$Q>`0``")1"0PQT0D+`````"#
+M[`S_="1<BU0D8/^2@`$``(E$)#C'1"0T`````(/$$(M,)#!F at WD,``^$%P(`
+M``^W1"0TB40D#(GVBU0D)(M,)#"+;)$0#[=U`,=$)"``````BT4$9H,X``^$
+MT0$```^W1"0TB40D$(M4)`R)5"0<P>H#B50D&(M,)!S!Z0*)3"04A70D*`^$
+MC@$``(M<)!"%WG4B5O]T)%3HS/C__X/$"*F`````#X1N`0``]L,L#X1E`0``
+MD(M$)%@Y1"0L#X."`0``BT4$BU0D(`^W/%`/M\=0#[?&4/]T)%CH'O?__P^W
+MV(/$#/?#@````'0DB=@ET````#W0````=!:!XP_^___V1"08`70%@,O`ZP2`
+MRZ"0A=L/A``!``"`Y_WWQ at P```!T68'_J`D``'0:@?^H"0``=PJ!_Z,)``!T
+M"NL/@?^T"0``=0>X`0```.L%N`$```"%P'0J@?^T"0``=0:`XS^`RZ`/M\=0
+M_W0D5.B$_/__@\0(A<!T"(#/`NL#@,\"BTPD+(M$)%1FB3R(9HE<B`)!B4PD
+M+(M4)%0/MT2*_B70````/<````!U*O9$)!0!=".+1"18.<$/@X<```"`XS^`
+MRZ!FB3R*9HE<B@)!B4PD+(UV`(M4)"R+3"14#[=$D?XE4`$``#U0`0``=2OV
+M1"0<`70DBT0D6#G"<TB!XZ_^__^!RT`!``!FB3R19HE<D0)"B50D+(GV_T0D
+M((M%!(M4)"!F at SQ0``^%4O[___]$)"2+3"0P#[=!##M$)"0/C_3]__^+1"1<
+MBU0D+(D0A=(/E<`/MM")T(/$/%M>7UW#BU0D!(M,)`AFBP)F.P%U&`^W4@*!
+MXO`!```/MT$")?`!```IPNL)D`^W$@^W`2G"B=##D%575E.#["R+?"1`BUPD
+M4(M$)%1FB40D(F;'1"0:6!M7Z%_\__^#Q`2Z`````(7`#X2C`P``5^A1]___
+M9HF'U@$``%?HR/?__X/$"(7`=`K'A]P!```!````9H._U@$```!U(0^WPU!7
+MZ)?[__^#Q`BZ`````(7`#X1;`P``9HF?U@$``(/L"`^WA]8!``!05^C\____
+MB40D+%?H,?O__X/$%(7`=$*#[`C_="1D9HM$)"XC1"0H#[?`4/]T)%S_="1<
+M_W0D7%?H&/S__X/$(+H`````A<`/A/P"``"+5"1,BRKI?`(``)"#?"18`'41
+M9H'[2`-U"F;'1"0:71:-=@"]`````+X$%@``@?X8%@``#X--`@``B?9FBUPD
+M(F:%'@^$+@(```^W!H5$)!P/A"$"``"-1"0J4(U$)"Q0#[=&`E!7_Y=X`0``
+M@\00A<`/A``"```/MT8"4`^WA]8!``!05^BE]?__B40D(`^W7@)35^A2\___
+M@\0(9HE$)!Y3#[>'U@$``%!7Z./U__]FB40D*,=$)"0`````@\08BT0D%&:#
+M>`(`#X2H`0``BU0D#(T$4HM<)!2-!$-FBU@&9CE8"`^"<P$``(U$)"2)1"0$
+M9CE<)"@/AT4!``!F.UPD*@^'.@$``&8[7"0:#X<O`0``9H'[M`EU#&:!?@+`
+M``^$'`$```^WPXM4)%P]J`D``'07/:@)``!W"3VC"0``=`GK"SVT"0``=02)
+MT.L%N`$```"%P`^$Y@```#ML)$@/@Q\!``!FB5PD)(U4)"1FBT8"9HE"`F:!
+M^[0)=0 at D/PR at 9HE"`HU4)"2)5"0(9H%B`O_]4@^W1"044%?H_??__X/$#(7`
+M=`R+1"0(9H%(`@`"ZU,/MP:H#'1,#[?#/:@)``!T%SVH"0``=PD]HPD``'0)
+MZPX]M`D``'4'N`$```#K!;@!````A<!T&P^WPU!7Z(SX__^#Q`B%P'0*BU0D
+M!&:!2@(``HM$)"2+5"1$B02J10^W1*K^)5`!```]4`$``'4>]D0D(@%T%SML
+M)$AS5(M$)`1F at V`"[XM$)"2)!*I%9 at -<)!*+5"0,C012BU0D%&8Y7$((#X.5
+M_O___T0D#(M<)!0/MT,".T0D#`^'6_[__XUV`(/&!('^&!8```^"M?W__X7M
+M=%5H@$4``&H$5?]T)%#H;`8``(/$$(/]0'8%O4````")K_@'``"Y`````#GI
+M<RB-!$F-!,>+7"1$9HL4BV:)D/@!``!FBU2+`F:)D/H!``!!.>ERVHGVBT0D
+M3(DH9HN'U@$``&:)1QB%[0^5P`^VT(UV`(G0@\0L6UY?7<.)]E575E.+3"04
+MBUPD&`^W>P*!Y_`!``"+D?P'``"%TG0<9HL"9CL#=10/MT(")?`!```Y^'1=
+MZP6)RNM7D(VQ^`$``(N1^`<``(72=$$/MQN0B=#1^(T$0(T,Q@^W`8G=*<6)
+MZ'4<#[=!`B7P`0``.?ATQ0^W00(E\`$``(G]*<6)Z(7`?@2-<1A*T?IUP[H`
+M````B=!;7E]=PXUV`%575E.#[`R+1"0D9HL`9HE$)`J+5"0 at xxxxx@$````/
+MA?D```!2Z(7N__^#Q`1FB40D"+@`````9H-\)`@`>!*+1"0()?\/``#K!XG0
+MZ2(!``!FB40D"&:%P`^$O@```+\`````C78`C03]`````"GXBUPD"&8Y',7@
+M$```#X61````BVPD)`^W70)3_W0D). at 7\/__@\0(B<93_W0D).C([___@\0(
+MB40D!+L`````.<-]8+D`````C00;B<(!V(T$@&:#?$8"`'0_B10DB?:-!!J-
+M!("-%$F-!!"-!$:-4`9FBVPD"F8Y:`9W"F8Y:@(/@U3___]!BQ0DC00:C02`
+M#[=$1@(YR'_&0SM<)`1\HXUV`$>#_PH/ADK___^+5"0D#[="`E"+7"0D#[>#
+MU@$``%!3Z%/Q__^)QK\`````@\0,9H-X`@!T)0^W2`*)]HT$?XT41HU"!F:+
+M;"0*9CEJ!G<&9CEJ"',*1SGY?^&X`````(/$#%M>7UW#_W0D"/]T)`CH=_[_
+M_X/$"+H`````A<!T&P^V4`71XHM$)`PHT`^^T(3`>0BZ`````(UV`(G0PY!5
+M5U93@^P<BWPD,(''Y`$```^V1P&#Z`.)1"00QT0D%`````"#^`(/CHD```"-
+M=@"+5"04C012`?AF#[90!6:)5"0:BE@'O@````"`>`8`=%&+1"04C2Q`A-MT
+M/(M4)#@X6 at 1R,XM$)#0/MP")1"0,@^P(BU0D/`^W0@)0#[=$)"90Z/S___^#
+MQ!`Y1"0,=0@/ML/K*8UV`$8/MD0]!CGP?[:#;"00`_]$)!2#?"00`@^/>O__
+M_XM4)#@/MD($@\0<6UY?7<.)]E93@^P$BUPD$(MT)!2#N]P!````=#.Z$@``
+M`/9&`H!U9[H`````9HL.C78`C012T>!F.XA`````<@EF.XA"````<CM"@_H&
+M=N-64^@T_?__@\0(N at 4```"%P'0L@[O@`0```'03@^P$4%93Z,C^__\/MM"#
+MQ!#K$`^V4`3K"@^VD$0```"-=@")T(/$!%M>PU575E.#[!"+1"0D9H.XU@$`
+M```/A1(!``!0Z(7K__^#Q`1FB40D#HM4)"AFBQ)FB50D#+@`````9H-\)`X`
+M>!1FBT0D#B7_#P``ZPB-!#GI]0```&:)1"0.9H7`#X3)````QT0D"`````")
+M]HM$)`C!X`,K1"0(9HML)`YF.2S%X!````^%D@```(M$)"@/MU@"4_]T)"CH
+M!.W__X/$"(G'4_]T)"CHM>S__X/$"(E$)`2^`````#G&?6&0NP````"-!#:)
+MP@'PC02`9H-\1P(`=$")%"2)]HT$,HT$@(T,`(T$6XT$00'XC5`&BVPD#&8Y
+M:`9W"F8Y:@(/@TS___]#BQ0DC00RC02`#[=$1P(YV'_%1CMT)`1\HHGV_T0D
+M"(-\)`@*#X9!____BU0D*`^W0@)0BVPD*`^WA=8!``!05>@Z[O__@\0,@\00
+M6UY?7<.-=@!3BUPD#%/_="0,Z)W^__\/ME`$#[=#`B7`````@\0(/<````!U
+M$HG0@^`/@_@!=0B#XO"#R@*)]HG06\-75E.+?"00BW0D%(/L#%?_EX`!``")
+MPX/$$*@"="H/M\905^B\\/__@\0(A<!U`X/C_0^WQE!7Z.CP__^#Q`B%P'4%
+M@^/?B?;VPP%T%P^WQE!7Z!'O__^#Q`B%P'4&@^/^C78`]L,(=!</M\905^C%
+M[O__@\0(A<!U!H/C]XUV`(G86UY?PXGVBU0D!(M$)`@/MT`"4`^W at M8!``!0
+M4NAUZO__N@````"#Q`RY[!<``&8Y!%%U!K@!````PT*#^@5V[K@`````PU57
+M5E.#[`R+;"0HBT0D((E$)`0!Z(D$)(M$)"1(B40D"'1#BSPDBT0D!#G'=B^)
+M]HG^*>Z#[`A75O]4)#R#Q!"%P'X9B?F)ZXH6B@&(!D:($4%+=?,I[SM\)`1W
+MTP$L)/],)`AUO8/$#%M>7UW#D)!55U93@>RH````BZPDO````&B4"0``Z/S_
+M__^)PX/$$+X"````A<`/A&@%``")QX/L!&@`"```:``8``!0Z/S___^+A"3$
+M````B4,,BX0DR````(E#$(N$),P```")0Q1FB:N@`0``9L>#H@$`````9L>#
+MS`$``#P`QX/0`0```````,>#Q`@```````#'@X0)````````QX.("0``____
+M_\>#C`D``/_____'@Y`)``#_____@\0(:@!3Z/S___^#Q!"^`P```(7`#X3%
+M!```QX.D`0```0```(M#%(N0($```&8/MM)FB9.H`0``BY`8F```9HF3J@$`
+M`&;'@ZX!`````,>`T)@``!8<``"^`````)"+1Q3'@("8``````$`1H/^`W;M
+MBT<4BX``G```P>@<@^P(:@10Z/S___]`9HF'K`$``(M7%(N"$$```(E$)!B#
+MR`&)@A!```"#Q`R-1"024&H]5^C\____@\00O@@```"%P`^$#`0``+X$````
+M9H%\)`ZE6@^%^@,``(/L!(U$)!)0:C]7Z/S___^#Q!"^"````(7`#X3:`P``
+M9HM$)`YFB8,""```@^P$C40D$E!HP0```%?H_/___X/$$+X(````A<`/A*L#
+M``!FBT0D#F;!Z`QFB8,`"```O at 4```!F at _@!#X6,`P``O0````"^`````(GV
+M@^P$C41T%%"-AL````!05^C\____@\00A<`/A%@#```/MT1T$#'%1H/^/W;3
+MO@<```"!_?__```/A4(#``"#[`2-1"024&B_````5^C\____@\00O@@```"%
+MP`^$'P,``&8/MD0D#F:)A]0!``"+1"049HF#!`@``&:+1"069HF#!@@``(M$
+M)!B(@P@(``!F#[9$)!F(@PD(``!FBT0D&HB#"P@``&8/MD0D&XB#"@@``&8/
+MMD0D'8B##@@``(M$)!R(@P\(``!F#[9$)!^(@Q`(``!FBT0D'HB#$0@``(I$
+M)""#X`&(@PP(``"+1"0 at 9M'H@^`'B(,-"```O@````"]"0```(T4]0`````I
+M\HT4E@':C8H2"```9HM$;!!FP>@*B((2"```9HM$;!!FP>@$@^`_B$$+#[=$
+M;!#!X`*#X#QFBU1L$F;!Z at X)T(A!`8I$;!.#X#^(00QFBT1L$F;!Z`*#X#^(
+M00(/MT1L$L'@!(/@,&:+5&P49L'J#`G0B$$-9HM$;!1FP>@&@^`_B$$#BD1L
+M%(/@/XA!#F:+1&P69L'H"HA!!&:+1&P69L'H!(/@/XA!#P^W1&P6P>`"@^`\
+M9HM4;!AFP>H."="(006*1&P9@^`_B$$09HM$;!AFP>@"@^`_B$$&#[=$;!C!
+MX`2#X#!FBU1L&F;!Z at P)T(A!$6:+1&P:9L'H!H/@/XA!!XI$;!J#X#^(01)F
+MBT1L'&;!Z`J(00AFBT1L'&;!Z`2#X#^(01,/MT1L','@`H/@/&:+5&P>9L'J
+M#@G0B$$)BD1L'X/@/XA!%&:+1&P>9L'H`H/@/XA!"@^W1&P>P>`$@^`P9HM4
+M;"!FP>H,"="(015FBT1L(&;!Z`:#X#^(01:*1&P@@^`_B$$79HM$;")FP>@*
+MB$$89HM$;")FP>@$@^`_B$$9#[=$;"+!X`*#X#QFBU1L)&;!Z at X)T(A!&HI$
+M;"6#X#^(01MFBT1L)&;!Z`*#X#^(01Q&@\4+ at _X$#X82_O__@^P,5^CW````
+MO0````"^`````(/$$(/L!(U$)!)0N!\````I\%!7Z/S___^#Q!"%P'1*#[=$
+M)`X!Q68/MD0D#XB$<Z,(``!FBT0D#HB$<Z0(``!&@_X"=KV%[70(@?W]_P(`
+M=0>^"0```.L at BT<4BU0D"(F0$$```(GXZT2^"````.L5O@@```"-=@"+1Q2+
+M5"0(B9`00```A=MT#(/L#%/H_/___X/$$(.\),``````=`F+A"3`````B3"X
+M`````('$G````%M>7UW#@^P8_W0D'.C\____@\0<P[@`````]D0D"0%T%XM$
+M)`QFQP``%(M$)!!FQP`V%;@!````PXM4)`2- at K0!``"#2`0!9L=`#``49L=`
+M#C85@(JT`0```H!@`7]FQT`(`P!FQT`*0`"X`0```,.05E.+3"0,BW0D$(M<
+M)!2+012)L"R```"#N;`!```!=#R+412)\"L%`````,'@`XF",(```(M1%(GP
+M*P4`````P>`#B8(T@```BU$4B?`#@80)``")@CB```#K*9"+013'@#"```#_
+M____BT$4QX`T@```_____XM!%,>`.(````$```"0@>/__X`!BT$4B9 at D@```
+M6U[#B?964X/L!(MT)!"[,@```(M6%(M"+*@$=0B+0 at BH`G0^D(/L#&H!Z/S_
+M__^#Q!!+=1R+1A2+5"04B5`$BT84QT`H"@```+@`````ZQ>0BU84BT(LJ`1U
+MRHM""*@"=<.X`0```(/$!%M>PXM4)`2+0A3'@"R`````````BTH4BX$$@```
+M#0``$``E__]?_XF!!(```(M"%,>`)(```/__``##5E.+="0,BTPD$&:#>1(`
+M=$>+5A2+@@2````E__]__PT``"``B8($@```BU84#[=!$(F"*(```(M6%`^W
+M01*)@DB```"+5A2+013!X`.)@C2```#K%XUV`(M6%(N"!(```"7__U__B8($
+M@```BU84BP&)@BR```"+7A2+ at R2````E``"`_P^W40@)PF:#>1@`=!$/MT$8
+M@\`$P>`0)0``?P`)PHF3)(```&:#OJ@!```#=B4/MT$:J`=T'8M.%(N1&(``
+M`(#F^,'@""4`!P``"<*)D1B```"06U[#D(M$)`2+0!2+@`A```"%P`^5P`^V
+MP,.)]E.+7"0,BTPD"(M!%(M0'(/Z_W40QP,`````N`````#K.8UV`(G0(X&L
+M"```);K8!0&)`_?"(`!P`'0'#0```$")`_;"!70#@PL!]\+`!0``=`.#"T"X
+M`0```%O#BT0D!(N`K`@``,.05U93BW0D$(M<)!2)\8N^K`@``(7_>0V+1A3'
+M0"0`````C78`B=J!XKK8!0'VPP%T`X/*!?;#0'0S@[FP"````'0#@\I`@[FT
+M"````'0#@,X!@[FX"````'0#@,J`@[F\"````'0&@,X$C78`BT84B5`@B9FL
+M"```A=MY#(M&%,=`)`$```")]HGX6UY?PY"0N$````##B?:+1"0(9H/X/W<?
+M#[?`P>`%BU0D!`-"%(N`')```+H!````J0"```!U!;H`````B=##C78`BTPD
+M!(M$)`BZ`````&:#^#\/AX$````/M\#!X`6)P at -1%,>"`)````````")P at -1
+M%,>"!)````````")P at -1%,>"")````````")P at -1%,>"#)````````")P at -1
+M%,>"$)````````")P at -1%,>"%)````````")P at -1%,>"&)`````````#013'
+M@!R0````````N@$```")T,.05U93BWPD$(M4)!B+="04N`````!F at _X_=VJ%
+MTG0X#[9:!<'C"`^V0 at 0)PP^V2@/!X1@/MD("P>`0"<$/MD(!P>`("<$/M@()
+MP='IB=C!X!\)P='KZPJ[`````+D`````#[?&P>`%B<(#5Q2)BAB0```#1Q2)
+MVH#.@(F0')```+@!````6UY?PU575E.#[!"+="0LBT0D*&:)1"0. at WPD-`$9
+MR??1@>&JJJJJN`````!F at WPD#C\/AU`!``"X`````(`^``^%0@$``+@`````
+M9H-^`@0/AC(!``#'!"0`````9H-^`@5V$&:#?@(.&=*#XOZ#P at .)%"0/MFX$
+M#[9&!<'@"`G%#[9&!L'@$`G%#[9&!\'@&`G%,<T/MD8)P>`(#[96"`G",<J!
+MXO__``")5"0(#[9^"@^V1 at O!X`@)QP^V1 at S!X!`)QP^V1 at W!X!@)QS'/#[9&
+M#\'@"`^V5 at X)PC'*@>+__P``B50D!`^V7A`/MD81P>`("<,/MD82P>`0"<,/
+MMD83P>`8"<,QRV:#?@(-=P:!X_\````/MTPD#HG(P>`%B<*+="0D`U84B:H`
+MD```B<(#5A2+="0(B;($D```B<*+="0D`U84B;H(D```B<(#5A2+="0$B;(,
+MD```B<*+="0D`U84B9H0D````T84BQ0DB9`4D```_W0D,%%6Z/S___^#Q`R-
+M=@"#Q!!;7E]=PX/L$&H&BT0D&`6C"```4/]T)"#H_/___X/$',.#[!!J!O]T
+M)!R+1"0<!:,(``!0Z/S___^X`0```(/$',.-=@!3@^P(BUPD$(M3%(M$)!2+
+MA((`8```:@)J`V@`;```4^C\____@\00N@````"%P'05BT,4BY``:```BT0D
+M&&:)$+H!````B="#Q`A;PXUV`%.+7"00BTPD"+H,````BT0D#&8Y@=0!``!T
+M&`^W at 0((``#!Z`>#X`&#^`$9TH/B^(/""X7;=`*)$[@`````6\.X`0```,.)
+M]HM$)`2`N`P(````#Y7`#[;`PXGV4X/L$(M<)!AJ`%/H_/___X/$#(7`#Y3`
+M#[;`4&H`4^C\____@\086\.-=@!3BT0D"(M8%(M,)`S1X;H#````T^+WTHN#
+M%$```"'"N`(```#3X`G"B9,40```N`$```!;PXUV`%.+1"0(BU at 4BTPD#-'A
+MN`,```#3X/?0BY,40```(=")@Q1```"X`0```%O#4XM,)`R+1"0(BU at 4BY,8
+M0```N/[____3P"'"BT0D$(/@`=/@"<*)DQA```"X`0```%O#D(M,)`BX____
+M_X/Y!7<5BT0D!(M`%(N`'$```(/@+]/H@^`!PU93@^P$BW0D$(M,)!2+1A2+
+MF!1```")RL'B#-'AN`,```#3X`G0#0"``0#WT"'#"=.`SX"#?"08`'0&@<L`
+M``$`BT84B9 at 40```@^P(BX:L"```#0````%05NC\____@\046U[#C78`4XM<
+M)`B+3"0,BT,4BY`00```A<ET"H/Y!'0-ZQ.-=@"#XI_K$8UV`(/BWX/*0.L&
+M@\H@@^*_BT,4B9`00```6\.-=@"+1"0$BT`4BX`$@```P>@7@^`!@_@!&<"#
+MP`+#BTPD!(M4)`B+012+@`2```"I``"``'0(@_H"=0C#B?:#^@%T#HM1%`T`
+M`(``B8($@```PU93@^P(BW0D%(M<)!QJ!O]T)!R-AL@(``!0Z/S___^+3A0/
+MMI;("```#[:&R0@``,'@"`G"#[:&R@@``,'@$`G"#[:&RP@``,'@&`G"B9$(
+M@```@\00BTX4#[:&S0@``,'@"`^VELP(```)PHG8)?\_``#!X!`)PHF1#(``
+M`&:%VW05@^P$:@!J`%;H_/___X/$$.L/C78`@^P,5NC\____@\00@\0$6U[#
+MB?93BT0D"(M(%(N!<(```(G"N`````"+B6R```"[``````G("=I;PXGVBT0D
+M!(M`%(N`;(```,.)]HM$)`2+4!2+ at B2````-`````8F")(```,.-=@"+5"0$
+MBT(4BX!DF```B<'!Z1.!X?\!``")R,'H"(7`=`:!\0#^__^+4A2+ at G"```"+
+MDFR````QT#'(PY"+1"0$#[>0J`$``(M`%(N`($```"7_````.<(/E,`/MM")
+MT,.+3"0$BT0D"(M1%(N2F(````$0BU$4BY*4@````5`$BU$4BY*<@````5`,
+MBU$4BY*0@````5`(BU$4BY*@@````5`0PXM4)`2+0A3'0"0`````BXJL"```
+M@,U`B8JL"```BT(4B4@@BT(4QT`D`0```,.)]E=64XM\)!"+="04B?N#_ at AV
+M%H/L"&C__P``5^C\____@\00.?!S$I#'@X@)``#_____N`````#K(X/L"%97
+MZ/S___^+5Q2)@A"```"#Q!")MX@)``"X`0```(GV6UY?PX/L%(M4)!B+0A2+
+M@!"````E__\``%!2Z/S___^#Q!S#55=64X/L%(ML)"AH_Q\``%7H_/___X/$
+M$#M$)"1S$<>%C`D``/____^X`````.L\BW44BYX4@```@>,`X/__@^P(_W0D
+M+%7H_/___R7_'P``"<.)GA2```"#Q!"+1"0DB86,"0``N`$```"0@\0,6UY?
+M7<.#[!2+5"08BT(4BX`4@```)?\?``!04NC\____@\0<PU575E.#[!2+;"0H
+M:/\?``!5Z/S___^#Q!`[1"0D<Q''A9`)``#_____N`````#K0(MU%(N>%(``
+M`('C__\`X(/L"/]T)"Q5Z/S____!X!`E``#_'PG#B9X4@```@\00BT0D)(F%
+MD`D``+@!````B?:#Q`Q;7E]=PX/L%(M4)!B+0A2+@!2````E``#_'\'H$%!2
+MZ/S___^#Q!S#D+@`````PXGVPXUV`,.-=@"#[`R+1"04BU0D&(/X`74, at _H!
+M&<#WT(/@#>L2_W0D'%)0_W0D'.C\____@\00@\0,PXGV@^P,BT0D$(M,)!2+
+M5"0<@_D+=1R#X@:)D,@!``"+0!2)D&B```"X`0```.L9C78`@^P,_W0D+%+_
+M="0L45#H_/___X/$((/$#,.)]H/L%/]T)"S_="0L_W0D+/]T)"S_="0L_W0D
+M+.C\____@\0LPU.+7"0(BTPD$(M3%(N"!(```"7__W__#0``!`")@@2```"#
+M?"0,`'01BU,4#[?!#0```P")@@1```!;PU93@^P$BW0D$(-\)!0`=&2+1A3'
+M@`1`````````@^P,:-`'``#H_/___[L*````@\00B?:+1A2+@!!```"I```!
+M`'0B@^P,:,@```#H_/___XM&%,>`!$````````"#Q!!+==#K!H7;=0J)]K@`
+M````ZQJ0BU84BX($@```)?__>_^)@@2```"X`0```(/$!%M>PY"+3"0$BU$4
+MBX($@```)?__?_\-```$`(F"!(```(-\)`@`=`V+013'@`1```````$`PXGV
+M5U93BU0D$(M<)!2+3"08BT0D'(G6OP$```"#^P)T)8/[`G<(@_L!=`KK.9"#
+M^P-T).LQ#[?`4%%2Z+[^__^#Q`SK*9"#[`A14NCJ_O__B<>#Q!#K%U%2Z'#_
+M__^#Q`CK"XUV`+@`````ZPF0B9[$"```B?A;7E_#BT0D!(N`Q`@``,.0N```
+M``##B?:+1"0$BU`4BX($@```)?__;_^)@@2```"X`0```,.)]HM$)`2+4!2+
+M@@2````E__]__PT``!``B8($@```N`$```##D%.#[`B+1"04 at _@!=`^[````
+M`(/X`G05ZPN-=@"[`````.L)D+@`````ZQ.0@^P(4_]T)!SH_/___XG8@\00
+M@\0(6\.0BT0D!(M`%(M`#,.0BT0D!(M0%(M$)`B)0 at S#D(M$)`2+0!3'0`@$
+M````PY!64X/L!(MT)!"+1A3'0`@@````NP````"+1A2+0`BH!'4*N`$```#K
+M'HUV`(/L#&H*Z/S___^#Q!!#@?OG`P``?M:X`````(/$!%M>PXUV`(M$)`2+
+M4!2+ at FB```"#X+^)@FB```##D(M$)`2+4!2+ at FB```"#R$")@FB```##D(M,
+M)`2+412+1"0(B8)0@```BU$4BT0D#(F"5(```,.04XM4)`B+3"0,N`````"#
+M^3]W0H/Y'W8 at BU(4BYI4@```@^D at N/[____3P"'8B8)4@```ZQN-=@"+4A2+
+MFE"```"X_O___]/`(=B)@E"```"X`0```%O#D%.+5"0(BTPD#+@`````@_D_
+M=T*#^1]V((M2%(N:5(```(/I(+@!````T^`)V(F"5(```.L;C78`BU(4BYI0
+M@```N`$```#3X`G8B8)0@```N`$```!;PY"+1"0$BT`4BX!,@```PXGVBU0D
+M"/;&`G0&@.;]@\H at BT0D!(M`%(F03(```,.-=@"+5"0,BTPD",=!"`````")
+MT"7_#P``B4$,N``````Y40QU(?9$)!`@=`>!20P`(```QT$4`````,=!$```
+M``"X`0```,.-=@!75E.+?"00BW0D%(GSBT0D'+H/````]D84`0^$!P$``/9`
+M%`%U%8M'%(M`#+H/````.T0D&`^$[````&:+0Q`E_P\``&:)1B"+2Q2!X0"`
+M_P_!Z0^+1Q2+@&R```")PL'J"H'B__\``(G0)?\?```YR',1C8(`X/__)?__
+M```)P>L$B?8)T8G()?]_``!FB48BQD8D`(M#%*@"=3BH!'0(QD8D`>LNB?;V
+M0Q00=`:`3B0(ZR#V0Q0(=`:`3B0$ZQ2`3B0"BT,4)>````#!Z`6(1B6)]HM#
+M$"4``/@'P>@3B$8FBT,4]L0!=`XE`'X``,'H"8A&)^L%D,9&)_^+0Q`E`(`'
+M`,'H#XA&*(M#$,'H#H/@`8A&*8M#$,'H#(/@`8A&*KH`````B=!;7E_#D)"0
+M55=64X/L#(M\)""+;"0LB7PD"+H,````BT0D*/9``P$/A/(%``"#?"0D!G0A
+M at WPD)`9W"8-\)"0!=PGK$8-\)"0(=`JZ#````.G*!0``BT<4BX`00```B<.#
+MXV"#[`C_="0P5^C\____@\00N@,```"%P`^$GP4``(M/%(MT)`@/MI:C"```
+M#[:&I`@``,'@"`G"#[:&I0@``,'@$`G"#[:&I@@``,'@&`G"B9$`@```@WPD
+M)`$/A)T```"#?"0D`7)8 at WPD)`9T$(-\)"0(#X3"````Z?D```"+3Q2+1"0(
+M#[:0J`@``,'B"`^V@*<(```)T`T``%$`B8$$@```BT<4QT`H`````(M'%,>`
+M$$```$`0``#IN````(M/%(MT)`@/MI:H"```P>((#[:&IP@```G0#0``4@")
+M at 02```"+1Q3'0"@!````BT<4QX`00```)!```.MZBT\4BT0D"`^VD*@(``#!
+MX@@/MH"G"```"=`-```4`(F!!(```(M'%,=`*`````"+1Q3'@!!````D$```
+MZSR+3Q2+="0(#[:6J`@``,'B"`^VAJ<(```)T`T``!``B8$$@```BT<4QT`H
+M`````(M'%,>`$$```$`0``"+5Q2+ at A!````)V(F"$$```(M/%(M$)`@/MI#(
+M"```#[:`R0@``,'@"`G"BUPD"`^V@\H(``#!X!`)P@^V@\L(``#!X!@)PHF1
+M"(```(M/%`^VD\T(``#!X@@/MH/,"```"=")@0R```"+1Q3'``````"+1Q3'
+M0`0`````BT<4QT`,`````(M'%(M0',=`(`````"+1Q3'0"0`````QX.L"```
+M`````(M'%(M0+,=`,`4```"+1Q3'0#0%````BT<4QT!$"````(M'%,=`2`@`
+M``"+1Q3'0%``````BT<4QT!,`````(M'%,>`#$````````"+1Q3'0$``````
+MBT<4QX`8@`````<``(M'%,>`2(````````"#[`AJ`%?H_/___XM'%,>`4(``
+M``````"#Q!"+1Q3'@%2`````````BT<4QX!8@````````(M'%,>`7(``````
+M``"+1Q3'@&"````!````BT<4QX!D@````0```(M'%,>`:(````````"+1Q3'
+M@"B`````````BT<4QX`L@````````(M'%,>`;(````````"+1Q3'@#"```#_
+M____BT<4QX`T@```_____XM'%,>`.(````$```"Y`````+X`(```D(L<SH7M
+M=`V- at P"`__\]_P\``'8*BU<4BT3.!(D$&D&!^:8```!VV8/L"/]T)#!7Z/S_
+M__^#Q!"Z`P```(7`#X0H`@``BT\4BY$HF```,/:+="0(#[:&"0@``,'@"`G"
+MB9$HF```BU\4#[:6"@@``(G0P>`8P>(0"=`/MHX+"```B<K!X@@)T`G(B8,T
+MF```BT\4BY%$F```@>)_P/__9HN&!`@``&;1Z"6`/P``"<*)D428``"+3Q2+
+MD4B8``"!XO\/_/\/MX8$"```P>`*)0#P`P`)PHF12)@``(M/%(N19)@``('B
+M_P_X_P^VA@@(``#!X`PE`/`'``G"B9%DF```BT\4BY$0F0``@^+\#[>&!`@`
+M`(/@`PG"B9$0F0``@^P(_W0D,%?HQ`P``(/$$+H#````A<`/A#`!``"+1Q3'
+M@!R8```!````@^P,:.@#``#H_/___XM7%(N"8)@``(/(`8F"8)@``(/$$&H`
+M:@%H8)@``%?H_/___X/$"/]T)#!7Z/S___^#Q!"[`````(7`=1.+1"0H9H-(
+M`@*Z`P```.G&````@^P(4U?H_/___X/$$$.#^P)^[8M4)`B`N at P(````=`R#
+M[`Q7Z/S___^#Q!"+5Q2+ at B2````E__]__HF")(```(M<)`B#NX@)``#_=!*#
+M[`C_LX@)``!7Z/S___^#Q!"+="0(@[Z,"0``_W02@^P(_[:,"0``5^C\____
+M@\00BT0D"(.XD`D``/]T$H/L"/^PD`D``%?H_/___X/$$(._R`$```!T#XM7
+M%(N'R`$``(F":(```(M4)"2)E[`!``"X`0```.L0BUPD,(,[`'0"B1.X````
+M`(/$#%M>7UW#BTPD!(M!%(N`!(```"7__P``BY&P`0``@_H!=#.#^@%R'H/Z
+M!G0)@_H(=#/#C78`BU$4#0``40")@@2```##D(M1%`T``%(`B8($@```PY"+
+M410-```4`(F"!(```,.+410-```0`(F"!(```,.)]H/L$&H*:@C_="0<Z#@#
+M``"#Q!S#4X/L"(M<)!!J`&H!:@)3Z/S___^#Q!"Z`````(7`=%"#[`1H($X`
+M`&H/4^@$`P``@\00N@````"%P'0T@^P,:.@#``#H_/___X/$#&@@3@``:A]3
+MZ-L"``#'!"0T"```Z/S___^Z`0```(/$$(UV`(G0@\0(6\.05E.#[`2+="00
+MBUPD%&H`:@%J`E;H_/___X/$$+H`````A<`/A)<```"+1A2Z`````(7;=`CV
+M0P(0=`*R`8F0!)@``(/L!&@@3@``:@]6Z&D"``"#Q!"Z`````(7`=&&#[`QH
+MZ`,``.C\____@\0,:"!.``!J'U;H0`(``(/$$+H`````A<!T.(/L#&@T"```
+MZ/S___]J`&H!:@)6Z/S___^#Q""Z`````(7`=!.#[`1J"FH`5N@"`@``B<*#
+MQ!"0B="#Q`1;7L-55U93@^P8BVPD+(M5%(N":(```(/(8(F":(```(M5%(NZ
+M)(```(GX)?__?_^)@B2```!HH`\``.C\____BU44BX((F```#0````B)@@B8
+M``#'!"0*````Z/S___^#Q`C_="0L5>@^"0``@\00N@````"%P`^$;@$``(/L
+M#&CH`P``Z/S___^+512+@@B8```E____]XF"")@``(/$!(M%%(N86)@``(NP
+M7)@``(N0:)@``(E4)!2)VH'*``#\`XF06)@``(M5%(GP)7\`P/\-@,`_`(F"
+M7)@``(M5%(M$)!0E'P#^_PV`$0``B8)HF```:A3H_/___XM5%(N"")@```T`
+M```(B8((F```QP0D"@```.C\____BT44QX#4F```(0```(M5%(N"")@``"7_
+M___WB8((F```QP0DZ`,``.C\____BU44BX)@F```@\@!B8)@F```@\00:@!J
+M`6A at F```5>C\____@\0(BT44B9A8F```BT44B;!<F```BT44BU0D$(F0:)@`
+M`/]T)"Q5Z/S___^#Q!"%P'4L@^P,:(@3``#H_/___X/$"/]T)"Q5Z/S___^#
+MQ!"%P'4+BT0D)&:#2`("B?:+112+D&B```"#XI^)D&B```"+112)N"2```"Z
+M`0```(G0@\0,6UY?7<.)]E=64XM\)!"+="04B?"%]G4%N/____^)PXM'%(FP
+M`$```(/L#/]T)"3H_/___X/F#X/C#U93:`!```!7Z/S___^)PH/$(/?&!```
+M`'4*BT<4QT`4`````(G06UY?PXUV`%93@^P$BTPD%(I$)!B(1"0#NP````"Z
+M"@````^VP(UP`8UV`(U$$@,Z1"0#=1J`/!$_=`P/M at 01Z9X```"-=@"[`0``
+M`.MQD(U$$@,/ML`YQG51A=)^38`\$3]T/X!\"O\_=#@/MAP1#[9,"O\IRXT$
+MFXT$@(T4Q0`````IPHV<$N<#``"XTTUB$/?CB=#!Z`:-'`@/ML/K0HUV`+L!
+M````ZQ60 at _L!=0^`/!$_=`D/M at 01ZR:-=@!*#XEE____N@````"`/!$_=`8/
+MM at 01ZPM"@_H*?NZX`0```(/$!%M>PXUV`%575E.#[`2+7"0<BVPD)(I4)""_
+M_____[[_____N0````")]H`\"S]T*C at 4"W1/.!0+<P*)SC at 4"W89B<^#_O]U
+M&HU$"0.(10`/MD0+"^F2````D$&#^0I^RNL%@_D*?BNX/P```(/^_W1Z at _D*
+M?AR-1#8#B$4`#[9$,POK9XU$"0.(10`/MD0+"^M9C40^`XA%``^VT@^V##,I
+MRHT4DHT4DHT4DHTLU0`````/M at 0[*<B)P8GHF??YB<4/MD0["P^V3#,+*<B)
+MZ@^OT('"YP,``+C336(0]^*)T,'H!@)$,PL/ML"#Q`1;7E]=PXUV`(M4)`B#
+M^CQV!;H\````BT0D!&:)D,P!``"X`0```,.055=64X/L$(ML)"2+5"0HBWPD
+M+(EL)`1FBX4&"```9L'H!(/@!XA'#XJ%!@@``(/@!XA'$&:+`BTR%```N0``
+M``!F/98`#X=,`@``NP````"+3"0$9@^VA`L."```9CF%U`$``'0&0X/[`W;D
+MN0````"#^P0/A!T"```/MPJ!Z3(4``"X9V9F9O?IP?H"B<C!^!^)T2G!@_D,
+M@]'_N*NJJJKWX8G1T>F-!,T`````*<B--(&+1"0$C;0&$@@``(I$'AF(1PZ*
+M1A8Z1!X9=@2*1!X9B$<)BD87.D0>&78$BD0>&8A'"(I&&#I$'AEV!(I$'AF(
+M1P>-1"0/4`^V1!X94%95Z.G]__^(PXU$)!Y0#[9'"5!65>C6_?__B$0D(X/$
+M((U$)`U0#[9'"%!65>B^_?__B$0D$HU$)!Q0#[9'!U!65>BI_?__B,*#Q""#
+MO=`!````#X3T````BX70`0``9HN,`$HE```/ME0D#P^WP8/``SG"?0?&1"0/
+M`^L$*$PD#P^V1"0/4%95Z'[\__^(1PZ-1"074`^V1PY05E7H3OW__XC#BD0D
+M*X/$'#I$)`YV!(I$)`Z(1"0.#[;`4%95Z$C\__^(1PF-1"074`^V1PE05E7H
+M&/W__XA$)!^*1"0K@\0<.D0D#78$BD0D#8A$)`T/ML!05E7H$/S__XA'"(U$
+M)!=0#[9'"%!65>C at _/__B$0D'HI$)"N#Q!PZ1"0,=@2*1"0,B$0D#`^VP%!6
+M5>C8^___B$<'C40D%U`/MD<'4%95Z*C\__^(PH/$'`^V1"0/T>!FB87.`0``
+MBD<.B$<*B$<+B$<,B$<-B- at HT(@'B- at J1"0"B$<!B- at J1"0#B$<"QD<&`,9'
+M!0#&1P0`QD<#`+D!````B<B#Q!!;7E]=PXUV`%=64X/L=(N\)(0```!J1&A@
+M)0``C40D#%#H_/___X/$#&H1:#@E``"-7"1<4^C\____4_^T))@```!7Z!S]
+M__^#Q!RZ`````(7`#X1@`@``BD0D7TBZ`````#P$#X=.`@``BD0D8$BZ````
+M`#P$#X<\`@``O@````"-=@`/M]Z#[`AJ!0^V1!Q<4.C\____B$0<8(/$$$9F
+M at _X&=M^^!P```(GV#[?>@^P(:@8/MD0<7%#H_/___XA$'&"#Q!!&9H/^#G;?
+M#[9$)%'!X`4EX`````^V5"10@^(?"=`)!"0/ME0D4\'B!X'B@`````^V1"12
+MP>`"@^!\"<**1"11P.@#@^`#"<()5"0$#[94)%3!X at 2!XO````"*1"13T.B#
+MX`\)P at E4)`@/ME0D5L'B!H'BP`````^V1"15T>"#X#X)PHI$)%3`Z`2#X`$)
+MP at E4)`P/ME0D5\'B`X'B^````(I$)%;`Z`*#X`<)P at E4)!`/ME0D6<'B!X'B
+M@`````^V1"18T>"#X'X)PHI$)%?`Z`6#X`$)P at E4)!0/ME0D6L'B!8'BX```
+M`(I$)%G0Z(/@'PG""50D&`^V5"1;P>(#@>+X````BD0D6L#H`X/@!PG""50D
+M'`^V5"1<T>*#XGZ*1"1;P.@%@^`!"<()5"0@#[9$)%W!X`4EX`````E$)"0/
+MME0D7L'B`X'B^````(I$)%W`Z`.#X`<)P at E4)"B*1"1>P.@%@^`!"40D+(/L
+M"&H##[9$)&M0Z/S____!X`<E@`````E$)#"#Q`AJ`P^V1"1K4.C\____T>B#
+MX`,)1"0T@\0(:@,/MD0D;%#H_/___\'@`H/@'`E$)#2^`````(/$$(GVBT<4
+M#[?6BQ24B9"<F```1F:#_@]VZHM'%`^WUHL4E(F0U)@``+H!````B?:)T(/$
+M<%M>7\.-=@!75E.+7"00BWPD%(/L"%=3Z/S___^)QH/$$+@`````A?9T3H/L
+M"&H%#[</@>D`%```N&=F9F;WZ8G0P?@"P?D?*<A0Z/S____1X(/(08M3%(F"
+MG)@``(/$$(M#%,>`P)@```````")L_P'``"X`0```%M>7\.-=@"+1"0$BT`4
+MBX!DF```P>@3)?\!``#VQ`%T!34`_O__F,.-=@!64X/L!(MT)!"+1A2+D&"8
+M``"#R@*)D&"8``!J`&H":&"8``!6Z/S___^#Q!"Z`````(7`=#N[`````)"#
+M[`QHZ`,``.C\____5NC\____F(/$%$.#^Q1_!X/XN'_>ZPRZ`````(/XN'\'
+MB?:Z`0```(G0@\0$6U[#C78`N`````##D)!3@^P(BUPD$(M4)!2X`````(/Z
+M`G\>@^P$_W0D'(T$4HT$@(V$@]`(``!04^C\____@\00@\0(6\.-=@!3@^P(
+MBUPD$(M4)!2X`````(/Z`G\>@^P$C012C02`C82#T`@``%#_="0 at 4^C\____
+M@\00@\0(6\.-=@!55U93@^P,BVPD((MT)"2)ZH/^`G06 at _X"=PF#_@%T'.LB
+MB?:#_ at -T"^L9D+\"````ZQF0OP$```#K$9"_`````.L)D+C_____ZVB0C01_
+MC02`C9R"T`@``+C_____ at WL$`'5/@^P(:CQ3Z/S___^)<P2#Q!"#?"0H`'4D
+MQT,,"P```,=#%`(```#'0QC_____9L=#(`H`9L=#(@H`ZQ*0@^P$_W0D+%=5
+MZ/S___^#Q!")^(/$#%M>7UW#D%.+3"0,BU0D"+L`````@_D"=TF-!$F-!("-
+MA(+0"```NP````"#>`0`=#''0`0`````N/[____3P"&"L`@``"&"M`@``"&"
+MN`@``"&"O`@``"&"P`@``+L!````B=A;PU575E.+="04BTPD&(GSBY;\!P``
+MN`````"#^0(/APX"``"-!$F-!("-O(;0"```N`````"#?P0`#X3R`0``N`$`
+M``"#?P0!#X7C`0``]D("$'1XBT84QX`0@```X`$``(M&%,>`%(`````(``B+
+M1A3'@""```#/C]T!BU84BT<4C01`C02`P>`0C8````\`#>`!``")@D"```"+
+M1A3'@$2```#``ZX%BU84BX)$F```@^"`@\@XB8)$F```BT84QX`$F```(R``
+M;^MZBT84QX`0@```:`$``(M&%,>`%(`````$``2+1A3'@""```"GC]T!BVX4
+MBU<4C022C01"C02"C03%,`(``,'@"PTP`@``B85`@```BT84QX!$@```F`/7
+M!(M6%(N"1)@``(/@@(/('(F"1)@``(M&%,>`!)@``"`0`&^X#P```(-_&/]T
+M`XM'&(MV%(G"P>(4#[='(L'@!"7P````"<(/MT<@@^`/"<*!R@`@"`")EAR`
+M``#V1PP!=`^X`0```-/@"8.P"```ZPVX_O___]/`(8.P"```]D<,`70/N`$`
+M``#3X`F#M`@``.L-N/[____3P"&#M`@``/9'#`)T#[@!````T^`)@[@(``#K
+M#;C^____T\`A@[@(``#V1PP$=`^X`0```-/@"8.\"```ZPVX_O___]/`(8.\
+M"```]D<,"'0/N`$```#3X`F#P`@``.L-N/[____3P"&#P`@``+@!````C78`
+M6UY?7<.-=@"+3"0$BT0D"(T$0(T$@+K_____@[R!U`@```%U!8M!%(L0B=##
+MC78`BU0D!(M$)`B+3"0,C01`C02`BX2"U`@``(/X`70. at _@!<AF#^`-W%.L*
+MB?:+0A2)".L1D(M"%(E(!.L(N`````##B?:X`0```,.)]E93@^P0BW0D'%;H
+M_/___XG#@\0()?___W]05NC\____BT84BY!D@```@\00 at WPD%`!T#;@E````
+M*=#1Z`'"ZQR#^@%V`TKK%(/L"%-6Z/S___^X`````(/$$.L;BT84B9!D@```
+M@^P(4U;H_/___[@!````@\00@\0$6U[#C78`BU0D!(M$)`B-!$"-!("+A(+4
+M"```@_@"=#J#^`)W"8/X`70,ZSJ)]H/X`W0/ZS&0BT(4QT`(`0```.LLBT(4
+MQT`(`@```(M"%,=`*`X```#K%HGVBT(4QT`H"@```.L(N`````##B?:X`0``
+M`,.)]HM,)`2+1"0(C01`C02`N@````"#O('4"````743BT$4BT`4)0!X``")
+MPL'J"XUV`(G0PY!64X/L!(MT)!"+1"04C01`C02`BX2&U`@``(/X`70'@_@"
+M=$WK7HM&%,=`"`@```"[`````(UV`(M&%(M`%/;$>'06@^P,:@KH_/___X/$
+M$$.!^^<#``!^WXM&%,=`"`````"!^^<#```/GL`/ML#K&&H`:@1J+%;H_/__
+M_X/$$.L'B?:X`````(/$!%M>PY"X`0```,.)]E=64XM4)""+?"0PBW0D.(M,
+M)!2-0OV[````#(/X`78%B=/!XQJ+1"08)?\/``"+5"0HP>(2"="+5"0<P>(,
+M@>(`\`,`"=`)V/?&`0```'0%#0````'WQA````!T!0T````@B<*#?"0T`'0&
+M@<H````"B5$(@___=!N)^,'@#24`X`<`B4$,B=`-````0(E!".L*B?;'00P`
+M````D/?&!````'03 at 4D(``!``(M$)$`E``#X_PE!#+@!````6UY?P[@`````
+MPXGV4XM,)!"+7"08BU0D#(-\)!0`=!2+0 at P)R(7;=0.`S!")0 at SK)XUV`(7;
+M=!"+1"0<BT`(B4((B4H,ZQ&0QT((`````(G(@,P0B4(,D,="%`````#'0A``
+M````N`$```!;PXUV`(M,)`B)RK@/````]D$4`0^$@````&:+010E_A\``&:)
+M02`/MT$29HE!(L9!)`"+01"H`74?J`)T!,9!)`'V0A`(=`2`220"]D(0!'0'
+M@$DD!(UV`(M"""4``#P`P>@2B$$EBT(4)0#@'P#!Z`V(02:+0A`E``\``,'H
+M"(A!)XM"$"7P````P>@$B$$HQD$J`+@`````PXGVPY"0D(/L#(M,)!"+013'
+M at -"8```6'```N@````"-=@"+013'@("8``````$`0H/Z!W[MBT$4BX``G```
+MP>@8B<+!Z at 2#X`_!X`0)T(/L"&H(4.C\____@\0<PY!55U93@^P8BVPD+&@4
+M&0``Z/S___^)PX/$$+X"````A<`/A"D$``")QX/L!&@`"```:,`E``!0Z/S_
+M__^+1"0TB4,,BT0D.(E#$(M$)#R)0Q1FB:N@`0``9L>#H@$`````9L>#S`$`
+M`#P`QX/0`0```````,>#W!@```````#'@]@8````````QX/X&```_____\>#
+M_!@``/_____'@P`9``#_____@\0(:@!3Z/S___^#Q!"^`P```(7`#X2/`P``
+M9H&[H`$``!OQ=2R+0Q3'@`"B````````BT,4QX!\F```&0```(/L#&CH`P``
+MZ/S___^#Q!")]HM'%(N`($```"7_````B<+!Z at 2)EZ0!``"#X`]FB8>H`0``
+M@^H"O at T```"#^@(/AR0#``"+1Q2+@!B8``!FB8>J`0``@^P,5^A'`P``@\00
+MO at X```"%P`^$^P(``(._I`$```-V$HM'%,>``)@```<```#K$(UV`(M'%,>`
+M`)@``$<```"#[`QHT`<``.C\____B3PDZ!;^__]FB8>L`0``#[?`)?````"#
+MQ!"^#0```(/X$`^%G@(``(/L!(U$)`Y0:,$```!7Z/S___^#Q!"^"````(7`
+M#X1[`@``O at 4```!F at 7PD"O\O#X9I`@``9HM$)`IFB8,`"```BT<4BX`00```
+M@^`8P>@#O at H```"#^`(/A4`"``"#[`2-1"0.4&H_5^C\____@\00O@@```"%
+MP`^$(`(``&:+1"0*9HF#`@@``+T`````O@````")]H/L!(U$)`Y0C8;`````
+M4%?H_/___X/$$(7`#X3@`0``#[=$)`HQQ4:!_C\#``!VT+X'````@?W__P``
+M#X7'`0``9L>#=@D```H`9L>#@`L```,`O@````"-!':-!(9FQX2#?@D```L`
+M1H/^"7;JO@````"ZP"T``&:+!')FB81SB`L``&:)A'.""P``C01VC02&C02#
+M9L>`,`P```L`9L>`E`L```L`1H/^`G;+@^P(C8,`"```4%?H_/___X/$$+X(
+M````A<`/A#L!``"#OZ0!```#=FMF@[L8"````'1ABT<4QX``F```!T```(/L
+M#&C0!P``Z/S___^)/"3H7OS__V:)AZX!``"+1Q3'@`"8```'````QP0DT`<`
+M`.C\____#[>'K@$``"7P````@\00O at T```"#^"`/A<D```#K"6;'@Q@(````
+M`(/L!(U$)`Y0:+\```!7Z/S___^#Q!"^"````(7`#X2;````9HM$)`IFB8,$
+M"```9HF'U`$``(/L#%?H]P$``(D\).C\____O0````"^`````(/$$(/L!(U$
+M)`Y0N!\````I\%!7Z/S___^#Q!"%P'0[#[=$)`H!Q68/MD0D"XB$<U at 6``!F
+MBT0D"HB$<UD6``!&@_X"=KV%[70*B?B!_?W_`@!U.[X)````ZQ*^"````.L+
+MC78`O@@```"-=@"%VW0,@^P,4^C\____@\00 at WPD,`!T!HM$)#"),+@`````
+M@\0,6UY?7<.)]H/L&/]T)!SH_/___X/$',-55U93@^PLBW0D0,=$)`@`@```
+MQT0D#""8``#'1"0055555<=$)!2JJJJJQT0D&&9F9F;'1"0<F9F9F;\`````
+MC6PD"(T$O0````"+'"B+5A2+%!J)%`2Y`````(G(P>`0B<()RHM&%(D4&(M&
+M%(L$&#G0=`>X`````.MA08'Y_P```'[7N0````"+5(P0BT84B108BT84BP08
+M.<)T"K@`````ZSB-=@!!@_D#?MR-!+T`````BTX4BQ0HBP0$B0011X/_`0^.
+M>?___X/L#&IDZ/S___^X`0```(/$$(/$+%M>7UW#C78`BU0D#(M,)!"+1"0(
+M]L0!=!-FQP(X$V;'`=07N`$```##C78`J8````!T(8M$)`1F@[@8"````'03
+M9L<""`EFQP&L"K@!````PXUV`+@`````PXGV5E.#[`2+="00C9ZT`0``9H.^
+MU`$```%U$X/L!(U$)`10:A!6Z/S___^#Q!#'0P0`````9H.^%@@```!T&\=#
+M!`$```!F@[X&"````'4*QT,$`P```(UV`&:#OA@(````=`2#2P0$9L=#$`@)
+M9L=#$JP*9L=###@39L=##M07@`L#@$L!@(!+`@%FQT,("@!FQT,*@`"X`0``
+M`(/$!%M>PU=64XM,)!"+="04BUPD&(G/BT$4B;`H@```BX&P`0``@_@!=`^#
+M^`%R)H/X!G0A at _@(=42+013'@"R```#__P``BT$4QX`P@```__\'`.LHBU$4
+MB?`K!0````#!X`.)@BR```"+412)\"L%`````,'@`XF",(```(M1%(U&`8._
+MV!@```!T"(GP`X?8&```B8(T@```@>/__X`!BT$4B9@@@```6UY?PU93@^P$
+MBW0D$+L`````B?:#[`AJ`E;H_/___X/$$(7`=!:#[`QJ"NC\____@\000X'[
+MYP,``'[8@?OG`P``#Y[`#[;`@\0$6U[#B?:+5"0$BT(4QX`H@````````(M*
+M%(N!!(````T```0`)?__S_^)@02```"+0A3'@""```#__P``PU93BW0D#(M<
+M)!!F at WL2`'0_BU84BX($@```#0``$`")@@2```"+5A0/MT,0B8(D@```BU84
+M#[=#$HF".(```(M6%(M#%,'@`XF",(```.L4BU84BX($@```)?__[_^)@@2`
+M``"+5A2+`XF"*(```(M.%(N!((```"4``(#_#[=3"`G"9H-[&`!T$0^W0QB#
+MP`3!X!`E``!_``G"B9$@@```BTX4BY$8@```,/8/MT,:P>`()0#_```)PHF1
+M&(```(M.%(N1!$```('B``#__XM#'(/H`\'@`R7__P``"<*)D01```!;7L.+
+M1"0$BT`4BX`(0```A<`/E<`/ML##B?:+3"0(BT0D!(M`%(N0P````(/Z_W4.
+MQP$`````N`````##B?:)T"6ZV`4!B0'WP@``"`!T!PT```!`B0'VP at 5T`X,)
+M`??"P`4``'0#@PE`]L(@=`:!"0```$"X`0```,.+1"0$BX!D%@``PY!75E.+
+M="00BUPD%(GQB[YD%@``A?]Y$8M&%,=`)`````"+1A2+0"20B=J!XKK8!0'V
+MPT!T,X.Y:!8```!T`X/*0(.Y;!8```!T`X#.`8.Y<!8```!T`X#*@(.Y=!8`
+M``!T!H#.!(UV`/;#`70#@\H']\,```!`=`:!R@``"`"+1A2)D*````")F606
+M``"%VWD+BT84QT`D`0```)")^%M>7\.0D+B`````PXGVBT0D"&:#^']W'P^W
+MP,'@!8M4)`0#0A2+@!R(``"Z`0```*D`@```=06Z`````(G0PXUV`(M,)`2+
+M1"0(N@````!F at _A_#X>!````#[?`P>`%B<(#413'@@"(````````B<(#413'
+M@@2(````````B<(#413'@@B(````````B<(#413'@@R(````````B<(#413'
+M at A"(````````B<(#413'@A2(````````B<(#413'@AB(`````````T$4QX`<
+MB````````+H!````B=##D%=64XM\)!"+5"08BW0D%+@`````9H/^?W=JA=)T
+M.`^V6 at 7!XP@/MD($"<,/MDH#P>$8#[9"`L'@$`G!#[9"`<'@"`G!#[8""<'1
+MZ8G8P>`?"<'1Z^L*NP````"Y``````^WQL'@!8G"`U<4B8H8B````T<4B=J`
+MSH")D!R(``"X`0```%M>7\-55U93@^P0BWPD+(M$)"AFB40D#H-\)#0!&<GW
+MT8'AJJJJJK@`````9H-\)`Y_#X=\`0``#[8'@_@!=!2#^`%_!X7`=!3K39"#
+M^`5T/.M%D,<$)`4```#K1[@`````9H-_`@0/AD<!``#'!"0`````9H-_`@5V
+M*6:#?P(.&=*#XOZ#P at .)%"3K%\<$)`<```#K#HGVN`````#I$@$``(GV#[9O
+M!`^V1P7!X`@)Q0^V1P;!X!`)Q0^V1P?!X!@)Q3'-#[9'"<'@"`^V5P@)PC'*
+M@>+__P``B50D"`^V=PH/MD<+P>`("<8/MD<,P>`0"<8/MD<-P>`8"<8QS@^V
+M1P_!X`@/ME<."<(QRH'B__\``(E4)`0/ME\0#[9'$<'@"`G##[9'$L'@$`G#
+M#[9'$\'@&`G#,<MF at W\"#7<&@>/_````#[=,)`Z)R,'@!8G"BWPD)`-7%(FJ
+M`(@``(G"`U<4BWPD"(FZ!(@``(G"BWPD)`-7%(FR"(@``(G"`U<4BW0D!(FR
+M#(@``(G"`U<4B9H0B````T<4BSPDB;@4B```_W0D,%'_="0LZ/S___^#Q`R#
+MQ!!;7E]=PX/L$&H&BT0D&`58%@``4/]T)"#H_/___X/$',.#[!!J!O]T)!R+
+M1"0<!5 at 6``!0Z/S___^X`0```(/$',.-=@!3@^P(BUPD$(M3%(M$)!2)@@!@
+M``"+0Q3'@`A@```!````:@)J`V@,8```4^C\____@\00N@````"%P'05BT,4
+MBY`$8```BT0D&&:)$+H!````B="#Q`A;PXUV`%.+7"00BTPD"+H,````BT0D
+M#&8Y@=0!``!T&`^W at 0((``#!Z`>#X`&#^`$9TH/B^(/""X7;=`*)$[@`````
+M6\.+5"0$N`````!F@[H6"````'0.9H.Z!@@```$9P(/@`D!F@[H8"````'0#
+M@\@$PY!3@^P(BUPD$&:#NPH(````=$:#[`2-1"0*4&H/4^C\____@\00N@``
+M``"%P'16#[=4)`:)T(/@','X`HF#!!D``(/B`M'ZB9,(&0``QX,0&0```0``
+M`.L>QX,(&0```````,>#!!D```````#'@Q`9````````9H.["@@````/E<`/
+MMM")T(/$"%O#D%.#[!"+7"08:@!3Z/S___^#Q`R%P`^4P`^VP%!J`%/H_/__
+M_X/$"/^S!!D``%/H_/___XM3%(N"`)@``(#DWXF"`)@``(/$"/^S!!D``%/H
+M_/___XF##!D``(/$##N#"!D```^5P`^VP%#_LP09``!3Z/S___^#Q!A;PY!3
+MBT0D"(M8%(N3%$```(M,)`S1X;@#````T^`)PHF3%$```+@!````6\.)]E.+
+M1"0(BU at 4BY,40```BTPD#-'AN`,```#3X/?0(<*)DQ1```"X`0```%O#4XM,
+M)`R+1"0(BU at 4BY,80```N/[____3P"'"BT0D$(/@`=/@"<*)DQA```"X`0``
+M`%O#D(M,)`BX_____X/Y!7<5BT0D!(M`%(N`'$```(/@+]/H@^`!PX/L#(M,
+M)!"+012+D!1```"!XOQ__O^`SH"#?"08`'0&@<H```$`BT$4B9`40```@^P(
+MBX%D%@``#0````%04>C\____@\0<PXM$)`2+2!2+ at 1!````EG__Q_XM4)`B#
+MX@<+!)7 at +0``B8$00```PY!64X/L"(M<)!2+="0<:@;_="0<C8->%@``4.C\
+M____BTL4#[:37A8```^V at U\6``#!X`@)P@^V at V`6``#!X!`)P@^V at V$6``#!
+MX!@)PHF1"(```(M+%`^V at V,6``#!X`@/MI-B%@``"<*!YO\_``#!YA`)\HF1
+M#(```(/$%%M>PU.+1"0(BT at 4BX%0@```B<*X`````(N)3(```+L`````"<@)
+MVEO#B?:+1"0$BT`4BX!,@```PXGVBT0D!(M0%(N"((````T````!B8(@@```
+MPXUV`(M4)`2+0A2+@&28``")P<'I$X'A_P$``(G(P>@(A<!T!H'Q`/[__XM2
+M%(N"4(```(N23(```#'0,<C#D%.+5"0(BT(4BX`@0```)?\```")P8/A#[L`
+M````P>@$.8*D`0``=0QF.8JH`0``=0.S`9")V%O#BTPD!(M$)`B+412+DI"`
+M```!$(M1%(N2C(````%0!(M1%(N2E(````%0#(M1%(N2B(````%0"(M1%(N2
+MF(````%0$,.#[!2+1"08BU`4QX)4F0```0```(N09!8``(#.0%)0Z/S___^#
+MQ!S#5U93BWPD$(MT)!2)^X/^"'86@^P(:/__``!7Z/S___^#Q!`Y\',.D(FS
+M^!@``+@`````ZR.#[`A65^C\____BU<4B8)P$```@\00B;?X&```N`$```")
+M]EM>7\.#[!2+5"08BT(4BX!P$```)?__``!04NC\____@\0<PU575E.#[!2+
+M;"0H:/\?``!5Z/S___^#Q!`[1"0D<Q''A?P8``#_____N`````#K/(MU%(N>
+M%(```('C`.#__X/L"/]T)"Q5Z/S___\E_Q\```G#B9X4@```@\00BT0D)(F%
+M_!@``+@!````D(/$#%M>7UW#@^P4BU0D&(M"%(N`%(```"7_'P``4%+H_/__
+M_X/$',-55U93@^P4BVPD*&C_'P``5>C\____@\00.T0D)',1QX4`&0``____
+M_[@`````ZT"+=12+GA2```"!X___`."#[`C_="0L5>C\____P>`0)0``_Q\)
+MPXF>%(```(/$$(M$)"2)A0`9``"X`0```(GV@\0,6UY?7<.#[!2+5"08BT(4
+MBX`4@```)0``_Q_!Z!!04NC\____@\0<PY"X`````,.)]L.-=@##C78`BT0D
+M!(M`%(N`')P``"7_````PY"+1"0$BT`4BX!8@```@^`'PXUV`(M$)`2+4!2+
+M1"0(@^`'B8)8@```PXUV`%575E.+?"04BVPD&(G[BT0D'`^W0`(EX`$``#W`
+M````="H]P````'\+/:````!T%.LBB?:^`````#U``0``="#K$HGVO@$```#K
+M%9"^`@```.L-D+@`````Z;L```")]@^WC'-X"```#[>$<WX(``#!X`8)P0^W
+MA'.$"```P>`,"<$/MX1SB@@``,'@$@G!#[>$<Y`(``#!X!@)P0^WE'.6"```
+M#[>$<YP(``#!X`8)P@^WA'.B"```P>`,"<(/MX1SJ`@``,'@$@G"#[>$<ZX(
+M``#!X!@)PH/]`70/@_T!<AJ#_0)T">L+C78`B<KK#(G1ZPBX`````.L>D(FK
+MW!@``(M'%(F(8)D``(M'%(F09)D``+@!````6UY?7<.)]H/L#(M$)!2+5"08
+M at _@!=12#^@%V![`- at _H%=1JX`````.L3D/]T)!Q24/]T)!SH_/___X/$$(/$
+M#,.)]H/L#(M$)!"+3"04BU0D'(/Y"W4<@^(&B9#(`0``BT`4B9!(@```N`$`
+M``#K&8UV`(/L#/]T)"Q2_W0D+%%0Z/S___^#Q""#Q`S#B?:#[!3_="0L_W0D
+M+/]T)"S_="0L_W0D+/]T)"SH_/___X7`#Y7`#[;`@\0LPU.+7"0(BTPD$(M3
+M%(N"!(```"7__]__#0``!`")@@2```"#?"0,`'01BU,4#[?!#0```@")@@1`
+M``!;PU93@^P$BW0D$(-\)!0`=&"+1A3'@`1`````````@^P,:@KH_/___[L*
+M````@\00D(M&%(N`$$```*D```$`="*#[`QHR````.C\____BT84QX`$0```
+M`````(/$$$MUT.L&A=MU"HGVN`````#K&I"+5A2+@@2````E___[_XF"!(``
+M`+@!````@\0$6U[#D(M,)`2+412+@@2````-```$`(F"!(```(-\)`@`=`V+
+M013'@`1```````$`PXUV`%.+7"0(BTPD$(M3%(N"!(````T```0`B8($@```
+M at WPD#`!T$8M3%`^WP0T```(`B8($0```6\.05U93BU0D$(M<)!2+3"08BT0D
+M'(G6OP$```"#^P)T*8/[`G<(@_L!=`_K39"#^P-T*(/[!'0OZT`/M\!045+H
+MB?[__X/$#.LX@^P(45+HMO[__XG'@\00ZR=14N at X____@\0(ZQL/M\!045+H
+M6/___X/$#.L+C78`N`````#K"9")GM08``")^%M>7\.+1"0$BX#4&```PY"X
+M`````,.)]K@`````PXGVN`````##D)!3@^P(BT0D%(/X`G0G at _@"=PJ#^`%T
+M#>LCC78`@_@$=`OK&9"[`````.L9D+L`````ZQ&0NP````#K"9"X`````.L3
+MD(/L"%/_="0<Z/S___^)V(/$$(/$"%O#D(M$)`2+0!2+0`S#D(M$)`2+4!2+
+M1"0(B4(,PY"+1"0$BT`4QT`(!````,.0@^P,BU0D$(M"%,=`""````!J`&H$
+M:@A2Z/S___^%P`^5P`^VP(/$',.-=@"+1"0$BU`4BX)(@```@^#?B8)(@```
+MPY"+1"0$BU`4BX)(@```@\@@B8)(@```PY"+3"0$BU$4BT0D"(F"0(```(M1
+M%(M$)`R)@D2```##D%.+5"0(BTPD#+@`````@_D_=T*#^1]V((M2%(N:1(``
+M`(/I(+C^____T\`AV(F"1(```.L;C78`BU(4BYI`@```N/[____3P"'8B8)`
+M@```N`$```!;PY!3BU0D"(M,)`RX`````(/Y/W="@_D?=B"+4A2+FD2```"#
+MZ2"X`0```-/@"=B)@D2```#K&XUV`(M2%(N:0(```+@!````T^`)V(F"0(``
+M`+@!````6\.0BT0D!(M`%(N`/(```,.)]HM$)`2+4!2+1"0(B8(\@```PXGV
+MBU0D#(M,)`C'00@`````B=`E_P\``(E!#+@`````.5$,=2'V1"00('0'@4D,
+M`"```,=!%`````#'01``````N`$```##C78`4XM,)`R)RHM$)!2[#P```/9!
+M%`$/A,<```#V0!0!=1F+1"0(BT`4BT`,NP\````[1"00#X2H````9HM"$"7_
+M#P``9HE!((M"%"4`@/\/P>@/9HE!(L9!)`"+0A2H`G4LJ`1T",9!)`'K(HGV
+M]D(4$'0&@$DD".L4@$DD`HM"%"7@````P>@%B$$EB?:+0A`E``#X!\'H$XA!
+M)HM"%/;$`70.)0!^``#!Z`F(02?K!9#&02?_BT(0)0"`!P#!Z`^(02B+0A`E
+M````.,'H&XA!*8M"$,'H#(/@`8A!*KL`````B=A;PY"0D%575E.#[&R+K"2`
+M````B6PD)&;'1"06``!FQT0D%```QT0D$`````#'1"0,`````(N4)(@````/
+MMT("B<+!Z@?!Z`@QT+H,````J`$/A+X(``"+C"2(````#[=!`HG"P>H&P>@%
+M,="Z#````*@!#X2<"```@^P(_[0DD````%7H_/___XE$)#"#Q!"Z#````(7`
+M#X1X"```@[PDA`````9T*H.\)(0````&=PR#O"2$`````7<,ZQ2#O"2$````
+M"'0*N at P```#I1`@``(.\)(P`````=$R+112+F$R```")7"00BY!0@```B50D
+M#(.]I`$```-V#(N`0!$``(E$)##K(+X`````BU44C02U`````(N$`D`1``")
+M1+0P1H/^"7;HBT44BX!8@```B40D"(7`=0C'1"0(`0```(M5%(N"!(```"4`
+M```"B40D&(N"$$```"5@`/X`B40D'(N*%$```(E,)"R+DAA```")5"0H@^P(
+MBYPDD`````^W0P)05>C\____@\00N@,```"%P`^$>P<``(N4)(@````/MT("
+M)?`!```]P````'10/<````!_"3V@````=#+K3CU``0``=`D]4`$``'02ZSYF
+MQT0D%@$`9L=$)!0!`.LN9L=$)!8"`&;'1"04`0#K'F;'1"06`P!FQT0D%`(`
+MZPYFQT0D%@0`9L=$)!0"`(.]I`$```-V#XM%%,>``)@```<```#K#8M%%,>`
+M`)@``$<```"#O:0!```##X;E````BXPDB`````^W00*I@`````^$I0```(M<
+M)"1F@;L`"````#`/AI(```"H('04#[>[_`@```^W at P`)``")1"0$ZQ:+5"0D
+M#[>Z_@@```^WB@()``")3"0$@^P(:@-7Z/S___^)QX/$"&H#_W0D$.C\____
+MB40D%`^W7"0DN<P,``"+%)F`XC^)^,'@!B7`````"<*)%)F^V`P``(L,GH/A
+M\(GZP>H"@^(!BT0D%-'@@^`."<()T8D,GH/$$+X`````OZ`+```/MUPD%(T$
+M=HM-%(L4A:`+```!V(L$AXD$$4:#_AMVY8/L"/^T))````!5Z,<,``"^````
+M`(/$$+\`+@``#[=<)!:0C02VBTT4BQ2%`"X```'8BP2'B0011H/^(G;EO@``
+M``"_(#8```^W7"04B?:-!':+312+%(4 at -@```=B+!(>)!!%&@_X_=N6^````
+M`+O`,```O\0P``")]HL,\X.\)(P`````=`V- at 0"`__\]_P\``'8)BU44BP3W
+MB00*1H'^J@```';4@[VD`0```W=$BU44BXH<@```B<@E__\/``T``-`!B8(<
+M@```BT44QT!<`````(M%%,>`>)@```@```"+512+ at DB```"`S`2)@DB```"#
+MO"2,`````'14BT44BUPD$(F83(```(M%%(M4)`R)D%"```"^`````(.]I`$`
+M``-V$8M5%(M$)#")@D`1``#K'8GVC0RU`````(M5%(M$M#")A`I`$0``1H/^
+M"7;EBTT4BUPD)`^VDU at 6```/MH-9%@``P>`("<(/MH-:%@``P>`0"<(/MH-;
+M%@``P>`8"<*)D0"```"#O"2$`````71U@[PDA`````%R1(.\)(0````&=`^#
+MO"2$````"'1^Z:(```"+312+1"0D#[:0718``,'B"`^V@%P6```)T`M$)!@-
+M``"!`(F!!(```.MWBTT4BUPD)`^VDUT6``#!X@@/MH-<%@``"=`-``""`(F!
+M!(```.M0BTT4BT0D)`^VD%T6``#!X@@/MH!<%@``"=`-```@`(F!!(```.LI
+MBTT4BUPD)`^VDUT6``#!X@@/MH-<%@``"=`+1"08#0``(`")@02```"+512+
+M at A!````+1"0<B8(00```BT44BU0D+(F0%$```(M%%(M,)"B)B!A```"+112+
+M7"0(B9A8@```BTT4BT0D)`^VD%X6```/MH!?%@``P>`("<*+7"0D#[:#8!8`
+M`,'@$`G"#[:#818``,'@&`G"B9$(@```BTT4#[:38Q8``,'B"`^V at V(6```)
+MT(F!#(```(M%%,>`@````/____^#O:0!```$=19F@[VH`0```7<,BU44BT(4
+M@,P$B4(4BT44QX`8@`````<``(/L"/^T))````!5Z/S___^#Q!"Z`P```(7`
+M#X2K`@``@^P(_[0DD````%7H`PT``(/$"/]T)"A5Z$H&``"#Q!"Z`P```(7`
+M#X1^`@``9H&]H`$``!OQ=1J+A"2(````]D`"@'0-BT44QX`(V````@4``(M%
+M%,>`')@```$```"+112+@!29``")P8'A_S\``(N4)(@```#V0@(@=!6-%(T`
+M````N*.++KKWXHG0P>@$ZPRXS<S,S/?AB=#!Z`.#[`R#P&10Z/S___^+512+
+M at F"8``"#R`&)@F"8``"#Q!!J`&H!:&"8``!5Z/S___^#Q`C_M"20````5>C\
+M____@\00A<!U'(N,)(@```#V00(@=05F at TD"`KH#````Z:H!``"+7"0D@[O@
+M&````'07BT44QX`@F0```/`!`,>#Y!@```$```"[`````+X!````C3R=````
+M`(M5%(GPB-G3X(F$.@`0``!#@_L)?N.[`````(/L"%-5Z/S___^#Q!!#@_L)
+M?NV+113'@*0````!``$`BT44QX"H`````0```(M%%,>`K`````$```"+512+
+M at K`0``")@K`0``"+113'@*````!E"0@`BUPD),>#9!8``&4)"`"+512+ at JP`
+M```-```'`(F"K````(.\)(0````&=1R+512+ at J````"`S!")@J````"!BV06
+M````$```@^P,5>C\____@\00A<!T#(/L#%7H_/___X/$$(M5%(N"((```"7_
+M_W_^B8(@@```BT0D)(.X^!@``/]T$H/L"/^P^!@``%7H_/___X/$$(M4)"2#
+MNOP8``#_=!*#[`C_LOP8``!5Z/S___^#Q!"+3"0D@[D`&0``_W02@^P(_[$`
+M&0``5>C\____@\00@[W(`0```'0/BU44BX7(`0``B8)(@```BYPDA````(F=
+ML`$``+@!````ZQ:-=@"+A"20````@S@`=`*)$+@`````@\1L6UY?7<.0@^P4
+M:@+_="0<Z#(#``"#Q!S#B?93@^P(BUPD$&H`:@%J`E/H_/___X/$$+H`````
+MA<!T+(/L"&H34^@!`P``@\00N@````"%P'05@^P,:#0(``#H_/___[H!````
+M@\00B="#Q`A;PY!64X/L!(M<)!"+="04:@!J`6H"4^C\____@\00N@````"%
+MP`^$AP$``(GP)?`!```]P`````^$B0```#W`````?Q8]H````'0]/;````!T
+M-ND/`0``C78`/4`!```/A*@````]0`$``'\,/=````!T4^GN````/5`!```/
+MA(H```#IW@```(M#%,>`!)@```````"+0Q3'@`"B```#````BT,4QX!\F```
+M&0```(/L#&@L`0``Z/S___^#Q!#IH@```(GVBT,4QX`$F````````&:#NZ`!
+M```2#X6%````BT,4QX!\F```&````(/L#&@L`0``Z/S___^+0Q3'@`"B```"
+M````@\00ZUGWQA````!T#XM#%,>`!)@```,```#K#8M#%,>`!)@```````!F
+M@[N@`0``$G4KBT,4QX!\F```&````(/L#&@L`0``Z/S___^+0Q3'@`"B````
+M````@\00D(/L"&H34^AI`0``@\00N@````"%P'0U@^P,:#0(``#H_/___VH`
+M:@%J`E/H_/___X/$(+H`````A<!T$(/L"&H`4^@M`0``B<*#Q!")T(/$!%M>
+MPU575E.#[`R+="0 at BWPD)(.^Y!@````/A(````"+7A2+ at R"9``"I```!`'5P
+MQX;D&````````(NK$)P``(N#%)P``(N3&)P``(E4)`B)ZM'JB<'1Z0'*B<'!
+MZ0;!Z@>)%"1T.87)=#6+1"0(]]B9]SPD@^`_B40D!(GHF??YB<&#X1^+1"0$
+MP>`%BY, at F0``"=`)R(#,"(F#()D``(/L"%=6Z"(#``"#Q!"%P'43]D<"('4%
+M9H-/`@*X`````.M9D(/L"%=6Z/S___^#Q!"%P'4S@^P,:(@3``#H_/___X/$
+M"%=6Z/S___^#Q!"%P'45]D<"('4%9H-/`@*X`````.L7C78`@^P,5N at W&```
+MN`$```"#Q!"-=@"#Q`Q;7E]=PU=64XM\)!"+="04B?"%]G4%N/____^)PXM'
+M%(M0#(FP`$```(/L#&H/Z/S___^#Y at .#XP-64V@`0```5^C\____B<*#Q"#W
+MQ@$```!U"HM'%,=`%`````")T%M>7\.)]E575E.#[!2+;"0L#[=%`E`/MT4`
+M4.C\____@\00]D4"@'0]#[_XC1Q_P>,"C;L$.@``@^P(:@C_=P3H_/___XG&
+M@>;_````P>8%BX,$.@``P>`$"<9FBT<(@\00ZP>)]KX`````9CV0`'\$J`%T
+M(8/L"&H(F(/H&%#H_/___XG#@>/_````N`$```"#Q!#K*(/L"&H(F(/H&(G"
+MP>H?`=#1^%#H_/___XG#@>/_````N`````"#Q!#!XP+1X`G#@<L!!```BT0D
+M((M(%(GP)?\```#!X`@/MM,)T(F!G)@``(M$)""+2!2)\"4`_P``#[;7"=")
+M@="8``"+1"0 at B:C\!P``N`$```"#Q`Q;7E]=PXUV`(M$)`2+0!2+@&28``#!
+MZ!,E_P$``/;$`70%-0#^__^8PXUV`%=64XM\)!"*7"04BT\4BY%DF```@>(`
+M\/__B=C!X`DE``X```G"#[]$)!@E_P$```G"B9%DF```BU<4BX)@F```@\@"
+MB8)@F```OC@```"$VW01#[;SC03U`````"GPB<;!Y at F[`````(UV`(M'%(N`
+M8)@``*@"=!2#[`Q6Z/S___^#Q!!#@_L[?N'K!8/[.WX'N`````#K"E?H_/__
+M_YB#Q`1;7E_#D(M,)`R+5"0$BT0D"`^W0`(EX`$``#W`````=#`]P````'\)
+M/:````!T%NLL/4`!``!U)6:+ at EH(``!FB0'K(9!FBX)<"```9HD!ZQ1FBX)>
+M"```9HD!ZPBX`````,.)]K@!````PXGV4X/L!(M<)`R-1"0"4/]T)!13Z('_
+M__^#Q`RZ`````(7`="R+0Q2+D&"8``"+@&28``#!Z!,E_P$``/;$`70%-0#^
+M__]F.T0D`@^>P`^VT(G0@\0$6\.)]E93@^P$BUPD$(MT)!2#NZ0!```#=U:#
+M[`A64^C\____@\00A<!T18U$)`)05E/H#?___X/$#+H`````A<!T1+X`````
+M@^P$:@`/MH9(.P``4%/H0?[__X/$$$:#_@)VY(/^`P^6P`^VT.L8D(M3%(N"
+M8)@``(/(`HF"8)@``+H!````B="#Q`1;7L.055=64X/L/(M4)%2+1"10B40D
+M+/9"`H`/E,!F#[;`9HE$)`BX`@```&8K1"0(9HE$)`@/MT(")>`!```]P```
+M``^$Q@$``#W`````?Q`]H`````^$9P$``.G\`0``/4`!```/A?$!``!FBP(M
+MH0\``&8]Z at 1W)8M4)"QFBY+D"```9HE4)`R+="0L9HNVY@@``&:)="0*Z9<`
+M``!FBP(MC!0``&8][P!W(HM$)"QFBX#H"```9HE$)`R+5"0L9HN2Z@@``&:)
+M5"0*ZV=FBP(M?!4``&8]X`!W(HMT)"QFB[;L"```9HET)`R+1"0L9HN`[@@`
+M`&:)1"0*ZS=F at 3I<%G8BBU0D+&:+DO`(``!FB50D#(MT)"QFB[;R"```9HET
+M)`KK#F;'1"0*``!FQT0D#```BT0D+&:+J&8(``!FBY!L"```9HE4)!!F@[AL
+M"`````^4P&8/ML!FB40D#@^W3"0(NSP-``"+%(N!XO___^^+="0L#[>&7`D`
+M`,'@'`G"B12+NT at -``"+%(N!XO____L/MX9>"0``P>`:"<*)%(N[_`T``(L4
+MBX/B]P^WAEH)``#!X`,)PHD4B^FD````BT0D+&:+@/0(``!FB40D#(M4)"QF
+MBY+V"```9HE4)`J+="0L9HNN:`@``&:+AFX(``!FB40D$&:#OFX(````#Y3`
+M9@^VP&:)1"0.ZU>+5"0L9HN2^`@``&:)5"0,BW0D+&:+MOH(``!FB70D"HM$
+M)"QFBZAJ"```9HN0<`@``&:)5"009H.X<`@````/E,!F#[;`9HE$)`[K"K@`
+M````Z1,!```/MW0D"+^$#0``BQRW at .,_@^P(:@0/M^U5Z/S____!X`</MU0D
+M'L'B!@G0)<`````)PXD<M[^0#0``BQRW@^/X@\0(:@15Z/S____1Z(/@!PG#
+MB1RW at .-_@\0(:@,/MVPD&%7H_/___\'@!R6`````"<.)'+>_G`T``(L<MX/C
+M_(/$"&H#5>C\____T>B#X`,)PXD<MX/CXX/$"&H##[=$)!90Z/S____!X`*#
+MX!P)PXD<M[G,#0``BQ2Q@^+WBT0D(,'@`X/@"`G"B12QNP````"#Q!"_``T`
+M``^W="0(B?:-!%N+5"10BTH4BQ2%``T```'PBP2'B0010X/[&';ABW0D+,>&
+M[!@```````"X`0```(/$/%M>7UW#B?955U93@^P,BWPD((G^BU0D)`^W0@(E
+MX`$``#W`````=$L]P````'\,/:````!T->M#C78`/4`!``!U.;T`````BT\4
+MBY%$F0``@^+'#[>'8`D``,'@`X/@.`G"B9%$F0``ZQV0O0$```#K%9"]`@``
+M`.L-D+@`````Z=<!``")]H._I`$```-W%XM7%(N"$)D``(/(`XF"$)D``.M7
+MC78`BT\4BX$0F0``)0;\__\/MY1N<@@``,'B!`G0@\@!B8$0F0``@^P$_W0D
+M*/^VW!@``%?H_/___XM7%`^_A&Y:"```)?\!``"`S`*)@FB9``"#Q!"0BT\4
+MBY%$F```@>)_P/__#[>$;C`(``#!X`<E@#\```G"B9%$F```BT\4BY%(F```
+M@>+_#_S_#[>$;C8(``#!X`PE`/`#``G"B9%(F```BT\4BY%0F```@>(``/__
+M#[Z$-5<(``#!X`@E`/\```G"#[:$-50(```)PHF14)@``(M?%`^WE&Y("```
+MB=#!X!C!XA`)T`^WC&Y."```B<K!X@@)T`G(B8,TF```BT\4BY$HF```,/8/
+MMX1N/`@``,'@"`G"B9$HF```BT\4BY%DF```@>+_#_C_#[>$;D((``#!X`PE
+M`/`'``G"B9%DF```NP(```!F@;X`"````C!W'&:!OZ(!```B$'4UBT0D)/9`
+M`D!T*[,(ZR>-=@"+5"0D9HL"@^`?#[?`A<!T$X/H"H/X#'8+#[>$;K0(```!
+MPY"+3Q2+D229``"`X@&-!!LE]P````G"B9$DF0``N`$```"#Q`Q;7E]=PXUV
+M`(M,)`2+5"0(B="#^CQV!;@\````9HF!S`$``(M!%(F0/)D``+@!````PXGV
+M55=64X/L+(M\)$2+7"1`#[='`B7@`0``/<````!T5CW`````?PX]H````'1[
+MZ:H```")]CU``0``#X6=````9HN#=@D``&:)1"04C8-B"0``B40D$(V#>`D`
+M`(E$)!@/MX/&#```B40D"(VKR`P``.F#````9HN#@`L``&:)1"04C8.""P``
+MB40D$(V#C at L``(E$)!@/MX,8#0``B40D"(VK&@T``.M09HN#@`L``&:)1"04
+MC8.("P``B40D$(V#*@P``(E$)!@/MX,X#0``B40D"(VK. at T``.L=D+@`````
+MZ98```")T,'@!HV<&%`-``")7"0,ZV2#[`0/MP=0C40D&%#_="1,Z'<```#'
+M1"0<`````(/$"%?_="1,Z/S___^)QKH`````@\009H.[!`D```!T)@^WBP0)
+M``"-=@!F@[Q3!@D```!T#`^WA%,&"0``.?!TCT(YT7_D@^P,5P^W1"084%7_
+M="0D_W0D7.A4`P``N`$```"#Q""#Q"Q;7E]=PU575E.#[#2+7"109L=$)"X`
+M`&;'1"0L/P!H"`$``&@`````Z/S___^#Q`AH@````&@@`0``Z/S___^]B```
+M`+\!````O@````"#Q!`/M\9FB;P`"````$=&9H/_/W;M9HDU!@```&;'!0(`
+M```!`&;'!00````_`+X`````9H,]!@````!V+@^W^P^WWH/L!/]T)$@/MX0;
+M"````%!7Z+4'``!FB41=`(/$$$9F.34&````=]5FBTT`#[<%!@```&:+7$7^
+MO@````!F at ST&`````'9V#[?&9CE,10!_"F:+3$4`9HET)":)\/?0B<=F`ST&
+M````=",/M]=FBT15_F8[1%4`?@IFBT15`&:)1%7^1F8Y-08```!WNKX`````
+M9H,]!@````!V(6:+%08````/M\9F.5Q%`'X*9HM<10!FB70D)$9F.?)WY@^_
+MR8/!&;L?A>M1B<CWZXG0P?@$P?D?*<B-!("-!("--`!FQT0D(@``OP`````/
+MO\Z)3"0<B<CWZXE$)!")5"04BT0D%,'X!,'Y'RG(0(7`?CJY(`$``+@?A>M1
+M]VPD'(G3P?L$BT0D')F)V"G0C5`!#[=$)")FBUPD)F:)'$%F_T0D(D</M\<Y
+MT'SEOP`````/MP4&````B<)F.71%_@^.V0```(/&,HE4)!AF.715_GY9#[_.
+MB<L/M\>)1"0,#[]$10")RBG"B50D$(M$)`P/OU1%`BG1BT0D$`^OP87`?BI'
+MBU0D&&8Y=%7^?AX/M]</OT15`(G9*<&)R`^_5%4"B=DIT0^OP87`?]:#[`AJ
+M``^WUP^WA!(*````T>`/M\!0#[>$$@@```#1X`^WP%`/MT15`E`/MT15`%`/
+MM\90Z)T'``!`#[=4)$*Y(`$``&;1Z&:)!%&#Q""+7"0D9CG8=@1FB1Q19O]$
+M)"(/MQ4&````9CET5?X/CR?___]F at WPD(C]W)+L@`0``N1X!```/MU0D(F:+
+M!%%FB0139O]$)")F at WPD(C]VYKF`H0``OP````"^(@$``+L@`0``#[?'#[<4
+MAL'B&('*``#_``^W!(/!X`@,_R7__P``"<*+;"1`BT44B10(@\$$1V:#_Q]V
+MS(/$+%M>7UW#55=64X/L1(MT)&2+1"18B40D(&;'1"0>``!FQT0D'```9L=$
+M)!H``&;'1"08``!FQT0D%C\`9L=$)!(``+^@`0``_W0D:/]T)%SH_/___]'@
+M9HE$)!R#Q!"+5"1@]D(#`706BTPD4(J!'`@``.L49HE<)!+I6`$``(M4)!B*
+M at AT(``"#[`0/OL!0_W0D:/]T)%SH_/___XA$)!F#Q!"#?"14``^$HP```+L`
+M````B?8/M].+3"149H,\T0!T$&:+!-%FB414($-F at _L'=N*)W8/L#(U$)"I0
+MC40D+%`/M\-0C40D.%"+5"1\#[<"4.C=!@``NP````"#Q"!F.>MS%(M4)!P/
+MM\-F.51$('0&0V8YZW+PBT0D'&8[1"0>=0Z+3"1 at 9HL!9CM$)!QT#@^WPXM4
+M)%2#?,($`'01#[?#BTPD5&:+1,$"9HE$)`Z[`````&8Y\W,<B?8/M\.-%("+
+M3"189HM440AFB51$($-F.?-RYH/L#(U$)"I0C40D+%`/M\90C40D.%"+5"1\
+M#[<"4.@[!@``NP````"#Q"!F.?-S+XGV#[?#9HM$1"!F.T0D''4%9HE<)!8/
+MM\-FBT1$(&8[1"0>#X2D_O__0V8Y\W+3NP````")]HM,)&#V00)`#X36````
+M9H/[!'<P#[=$)!:-!("+5"189HM$0 at 9FB40D%`^W1"02C02`9HM$0 at 9FB40D
+M$.E;`0``C78`9H/[!74N#[=$)!:-!("+3"189HM$001FB40D%`^W1"02C02`
+M9HM$001FB40D$.DE`0``D&:#^P9U+@^W1"06C02`BU0D6&:+1$("9HE$)!0/
+MMT0D$HT$@&:+1$("9HE$)!#I\0```)!F at _L'#X7F````#[=$)!:-!("+3"18
+M9HL$06:)1"04#[=$)!*-!(!FBP1!9HE$)!#INP```(UV``^WPX/X!P^'K```
+M`/\DA5@[```/MT0D%HT$@(M4)%AFBT1"!F:)1"04#[=$)!*-!(!FBT1"!F:)
+M1"00ZWL/MT0D%HT$@(M,)%AFBT1!!&:)1"04#[=$)!*-!(!FBT1!!&:)1"00
+MZU$/MT0D%HT$@(M4)%AFBT1"`F:)1"04#[=$)!*-!(!FBT1"`F:)1"00ZR</
+MMT0D%HT$@(M,)%AFBP1!9HE$)!0/MT0D$HT$@&:+!$%FB40D$)"#[`AJ``^W
+M1"0<4`^W1"0D4`^W1"0R4`^W1"0T4(M4)'P/MP)0Z$H#``")P8/$(&8[1"0.
+M=@5FBTPD#HM$)&#V0`(0=!^+5"089H&Z``@````P=A!F.XH."```=@=FBXH.
+M"```#[?S#[=4)`P/OD0D"2G"#[?!.<)^`HG"9HD4=V:%VW4KBTPD4(N!T`$`
+M``^WA`!,.P``T>!FBQ=F*<)FB50D"F:%TG\'9L=$)`H!``^WRP^_1"0*#[<4
+M3SG0?@*)T&:)!$]#9H/[!P^&D_W__P^W!XM4)!B)@O`8``"+3"10BUD4N0``
+M``"#X0'!X1ZX/P```&8C1P;!X!B)R at G"N#\```!F(T<$P>`0"<*X/P```&8C
+M1P(/M\#!X`@)PK at _````9B,'#[?`"<*)DS29``"+1"10BU`4N#\```!F(T<.
+MP>`8"<&X/P```&8C1PS!X!`)P;@_````9B-'"@^WP,'@"`G!N#\```!F(T<(
+M#[?`"<&)BCB9```/MP=0_W0D5.C\____9HL'BU0D6&:)@LX!``"#Q$1;7E]=
+MPXUV`%575E.#[!R+?"0XBW0D,(M<)#2-1"0:4%</M\-0#[?&4.@^`0``@\00
+MA<!T"P^W1"0:Z24!``"0@^P,C40D)%"-1"0F4`^W1P10_S</M_96Z&<"``"#
+MQ!2-1"0 at 4(U$)")05P^W1"0N4`^WVU/H*@,``(/$%(U$)!Q0C40D'E!7#[=$
+M)#!04^@0`P``@\0 at C40D#%!7#[=$)!I0#[=$)")0Z+X```"-1"0:4%</MT0D
+M+%`/MT0D,E#HIP```(/$&&H`#[=$)!90#[=$)!Q0#[=$)"A0#[=$)"I04^CT
+M````B<6#Q""-1"0,4%</MT0D%E`/MT0D)%#H:````(U$)!I05P^W1"0H4`^W
+M1"0T4.A1````@\08:@`/MT0D%E`/MT0D'%`/MT0D)%`/MT0D)E!3Z)X```"#
+MQ!AJ``^WP%`/M\50#[=$)"Q0#[=$)"Y05NA_````#[?`@\0 at D(/$'%M>7UW#
+M55=64XMT)!R+;"04BWPD&(M."+L`````9H-^!`!V1Y!F.2EU-XU1"+@`````
+M9H-Y!@!V*&8Y.G47#[?`9HM401Z+1"0 at 9HD0N`$```#K')"#P@)`9CE!!G?:
+MB?:#P31#9CE>!'>ZN`````!;7E]=PU575E.#[`2+;"08BTPD'(M\)""+="0D
+MBUPD*&;'1"0"9``/M\8/M],/K\*Z`````(7`#X2C````@WPD+`!T!V;'1"0"
+M`0!F.<]T=0^WQ0^WR2G(C02`C02`P>`"#[?7*<J)T9GW^6:%P'D5#[?&@WPD
+M+`!T8XT$@(T$@,'@`NM89H/X9'X6#[?#@WPD+`!T2(T$@(T$@,'@`NL]D`^_
+MR`^WPP^OP;ID````*<H/M\X/K]$!T`^W5"0"B=&9]_GK&(GP at WPD+`!T#P^W
+MQHT$@(T$@,'@`HUV``^WT(G0@\0$6UY?7<.-=@!55U93BW0D&(M\)""+;"0D
+MBUPD'`^WPV:+3$;^#[=4)!2-!)*-!("-%(4`````#[<&C02`C02`C02%]O__
+M_SG0=A)FBP9FB0=FBP9FB44`Z80````/M\&-!("-!("-!(4*````.=!S+&:)
+M#V:)30#K9V:+!$YFB0=FBP1.9HE%`.M69HL$3F:)!V:+1$X"9HE%`.M$N0``
+M``!FA=MT.@^WVXUV``^W!$Z-!("-!(#!X`(IT(/`"8/X$G:V#[=$3@*-!("-
+M!("-!(7V____.=!WL4$YRW_.B?9;7E]=PXUV`%=64XM$)!B+?"00BW0D%(M0
+M"+D`````#[=`!(G#@_@`?@^)]F8Y,G0(@\(T03G+?_/_="0 at _W0D(`^W0 at 90
+MC4((4`^WQU#HU?[__X/$%%M>7\.)]HM$)`2-B#P6``"+%80[``")D#P6``"-
+M!-4`````*="-!(6(.P``B4$8QT$4`0```,=!#!0```#'01`C````PY!75E.+
+M5"04BT0D$(N`_`<``/9``B!T);\`````NQP```"Y0````+YR````QT(,````
+M`,="$'<```#K(Y"_`````+LR````N0````"^,@```,="#!0```#'0A`C````
+MBU($N``````Y^G($.=IV#3G*<@0Y\G8%N`$```!;7E_#D(M$)`2+2!2+D/`8
+M``#!X at F!X@!^``"`SH")D3"9``#'@.P8```!````PXGV5U93BWPD$(G[C;<\
+M%@``N`````"#?A0`='B#O^P8```!=6F+1Q2+@#"9``"I`(```'59P>@9B48$
+MQX?L&````````%97Z`K___^#Q`B%P'4[@^P(5E?H/0```(/$$(7`="J#[`A6
+M5^A(````@\00A<!^&8/L"%97Z$L!``#'A^P8```"````@\00B?:+@^P8``!;
+M7E_#B?:+3"0(N`````"+400[40QV!3M1$'(%N`$```##5E.+7"00NO____^#
+M>Q0`#X3W````BP.-%,4`````*<*-%)6(.P``B5,8BT,$.T,0<F*Z_____X,[
+M``^$S````(M#!(E#"#M#$')!@SL`=#R+`TB)`XT4Q0`````IPL'B`HM+&(N"
+MD#L``"M!"-'@BTL(*<&)2PB!PH@[``")4Q@[2Q!R!X,[`'7&B?:Z`0```.MY
+MD(M#!+H`````.T,,=VNA@#L``$BZ_O___SD#=%R+0P2)0P@[0PQW2:&`.P``
+M2#D#<S^)QI"+`T")`XT4Q0`````IPL'B`HM+&(N"D#L``"M!"-'@BTL(*<&)
+M2PB!PH@[``")4Q@[2PQW!SDS<L>-=@"Z`@```(UV`(G06U[#C78`BTPD!(M4
+M)`B#>A0`=#>+0AAFBP!FB8%@"0``BT(89HM``F:)@5X)``"+0AAFBT`$9HF!
+M7`D``(M"&&:+0`9FB8%:"0``PXGVBTPD!(M!%(N`!(```"7__P``BY&P`0``
+M at _H!=#.#^@%R'H/Z!G0)@_H(="3#C78`BU$4#0``@0")@@2```##D(M1%`T`
+M`$(`B8($@```PY"+410-```@`(F"!(```,.05U93BWPD$(/L#%?H_/___XG&
+M@\0()?___W]05^C\____BT<4BU`PB=.!X_`#``#!ZP2#Q!"#?"04`'00N"4`
+M```IV-'H`</K'XUV`(/[`78#2^L4@^P(5E?H_/___[@`````@\00ZRJ+3Q2!
+MX at _\__^)V,'@!"7P`P``"<*)43"#[`A65^C\____N`$```"#Q!!;7E_#B?93
+M@^P(BUPD$(M4)!2X`````(/Z"7\>@^P$_W0D'(T$4HT$@(V$@WP6``!04^C\
+M____@\00@\0(6\.-=@!3@^P(BUPD$(M4)!2X`````(/Z"7\>@^P$C012C02`
+MC82#?!8``%#_="0 at 4^C\____@\00@\0(6\.-=@!55U93@^P,BVPD((MT)"2)
+MZH/^`G0:@_X"=PF#_@%T*.L^B?:#_ at -T#X/^!'01ZS"_"0```.LQD+\(````
+MZRF_`0```.LBB?:_`````+@`````@[V`%@```'5\ZPN-=@"X_____^MPD(T$
+M?XT$@(V<@GP6``"X_____X-[!`!U5X/L"&H\4^C\____B7,$@\00 at WPD*`!U
+M+,=##`L```#'0Q0"````QT,8_____\=#'/\#``!FQT,@"@!FQT,B"@#K$XGV
+M@^P$_W0D+%=5Z/S___^#Q!")^(/$#%M>7UW#D%.+3"0(BUD4BY%H%@``@>+_
+M`P``BX%P%@``P>`0)0``_P,)PHF3I````(M9%(N1;!8``('B_P,``(N!=!8`
+M`,'@$"4``/\#"<*)DZ@```"+612+ at ZP````E`/S__XN1>!8``('B_P,```G0
+MB8.L````6\.05E.+="0,BTPD$+@`````@_D)=U2-!$F-!("-G(9\%@``N```
+M``"#>P0`=#S'0P0`````N/[____3P"&&:!8``"&&;!8``"&&<!8``"&&=!8`
+M`"&&>!8``%-6Z"W___^X`0```(/$")!;7L.055=64XM\)!B+;"04BY7\!P``
+MN`````"#_PD/AP@$``"-!'^-!("-M(5\%@``N`$```"#?@0`#X3L`P``@WX8
+M_W4VA=)T%0^W0@(EH````+H?````/:````!T!;H/````N`$````YT',2C78`
+MT>"#R`$YT'+WZP20BT88C1R]`````(M4)!2+2A2)PH'B_P,``(M&','@"B4`
+M_`\`"<*+1A3!X!0E``#P#PG"B9090!```(M$)!2+2!0/MT8BP>`$)?`````/
+MMU8@@^(/"=`-`"`(`(F$&8`0``"+5"04BT(4QX08P`D````(``"#NJ0!```#
+M=PZ+0A3'A!@`$0```````8-^)`!T3HT,O0````"+1"04BU at 4BU8D@>+___\`
+MBT8HP>`8"<*)E`O`"```BU0D%(M:%(N$"\`)``")PH/*`8-^*`!T"(G"@<H!
+M`0``B90+P`D``(-^,`!T(HT,O0````"+1"04BU`4BT8P)?___P`-`````8F$
+M"@`)``"#?BP`=$.-#+T`````BT0D%(M0%(M&+"7__P\`#0``$`")A`K`$```
+M]D8,0'0;BT0D%(M0%(N$"L`)``"`S`*)A`K`"0``C78`]D8,$'0AC0R]````
+M`(M$)!2+4!2+A`H`$0``#0``(`")A`H`$0``]D8,@'0?C0R]`````(M$)!2+
+M4!2+A`H`$0``@,P"B80*`!$``(M&!(/X`W1I at _@#=PJ#^`)T$^GY````@_@$
+M#X30````Z>L```"-#+T`````BT0D%(M0%(N$"L`)```,HHF$"L`)``"+5"04
+M@[JD`0``!!G;@>,````!@<,``"4`B<J+3"04BT$4B9P0`!$``.F?````C0R]
+M`````(M$)!2+4!2+A`K`"0``#6("``")A`K`"0``H0`````K!0````"+E?08
+M``")TRG#*QT`````P>,*BT0D%(M0%(G8#0````&)A`H`"0``BU0D%(.ZI`$`
+M``09VX'C`````8'#```$`(G*BTPD%(M!%(F<$,`)``#K((T,O0````"+1"04
+MBU`4BX0*P`D``(/((XF$"L`)``"0]D8X`70AC0R]`````(M$)!2+4!2+A`H`
+M$0``#0``)`")A`H`$0``]D8,`703N`$```")^=/@"85H%@``ZQ&)]KC^____
+MB?G3P"&%:!8``/9&#`%T$[@!````B?G3X`F%;!8``.L1B?:X_O___XGYT\`A
+MA6P6``#V1 at P"=!.X`0```(GYT^`)A7`6``#K$8GVN/[___^)^=/`(85P%@``
+M]D8,!'03N`$```")^=/@"85T%@``ZQ&)]KC^____B?G3P"&%=!8``/9&#`AT
+M$[@!````B?G3X`F%>!8``.L1B?:X_O___XGYT\`AA7 at 6``!6_W0D&.@#^___
+MN`$```"#Q`B-=@!;7E]=PXUV`(M$)`C!X`*+5"0$BU(4BX0"``@``,.)]HM4
+M)`C!X@*+1"0$BT at 4BT0D#(F$$0`(``"X`0```,.0BTPD"(M$)`2+4!2X`0``
+M`-/@B8)`"```N`$```##B?964XM4)!#!X@*+1"0,BW`4BX06``H``(G#@^,#
+M=1JX`0```(I,)!#3X(N60`@``(7"=`6[`0```(G86U[#C78`5U93BWPD$(MT
+M)!2+5Q2X`0```(GQT^")@H`(``"[`````(GV5E?H_/___X/$"(7`=!:#[`QJ
+M"NC\____@\000X'[#R<``'[<BT<4QX"`"````````('[#R<```^>P`^VP%M>
+M7\.-=@"X`0```,.)]E93BW0D+(M<)#2+3"00BT0D%"7_#P``BU0D),'B$@G0
+MBU0D,,'B&0G0]L,!=`4-`````?;#$'0%#0```"#VPP1T!0T``$``]L,@=`4-
+M``"``(E!"(M$)!R)PL'B&O;#`G0&@<H``(``B5$, at _[_=!:)\,'@#24`X`\`
+M"=")00R!20@```!`N`$```!;7L.)]K@`````PXGV4XM,)!"+7"08BU0D#(-\
+M)!0`=!2+0 at P)R(7;=0.`S!")0 at SK)XUV`(7;=!"+1"0<BT`(B4((B4H,ZQ&0
+MQT((`````(G(@,P0B4(,D,="%`````#'0A``````N`$```!;PXUV`%.+3"0,
+MB<JX#P```/9!%`$/A*(```"+010E_A\``-'H9HE!(`^W01)FB4$BQD$D`(M!
+M$*@!=1VH`G0$QD$D`?9"$`AT!(!))`+V0A`$=`6`220$D(M"""4``#P`P>@2
+MB$$EBT(4)0#@'P#!Z`V(02:+0A`E``\``(G#P>L(B%DGBT(0)?````#!Z`2(
+M02B+0A`E`/```,'H#(A!*<9!*@"$VW0&C4,!B$$G@'DH`'0#_D$HN`````!;
+MPXGVPY"0D(/L#(M,)!"+013'@-"8```6'```N@````"-=@"+013'@("8````
+M``$`0H/Z!W[MBT$4BX``G```P>@8B<+!Z at 2#X`_!X`0)T(/L"&H(4.C\____
+M@\0<PY!75E.![)````"+G"2@````C7PD<+ZL1```_+D%````\Z6-?"10OL!$
+M``"Q!?.EC7PD,+[41```L07SI8U\)!"^Z$0``+$%\Z6Z`````(UV`(M$E'")
+MA).T+@``BT244(F$D\@N``"+1)0PB823W"X``(M$E!")A)/P+@``0H/Z!'[.
+M@<20````6UY?PU93@^P0BW0D'&@P+P``Z/S___^)PX/$$(7`=12+1"0 at QP`"
+M````N`````#INP```(/L!&@`"```:*`\``!0Z/S___^+1"0DB4,,BT0D*(E#
+M$(M$)"R)0Q1FB;.@`0``9L>#H@$`````9L>#S`$``#P`QX/0`0```````,>#
+M5!D```````#'@U at 9````````QX-<&0```````,>#:!D```$```#'@VP9````
+M````QX-P&0``9````,>#=!D```(```#'@X09``#_____QX.(&0``_____\>#
+MC!D``/_____&@Y`9````B=B#Q!"#Q`1;7L.-=@!55U93@^PHBWPD3%?_="1,
+M_W0D3/]T)$P/MT0D3%#H_/___XG&@\0 at N`````"%]@^$:@4``(GS:@!J`6H"
+M5NC\____@\00A<!U#L=$)!0#````Z2D%``"0@^P(:@!6Z/S___^#Q!"%P'4.
+MQT0D%`,```#I"04``)"+1A2+@"!````/MM")T,'H!(F&I`$``(G0@^`/9HF&
+MJ`$``(.^I`$```5T"8.^I`$```=U"F:#NZ@!```!=PW'1"04#0```.F\!```
+MBT,4BX`8F```9HF#J@$``(/L#%/H_/___X/$$(7`=0W'1"04#@```.F/!```
+MBT,4QX``F```!P```(/L#%/H_/___V:)@ZP!``"#Q!`/M\`E\````(/X('18
+M at _@@?PR#^`=T3H/X$'1)ZPJ#^#!T0H/X4'0]9H.[K`$```!U,X.[I`$```5U
+M&(&[J`$```D`0P!U#&;'@ZP!``!&`.L3D(.[I`$```=U"6;'@ZP!``!1``^W
+MDZP!``")T"7P````@_ at O?A>)T(/@#X/X!'\-QT0D%`T```#IX`,``(/L!(U$
+M)!Y0:,$```!3Z/S___^#Q!"%P'4-QT0D%`@```#IN0,``&:!?"0:`3!W#<=$
+M)!0%````Z:,#``!FBT0D&F:)A@`(``"+0Q2+D!!```"#XAC!Z at .#^@)T#<=$
+M)!0*````Z78#``"#[`2-1"0>4&H_4^C\____@\00A<!U#<=$)!0(````Z5(#
+M``!FBT0D&F:)A@((``"#[`2-1"0>4&H<4^C\____@\00A<!U#<=$)!0(````
+MZ2(#``#'1"0,0`,``&:#?"0:`'1&#[=$)!HE\/\``,'@#(E$)`R#[`2-1"0>
+M4&H;4^C\____@\00A<!U#<=$)!0(````Z=T"```/MT0D&@E$)`R!;"0,P```
+M`,=$)!``````O0````"+1"0,.<5S,(/L!(U$)!Y0C87`````4%/H_/___X/$
+M$(7`#X2/`@``#[=$)!HQ1"0013ML)`QRT(%\)!#__P``=`W'1"04!P```.EP
+M`@``9L>&=@D```H`9L>&@`L```,`O0````"-1&T`C42%`&;'A(9^"0``"P!%
+M at _T)=NB]`````+J at 1```N:9$``!FBP1J9HF$;H at +``!FBP1I9HF$;H(+``"-
+M1&T`C42%`(T$AF;'@#`,```+`&;'@)0+```+`$6#_0)VQ8/L"(V&``@``%!3
+MZ/S___^#Q!"%P'4-QT0D%`@```#ITP$``&:#OA@(````='4/MX.L`0``)?``
+M``"#^!!U9(M#%,>``)@```=```"#[`QHT`<``.C\____B1PDZ/S___]FB8.N
+M`0``BT,4QX``F```!P```,<$)-`'``#H_/___P^W at ZX!```E\````(/$$(/X
+M('0.QT0D%`T```#I50$``)"#[`2-1"0>4&B_````4^C\____@\00A<!U#<=$
+M)!0(````Z2T!``!FBT0D&F:)A at 0(``!FB8/4`0``@^P,4^C\____@\00A<!U
+M#<=$)!0(````Z?T```"#NZ0!```'=12#[`B-1"0<4%/H_/___X/$$.LVD`^W
+M at ZP!```E\````(/X+WX3@^P(C40D'%!3Z/S___^#Q!#K$8/L"(U$)!Q04^C\
+M____@\00A<`/A*,```"#[`Q3Z/S____'1"0@`````+T`````@\00@^P$C40D
+M'E"X'P```"GH4%/H_/___X/$$(7`=%D/MT0D&@%$)!!F#[9$)!N(A&Y<%@``
+M9HM$)!J(A&Y=%@``18/]`G:[@WPD$`!T"H%\)!#]_P(`=0K'1"04"0```.LJ
+M4^@D^?__@^P(4^C\____B=B#Q!#K,\=$)!0(````ZPJ)]L=$)!0(````A?9T
+M#(/L#%;H_/___X/$$(7_=`:+1"04B0>X`````(/$'%M>7UW#D%.#[!2+7"0<
+M4^C\____@\00@[O(%@```'0.@^P,4_^3R!8``(/$$)!J`&H!:@-3Z/S___^)
+M'"3H_/___X/$&%O#C78`55=64X/L+(MT)$#'1"0(`(```,=$)`P at F```QT0D
+M$%55557'1"04JJJJJL=$)!AF9F9FQT0D')F9F9F_`````(UL)`B-!+T`````
+MBQPHBU84BQ0:B10$N0````")R,'@$(G""<J+1A2)%!B+1A2+!!@YT'0'N```
+M``#K84&!^?\```!^U[D`````BU2,$(M&%(D4&(M&%(L$&#G"=`JX`````.LX
+MC78`08/Y`W[<C02]`````(M.%(L4*(L$!(D$$4>#_P$/CGG___^#[`QJ9.C\
+M____N`$```"#Q!"#Q"Q;7E]=PXUV`(M4)`R+3"00BT0D"/;$`7039L<".!-F
+MQP'4%[@!````PXUV`*F`````="&+1"0$9H.X&`@```!T$V;'`@@)9L<!K`JX
+M`0```,.-=@"X`````,.)]E575E.#[`R+;"0 at B>^)ZXVUM`$``&;'1"0*``!F
+M@;T`"````%!V)8/L!(U$)`Y0:,D```!5_Y6$`0``@\00N@````"%P`^$3`(`
+M`)!F at WPD"@!U+(.]I`$```=U(V:#O:@!```(=1EFQT0D"@4`9L>'!@@```$`
+M9L>'"`@```$`@[NP`0``!G0[9H&[H@$``&5P=3!FBY/4`0``C4*<9H/X`7<,
+MC4(%9HF#U`$``.L49H.[U`$``$%U"F;'@]0!``!#`)#'1 at 0`````9H._%@@`
+M``!T&\=&!`$```!F@[\&"````'4*QT8$`P```(UV`&:#OQ@(````=`2#3 at 0$
+M9H._&@@```!T*&:!NZ(!```1#G0=BU8$B="#R`B)1 at 1F@[\("````'4(B="#
+MR"B)1 at 1FQT80"`D/MX6L`0``)?````"#^"]_"8.]I`$```=U"&;'1A+$">L&
+M9L=&$JP*9L=&##@39L=&#M07BD8!@^#?@\A`B$8!N`````!F@[NH`0```W8+
+M]D0D"@)U!+`!B?;!X`2*5@&#XNL)PH/*"`^W3"0*B<C1Z(/P`8/@`='@@^+]
+M"<*(5@&`#@-F@[NH`0```79=N`````#VP0%U"O9&!`ET!+`!B?:-'(4`````
+MBA:#XO,/MTPD"HG(P>@#@_`!@^`!P>`#"=H)PH at 6N`````#VP01U"?9&!`ET
+M`[`!D(C"P>($B@:#X.\)T(/(H(@&@":_N@````"#O\@(````=`F#O\P(````
+M=06Z`0```(I&`8/@_ at G0@\B`B$8!@$X"`0^W1"0*)?`!``!T"L'X!&:)1 at CK
+M!Y!FQT8("@!F]T0D"@#P=!4/MTPD"L'I#+@!````T^!FB48*ZP9FQT8*@`"Z
+M`0```(G0@\0,6UY?7<.0D)!75E.+3"00BW0D%(M<)!B)SXM!%(FP*(```(N!
+ML`$``(/X`70/@_@!<B:#^`9T(8/X"'5$BT$4QX`L@```__\``(M!%,>`,(``
+M`/__!P#K*(M1%(GP*P4`````P>`#B8(L@```BU$4B?`K!0````#!X`.)@C"`
+M``"+412-1@&#OU09````=`B)\`.'5!D``(F"-(```('C__^``8G8P>@8A<!T
+M#8M!%,>`((````````&+012)F""```!;7E_#B?964X/L!(MT)!"[`````(GV
+M@^P(:@)6Z/S___^#Q!"%P'06@^P,:@KH_/___X/$$$.!^^<#``!^V('[YP,`
+M``^>P`^VP(/$!%M>PXGVBU0D!(M"%,>`*(````````"+2A2+ at 02````-```$
+M`"7__\__B8$$@```BT(4QX`@@```__\``,-55U93BVPD%(M<)!AF at WL2`'1!
+MBU44BX($@```#0``$`")@@2```"+510/MT,0B8(D@```BU44#[=#$HF".(``
+M`(M5%(M#%,'@`XF",(```.L6B?:+512+@@2````E___O_XF"!(```(M5%(L#
+MB8(H@```BTT4BX$@@```)0``@/\/MU,("<)F at WL8`'01#[=#&(/`!,'@$"4`
+M`'\`"<*)D2"```"+312+D1B````P]@^W0QK!X`@E`/\```G"B9$8@```BWL(
+M.7L<=@.+>QR+<PPY<QQV`XMS'#GW=0:+2P3K`Y"+"XM5%(M#!(/H`\'@`R7_
+M_P<`#0``&`J)@M2```"+512-!,WH____)?__!P`-````"HF"V(```(M-%(GX
+M)?__``")\L'B$`G0B8'<@```6UY?7<-3@^P(BUPD$(M3%(M$)!2)@@!@``"+
+M0Q3'@`A@```!````:@)J`V@,8```4^C\____@\00N@````"%P'05BT,4BY`$
+M8```BT0D&&:)$+H!````B="#Q`A;PY"0D%.+1"0(BU at 4BTPD#-'AN@,```#3
+MXHN#%$````G0B8,40```N`$```!;PXGV4XM$)`B+6!2+3"0,T>&X`P```-/@
+M]]"+DQ1````AT(F#%$```+@!````6\-3BTPD#(M$)`B+6!2+DQA```"X_O__
+M_]/`(<*+1"00@^`!T^`)PHF3&$```+@!````6\.0BTPD"+C_____ at _D%=Q6+
+M1"0$BT`4BX`<0```@^`OT^B#X`'#5E.#[`2+="00BU0D%(M&%(N8%$```(T,
+M$K@#````T^`-`/`!`/?0(</!X at P)TX#/@(-\)!@`=`:!RP```0"+1A2)F!1`
+M``"#[`B+AF at 6```-`````5!6Z/S___^#Q!1;7L.0D(M$)`2+0!2+@`A```"#
+M^`$/E,`/ML##D%=64XM\)!"+="04BT<4BYC`````@_O_=1/'!@````"X````
+M`.F9````C78`B=@ENM@%`8D&]\,```@`=`<-````0(D&]L,%=`.##@'WP\`%
+M``!T7H,.0(M/%(N1Q````('B_P,```N7+"\``(F7+"\``(N!Q````"4``/\#
+MP>@0"=")ARPO``"+D<@```"!XO\#```)PHF7+"\``(N!R````"4``/\#P>@0
+M"=")ARPO``#VPR!T!H$.````0+@!````6UY?PXGVBT0D!(N`:!8``,.05U93
+MBW0D$(M<)!2)\8N^:!8``(7_>1&+1A3'0"0`````BT84BT`DD(G:@>*ZV`4!
+M]L-`=#.#N>06````=`.#RD"#N>@6````=`.`S@&#N>P6````=`.`RH"#N?`6
+M````=`:`S at 2-=@#VPP%T`X/*!_?#````0'0&@<H```@`BT84B9"@````B9EH
+M%@``A=MY"XM&%,=`)`$```"0B?A;7E_#D)"+1"0$#[>`O@$``,.+5"0$BT0D
+M"&8Y at KX!``!V&P^WP,'@!0-"%(N`'(@``+H!````J0"```!U!;H`````B=##
+M5E.+7"0,BW0D$+@`````9CFSO@$```^&V0````^WQL'@!8G"`U,4BXH4B```
+MQX(`B````````(G"`U,4QX($B````````(G"`U,4QX((B````````(G"`U,4
+MQX(,B````````(G"`U,4QX(0B````````(G"`U,4QX(4B```!P```(G"`U,4
+MQX(8B`````````-#%,>`'(@```````"#^01U38.[:!D```!T1(U&0`^WP,'@
+M!8G"`U,4QX(`B````````(G"`U,4QX($B````````(G"`U,4QX((B```````
+M``-#%,>`#(@```````"0N`$```!;7L-75E.+?"00BU0D&(MT)!2X`````&8Y
+MM[X!``!V:X72=#@/MEH%P>,(#[9"!`G##[9*`\'A&`^V0 at +!X!`)P0^V0@'!
+MX`@)P0^V`@G!T>F)V,'@'PG!T>OK"[L`````N0````"0#[?&P>`%B<(#5Q2)
+MBAB(```#1Q2)VH#.@(F0'(@``+@!````6UY?PU575E.#[!2+="0PBTPD+(M4
+M)"B!PK0!``"#?"0X`1G;]].!XZJJJJJ_`````&8Y2 at H/AD$#```/M@:#^`4/
+MAXT```#_)(7\1```QT0D!`4```#IA0```+\`````]D(!$`^$$@,``,=$)`0&
+M````ZVS'1"0$!`````^WP8/`0`^W4 at J_`````#G0#XWI`@``ZTN_`````&:#
+M?@($#X;7`@``QT0D!`````!F at WX"!78L9H-^`@X9P(/@_H/``XE$)`3K&<=$
+M)`0'````ZP^-=@"_`````.F>`@``B?8/ME8$#[9&!<'@"`G"#[9&!L'@$`G"
+M#[9&!\'@&`G",=J)5"00#[9&"<'@"`^V5@@)PC':@>+__P``B50D#`^V;@H/
+MMD8+P>`("<4/MD8,P>`0"<4/MD8-P>`8"<4QW0^V1 at _!X`@/ME8."<(QVH'B
+M__\``(E4)`@/MGX0#[9&$<'@"`G'#[9&$L'@$`G'#[9&$\'@&`G',=]F at WX"
+M#7<&@>?_````@WPD!`0/A7,!``"+7"0H@[MH&0````^$8@$``(U90`^WR8G(
+MP>`%B00DB<*+1"0H`U`4BT0D$/?0B8(`B```BQ0DBT0D*`-0%(M$)`SWT(F"
+M!(@``(L$)(M4)"@#0A2)J`B(``"+!"0#0A2+5"0(B9`,B```BP0DBU0D*`-"
+M%(FX$(@``(L$)`-"%,>`%(@```0```#_="0T45+H_/___P^V3A0/MD85P>`(
+M"<$/MD86P>`0"<$/MD87P>`8"<$/ME88#[9&&<'@"`G"#[9&&L'@$`G"#[9&
+M&\'@&`G"#[?;P>,%B=B+="0T`T84B8@`B```@\0,B=@#1A3'@`2(````````
+MB=@#1A2)D`B(``")V`-&%,>`#(@```````")V`-&%,>`$(@```````")V`-&
+M%,>`%(@```<```")V`-&%,>`&(@````````#7A3'@QR(````````BP0D`T84
+MBU0D$(F0`(@``(L$)`-&%(M,)`R)B`2(``#K=(UV``^WR8G(P>`%B<*+7"0H
+M`U,4BW0D$(FR`(@``(G"`U,4BUPD#(F:!(@``(G"BW0D*`-6%(FJ"(@``(G"
+M`U84BUPD"(F:#(@``(G"`U84B;H0B````T84BW0D!(FP%(@``/]T)#11_W0D
+M,.C\____@\0,OP$```")]HGX@\046UY?7<.0D(/L$&H&BT0D&`5<%@``4/]T
+M)"#H_/___X/$',.#[!!J!O]T)!R+1"0<!5P6``!0Z/S___^X`0```(/$',.-
+M=@!3BUPD$(M,)`BZ#````(M$)`QF.8'4`0``=!@/MX$""```P>@'@^`!@_@!
+M&=*#XOB#P at N%VW0"B1.X`````%O#BTPD!(G*N`````!F@[D6"````'0.9H.Y
+M!@@```$9P(/@`D!F@[H8"````'0#@\@$9H.Z&@@```!T'F:!N:(!```1#G03
+M@\@(9H.Z"`@```!U!H/(((UV`,.-=@!3@^P(BUPD$&:#NPH(````=$J#[`2-
+M1"0*4&H/4_^3A`$``(/$$+H`````A<!T60^W5"0&B="#X!S!^`*)@Y09``"#
+MX at +1^HF3F!D``,>#H!D```$```#K(8UV`,>#E!D```````#'@Y at 9````````
+MQX.@&0```````&:#NPH(````#Y7`#[;0B="#Q`A;PY!3@^P0BUPD&/^SE!D`
+M`%/_DY`!``"+4Q2+@@"8``"`S"")@@"8``"#Q`C_LY09``!3_Y.4`0``B8.<
+M&0``@\0,.X.8&0``#Y7`#[;`4/^SE!D``%/_DYP!``"#Q!A;PY"+1"0$BT at 4
+MBX$00```)9__\?^+5"0(@^('"P25($4``(F!$$```,.05E.#[`B+7"04BW0D
+M'&H&_W0D'(V#8A8``%#H_/___XM+%`^VDV(6```/MH-C%@``P>`("<(/MH-D
+M%@``P>`0"<(/MH-E%@``P>`8"<*)D0B```"+2Q0/MH-G%@``P>`(#[:39A8`
+M``G"@>;_/P``P>80"?*)D0R```"+4Q2+@@R````E__\``(F"#(```(/$%%M>
+MPU.+1"0(BT at 4BX%0@```B<*X`````(N)3(```+L`````"<@)VEO#B?:+1"0$
+MBT`4BX!,@```PXGVBTPD!(M1%(N"((````T````!B8(@@```BU$4B8(@@```
+MPXGV5E.+="0,BUPD$(N&_`<``(7`=&#V0`(@=%JP`+D`````.PMS'(UV`(I4
+M"P2$TGD,@^)_.,)R!8C0C78`03L+<N>+5A2+B at 2```"$P'06T.@\`G<0B<@-
+M`````HF"!(```.L1D(M6%(G()?____V)@@2```!;7L.0BU0D!(M"%(N`9)@`
+M`(G!P>D3@>'_`0``B<C!Z`B%P'0&@?$`_O__BU(4BX)0@```BY),@```,=`Q
+MR,.04XM4)`B+0A2+@"!````E_P```(G!@^$/NP````#!Z`0Y at J0!``!U#&8Y
+MBJ@!``!U`[,!D(G86\.+3"0$BT0D"(M1%(N2D(````$0BU$4BY*,@````5`$
+MBU$4BY*4@````5`,BU$4BY*(@````5`(BU$4BY*8@````5`0P[@!````PXGV
+MBT0D!(M0%(N"5)D``(/(`8F"5)D``,.0BT0D!(M`%(N`')P``"7_````PY"+
+M1"0$BT`4BX!8@```@^`'PXUV`(M$)`2+4!2+1"0(@^`'B8)8@```PXUV`%57
+M5E.+?"04BVPD&(G[BT0D'`^W0`(EX`$``#W`````="H]P````'\+/:````!T
+M%.LBB?:^`````#U``0``="#K$HGVO@$```#K%9"^`@```.L-D+@`````Z;L`
+M``")]@^WC'-X"```#[>$<WX(``#!X`8)P0^WA'.$"```P>`,"<$/MX1SB@@`
+M`,'@$@G!#[>$<Y`(``#!X!@)P0^WE'.6"```#[>$<YP(``#!X`8)P@^WA'.B
+M"```P>`,"<(/MX1SJ`@``,'@$@G"#[>$<ZX(``#!X!@)PH/]`70/@_T!<AJ#
+M_0)T">L+C78`B<KK#(G1ZPBX`````.L>D(FK6!D``(M'%(F(8)D``(M'%(F0
+M9)D``+@!````6UY?7<.)]K@!````PXGV5U93BWPD$(MT)!2)^X/^"'86@^P(
+M:/__``!7Z/S___^#Q!`Y\',2D,>#A!D``/____^X`````.LC@^P(5E?H_/__
+M_XM7%(F"<!```(/$$(FWA!D``+@!````B?9;7E_#@^P4BU0D&(M"%(N`<!``
+M`"7__P``4%+H_/___X/$',-55U93@^P4BVPD*&C_/P``5>C\____@\00.T0D
+M)',1QX6(&0``_____[@`````ZSR+=12+GA2```"!XP#`__^#[`C_="0L5>C\
+M____)?\_```)PXF>%(```(/$$(M$)"2)A8 at 9``"X`0```)"#Q`Q;7E]=PX/L
+M%(M4)!B+0A2+@!2````E_S\``%!2Z/S___^#Q!S#55=64X/L%(ML)"AH_S\`
+M`%7H_/___X/$$#M$)"1S$<>%C!D``/____^X`````.M`BW44BYX4@```@>/_
+M_P#`@^P(_W0D+%7H_/___\'@$"4``/\_"<.)GA2```"#Q!"+1"0DB86,&0``
+MN`$```")]H/$#%M>7UW#@^P4BU0D&(M"%(N`%(```"4``/\_P>@04%+H_/__
+M_X/$',.0@^P4BT0D&/^PL`$``%#H_/___X/$',.0BU0D!+@`````@WPD"`9T
+M%("Z(0@```!T"X.Z=!D```%W`K`!PXUV`%.+7"0(_W0D#%/H_/___X/$"(7`
+M#X2R````BT,4QX!PF```'P```(M#%,>`=)@```T```"+0Q3'@'B8```,````
+MBT,4QX#PF0```P```(M#%,>`])D```4```"+4Q0/MX.L`0``)?````"#^#`/
+MG,`/ML"-!(44````B8+XF0``BU,4BX(<@```)7_`__\,@(F"'(```(M#%,>`
+M!($``#T```"+4Q2+ at A!```"#R`*)@A!```"+4Q2+ at A!````-`````XF"$$``
+M`.MGD(M#%,>`<)@``!\```"+0Q3'@'28``!_````BT,4QX!XF```#@```(M#
+M%,>`\)D```P```"+0Q3'@/29``#_````BU,4#[>#K`$``"7P````@_ at P#YS`
+M#[;`C02%%````(F"^)D``%O#4XM<)`C_="0,4^C\____@\0(A<`/A+(```"+
+M4Q2+ at A!````E_____(F"$$```(M3%(N"$$```(/@_8F"$$```(M#%,>`!($`
+M``$```"+4Q2+ at AR````E?\#__PV`#P``B8(<@```BT,4QX!PF```'P```(M#
+M%,>`=)@``'\```"+0Q3'@'B8```.````BT,4QX#PF0``#````(M#%,>`])D`
+M`/\```"+4Q0/MX.L`0``)?````"#^#`/G,`/ML"-!(44````B8+XF0``6\.)
+M]E.#[`B+3"00BT0D%(M<)!B#^!`/A[,```#_)(5`10``@_L"=!:#^P)R(X/[
+M!'(H at _L%=AF#^W]T%.L<9H.YJ`$```09P(/@#>F.````N`````#IA````+ at -
+M````ZWV#N6 at 9````#Y3`#[;`ZVZX`````.MG@[FH&0````^4P`^VP.M8N```
+M``"%VW1/N`P```"#^P%U18M!%(N`"*(``/;$(`^4P`^VP.LQN`````"%VW0H
+MN`````"#^P%U'H.Y;!D````/E,`/ML#K#_]T)!Q34%'H_/___X/$$(/$"%O#
+M4X/L"(M<)!"+1"04BU0D'(/X!70S at _@%=PJ#^`)T$>M_C78`@_ at +=$>#^!!T
+M6^MPA=(/E<`/ML")@V at 9``"X`0```.MQC78`BT,4BX@(H@``A=)T!X#-(.L%
+MB?:`Y=^+0Q2)B`BB``"X`0```.M'D(/B!HF3R`$``(M#%(F02(```+@!````
+MZRV%T@^5P`^VP(F#;!D``+@!````ZQB)]H/L#/]T)"Q2_W0D+%!3Z/S___^#
+MQ""#Q`A;PY"#[!3_="0L_W0D+/]T)"S_="0L_W0D+/]T)"SH_/___X7`#Y7`
+M#[;`@\0LPU93@^P$BW0D$(-\)!0`=&"+1A3'@`1`````````@^P,:@KH_/__
+M_[L*````@\00D(M&%(N`$$```*D```$`="*#[`QHR````.C\____BT84QX`$
+M0````````(/$$$MUT.L&A=MU"HGVN`````#K&I"+5A2+@@2````E___[_XF"
+M!(```+@!````@\0$6U[#D(M,)`2+412+@@2````-```$`(F"!(```(-\)`@`
+M=`V+013'@`1```````$`PXUV`%.+7"0(BTPD$(M3%(N"!(````T```0`B8($
+M@```@WPD#`!T$8M3%`^WP0T```(`B8($0```6\.05U93BU0D$(M<)!2+3"08
+MBT0D'(G6OP$```"#^P-T)8/[`W<(@_L"=`KK-9"#^P1T'^LM@^P(45+HR_[_
+M_XG'@\00ZR114NA-____@\0(ZQ@/M\!045+H;?___X/$#.L(N`````#K"9")
+MGE`9``")^%M>7\.+1"0$BT`4BX`$0```)0```P#!Z!##B?:+1"0$BT`4BX`0
+M0```P>@0@^`!P[@`````PXGVN`````##B?:X`````,.0D%.#[`B+1"04 at _@$
+M=#N#^`1W#H/X`70I at _@"=#SK2HGV at _@@=#.#^"!W"H/X"'0 at ZS>-=@`]``$`
+M`'0DZRN-=@"[`````.LID+L`````ZR&[`````.L:B?:[`````.L1NP````#K
+M"HGVN`````#K$Y"#[`A3_W0D'.C\____B=B#Q!"#Q`A;PY"+1"0$BT`4BT`,
+MPY"+1"0$BU`4BT0D"(E"#,.0BT0D!(M`%,=`"`0```##D(/L#(M4)!"+0A3'
+M0`@@````:@!J!&H(4NC\____A<`/E<`/ML"#Q!S#C78`4X/L%(M<)!R+4Q2+
+M at DB```"#X-^)@DB```!3Z/S___^)'"3H_/___X/$&%O#C78`@^P8BTPD'(M1
+M%(N"2(```(/(((F"2(```%'H_/___X/$',.0BTPD!(M1%(M$)`B)@D"```"+
+M412+1"0,B8)$@```PY!3BU0D"(M,)`RX`````(/Y/W="@_D?=B"+4A2+FD2`
+M``"#Z2"X_O___]/`(=B)@D2```#K&XUV`(M2%(N:0(```+C^____T\`AV(F"
+M0(```+@!````6\.04XM4)`B+3"0,N`````"#^3]W0H/Y'W8 at BU(4BYI$@```
+M@^D at N`$```#3X`G8B8)$@```ZQN-=@"+4A2+FD"```"X`0```-/@"=B)@D"`
+M``"X`0```%O#D(M$)`2+0!2+D#R```"+@`R!``"H('0#@,X"J0```@)T`X#.
+M`8G0PY!3BUPD"(M,)`R+4Q0/ML&)@CR```"Z`````/;%`G0"LB#VQ0%T!H'*
+M```"`HM#%(F0#($``(72=`^+4Q2+0C2#R!")0C3K#9"+4Q2+0C2#X.^)0C1;
+MPXGVBU0D",=""`````"+1"0,)?\/``")0 at SV1"00('0&@,P at B4(,QT(4````
+M`,="$`````"X`0```,-64X/L!(M,)!"+="04B?.+1"0<N@\```#V1A0!#X3G
+M````]D`4`745BT$4BT`,N@\````[1"08#X3,````9HM#$"7_#P``9HE&((M#
+M%"4``/]_P>@09HE&(L9&)`"+0Q2H`G50J`1T",9&)`'K1HGV]D,4$'0F@$XD
+M`@^V0Q6(1B6#N:@9````=2J#[`B-1B!04>C\____@\00ZQCV0Q0(=`:`3B0(
+MZPSV0Q0@=`:`3B00B?:+0Q`E``#P#\'H%(A&)HM#%/;$`70.)0#^``#!Z`F(
+M1B?K!9#&1B?_BT,0)0"`#P#!Z`^(1BB+0Q`E````\,'H'(A&*8M#$,'H#(/@
+M`8A&*KH`````B="#Q`1;7L.0D)!55U93@^P\BWPD4(ML)%R)?"0LO@````"+
+M5"18#[="`HG"P>H'P>@(,="Z#````*@!#X0-"```BTPD6`^W00*)PL'J!L'H
+M!3'0N at P```"H`0^$[@<``&H`:@%J`E?H_/___X/$$+H`````A<`/A.('``"#
+M[`C_="1 at 5^C\____B40D.(/$$+H,````A<`/A+$'``"#?"14!G0A at WPD5`9W
+M"8-\)%0!=PGK$8-\)%0(=`JZ#````.F)!P``QT0D)`````"%[70=BT<4BX!`
+M$0``B40D)(/L"/]T)#!7Z-,,``"#Q!"+1Q2+@%B```")1"0 at A<!U",=$)"`!
+M````BU<4BX($@```)0````*)1"08BX(00```)6``_@")1"0<BYH40```B5PD
+M-(N2&$```(E4)#"#[`C_="1<5^C\____B3PDZ/S___^#Q`C_="1 at 5^C\____
+M@\00N@,```"%P`^$X`8``(M4)%@/MT(")?`!```]0`$``'14/4`!``!_)CW`
+M````='P]P````'\,/:````!T7.F-````/=````!T=.F!````/<`(``!T5CW`
+M"```?PD]4`$``'0DZVH]0`D``'0)/5`)``!T%.M:QT0D%`$```#'1"00`0``
+M`.M2QT0D%`(```#'1"00`0```.M`QT0D%`,```#'1"00`@```.LNQT0D%`0`
+M``#'1"00`@```.L<QT0D%`4```#'1"00`@```.L*N at P```#I%`8``(M'%,>`
+M`)@```<```"[`````(GVC01;BT\4BQ3%H$4``(E4)`R+5"04C01"BP2%H$4`
+M`(M4)`R)!!%&]\8_````=0V#[`QJ`>C\____@\000X/[&':^NP````"0BPS=
+M`$@``(7M=`V- at 0"`__\]_P\``'8DBU<4BP3=!$@``(D$"D;WQC\```!U#H/L
+M#&H!Z/S___^#Q!"00X'[Q@```':[5O]T)!3_="0<5XM,)#S_D<P6``"#Q`C_
+M="1 at 5^C\____@\009H._J@$``$$/AMT```"+1Q3'@"R8```"H`(`BUPD6&:!
+M.[0)=2F+1"0L#[:((`@```^W@"H(```IP;AG9F9F]^F)T-'XP?D?B<-F*<OK
+M%HM,)"P/MI$@"```N&=F9F;WZHG3T>N+5"18#[="`B70````/<````!U+XM/
+M%(M$)"P/MI`@"```]]K!X@:!XL`/``")V,'@$O?8)0``_``)PHF13)D``.L-
+MBT<4QX!,F0```````(M7%(N"**(``(#D_8F"**(``(M7%(N"**(``"7_`_[_
+M@,P(B8(HH@``BT<4QX!@@```#P```&:#OZH!``!"=@V+1Q3'@%2B````````
+MBT<4QX!XF```#@```(/L!(U$)#Q0_W0D,%?H_/___X/$$+H#````A<`/A!X$
+M``"-1"0X4`^W1"084/]T)#!7BU0D//^2V!8``(/$$+H#````A<`/A/0#``"+
+M3"18]D$"0'0-@^P(45?H_/___X/$$(/L"/]T)#!7Z/S___^#Q!"Z`P```(7`
+M#X3``P``A>UT#8M'%(M<)"2)F$`1``"+3Q2+1"0L#[:07!8```^V@%T6``#!
+MX`@)PHM<)"P/MH->%@``P>`0"<(/MH-?%@``P>`8"<*)D0"```"+3Q0/MI-A
+M%@``P>((#[:#8!8```G0"T0D&(G"@<H``(``@[MH&0```'0(B<*!R@``@`B)
+MD02```"#[`C_="1<5^C\____BT<4QX#@@```_____X/$"(M'%,>`Y(```/__
+M__^+5Q2+ at A!````+1"0DB8(00```BT<4BU0D/(F0%$```(M'%(M,)#B)B!A`
+M``"+1Q2+7"0HB9A8@```BT\4BT0D-`^VD&(6```/MH!C%@``P>`("<*+7"0T
+M#[:#9!8``,'@$`G"#[:#918``,'@&`G"B9$(@```BT\4#[:39Q8``,'B"`^V
+M at V86```)T(F!#(```(M'%,>`@````/____^+1Q3'@!B`````!P``_W0D,%?_
+MD]06``"#Q!"Z`P```(7`#X1)`@``BT<4BX`4F0``B<*!XO\_``"+1"18]D`"
+M('0/P>("N*.++KKWXL'J!.L*N,W,S,SWXL'J`XM'%,>`')@```$```"#[`R-
+M0F10Z/S___^+5Q2+ at F"8``"#R`.)@F"8``"#Q!"+5"18#[="`B6@````/:``
+M``!T/XM,)"R#N5P9```"=#*+5Q2+ at B"9``"`S/")@B"9``"+5Q2+ at B"9```-
+M```!`(F"()D``,>!7!D```$```#K#HM<)"S'@UP9````````NP````"^`0``
+M`(TLG0````"+5Q2)\(C9T^")A"H`$```0X/["7[CBUPD+,>#+"\```````"[
+M`````&:#O[P!````=!F#[`A35^C\____@\000P^WA[P!```YV'_GBT0D+,>`
+M:!8``&4)"`"#?"14!G4*QX!H%@``91D(`(M7%(M,)"R+ at 6@6``")@J````"+
+M5Q2+ at JP````-```'`(F"K````(/L#%?H_/___X/$$(7`=`R#[`Q7Z/S___^#
+MQ!!J`&H!:&"8``!7Z/S___^#Q`B+1Q3'@`B!``!2````_W0D7%?H_/___XM7
+M%(N"((```"7__W_^B8(@@```@\00BUPD+(.[A!D``/]T$H/L"/^SA!D``%?H
+M_/___X/$$(M$)"R#N(@9``#_=!*#[`C_L(@9``!7Z/S___^#Q!"+5"0L@[J,
+M&0``_W02@^P(_[*,&0``5^C\____@\00@[_(`0```'0/BU<4BX?(`0``B8)(
+M@```BTPD5(F/L`$``+H!````ZQ&0BUPD8(,[`'0"B1.Z`````(G0@\0\6UY?
+M7<.)]E.+7"0(BU0D#(M#%(N(!(```('A___\_X/Z`713 at _H!?P:%TG0LZU&#
+M^@9T!X/Z"'0^ZT6+4Q2)R`T```$`B8($@```BU,4BT(4@^#?B4(4ZR>+4Q2)
+MR`T```(`B8($@```BU,4BT(4@\@@B4(4ZPF+0Q2)B`2```!;PY"#[!1J`O]T
+M)!SHP@,``(/$',.)]E.#[`B+7"00:@!J`6H"4^C\____@\00N@````"%P'00
+M@^P(:A-3Z)$#``")PH/$$(G0@\0(6\.055=64X/L%(M<)"B+="0L:A-3Z&T#
+M``"#Q!"Z`````(7`#X1=`0``:@!J`6H"4^C\____@\00N@````"%P`^$00$`
+M`(/L"&H`4^@V`P``@\00N@````"%P`^$)@$``(7V#X09`0``#[>#K`$``"7P
+M````@_ at O?B6]"`````^W1@*H('41)=````"YZ@```#W`````=2JYZP```.LC
+MO0`````/MT8"J"!U$270````N:H````]P````'4%N:L````/MT8"J$!T%:@@
+M=0PET````#W`````=06#S03K"?9&`D!U`X/-`?9&`P%U`X/-`@^W1 at +!Z`2#
+MX`&#^`$9__?7@^<#BT,4BY!\F```#[=&`J@@=0PET````#W`````=3&+0Q2)
+MN`28``"+0Q2)J`"B```YRG1*BT,4B8A\F```@^P,:"P!``#H_/___X/$$.LO
+M.<IT&8M#%(F(?)@``(/L#&@L`0``Z/S___^#Q!"+0Q2)N`28``"+0Q2)J`"B
+M``"Z`0```(G0@\0,6UY?7<.)]E575E.#["2+;"0XB6PD(/]T)#Q5Z/S___^)
+MQX/$$+@`````A?\/A+X!``"+1"08@[A<&0```0^%Z````(M%%(N`()D``*D`
+M``$`#X74````BU0D&,>"7!D```````"[`````)"+112+L!"<``"+B!2<``"+
+M@!B<``")1"04A?9T!(7)=1J+512+ at B"9```-```!`(F"()D``$.#^PE^Q8GP
+MT>B)RM'J`=#!Z0:)PL'J!P^$Q0```(/Y`0^.O````(I$)!3WV`^^P(G3F??[
+MB40D#(/@/XE$)!")\)GW^8G!@^$?BUT4BT0D$,'@!8N3()D```G0"<B`S`B)
+M at R"9``"+5"08QX)<&0```@```,='#`$```"*7"00B%\0B$\1ZUN-=@"+5"0T
+M#[="`B6@````/:````!T1(M<)!B#NUP9```"=3>#?PP`=3&+512+ at B"9``"`
+MS/")@B"9``"+512+ at B"9```-```!`(F"()D``,>#7!D```$```"0@^P(5U7H
+M=@$``(/$$+@`````]D<"`G55BU44BX)@F```@\@"B8)@F```BT0D-`^W4`+V
+MQ@%U&0^WA:P!```E\````(/X+WX@]L)`=!N-=@"#O:0!```'=`^#[`Q5Z/H4
+M``"#Q!"-=@"X`0```(/$'%M>7UW#C78`5U93BWPD$(MT)!2)\(7V=06X____
+M_XG#BT<4BU`,B;``0```@^P,:@_H_/___X/C`X/F`U93:`!```!7Z/S___^)
+MPX/$(/?&`0```'4FBT<4QT`4`````&H`:@%J`E?H_/___X/$$(7`=`F+1Q2+
+M@,````")V%M>7\.)]HM$)`2+0!2+@&28``#!Z!,E_P$``/;$`70%-0#^__^8
+MPXUV`(M,)`R+5"0$BT0D"`^W0`(EX`$``#W`````=#`]P````'\)/:````!T
+M%NLL/4`!``!U)6:+ at EH(``!FB0'K(9!FBX)<"```9HD!ZQ1FBX)>"```9HD!
+MZPBX`````,.)]K@!````PXGV5U93@^P$BW0D%(M\)!B+1A2+@&"8``"[````
+M`*@"=2A6Z/S___^)PXU$)`905U;H9/___X/$$(7`=`UF.UPD`GX&9H-/`@*0
+M9HE?$@^_PX/$!%M>7\.)]E575E.#[`R+1"0HQT0D"`````"->/_![P-(@^`'
+MB00DBW0D)+T`````A?9^9XM$)"S!X`.)1"0$BQPD`?.#^PAV!;L(````N`$`
+M``"(V=/@2+H!````B at PDT^)*,="*3"0$T^"+5"0@(P2ZT^B*#"33Z(GIT^`)
+M1"0(B=@K!"0!Q8L$)(UT!OC'!"0`````1X7V?Z2#[`C_="0L_W0D%.C\____
+MB40D&(M$)!B#Q!Q;7E]=PU575E.#[$R+;"1 at B>Z+1"1D#[=X`L'O!X/G`0^W
+M0`(E\`$``#U``0``=&$]0`$``'\R/<`````/A*,````]P````'\0/:`````/
+MA(0```#IF````#W0````#X2!````Z8@````]P`@``'1U/<`(``!_"CU0`0``
+M=!/K<9`]0`D``'0)/5`)``!U8HGVQT0D/``````/MX6L`0``)?````"#^"]_
+M4X.]I`$```=T2HM-%(N11)D``(/BQXN&6!8```^_`,'@`X/@.`G"B9%$F0``
+MZR7'1"0\`0```.L;C78`QT0D/`(```#K#HGVN`````#I#0,``(GVBTT4BX$0
+MF0``)0;\__^+7"0\#[>47G((``#!X at 0)T(/(`8F!$)D``(/L!/]T)&C_ME at 9
+M``!5Z/S___^+510/OX1>6@@``"7_`0``@,P"B8)HF0``@\009H&^``@``/]/
+M=D*+1"1D]D`"$'0X9HN$?M0(``"*G#?<"```BI0WW@@``(A4)"!FBXQ^V`@`
+M`&:)3"009HN4?N`(``!FB10DZSJ-=@"+3"0\9HN$3C`(``"*G#%4"```BI0W
+M5P@``(A4)"!FBXQ^-@@``&:)3"009HN4?L`(``!FB10DBTT4BY%$F```@>)_
+MP/__P>`')8`_```)PHF11)@``(M-%(N!4)@``+``#[;3"=")@5"8``"+312+
+MD5"8```P]HI$)"#!X`@E`/\```G"B9%0F```BTT4BY%(F```@>+_#_S_BT0D
+M$,'@#"4`\`,`"<*)D4B8``"+712+3"0\#[>43D@(``")T,'@&,'B$`G0#[>,
+M3DX(``")RL'B"`G0"<B)@S28``"+312+D2B8```P]HM<)#P/MX1>/`@``,'@
+M"`G"B9$HF```BTT4BY%DF```@>+_#_C_#[>$7D((``#!X`PE`/`'``G"B9%D
+MF```NP(```!F@;X`"````C!W&6:!O:(!```B$'4RBT0D9/9``D!T*+,(ZR2+
+M5"1D9HL"@^`?=!B#Z`IF at _@,=@^+3"0\#[>$3K0(```!PY"+312+D229``"`
+MX@&-!!LE]P````G"B9$DF0``BUPD9(-[#`!T"@^^0Q`/OEL1ZQ`/MX1^1 at D`
+M``^WG'Y*"0``BTT4BY$@F0``@>(?^/__P>`%)>`'```)PHF1()D``(M-%(N!
+M()D``(/@X(G:@^(?"=")@2"9``"+512+ at B"9``"`S`B)@B"9``!F@;X`"```
+M`$!V08M4)&0/MT(")=`````]T````'4+9H&^``@``/]/=B*+312+D0RB``"!
+MXO__`_^+!"3!X!(E``#\``G"B9$,H@``9H&^``@```!0=@V+113'@."9````
+M````N`$```"#Q$Q;7E]=PY!75E.+="00BU0D%`^W0 at +!Z`2#X`&#^`$9P(/@
+MV(V$@)`!``#!X!B)P]'K#[<2B=B)T;H`````]_&)P[\?````B=B)^=/HJ`%U
+M`T]U\[@F````*?B)Q[@7````B<(I^K`!B-'3X`'8NA@````I^HC1T^B+7A2+
+MDQ28``"!XO__`0#!X!$)PHF3%)@``(M>%(N3%)@``('B_Q_^_XGXP>`-)0#@
+M`0`)PHF3%)@``%M>7\.-=@"#[`R+5"00BT0D%(/X/'8%N#P```!FB8+,`0``
+M@^P$C40D#%#_LOP'``!2Z/S___^#Q!S#55=64X/L%(ML)"B+7"0LB>__M0 at O
+M``#_M00O``#H_/___X/$"&H at C84,+P``4.C\____9HN5S`$``(/$$&:#^CQ^
+M!;H\````9H/Z.W\*N0````!FA=)U#HN%T`$``&:+C``X3@``@^P(C40D$E"-
+M1"044`^_PE`/O\%04U7H9@(``(/$(+H`````A<`/A$H"``"#[`S_="0T4XU$
+M)!Y0C40D(%!5_Y?<%@``@\0 at N@````"%P`^$(0(``(.]I`$```=T"&:#ARHO
+M```$O@````!FBX>`&0``9@&$=PPO``!F@[QW#"\``#]V"F;'A'<,+P``/P!&
+M at _X/=MUF@[\L"````0^'I@```&:#O:H!``!!=UP/MT,")=`````]P````'5,
+M9H$[M`EU(P^VCR`(```/MX<J"```*<&X9V9F9O?IB=#1^,'Y'V8IR.L2#[:7
+M(`@``+AG9F9F]^J)T-'H@^P(#[?`4%7H4`8``(/$$+X`````C1RU`````(M-
+M%(N'!"\```^W5+`"P>(8@<H``/\`#[<$L,'@"`S_)?__```)PHF4&8"A``!&
+M at _X??L6+310/MY<2+P``@^(_P>(8#[>'$"\``(/@/\'@$`G"#[>'#B\``(/@
+M/\'@"`G"#[>'#"\``(/@/PG"B9$TF0``BTT4#[>7&B\``(/B/\'B&`^WAQ at O
+M``"#X#_!X!`)P@^WAQ8O``"#X#_!X`@)P@^WAQ0O``"#X#\)PHF1.)D``(M-
+M%`^WER`O``"#XC_!XA@/MX<>+P``@^`_P>`0"<(/MX<J+P``@^`_P>`("<(/
+MMX<<+P``@^`_"<*)D32B``"+310/MY<H+P``@^(_P>(8#[>')B\``(/@/\'@
+M$`G"#[>')"\``(/@/\'@"`G"#[>'(B\``(/@/PG"B9$XH@``BU44@[]L&0``
+M`1G`@^#`@\!\B8(\F0``9HN'#"\``&:)A<X!``"Z`0```(GVB="#Q`Q;7E]=
+MPXGV55=64X/L5(M$)'!FB40D)HM4)'1FB50D)(MT)&B-O at PO``!FQT0D(CP`
+M9L=$)"`\`/]T)&Q6Z/S____1X&:)1"0FBXPDA````&;'`<3_BX0D@````&;'
+M`#P`@\0(_W0D;%;H_/___XA$)"6[`````(/$$&:#O at 0)````#X2'````#[9L
+M)!6)ZH/B^(E4)!"09H.\7 at 8)````=&%F#[9$)!5F.81>!@D``'01#[>$7 at 8)
+M``"#X`<,X#G%=4")V,'@!HV$,%`-``"#[`A0BTPD<`^W`5#H2P4``(/$$(%\
+M)!#@````=0YF.40D&G8/9HE$)!KK"&:)1"0:ZPV00P^WA at 0)```YV'^(BU0D
+M9`^W0@(ET````#W`````#X6F````@&0D%0>`3"05`;L`````9H.^!`D````/
+MA),````/MFPD%8GI@^'XB4PD#)!F@[Q>!@D```!T868/MD0D%68YA%X&"0``
+M=!$/MX1>!@D``(/@!PS at .<5U0(G8P>`&C80P4`T``(/L"%"+5"1P#[<"4.B/
+M!```@\00 at 7PD#.````!U#F8Y1"08=@]FB40D&.L(9HE$)!CK&9!##[>&!`D`
+M`#G8?XCK"F:+3"0:9HE,)!B+1"1D]D`#`70(BH8<"```ZP:*AAT(``"#[`0/
+MOL!0_W0D;/]T)&SH_/___XC#BU0D=`^W0@*#Q!"H0`^$I@$``"70````/<``
+M``!U)H/L#(U$)#Q0#[>&&`T``%"-AAH-``!04O]T)'SH&@4``(/$(.LG@^P,
+MC40D/%`/MX;&#```4(V&R`P``%#_="1\_W0D?.CQ!```@\0@#[=4)!8/OL,I
+MP@^W1"0:.<)^`HG"B=&+1"1D]D`"$'1$9H&^``@````P=A(/MX8."```#[_2
+M.=!^`HG0B<%F@;X`"```_S]V'(M4)&3V0@*`=!(/MX80"```#[_1.=!^`HG0
+MB<$/MU0D-@^_P3G"?@*)PF:+1"0>T>")T68IP7D%N0````!F.TPD''X$BTPD
+M'&:)3PAFB4\&9HE/!&:)3P)FB0^+1"0T9CG(=@*)R&:)1PIFBT0D,F8[!W8#
+M9HL'9HE'#(M$)#!F.P=V`V:+!V:)1PYF@;X`"```_S]V20^_T8M$)&3V0`*`
+M=`T/MX84"```.<)\#>L/#[>&$@@``#G"?02)R.L:BU0D9/9"`H!T"6:+AA0(
+M``#K!V:+AA((``!FB4<>ZP=FBP=FB4<>9HM'#HM,)'!FB0%FBP>+5"1T9HD"
+M#[\"B89\&0``BTPD9`^W00*H('40)=`````]P`````^%Y0```(/L#(U$)"Q0
+M#[>&.`T``%"-ACH-``!0_W0D?/]T)'SH80,```^W3"0V#[[#*<$/MT0D.(/$
+M(#G!?@*)P0^W5"0F#[_!.<)^`HG"9HM$)![1X(G19BG!>06Y`````&8[3"0<
+M?@2+3"0<#[=$)"8/O]$YT'X"B=!FB4<0#[=$)"0/O]$YT'X"B=!FB4<29HE'
+M%`^W1"0B#[_1.=!^`HG09HE'%F:)1Q@/MUPD(`^_P3G#?@*)PV:)7QIFB5\<
+M#[?3BTPD<`^_`3G"?0-FB1D/MU<2BTPD=`^_`3G"?@EFBT<29HD!B?:+5"1T
+M#[\"B89D&0``N`$```"#Q$Q;7E]=PXGV55=64X/L0(ML)%1FBX4H"```9HE$
+M)`YJ((V%#"\``%"-1"0<4.C\____9L=$)!X(`(/$$`^W1"0.9HM4)`IF*51$
+M$+X`````9L=$)`P```^W?"0.B7PD!&:#?'P0`'D(9L=$?!```)!F at _X(=0J^
+M#P```.M0C78`BTPD!`^_5$P0#[?>#[>$70PO```YPG4T9H.\70PO````=0=F
+M_T1,$.L69H-\)`PR=@=F_T1\$.L'9O],?!")]KX`````9O]$)`SK`49F at _X/
+M=HT/MT0D#F:#?$00/WX'9L=$1!`_``^W1"0.#[><10PO```K7"14>0:[````
+M`)`/MT0D#@^_3$00BY4$+P``9HL$6F:)!$IF_T0D#F:#?"0.#@^&$____V;'
+M1"0."``/MU0D#F:+1%009HF$50PO``!F_T0D#F:#?"0.#G;A@\0\6UY?7<.0
+M5U93@^P at BUPD-(M\)#"Z`````)!F at SS3`'0/9HL$TV:)1%000H/Z!W[JB=:#
+M[`R-1"0:4(U$)!Q0#[?"4(U$)"A0#[?'4.C\____N@````"#Q"`Y\GT=BT0D
+M$&8[1"0,=!*+1"0,B?9".?)]!V8Y1%00=?2+1"0,9CM$)`YU!68Y^'0,N#P`
+M``"#?-,$`'0%9HM$TP(/M\"#Q"!;7E_#D%575E.+;"04BTPD&(M\)!R+7"0@
+MBW0D)`^WPP^WU@^OPKH`````A<!T88G89CG/=%</M\4/M\DIR(T$@(T$@,'@
+M`@^WURG*B=&9]_F)PHG89H72>#.)\&:#^F1_*P^_T@^WS@^ORKAD````*=`/
+MM],/K\(!P;@?A>M1]^F)T,'X!<'Y'V8IR)`/M]")T%M>7UW#B?955U93@^P\
+MBVPD6(M\)%RZ``````^WQXG!@_@`?A2)]HT$DF:+1$4(9HE$5"!".=%_[H/L
+M#(U$)"I0C40D+%`/M]]3C40D.%"+5"1P#[<"4.C\____O@````!FQT0D.@``
+MN@````"#Q""#^P!^*XG8BWPD'&:+3"0>C78`9CE\5"!U!6:)5"0:9CE,5"!U
+M!8G6ZP:00CG0?^,/M_:--+;1Y@^W3"X&#[=<)!J-')O1XP^W1"L&B40D#`^W
+M?"0>#[=4)!R)5"04BT0D5`^W$%'_="005_]T)"!2Z([^__^#Q!2+5"1 at 9HE"
+M!@^W1"X$B40D#`^W5"L$BT0D5`^W"/]T)`Q25_]T)"!1Z%[^__^#Q!2+5"1@
+M9HE"!`^W1"X"B40D#`^W5"L"BT0D5`^W"/]T)`Q25_]T)"!1Z"[^__^#Q!2+
+M5"1 at 9HE"`@^W!"X/MQ0KBUPD5`^W"U!25_]T)"!1Z`?^__^+5"1T9HD"@\10
+M6UY?7<-75E.+3"04BWPD'(MT)"`/MU0D$(T$DHT$@(T4A0`````/MUPD&(T<
+M60^W`8T$@(T$@(T$A?;___\YT'8+9HL!9HD&9HD'ZW0/MT/^C02`C02`C02%
+M"@```#G0<R=FBT/^9HD&9HD'ZU-FBP%FB09FB0?K2&:+`6:)!V:+00)FB0;K
+M.9`YV7,T#[<!C02`C02`P>`"*="#^/=\!8/X"7[)#[=!`HT$@(T$@(T$A?;_
+M__\YT'>_@\$".=ERS%M>7\.+3"0$C9$\%@``#[>!K`$``"7P````@_ at O?C&A
+MY$\``(F!/!8``(T$P(T$A>A/``")0AS'0A@!````QT(,%````,="$%4```##
+MC78`H61.``")`HT$P(T$A6A.``")0AS'0A@!````QT(,%````,="$",```##
+MB?955U93@^P,BU0D((M\)"0/MX*L`0``)?````"#^"]^2&H`:B1J`8/L#&H'
+M4O^2T!8``(/$%%#H,>W__[T`````OFL```"Y`````+MK````@\00 at _@!=7-F
+MOE,`LU/'1Q`W````ZV2)]FH`:B5J!H/L#&H'4O^2T!8``(/$%%#HZ>S__[T`
+M````@\00OC(```"#^#]T`XUP!(GI at _@_=`6Y0````(U9,HGJ at _@_=0:-512-
+M=@")5PR#^#]U"(U#\>L&C78`C4,%B4<0BU<$N``````YZG($.?)V#3G*<@0Y
+MVG8%N`$```"#Q`Q;7E]=PY"+1"0$#[^0@!D```.0?!D``(M(%,'B"8'B`'X`
+M`(#.@(F1,)D``,>`8!D```$```##C78`5U93BWPD$(G[C;<\%@``N`````"#
+M?A@`#X3@````@[]@&0```0^%S0```(M'%(N`,)D``*D`@```#X6Y````B<+!
+MZAF)5 at 0E``"``<'H%X/X`G4<9H._J@$``$%V#@^WAR@(```!T(E&!.L$@T8$
+M!0^WAZP!```E\````(/X+WXC@^P,5^B2`0``@\00BT8$.T84<@@K1A2)1 at 3K
+M!\=&!`````#'@V`9````````@^P(5E?H)/[__X/$$(7`=3F#[`A65^@[````
+M@\00A<!T*(/L"%97Z$8```"#Q!"%P'X7QX-@&0```@```,>#7!D```````"-
+M=@"+ at V`9``!;7E_#B?:+3"0(N`````"+400[40QV!3M1$'(%N`$```##5E.+
+M7"00BT0D#`^W@*P!```E\````+[@3P``@_ at O?P6^8$X``(L#C03`C42&"(E#
+M'(M#!#M#$')7NO____^#.P`/A*T```"+0P2)0P@[0Q!R-H,[`'0QD(L32HD3
+MC132C126BTL<BT(8*T$0T>"+2P at IP8E+"(/""(E3'#M+$'('@SL`==*)]KH!
+M````ZV60BT,$N@`````[0PQW5XL&2+K^____.0-T2XM#!(E#"#M##'<XBP9(
+M.0-S,9"+$T*)$XT4THT4EHM+'(M"&"M!$-'@BTL(*<&)2PB#P at B)4QP[2PQW
+M!XL&2#D#<M"Z`@```(UV`(G06U[#C78`5U93BUPD$(VS/!8``,=&%`````!J
+M`&HD:@&#[`QJ!U/_D]`6``"#Q!10Z/WI__^#Q!"#^`%U68M&'`^_.&H`:B!J
+M!(/L#&H'4_^3T!8``(/$%%#HU.G__X/$$(/_`70:@_\!<@R#_P)T%8/_`W09
+MZQ_'1A0`````ZQ:)1A3K$8U$`/N)1A3K"-'@B484C78`6UY?PU575E.#[!2+
+M="0PBUPD-%;_="0PZ/S___^)Q8U[_\'O`TN#XP>#Q!"%]GYRBT0D,,'@`XE$
+M)`B0C0PS at _D(?@6Y"````+H!````T^)*N`$```"(V=/@2#'"BDPD"-/BB='W
+MT8M$)"`C#+B)3"0$B>B(V=/@BDPD"-/@(=`+1"0$BU0D((D$NHUT'OBY"```
+M`"G9T^V[`````$>%]G^:@\0,6UY?7<.)]E575E.#[!2+5"0L#[="`L'H!(/@
+M`8/X`1G`)?X```"#P`)0_W0D+.C\____B<>]`````(/$$(,X`'X\D(U$;0"-
+M1(<@#[98",'C`HM4)""+<A2#[`QJ``^V0`M0:@Y74NC\____#[?`B80>`(<`
+M`(/$($4Y+W_%BT0D)/9``A`/A:L```"#[`AJ!/]T)"SH_/___XG'O0````"#
+MQ!"#.``/CHH```")]HU$;0"-=(<@#[9&"(T$A0"'``")1"0(BU0D((M:%(/L
+M#&H`#[9&"U!J#E=2Z/S___\/M\"+5"0HB003@\0@@'X)`'0X#[9&"8T$@HE$
+M)`B+1"0 at BU@4@^P,:@$/MD8+4&H.5_]T)#SH_/___P^WP(M4)"B)!!.#Q""-
+M=@!%.2\/CWO___^-=@"#Q`Q;7E]=PU575E.#[!2+?"0HBX=H%@``)?___W]0
+M5^C\____B<6+1Q2+4#")TX'C\`,``,'K!(G>@\00 at WPD)`!T$+ at E````*=C1
+MZ(TT`^L(B?:#^P*#UO\YWG08BT\4@>(/_/__B?#!X`0E\`,```G"B5$P@^P(
+M55?H_/___SG>#Y7`#[;`@\0<6UY?7<-64X/L!(M,)!"+5"04#[>!O`$``+L`
+M````.=!^(X/L!/]T)!R-!%*-!("-A('X%@``4%'H_/___XG#@\00C78`B=B#
+MQ`1;7L-64X/L!(M,)!"+5"04#[>!O`$``+L`````.=!^(X/L!(T$4HT$@(V$
+M@?@6``!0_W0D(%'H_/___XG#@\00C78`B=B#Q`1;7L-55U93@^P,BVPD((M\
+M)"2)ZXV5M`$``(/_`G08 at _\"=P>#_P%T)NM8 at _\#=`^#_P1T$^M,#[=R"$[K
+M39`/MW((@^X"ZT.^`0```.L\O@`````/MT((B<&#^`!^%8T$=HT$@(.\@_P6
+M````=`5&.?%_ZP^W0 at BZ_____SGP='GK"+K_____ZW"0C01VC02`C9R#^!8`
+M`+K_____ at WL$`'57@^P(:CQ3Z/S___^)>P2#Q!"#?"0H`'4LQT,,"P```,=#
+M%`(```#'0QC_____QT,<_P,``&;'0R`*`&;'0R(*`.L3B?:#[`3_="0L5E7H
+M_/___X/$$(GRB="#Q`Q;7E]=PXUV`%.+3"0(BUD4BY'D%@``@>+_`P``BX'L
+M%@``P>`0)0``_P,)PHF3I````(M9%(N1Z!8``('B_P,``(N!\!8``,'@$"4`
+M`/\#"<*)DZ@```"+612+ at ZP````E`/S__XN1]!8``('B_P,```G0B8.L````
+M6\.05U93BWPD$(M,)!0/MX>\`0``O@`````YR'95C01)C02`C9R'^!8``+X`
+M````@WL$`'0]QT,$`````+C^____T\`AA^06```AA^@6```AA^P6```AA_`6
+M```AA_06``!35^@F____O@$```"#Q`B)]HGP6UY?PXGV55=64XMT)!B+?"04
+MBX_\!P``#[>'O`$``+H`````.?`/AL8#``"-!':-!("-G(?X%@``N@$```"#
+M>P0`#X2J`P``@WL8_W4TA<ET%0^W00(EH````+H?````/:````!T!;H/````
+MN`$````YT',0D-'@@\@!.=!R]^L$D(M#&(TLM0````"+5"04BTH4B<*!XO\#
+M``"+0QS!X`HE`/P/``G"BT,4P>`4)0``\`\)PHF4*4`0``"+1"04BT at 4#[=#
+M(L'@!"7P````#[=3((/B#PG0#0`@"`")A"F`$```BU0D%(M"%,>$*,`)````
+M"```@WLD`'1#BTH4BU,D@>+___\`BT,HP>`8"<*)E"G`"```BT0D%(M(%(N$
+M*<`)``")PH/*`8-[*`!T"(G"@<H!`0``B90IP`D``(-[,`!T(HT,M0````"+
+M;"04BU44BT,P)?___P`-`````8F$"@`)``"-#+4`````BT0D%(M0%(M#+"7_
+M_P\`@WLL`'0%#0``$`")A`K`$```@WLL`'0E]D,,0'0?C0RU`````(ML)!2+
+M512+A`K`"0``@,P"B80*P`D``/9##!!T(8T,M0````"+1"04BU`4BX0*`!$`
+M``T``"``B80*`!$``/9##(!T'XT,M0````"+;"04BU44BX0*`!$``(#,`HF$
+M"@`1``"+0P2#^`-T6H/X`W<*@_@"=!/IT0```(/X!`^$J0```.G#````C12U
+M`````(M$)!2+2!2+A!'`"0``#**)A!'`"0``BVPD%(M-%(N$$0`1```-```E
+M`(F$$0`1``#IA@```(T,M0````"+1"04BU`4BX0*P`D``(/(8HF$"L`)``"+
+M%0`````K%0````"+AW`9```IT"L%`````,'@"HML)!2+510-`````8F$"@`)
+M``"+512+A`H`$0``#0``!`")A`H`$0``ZQ^-#+4`````BT0D%(M0%(N$"L`)
+M``"#R"")A`K`"0``]D,X`70AC0RU`````(ML)!2+512+A`H`$0``#0``)`")
+MA`H`$0``]D,,`703N`$```")\=/@"8?D%@``ZQ&)]KC^____B?'3P"&'Y!8`
+M`/9##`%T$[@!````B?'3X`F'Z!8``.L1B?:X_O___XGQT\`AA^@6``#V0PP"
+M=!.X`0```(GQT^`)A^P6``#K$8GVN/[___^)\=/`(8?L%@``]D,,!'03N`$`
+M``")\=/@"8?P%@``ZQ&)]KC^____B?'3P"&'\!8``/9##`AT$[@!````B?'3
+MX`F']!8``.L1B?:X_O___XGQT\`AA_06``!3_W0D&. at S^___N@$```"#Q`B-
+M=@")T%M>7UW#D(M$)`C!X`*+5"0$BU(4BX0"``@``,.)]HM4)`C!X@*+1"0$
+MBT at 4BT0D#(F$$0`(``"X`0```,.0BTPD"(M$)`2+4!2X`0```-/@B8)`"```
+MN`$```##B?964XM4)!#!X@*+1"0,BW`4BX06``H``(G#@^,#=1JX`0```(I,
+M)!#3X(N60`@``(7"=`6[`0```(G86U[#C78`5U93BWPD$(MT)!2+5Q2X`0``
+M`(GQT^")@H`(``"[Z`,``(GV5E?H_/___X/$"(7`=!"#[`QJ9.C\____@\00
+M2W7BBT<4QX"`"````````(7;#Y7`#[;`6UY?PY!55U93@^P,BUPD)(ML)"B+
+M?"0LBW0D,,=$)`@`````QT0D!`````#&1"0#`<9$)`(`@WPD-``/A.0```"#
+M[`B+5"0HBX)H%@``)?___W]04NC\____B<&#Q!"%]G07BD8<@^`!B$0D`XM7
+M$('B_W\``(E4)`B+4Q"!XO]_``"+0P at E``!`@(!\)`,`=6>%P'1CL`"$P'0(
+MBT0D.(E$)`2+1"0(`=`K1"0$.T0D-'=%B=`K1"0$`40D"(7_="*+1Q`E`(#_
+M_XM4)`B!XO]_```)T(E'$/9&'`%U!<9$)`(!@'PD`@!T#H%C"/__OW^!8Q``
+M@/__ at 4L(``"`((!\)`(`=`N%[70'@64(__]_WX/L"%'_="0LZ/S___^#Q!"-
+M=@`/MD0D`H/$#%M>7UW#C78`5U93BW0D,(M<)#B+?"04BU0D&('B_P\``(M$
+M)"3!X!`)PO;#('0&@<H``(``BT0D-,'@&24````>"=#VPP%T!0T````!]L,0
+M=`4-````((E'"(M$)"")PL'B%/;#`G0&@<H````!B5<,BT0D+,'@$"4```\`
+MB4<0BT0D*(E'%(/^_W06B?#!X`TE`.`/``G0B4<, at 4\(````0/;##'1+N`#_
+M`'^*3"0\T_BZ`````*@!=#R)V,'H`X/@`8/X`1G`]]`E````@/;#!'0%#0``
+M0``)1PB+1"1`)?]_```)1Q"+1"0\P>`4"4<4N@$```")T%M>7\.-=@!3BT0D
+M%(M,)!R+7"0DBU0D#(7`=!7!X!0E``#P``E"$(M$)!#!X`4)0A2%R707B<C!
+MX!@E````#PE"$(M$)!C!X`H)0A2%VW02B=C!X!P)0A"+1"0 at P>`/"4(4N`$`
+M``!;PXGV4XM$)!"+3"08BUPD'(M4)`R#?"04`'00"T(,A<EU`X#,$(E"#.LY
+MD(7)=!C'0@@`````B4(,BT,0B4(0BT,4B4(4ZQS'0@@`````@,P0B4(,QT(0
+M`````,="%`````"0QT(<`````,="&`````"X`0```%O#C78`BTPD"(G*N`\`
+M``#V01P!#X1$`0``BT$<)?X?``#1Z&:)02`/MT$:9HE!(L9!)`"+01BH`74>
+MJ`)T!,9!)`'V0A@(=`2`220"]D(8!'0&@$DD!(GVBT(<)0``8`#!Z!6#^`%T
+M((/X`7(/@_@"="F#^`-T-^M'C78`BD(4@^`?B$$EZSF0BT(4)>`#``#!Z`6#
+MR("(027K)8M"%"4`?```P>@*@\B`B$$EZQ*+0A0E`(`/`,'H#X/(@(A!)9"+
+M0APE`.`?`,'H#8A!)HM"&"7P````P>@$B$$GBT(8)0`/``#!Z`B(02B+0APE
+M``!@`,'H%8/X`G0B at _@"=PF#^`%T)NLRB?:#^`-U*XM"$"4````/P>@8`$$H
+MD(M"$"4``/``P>@4`$$HBT(0)0``#P#!Z!``02B+0A at E`/```,'H#(A!*0^V
+M0A^#X`&#^`$9P(/``HA!*K@`````PXGVBT0D!(M,)`B+$2.0+"\``(D1]](A
+MD"PO``##D%.#[!"+7"08C8.H%@``4%/H_/___XM#%,>`)($```````"+0Q3'
+M@"B!````````BT,4QT!``````(M#%,>`,($``````@"+0Q3'@#B!```````"
+M@\086\-3@^P0BUPD&(M#%,=`0`8```"- at Z@6``!04^C\____BT,4QX`D at 0``
+M`````(M#%,>`*($```````"#Q!A;PXGV5E.+="00BUPD#+H`````D(T$DHT$
+M at F:+A(.T&0``9CL&=06)T.LTD(T$DHT$@HV,@[`9``!F at WD$`'449HL&9HE!
+M!&:+1@)FB4$&B=#K#)!"@_H_=KJX`````%M>PXGVBT0D!(N`L!D``,.0BT0D
+M!`5L%@``PXGV5E.#[`2+="00B?.X`````(.^I`$```5U#&:#OJ@!```)=0*P
+M`8F#J!D``(/L"&@`%0``C8.T&0``4.C\____N0````"#Q!"0C02)C02!C12#
+MQX+$&0``]`$``,>"R!D``,@```#'@LP9``#(````QX+0&0``9````,>"V!D`
+M`"@```#'@M09```'````C8*P&0``QD`+`,9`#`#&0`D'QD`*`(.[J!D```!T
+M%,>"]!D```S^OP#'@O at 9```X_[\`08/Y/W:"@[NH&0```'0JBU84BX/T&0``
+MB8(L at 0``BU84BX/X&0``B8(T at 0``@^P,5NC\____@\00QX.L&0``9````,>#
+MI!D```$```"#Q`1;7L.04X/L"(M<)!"#NZ at 9````=">#[`Q3Z/S___^+0Q3'
+M@"R!````````@\00BT,4QX`T at 0```````)"#Q`A;PXUV`%575E.![(P```"+
+ME"2D````BYPDJ````(NL)*````"+A;`9``")1"04N`````"#^@4/AQ($``#_
+M))5P40``B=ZX`````(/[!`^'^P,``(N4)*````"+2A2+D5"8``"!XO__#_"+
+MA)VT+@``P>`4)0``\`\)PHF14)@``(N\)*````"+3Q2+D5R8``"!XG^`__^+
+MA)W<+@``P>`')8!_```)PHF17)@``(M/%(N17)@``('B_W_`_XN$G<@N``#!
+MX`\E`(`_``G"B9%<F```BT\4BY%8F```@>+__P/\BX2=\"X``,'@$B4``/P#
+M"<*)D5B8``"+5"04#[9"!#G8<PC_A6P6``#K$HM,)!0/MD$$.=AV!O^%<!8`
+M`(GPBU0D%(A"!.D<`P``QT0D2'\```"-="1(QT8$,@```,=$)$!_````QT0D
+M1"@```#'1"0X?P```,=$)#Q-````QT0D,'\```#'1"0T0````,=$)"@?````
+MQT0D+!````#'1"0@/P```,=$)"0P````A=L/E<`/ML")1"00B[PDH````(M/
+M%(N1;)@``('B_S_ at _XG#P>,"BP0SP>`.)0#`'P`)PHF1;)@``(M/%(N1;)@`
+M`('B__\?\(M$'$#!X!4E``#@#PG"B9%LF```BT\4BY%HF```@>+__P'_BT0<
+M.,'@$24``/X`"<*)D6B8``"+3Q2+D6B8``"!XO___X"+1!PPP>`8)0```'\)
+MPHF1:)@``(M/%(N!:)@``(/@X(M4'"B#XA\)T(F!:)@``(M/%(N1;)@``(#F
+MP(M$'"#!X`@E`#\```G"B9%LF```@WPD$`!T%(M7%(N";)@``(/(`8F";)@`
+M`.L9BX0DH````(M0%(N";)@``(/@_HF";)@``(-\)!``#Y3`#[;`BTPD%`^V
+M40<YT`^$C`$``(-\)!``=`C_A7P6``#K!O^%@!8``(-\)!``BWPD%`^41P?I
+M90$``,=$)!@(````C50D&,="!`8```"%VP^5P`^VV(N$)*````"+2!2+ at 0BB
+M``"#X,"+%)J#XC\)T(F!"*(``(M4)!0/MD((.=@/A!D!``"%VW0(_X6$%@``
+MZP;_A8 at 6``"+3"04B%D(Z?L```#'1"1P`````,=$)'0$````QT0D>`@```")
+MWK@`````@_L"#X?8````B[PDH````(M/%(N16)@``('B_P_\_XM$G'#!X`PE
+M`/`#``G"B9%8F```BU0D%`^V0 at 8YV',(_X6,%@``ZQ*+3"04#[9!!CG8=@;_
+MA9`6``")\(M4)!2(0@;K>(U,)%")3"0,OE!1``#\N0@```"+?"0,\Z6X````
+M`(/[!W=:BX0DH````(M(%(N1))D``(#B`8M$G%#1X"7^````"<*)D229``"+
+M5"04#[9"!3G8<PC_A706``#K$HM,)!0/MD$%.=AV!O^%>!8``(M\)!2(7P6)
+M]K@!````@<2,````6UY?7<-64X/L!(M,)!")SHN9L!D``,=##`````"#N:@9
+M````#X2#````@7L0``#``'8*QT-``````.L,D+@``,``*T,0B4-`@7L8``#`
+M`'X,QT-$`````.L.C78`N```P``K0QB)0T2+412+0T")@BR!``"+412+0T2)
+M at C2!``"+013'@#"!``````(`BT$4QX`X at 0```````H/L"(V&J!8``%!1Z/S_
+M__^#Q!#'0S@`````QT,\`````(/$!%M>PU=64XM<)!"+B_P'``"#NZ09````
+M#X0B`0``@[NP`0```0^%%0$``(NSL!D``(!^!`-W&0^V1 at 1`4&H!4^C\____
+M@\0,Z?,```"-=@"`?@4&=Q8/MD8%0%!J!5/H_/___X/$#.G4````B[N\%@``
+MB?B#X'^#^#]V"(U'?\'H!^L)BX.\%@``P>@'.48D?3^`?@<`=1QJ`&H"4^C\
+M____:@!J!5/H_/___X/$&.F,````@'X&`0^'@@````^V1 at 9`4&H$4^C\____
+M@\0,ZV\Y1B!]+(!^!P!T#6H!:@)3Z/S___^#Q`R`?@8!=U$/MD8&0%!J!%/H
+M_/___X/$#.L^@^P(45/H_/___X/H`H/$$(/X`7<I@'X'`'4-:@!J`E/H_/__
+M_X/$#(!^!@!T$&H`:@13Z/S___^#Q`R-=@!;7E_#5U93BUPD$(NS_`<``(.[
+MI!D````/A)8```"#N[`!```!#X6)````B[NP&0``@'\$`W<5#[9'!$!0:@%3
+MZ/S___^#Q`SK:HGVBXN\%@``B<B#X'^#^#]V"(U!?\'H!^L)BX.\%@``P>@'
+M.4<@?1F`?P8!=SP/MD<&0%!J!%/H_/___X/$#.LI@^P(5E/H_/___X/H`H/$
+M$(/X`7<4@'\&`'0.:@!J!%/H_/___X/$#)!;7E_#5U93BW0D$(GW_[;\!P``
+M5N@;]___C12`C120C9R6M!D``(F>L!D``(/$"(.^I!D```!T;(.^L`$```%T
+M8_^&G!8``&H`:@%6Z/S___]J`&H%5NC\____:@!J!%;H_/___X/$)&H`:@)6
+MZ/S___]J`&H#5NC\____@^P$5NC\____@\0(@,P!4%;H_/___X/$%%;HN?S_
+M_X/$$.GB````D(![!`!T$@^V0P10:@%6Z/S___^#Q`R)]H![!0!T$@^V0P50
+M:@56Z/S___^#Q`R)]H![!P!T$@^4P`^VP%!J`E;H_/___X/$#(!["`!T$@^V
+M0PA0:@-6Z/S___^#Q`R)]H![!@!T$@^V0P90:@16Z/S___^#Q`R)]H._J!D`
+M``!T/X/L#%;H_/___X/$"(#D_E!6Z/S___^)-"3H$?S__XM&%,>`,($`````
+M`@"#Q!"+1A3'@#B!```````"ZR.)]H/L#%;HY_O__XDT).C\____@\0(@,P!
+M4%;H_/___X/$$%M>7\.-=@!75E.+="00BUPD%(GWBT84QX`D at 0```````(M&
+M%,>`*($```````"#[`B-AJ at 6``!05NC\____BP.)AKP6``"+0P2)AL`6``"+
+M0PB)AL06``"+1A2+D"R!``"+B#2!``")T"4``,``@\00/0``P`!T#HG()0``
+MP``]``#``'5ABY^P&0``*U-`B=`K0S@!AY06``")4SB)RBM31(G0*T,\`8>8
+M%@``B5,\BT,X.T,0=@^#[`Q6Z,K[__^#Q!"-=@"+0SP[0QAV#(/L#%;H\_S_
+M_X/$$(/L#%;HZ_K__X/$$%M>7\-3@^P(BUPD$(M$)!2+D[`9``"`>`41=2C_
+M0CC_ at Y06``"+0C@[0A!V0X/L#%/H;OO__XD<).BJ^O__@\00ZRV0@'@%&74F
+M_T(\_X.8%@``BT(\.T(8=A6#[`Q3Z(#\__^)'"3H?/K__X/$$)"#Q`A;PXUV
+M`%93BW0D#(N6L!D``(N.O!8``(G(@^!_ at _@_=@B-07_!Z`?K"8N&O!8``,'H
+M!SE")'Q2.4(@?3&`>@<`=`]J`6H"5NC\____@\0,ZVR`>@8`=#(/MD(&2%!J
+M!%;H_/___X/$#.M3C78`@'H&`'06#[9"!DA0:@16Z/S___^#Q`SK-XUV`(!Z
+M!0!T%@^V0 at 5(4&H%5NC\____@\0,ZQN-=@"`>@0`=!(/MD($2%!J`5;H_/__
+M_X/$#)!;7L.055=64XM4)!2+0A2+N.R```"+J/"```"+L/B```"+FK`9``"#
+M>S0`=`4Y<S1V#KD`````_X*@%@``ZRF0B?$K2S2)Z"M#,(GZ*U,L*<$IT;@I
+M.5-?]^G!^@Z)R,'X'XG1*<&)]HES-(E[+(EK,(G(6UY?7<-55U93@^P,BWPD
+M((M4)"2)^XNWL!D``(L"B8>\%@``BT($B8?`%@``BT((B8?$%@``5^A5____
+M@\0$A<!Y&O^'I!8``(/L#%?H\_C__X/$$.FO`0``C78``48,@[^H&0````^$
+MS````(/L"(V'J!8``%!7Z/S___^+1Q2+D"R!``"+J#2!``"#Q!`Y5D!W"CEN
+M1'9Y.59`=C.#[`S_=D!2:(A1``!H`````%?H_/___XM'%(M60(F0+($``(/$
+M((M'%,>`,($``````@`Y;D0/AB\!``"#[`S_=D15:(A1``!H-````%?H_/__
+M_XM'%(M61(F0-($``(/$((M'%,>`.($```````+I]P```"M60(G0*T8X`8>4
+M%@``B58XB>HK5D2)T"M&/`&'F!8``(E6/(UV`(.[I!D````/A,,```"#O[`!
+M```!#X6V````BX.L&0``C02`.48,=D.+3 at R)R`^O1A2[TTUB$/?CB=#!Z`8Y
+M1CAW&XG(#Z]&'/?CB=#!Z`8Y1CQW"5?H0OW__X/$!(/L#%?HIO?__X/$$.ME
+MBT8,.X.L&0``=EJ)P@^O5A"XTTUB$/?BB=#!Z`8Y1CAV%H/L#%?H,OC__XD\
+M).AN]___@\00ZRV+1 at P/KT88NM--8A#WXHG0P>@&.48\=A6#[`Q7Z$3Y__^)
+M/"3H0/?__X/$$)"#Q`Q;7E]=PU575E.#[`R+?"0 at BVPD*(M<)"R^`````(T$
+M=HM/%(L4Q:!1``")5"0(BU0D)(T$0HL$A:!1``"+5"0(B0010_;#/W40@^P,
+M:@'H_/___X/$$(UV`$:#_ at UVOKX`````D(M/%(L4]0!3``"+!/4$4P``B001
+M0_;#/W4.@^P,:@'H_/___X/$$)!&@_Y%=M*^`````)"-!':+3Q2+%(7 at 5@``
+M`>B+!(7 at 5@``B0010_;#/W4-@^P,:@'H_/___X/$$$:#_C]VSH/$#%M>7UW#
+MB?955U93@^P4BVPD+`^W10)0#[=%`%#H_/___X/$$/9%`H!T;0^_\(T<=L'C
+M`HVS9%T``(/L"&H(_W8$Z/S___^)QX'G_P```,'G!8N#9%T``,'@!`G'9HM.
+M"(M$)#"+4!2+@@2B``"#Q!!F at 7T`M`EU"X/($(F"!*(``.L;BUPD((M3%(/@
+M[XF"!*(``.L)B?:)P;\`````9H'YD`!_!?;!`70E@^P(:@@/O\&#Z!A0Z/S_
+M__^)PX'C_P```+@!````@\00ZRZ)]H/L"&H(#[_!@^@8B<+!ZA\!T-'X4.C\
+M____B<.!X_\```"X`````(/$$(GVP>,"T>`)PX'+`00``(M$)""+2!2)^"7_
+M````P>`(#[;3"=")@9R8``"+1"0 at BT@4B?@E`/\```^VUPG0B8'0F```BUPD
+M((FK_`<``+@!````@\0,6UY?7<.-=@"+5"0(BT0D!(N`>!D``(/Z!W<A_R25
+MJ%X``(GV@\!$PX/`2,.#P$S#@\!0PP64````PXGVN`````##B?955U93@>R,
+M````B[PDH````(N$)*@```!FB40D+HG]O@````"+E"2D````#[="`B7P`0``
+M/<`````/A*0!```]P````'\0/:`````/A"H!``#I]0$``#U``0``=`L]4`$`
+M``^%XP$``(N4)*0```!FBP(MH0\``&8]Z at 1W'6:+A>0(``!FB40D)&:+E>8(
+M``!FB50D(NF4````BY0DI````&:+`BV,%```9CWO`'<:9HN%Z`@``&:)1"0D
+M9HN5Z@@``&:)5"0BZV6+E"2D````9HL"+7P5``!F/>``=QIFBX7L"```9HE$
+M)"1FBY7N"```9HE4)"+K-HN$)*0```!F at 3A<%G8:9HN5\`@``&:)5"0D9HN%
+M\@@``&:)1"0BZPYFQT0D(@``9L=$)"0``,=$)!@`````QT0D'`````!FBY5F
+M"```9HE4)"QFBX5L"```9HE$)"IF@[UL"`````^4P&8/ML!FB40D*&:+E;H(
+M``!FB50D)NG:````9HN']`@``&:)1"0D9HN7]@@``&:)5"0B#[>'_`@``(E$
+M)!P/MY<`"0``B50D&&:+AV@(``!FB40D+&:+EVX(``!FB50D*F:#OVX(````
+M#Y3`9@^VP&:)1"0H9HN'O`@``&:)1"0FZW)FBY?X"```9HE4)"1FBX?Z"```
+M9HE$)"(/MY?^"```B50D'`^WAP()``")1"089HN7:@@``&:)5"0L9HN'<`@`
+M`&:)1"0J9H._<`@````/E,!F#[;`9HE$)"AFBY>^"```9HE4)";K"K@`````
+MZ00#``"[``````^W5"0NN4!5``"-!%N-!$*+!(&)1)PP0X/[$';MBX0DI```
+M`/9``H!T+8/L#&H`:G=J`_]T)#2-7"1,4^C\____@\04:@!J>FH#_W0D,%/H
+M_/___X/$(+L`````B?:-!%N+3Q2+%,5`50``BT2<,(D$$4;WQC\```!U#X/L
+M#&H!Z/S___^#Q!")]D.#^Q!VSKL`````BT\4BQ3=X%D``(L$W>19``")!!%&
+M]\8_````=0V#[`QJ`>C\____@\000W33NP`````/MU0D+HE4)!2-!%N+3Q2+
+M%,7H60``B50D#(M4)!2-!$*+!(7H60``BU0D#(D$$4;WQC\```!U#8/L#&H!
+MZ/S___^#Q!!#=,&[``````^W1"0NB40D$(T$6XM/%(L4Q0!:``")5"0,BU0D
+M$(T$0HL$A0!:``"+5"0,B0011O?&/P```'4-@^P,:@'H_/___X/$$$-TP;L`
+M````#[=4)"ZY(%H``(UV`(T$6XT$0HL$@8E$G#!#@_L0=NV+E"2D````#[="
+M`B5``0``/4`!``!U-8/L#&H#:C-J`0^WA5P)``!0C5PD3%/H_/___X/$%&H#
+M:BUJ`0^WA5X)``!04^C\____@\0@@^P,:@!J7VH!#[=$)$!0C5PD3%/H_/__
+M_X/$%&H`:F!J!`^W1"1$4%/H_/___X/$%&H`:FAJ`P^W1"0\4%/H_/___X/$
+M%&H`:FMJ`P^W1"0Z4%/H_/___[L`````@\0 at D(T$6XM/%(L4Q2!:``"+1)PP
+MB0011O?&/P```'4/@^P,:@'H_/___X/$$(GV0X/[$';.NP`````/MU0D+KG`
+M6P``C78`C01;C01"BP2!B42<,$.#^P=V[8/L#&H`:AUJ!@^W1"0^4(U<)$Q3
+MZ/S___^#Q!1J`&H$:@$/MT0D0E!3Z/S___^[`````(/$((UV`(T$6XM/%(L4
+MQ<!;``"+1)PPB0011O?&/P```'4/@^P,:@'H_/___X/$$(GV0X/[!W;.QX5@
+M&0```````+@!````@<2,````6UY?7<-55U93BVPD%(M,)!B+?"0<BUPD((MT
+M)"0/M\,/M]8/K\*Z`````(7`=&&)V&8YSW17#[?%#[?)*<B-!("-!(#!X`(/
+MM]<IRHG1F??YB<*)V&:%TG at SB?!F at _ID?RL/O](/M\X/K\JX9````"G0#[?3
+M#Z_"`<&X'X7K4??IB=#!^`7!^1]F*<B0#[?0B=!;7E]=PXGV55=64X'L3`$`
+M`(N$)&`!``")1"0<9L=$)!@``&;'1"06/P"+E"1L`0``#[="`B7P`0``/=``
+M```/A(,````]T````'\3/:````!T3CW`````=&[IC0```#U0`0``=!<]4`$`
+M`'\)/4`!``!T">MV/4`)``!U;XM,)!QFBX%V"0``9HE$)"2)R`5B"0``B40D
+M((U`%HE$)"CK58M<)!QFBX.`"P``9HE$)"2)V`6("P``B40D((V`H@```(E$
+M)"CK+HM4)!QFBX*`"P``9HE$)"2)T`6""P``B40D((U`#(E$)"CK"K@`````
+MZ=@"``"-="0@@^P(:`@!``"-1"0\4.C\____C:PDR````+\!````NP````"#
+MQ!"-=@!FB7Q<.$=#@_\_?O1FB5PD-F;'1"0R`0!FQT0D-#\`NP````!F at WPD
+M-@!T+(/L!%8/MT1<0%"+C"1X`0``#[<!4.AR`@``9HE$70"#Q!!##[=$)#8Y
+MV'_49HM5``^W1"0V9HM,1?Z[`````(/X`'YJ9CE470!_"F:+5%T`9HE<)!B)
+MV/?09 at -$)#8/M_B%_W0 at 9HM$??YF.T1]`'X*9HM$?0!FB41]_D,/MT0D-CG8
+M?[^[`````&:#?"0V`'0<#[=$)#:09CE,70!^"F:+3%T`9HE<)!9#.=A_Z@^_
+MRH/!&;L?A>M1B<CWZXG0P?@$P?D?*<B-!("-!(#1X&:)1"0:9L=$)!0``+\`
+M````#[_(B<Z)R/?KB40D"(E4)`R+1"0,P?@$P?D?*<A`@_@`?CJX'X7K4??N
+MB='!^02)\)F)R"G0C4@!C78`#[=4)!2+7"0<BX,$+P``BUPD&&:)'%!F_T0D
+M%$<Y^7_?OP`````/MT0D-HG"9HM,)!IF.4Q%_@^.YP```&:#?"04/P^'"`$`
+M`&:#1"0:,HG69HM<)!IF.5Q5_GY'#[_#B<,/OU1]`(G!*=&)R@^_3'T"*<@/
+MK]"%TGXID$=FBT0D&F8Y1'7^?AL/OT1]`(G:*<*)T`^_5'T"B=DIT0^OP87`
+M?]@/MU1\.M'B#[?2#[=$?#C1X`^WP`^W3'T"#[=<?0`/MW0D&E)045-6Z#?\
+M__^#Q!1`#[=,)!2+7"0<BY,$+P``9M'H9HD$2HN#!"\``&:+5"069CD42'8$
+M9HD42&;_1"04#[=4)#9FBTPD&F8Y3%7^?@YF at WPD%#\/AB?____K+6:#?"04
+M/W<E#[=,)!2+7"0<BY,$+P``9HM$2OYFB01*9O]$)!1F at WPD%#]VVXM$)!QF
+MQX"`&0````"X`0```('$3`$``%M>7UW#B?955U93@^P<BVPD.(M<)#"+="0T
+MC40D&E!5#[?&4`^WPU#H0@$``(/$$(7`=`L/MT0D&NDI`0``D(/L#(U$)"10
+MC40D)E`/MT4$4/]U``^WVXE<)"!3Z/S___^#Q!2-1"0 at 4(U$)")050^W1"0N
+M4`^W]E;H=0$``(/$%(U$)!Q0C40D'E!5#[=$)#!05NA;`0``@\0 at C40D#%!5
+M#[=$)!I0#[=$)")0Z+T```"-1"0:4%4/MT0D+%`/MT0D,E#HI@```(/$(`^W
+M1"0*#[=4)`P/MTPD%`^W7"024%)14U;HM?K__X/$%(G'C40D#%!5#[=$)!90
+M#[=$)"10Z&D```"-1"0:4%4/MT0D*%`/MT0D-%#H4@```(/$(`^W1"0*#[=4
+M)`P/MTPD$`^W7"0.4%)14U;H8?K__X/$%`^WP`^WWP^W5"08#[=,)!904U)1
+M_W0D%.A!^O__@\04#[?`C78`@\0<6UY?7<-55U93@^P$BVPD((M$)!AFB40D
+M`HM\)!R+30B^`````&:#?00`=$V09HM$)`)F.0%U-HU1"+L`````9H-Y!@!T
+M)XUV`&8Y.G439HM461Z+1"0D9HD0N`$```#K'8/"`D,/MT$&.=A_W(/!-$8/
+MMT4$.?!_M+@`````@\0$6UY?7<.-=@!75E.+1"08BWPD$(MT)!2+4`BY````
+M``^W0`2)PX/X`'X/B?9F.3)T"(/"-$$YRW_S@^P,_W0D+/]T)"P/MT(&4(U"
+M"%`/M\=0Z/S___^#Q"!;7E_#C78`4X/L"(M<)!"#NP0O````=!N#[`S_LP0O
+M``#H_/___\>#!"\```````"#Q!"#NW at 9````=!N#[`S_LW at 9``#H_/___\>#
+M>!D```````"#Q!"#Q`A;PXUV`%93@^P0BW0D((M<)!QHM````.C\____B8-X
+M&0``@\00A<!U",<&`@```.MPQX,(+P``@````(/L#&B`````Z/S___^)@P0O
+M``"#Q!"%P'4(QP8"````ZT3'@\@6``"H1@$`QX/,%@``/#@!`,>#T!8``%PZ
+M`0#'@]06```,.0$`QX/8%@``F#H!`,>#W!8``)!``0"X`0```(UV`(/$!%M>
+MPY"055=64X/L#(M\)""+;"0HBUPD++X`````C01VBT\4BQ3%X%X``(E4)`B+
+M5"0DC01"BP2%X%X``(M4)`B)!!%#]L,_=1"#[`QJ`>C\____@\00C78`1H/^
+M#W:^O@````"0BT\4BQ3U8&```(L$]61@``")!!%#]L,_=0Z#[`QJ`>C\____
+M@\00D$:#_D)VTKX`````D(T$=HM/%(L4A8!B```!Z(L$A8!B``")!!%#]L,_
+M=0V#[`QJ`>C\____@\001H/^/W;.@\0,6UY?7<.)]E575E.#[`R+;"0 at BW0D
+M),=$)`@`````OP````!F at 3Z_$@^'S0````^W!HE$)`0MD`@``+H%````B=.9
+M]_N%TG4CBT0D!(V<`.#N__^X9V9F9O?KP?H"B=C!^!^)TRG#ZT6-=@`/MPZ-
+M at 5#W__^Z!0```(G3F??[N`````"%T@^%N`$``(V<":#N__^X9V9F9O?KP?H"
+MB=C!^!^)TRG#QT0D"`$```#!XP*!X_\```"#[`AJ"%/H_/___XG#BU44BX($
+MH@``@\009H$^M`EU$8/($(F"!*(``.D;`0``C78`BU44@^#OB8($H@``Z0<!
+M``"-=@!FBP:Y%````+H`````9O?Q9H72=4=F at 3[_$W9`@^P(:@@/MPZ!Z<`2
+M``"X9V9F9O?IB=#!^`/!^1\IR,'@`E#H_/___XG#@\0(:@)J`^C\____B<>#
+MQ!#IJ````&:+!KD*````N@````!F]_%FA=)U/X/L"&H(#[<.@>G`$@``N&=F
+M9F;WZ8G0P?@"P?D?*<C1X%#H_/___XG#@\0(:@)J`NC\____B<>#Q!#K5XUV
+M`&:+!KD%````N@````!F]_&X`````&:%TG5^@^P(:@@/MPZ!Z<`2``"X9V9F
+M9O?IB=#1^,'Y'RG(4.C\____B<.#Q`AJ`FH!Z/S___^)QX/$$(UV`(G9P>$$
+MC02]``````G!BT0D"-'@"<&!R0$0``"+510/ML&)@IR8``#!Z0B+512)R(/@
+M?XF"V)@``(FU_`<``+@!````@\0,6UY?7<.+5"0(BT0D!(N`>!D``(/Z!W<=
+M_R25N&H``(GV@\`$PX/`",.#P`S#!:P```##B?:X`````,.)]E575E.#[!R+
+M;"0PBT0D.&:)1"0:B6PD%&;'1"0.``!FQT0D#```9L=$)`H``&;'1"0(``"+
+MM7 at 9``")ZH'"/!8``(E4)`2_`````(M4)#0/MT(")?`!```]T`````^$>`$`
+M`#W0````?QL]H`````^$)`$``#W`````#X1;`0``Z8T!```]4`$``'0>/5`!
+M``!_##U``0``=!#I<P$``#T`"```#X5H`0``BU0D-&:+`BVA#P``9CWJ!'<E
+MBT0D%&:+ at .0(``!FB40D#HM4)!1FBY+F"```9HE4)`SIDP```(M4)#1FBP(M
+MC!0``&8][P!W(HM$)!1FBX#H"```9HE$)`Z+5"049HN2Z@@``&:)5"0,ZU^+
+M5"0T9HL"+7P5``!F/>``=R*+1"049HN`[`@``&:)1"0.BU0D%&:+DNX(``!F
+MB50D#.LKBT0D-&:!.%P6=B"+5"049HN2\`@``&:)5"0.BT0D%&:+@/((``!F
+MB40D#(M4)!1FBY)L"```9HE4)!*+1"049HN`N@@``&:)1"00Z8,```"+5"04
+M9HN2_`@``&:)5"0*BT0D%&:+@``)``!FB40D"(M4)!1FBY)N"```9HE4)!*+
+M1"049HN`O`@``&:)1"00ZT&+5"049HN2_@@``&:)5"0(9HE4)`J+1"049HN`
+M<`@``&:)1"02BU0D%&:+DKX(``!FB50D$.L*N`````#I;00``+H`````N81E
+M``"+!-&)!)9"=/>Z``````^W3"0:NXAE``"-!%*-!$&+!(.)1)8$0G3PN@``
+M```/MTPD&KN at 90``C012C01!BP2#B426"$)T\+H`````#[=,)!J[P&4``(T$
+M4HT$08L$@XE$E at Q"@_HG=NV#[`QJ`&@N`0``:@$/MT0D*E"-7 at Q3Z/S___^#
+MQ!1J`&@.`0``:@*+5"14#[<"4%/H_/___X/$%&H`:`$!``!J`HM4)%0/MT("
+M4%/H_/___X/$((M$)#3V0`)`#X2W````@^P,:@-HJ````&H!BU0D'(M"'`^_
+M0`)04^C\____@\04:@-HJ0```&H!BU0D'(M"'`^_0`104^C\____@\04:@-H
+MJ@```&H!BU0D'(M"'`^_0`904^C\____@\04:@-HK@```&H!BU0D'(M"'`^_
+M0`A04^C\____@\04:@-HKP```&H!BU0D'(M"'`^_0`I04^C\____@\04:@-H
+ML````&H!BU0D'(M"'`^_0`Q04^C\____@\0 at BT0D-/9``H!T.(/L#&H`:!\!
+M``!J`P^W1"0B4(U>#%/H_/___X/$%&H`:"(!``!J`P^W1"0 at 4%/H_/___X/$
+M(.LV@^P,:@!H%P$``&H##[=$)"90C5X,4^C\____@\04:@!H&@$``&H##[=$
+M)"104^C\____@\0@#[>%K`$``"7P````@_ at O?F%F@[VL`0``-70*9H.]K`$`
+M`$5U38/L#&H":EIJ`FH"C5X,4^C\____@\04:@)J7&H":@)3Z/S___^#Q!1J
+M`FI>:@)J`E/H_/___X/$%&H":/X```!J`6H"4^C\____@\0 at 9H.]J@$``$%V
+M9(/L#&H!:!D!``!J`6H!C5X,4^C\____@\04:@-J`6H":@%3Z/S___^#Q!1J
+M`VH#:@)J`5/H_/___X/$%&H#:(L```!J`6H!4^C\____@\04:@-HC````&H!
+M:@%3Z/S___^#Q""Z``````^W3"0:NX!I``")]HT$4HT$08L$@XF$EJP```!"
+M at _H,=NJ+5"0T]D("0'0C@^P,:@!J)6H"BU0D'(M"'`^_`%"-AJP```!0Z/S_
+M__^#Q""#[`QJ`&H.:@8/MT0D*%"-AJP```!0Z/S___^[`````(/$((M-%(L4
+MW8!E``"+!)Z)!!%']\<_````=0V#[`QJ`>C\____@\000W37NP````"-!%N+
+M312+%,6(90``BT2>!(D$$4?WQS\```!U#8/L#&H!Z/S___^#Q!!#=-.[````
+M`(T$6XM-%(L4Q:!E``"+1)X(B0011_?'/P```'4-@^P,:@'H_/___X/$$$-T
+MT[L`````C01;BTT4BQ3%P&4``(M$G at R)!!%']\<_````=0^#[`QJ`>C\____
+M@\00B?9#@_LG=LZ[`````)"-!%N+312+%,6`:0``BX2>K````(D$$4?WQS\`
+M``!U$(/L#&H!Z/S___^#Q!"-=@!#@_L,=LJ+1"04QX!@&0```````+@!````
+M@\0<6UY?7<.-=@!55U93@>S\`P``BX0D$`0``(E$)!@/MX"L`0``)?````"#
+M^"]^(HM4)!AF@[JL`0``-70*9H.ZK`$``$5U"L=$)!0"````ZPC'1"04`0``
+M`(N,)!P$```/MT$")?`!```]T````'1I/=````!_$#V@````=$$]P````'14
+MZVP]4`$``'07/5`!``!_"3U``0``=`GK53T`"```=4Z+1"08!5`1``")1"0,
+MBTPD&`^WD68(``#K/HM$)!@%7!$``(E$)`R+3"08#[>1:`@``.LDBT0D&`5H
+M$0``B40D#(M,)!@/MY%J"```ZPJY`````.GV!0``BTPD#`^W00:Y`````(7"
+M#X3A!0``BY0D&`0```^_`M'@9HE$)`2+C"04!```#[\!T>!FB40D!F;'1"0H
+MK=YFQT0D*JW>QT0D"`````"^`````(M$)`P/MU`&C5PD*(GVB=")\=/XJ`%T
+M&(-\)`@!#X?1!```BT0D"&:)-$-`B40D"$:#_ at -VV(/L#(U$)#!0C40D,%"+
+M5"0@#[="!%#_,HN,)#@$```/MP%0Z)P%``#'1"0H`````(ML)$"#Q"`[;"0D
+M#X?U`0``C78`C42M`(U$A0"+3"0,BU$(C02"B40D$&:!?"0JK=X/A9<````/
+MMW0D*(T<MHT<F(/L!`^W>P:--#]6C4,(4(V$)-P"``!0Z/S___^#Q`Q6@\,0
+M4XV4)+P"``!2Z/S___^#Q`2-3"0\48M4)"`/OT("4(V,),0"``!1C80DZ`(`
+M`%!7Z(H%``"#Q""%P`^$\P,``(/L!&B`````C50D.%*+1"04P>`'C80$O`$`
+M`%#H_/___X/$$.DI`0``#[=T)"B-'+:+3"00C1R9@^P$#[=[!HTT/U:-0PA0
+MC80DW`(``%#H_/___X/$#%:#PQ!3C90DO`(``%+H_/___X/$!(U,)#Q1BU0D
+M(`^_0@)0C8PDQ`(``%&-A"3H`@``4%?H[P0``(/$((7`#X1B`P``@^P$:(``
+M``"-5"0X4HM$)!3!X`>-A`2\`0``4.C\____#[=T)#J-'+:+3"0 at C1R9@\0,
+M#[=[!HTT/U:-0PA0C80DW`(``%#H_/___X/$#%:#PQ!3C90DO`(``%+H_/__
+M_X/$!(U,)#Q1BU0D(`^_0@)0C8PDQ`(``%&-A"3H`@``4%?H7`0``(/$((7`
+M#X39`@``@^P$:(````"-5"0X4HM$)!3!X`>-A`2\````4.C\____@\00_T0D
+M"$4[;"0D#X8._O__BTPD((M$)`R+$&:+'$J+1"0D9HL\0BG(B40D"&:!?"0J
+MK=X/A;@```"^`````(/L#(M$)!3!X`8!\`^_A$2\`0``4`^_A'3``0``4`^W
+MQU`/M\-0BY0D.`0```^W`E#HX@(``&:)A'20`P``@\0 at 1H/^/W:Y@^P(BTPD
+M(/^Q!"\``(V$)'P#``!0Z&<$``")PF;!Z`^-!`)FT?B+E"0D!```9HD"9HN$
+M)/X#``")PF;!Z@\!T&;1^&:)1"0^BXPD*`0``&:)`8M$)#B+E"0P!```9HD"
+M9HE"`H/$$.DX`@``O@`````/M^\/M_N)]H/L#(M<)!3!XP8!\P^_A%R\`0``
+M4`^_A'3``0``4%57BXPD.`0```^W`5#H*`(``&:)A'20`P``@\04#[^$7+P`
+M``!0#[^$=,````!055>+E"0X!```#[<"4.CY`0``9HF$=!`#``"#Q"!&@_X_
+M=I"#?"04`@^%BP```(U$)!Y0BTPD'/^Q!"\``(V$)/@"``!0C80D?`,``%#H
+M-00``(G"9L'H#XT$`F;1^(N4)"0$``!FB0)FBT0D+HG"9L'J#P'09M'X9HE$
+M)#YFBX0D_@,``(G"9L'J#P'09M'XBXPD*`0``&:)`8M$)#B+E"0P!```9HD"
+M9HM$)#IFB4("@\00Z24!``!FBTPD!F8YC"1N`P``#XRI````BT0D!&8YA"1N
+M`P``#XR7````@^P(BU0D(/^R!"\``(V$)/P"``!0Z,,"``")PF:+1"0ZBXPD
+M,`0``&:)`6:)00*)T&;!Z`^-!`)FT?B+E"0D!```9HD"9HN$)'X#``")PF;!
+MZ@\!T&;1^&:)1"0^BXPD*`0``&:)`8/$$.F1````N0````#II@```+D`````
+MZ9P```"Y`````.F2````N0````#IB````(/L"(M$)"#_L`0O``"-A"1\`P``
+M4. at L`@``B<*+1"0XBXPD,`0``&:)`6:)00*)T&;!Z`^-!`)FT?B+E"0D!```
+M9HD"9HN$)/X#``")PF;!Z@\!T&;1^&:)1"0^BXPD*`0``&:)`8/$$+ at _````
+MBY0D&`0``&8K`HM,)!AFB8&`&0``N0$```")R('$_`,``%M>7UW#D%=64XM<
+M)!"+="04BTPD&(M\)!R+5"0 at B?AF.?%T)`^WVP^W]HG8*?`/O](/K\(/M\F)
+MRBG:#[_?#Z_3`=`I\9GW^9A;7E_#D%575E.+="08BVPD((M\)"2+5"0<#[=<
+M)!0/M\J-#$X/MP8YV'82QP<`````QT4``````.M3C78`#[=!_CG8=R@/M\)(
+MB0>)10#K/8G0*?#1^(D'B44`ZS")T"GPT?B)10!`B0?K(HGVB?(YSG,:B?8/
+MMP(YV'34#[="`CG8=]F#P@(YRG+JB?9;7E]=PXUV`%575E.#[`R+;"0PBU0D
+M((M$)"QFB40D"F;'1"0(``!FQT0D!@$`N`````!F at _H!#X:;````OP`````/
+MM\)(B00DC78`#[=$)`:+5"0D9CD\0G,1.P0D?0QF_T0D"&;_1"0&B?8/MU0D
+M!HM<)"@/OS13#[=$)`@/OPQ#BUPD)`^W%%,/MP1##[??5E%24%/HE?[__X/$
+M%&:)1%T`9HM4)`IF.=!\&V:#_S]W%9`/M\=FBUPD"F:)7$4`1V:#_S]V[$=F
+M at _\_#X9Y____N`$```"#Q`Q;7E]=PU575E.#[`2+?"08BVPD'`^_1WX/OQ<I
+MT(/X?GX.9HM'?H/H?F:)1"0"ZPAFBP=FB40D`F:+=WZY/P````^_5WZ)T$@Y
+MPGX6B<*-=@!)#[_!#[\$1SG0?@5FA<EY[XG*B?.^/P```(GVC4+_B<%F at _@^
+M=QT/O\)F.1Q'?!2)RHU*_V:#^3YW"0^_PF8Y'$=][&:%TG4;9H7V>"6)]@^_
+MUF:+1%4"9HE$50!F3GGOZQ"0#[_&9HE410"#ZP)F3GFI#[]$)`*#Q`1;7E]=
+MPU575E.#[!"+="0D9L=$)`)```^_1GZ+3"0H#[\1*="#^'Y^#F:+;GZ#[7YF
+MB6PD#NL,BT0D*&:+`&:)1"0.9HM6?F:)5"0,N3\````/OU9^#[]$)`Q(.<)^
+M$XG"20^_P0^_!$8YT'X%9H7)>>^+;"0H9HM%?HM4)#!FB0*_/P```(M<)`R-
+M=@"+;"0P9CE=`'T.9H7)=1EF at WPD`D!U$9!FQT0D`@``BW0D*+D_````9H7)
+M?AH/O\%F.1Q&?!&)]DEFA<E^"0^_P68Y'$9]\6:%R74D9H-\)`(`=1QFA?]X
+M-`^_UXM,)"QFBT11`F:)!%%F3WGLZQZ0#[_O9HM$)`()R(M4)"QFB01J@^L"
+M9D\/B7/___\/OT0D#H/$$%M>7UW#B?93@^P(BUPD$(.[!"\```!T&X/L#/^S
+M!"\``.C\____QX,$+P```````(/$$(.[>!D```!T&X/L#/^S>!D``.C\____
+MQX-X&0```````(/$$(/$"%O#C78`5E.#[!"+="0 at BUPD'&C@````Z/S___^)
+M at W@9``"#Q!"%P'4(QP8"````ZWC'@P at O``"`````@^P,:(````#H_/___XF#
+M!"\``(/$$(7`=0C'!@(```#K3,>#""\``$````#'@\@6```<7`$`QX/,%@``
+MI$<!`,>#T!8``+A*`0#'@]06``!T2`$`QX/8%@``\$H!`,>#W!8``(11`0"X
+M`0```)"#Q`1;7L.0D%575E.#[`R+?"0 at BVPD*(MT)"R[`````(T$6XM/%(L4
+MQ>!J``")5"0(BU0D)(T$0HL$A>!J``"+5"0(B0011O?&/P```'4-@^P,:@'H
+M_/___X/$$$.#^P]VOKL`````D(M/%(L4W6!L``"+!-UD;```B0011O?&/P``
+M`'4/@^P,:@'H_/___X/$$(GV0X'[B````';+NP````")]HT$6XM/%(L4A<!P
+M```!Z(L$A<!P``")!!%&]\8_````=0Z#[`QJ`>C\____@\00D$.#^S]VRH/$
+M#%M>7UW#B?955U93@^P,BVPD((MT)"3'1"0(`````+\`````9H$^OQ(/A\T`
+M```/MP:)1"0$+9`(``"Z!0```(G3F??[A=)U(XM$)`2-G`#@[O__N&=F9F;W
+MZ\'Z`HG8P?@?B=,IP^M%C78`#[<.C8%0]___N at 4```")TYGW^[@`````A=(/
+MA;@!``"-G`F@[O__N&=F9F;WZ\'Z`HG8P?@?B=,IP\=$)`@!````P>,"@>/_
+M````@^P(:@A3Z/S___^)PXM5%(N"!*(``(/$$&:!/K0)=1&#R!")@@2B``#I
+M&P$``(UV`(M5%(/@[XF"!*(``.D'`0``C78`9HL&N10```"Z`````&;W\6:%
+MTG5'9H$^_Q-V0(/L"&H(#[<.@>G`$@``N&=F9F;WZ8G0P?@#P?D?*<C!X`)0
+MZ/S___^)PX/$"&H":@/H_/___XG'@\00Z:@```!FBP:Y"@```+H`````9O?Q
+M9H72=3^#[`AJ"`^W#H'IP!(``+AG9F9F]^F)T,'X`L'Y'RG(T>!0Z/S___^)
+MPX/$"&H":@+H_/___XG'@\00ZU>-=@!FBP:Y!0```+H`````9O?QN`````!F
+MA=)U?H/L"&H(#[<.@>G`$@``N&=F9F;WZ8G0T?C!^1\IR%#H_/___XG#@\0(
+M:@)J`>C\____B<>#Q!"-=@")V<'A!(T$O0`````)P8M$)`C1X`G!@<D!$```
+MBU44#[;!B8*<F```P>D(BU44B<B#X'^)@MB8``")M?P'``"X`0```(/$#%M>
+M7UW#55=64X/L#(ML)""+1"0H9HE$)`J);"0$OP````"+M7 at 9``"+1"0D#[=`
+M`B7P`0``/<````!T.CW`````?PD]H````'0+ZTL]T````'0CZT*+1"0$9HN`
+M]`@``&:)1"0"BT0D!&:+@/8(``!FB00DZRN+1"0$9HN`^`@``&:)1"0"BT0D
+M!&:+@/H(``!FB00DZPJX`````.GA`0``N@````"YQ',``(L$T8D$ED)T][H`
+M````#[=,)`J[R',``(T$4HT$08L$@XE$E at 1"=/"Z``````^W3"0*N^!S``"-
+M!%*-!$&+!(.)1)8(0G3PN@`````/MTPD"KL`=```C012C01!BP2#B426#$*#
+M^AIV[8/L#&H`:*@```!J`P^W1"0:4(U>#%/H_/___X/$%&H`:*4```!J`P^W
+M1"084%/H_/___[H`````@\0@#[=,)`J[H'8``(T$4HT$08L$@XE$EGA"@_H"
+M=NV[`````(M-%(L4W<!S``"+!)Z)!!%']\<_````=0V#[`QJ`>C\____@\00
+M0W37NP````"-!%N+312+%,7(<P``BT2>!(D$$4?WQS\```!U#8/L#&H!Z/S_
+M__^#Q!!#=-.[`````(T$6XM-%(L4Q>!S``"+1)X(B0011_?'/P```'4-@^P,
+M:@'H_/___X/$$$-TT[L`````B?:-!%N+312+%,4`=```BT2>#(D$$4?WQS\`
+M``!U#X/L#&H!Z/S___^#Q!")]D.#^QIVSKL`````C01;BTT4BQ3%H'8``(M$
+MGGB)!!%']\<_````=1"#[`QJ`>C\____@\00C78`0X/[`G;-BT0D!,>`8!D`
+M``````"X`0```(/$#%M>7UW#C78`BU0D"(M$)`2+@'@9``"#^@=W&?\DE>AV
+M``")]H/`!,.#P`C#@\`,PX/`>,.X`````,.)]E575E.+?"08BVPD)(MT)!R+
+M7"04#[?.C0Q/#[_3#[<'.<)]%,=%``````"+1"0 at QP``````ZU&09CE9_G\N
+M#[?&2(E%`(M4)"")`NL[*?C1^(E%`(M4)"")`NLL*?C1^(M4)"")`D")10#K
+M'(GX.<]S%HGV9CD8=-1F.5@"?]V#P`(YR'+NB?9;7E]=PXUV`%575E.#[!B+
+M1"0P9HE$)`Z+3"0T9HE,)`R+7"1`9HE<)`J)Q='E9L=$)`@``+@`````9H/[
+M`0^&!@$``(M$)`QF*T0D#F8[1"0(#X+M````#[=$)`J)1"0$C78`C40D%%"-
+M1"044`^W1"024/]T)$0/O\50Z/?^__^#Q!2#?"04`'4(QT0D%`$```"+5"0$
+MC4+_.T0D$'4(C4+^B40D$)"+3"00BUPD.&:+%$N+1"049CL40W4*BT0D/&:+
+M#$CK2P^_S8M<)!"+1"0X#[\T6(G/*?>+5"04BT0D/`^_!%`/K_B+1"0X#[\4
+M4(G0*<B)!"2+1"0\#[\,6(L$)`^OP8T$!RGRB=&9]_F)P0^W1"0(BU0D+,'B
+M!@'"BUPD1&:)#%-F_T0D"(/%`HM$)`QF*T0D#F8[1"0(#X,?____N`$```"#
+MQ!A;7E]=PXGV5U93BUPD$(MT)!2+3"08BWPD'(M4)"")^&8Y\70D#[?;#[?V
+MB=@I\`^_T@^OP@^WR8G**=H/O]\/K],!T"GQF??YF%M>7\.055=64X'L7`8`
+M`(N$)'0&``!FB40D-HN4)'P&``!FB50D-,=$)"@`````C40D3%"-1"1,4(N,
+M)(`&```/MT$(4%$/OT0D1E#HD/W__\=$)$0`````@\04D+@#````*T0D,(E$
+M)"R+5"1(C01`C1S%`````(T$DHT$@(T$0HT$0XNT)'@&``"-%#!FBWH49H7_
+M#X3_`0``9HM"$HM,)"B+M"2(!@``9HD$3HG-T>6-="1`9HM*(&:)3#4`BU0D
+M3(T$DHT$@(T$0HT$0P.$)'@&``!F.T@@?@EFBT`@9HE$-0"+;"0HT>5FBT0L
+M0(G"9L'J#P'09M'X9HE$+$"-1"0XB40D((M4)$@/M_>+7"0LC0Q;P>$"C022
+MC02`C01"C00!`?"+E"1X!@``9HM<0AZ+1"0 at 9HE<!0"+5"1,C022C02`C01"
+M`<&-!#&+E"1X!@``9CM<0AY^#F:+1$(>BTPD(&:)1`T`BUPD*-'C9HM$'#B)
+MPF;!Z@\!T&;1^&:)1!PXC80D4`0``%`/M_]7BU0D4(T$DHT$@(T$0HM4)#2-
+M-%+!Y at .-!$8#A"2`!@``C5`64H/`(%`/OT0<2%`/OT0<5%#_="1`Z*+\__^-
+MA"1L`@``4%>+5"1PC022C02`C01"C01&`X0DG`8``(U0%E*#P"!0#[]$'&10
+M#[]$''!0_W0D7.AE_/__OP````"#Q#AFBT0<.&8[1!Q`='8/MTPD-HE,)!R+
+M;"0HT>6)]HM<)"C!XP8!^P^_C%Q0`@``#[^T7%`$``"+5"1,BX0D>`8```^W
+M%%")5"0(BT0D2(N4)'@&```/MP1"45;_="004/]T)"SH0?W__X/$%&:)1%Q0
+M1V:+1"PX9BM$+$`/M\`Y^'>;_T0D*/]$)#"#?"0P`P^&M/W__XM4)$"+A"2`
+M!@``9HD0OP````#'1"0P`````#M\)"@/@\T!``"-=@"+1"0H2#M$)#!U&8M,
+M)#!FBT1,.(/`!(N<)(0&``!FB01+ZRJ+5"0PT>(/OT04.`^_5!1"`=")PL'J
+M'P'0T?B+="0PBY0DA`8``&:)!'*Y`````(-\)#``=!Z+7"0PB[0DA`8```^W
+M3%[^#[]$7$`IP0^W1"0T*<&+5"0PP>('9HM$%%*)PV8K7!10B=AFA=MU!;@!
+M````B<.%R7DSBT0D,,'@!P^WTV:+;`10B=`/K\&)^T>^`````&8!Z'@"B<:+
+MA"2,!@``9HDT6$%XWHGVBT0D,-'@#[]4!#B)5"0,#[]4!$`I5"0,BUPD,(NT
+M)(0&```/MRQ>#[=$)#0!Q2G5BT0D#(E$)"0YZ'8$B6PD)`^_1"0DB<,YR'XB
+MBU0D,,'B!HGVC00*06:+1$10B[0DC`8``&:)!'Y'.<M_YXM$)##!X`8#1"0,
+M9HM41$Z)TV8K7$1,B=AFA=MU!;@!````B<,[;"0D=E`/O\4YR'Y)BT0D,,'@
+M!@-$)`QFBT1$3F:)1"0:B<@K1"0D#[?3#Z_"9 at -$)!J)_D>)PF:#^']^!;I_
+M````BX0DC`8``&:)%'!!#[_%.<A_S/]$)#"+5"0H.50D,`^"-O[__X-\)#`#
+M=R&-=@"+3"0PBYPDA`8``&:+1$O^9HD$2T&)3"0P at _D#=N*#_W]W%Y"+M"2,
+M!@``9HM$?OYFB01^1X/_?W;J@<1<!@``6UY?7<.-=@!55U93@^PLBWPD0(M,
+M)$R)_8NW!"\```^W00(ET````#W`````=`<]T````'4(C9V8%```ZQ\/MT$"
+M):````"Z`````#V@````#X4'`0``C9_T$@``BT<4BX!LH@``@^`/9HD$)%;_
+M="14C40D(%"-1"0B4`^W1"004%,/MP%05^B3^O__BT\4BY%8H@``@.8_#[=#
+M$$C!X`XE`,````G"B9%8H@``@\0 at 9H-\)!8`=!!FBT0D%O?89HF%@!D``.L)
+M9L>%@!D`````NX"B``"Y``````^V%,X/MD3.`L'@"`G"#[9$S at 3!X!`)P@^V
+M1,X&P>`8"<*+1Q2)%!B#PP1!@_D?=L^+7Q2+%"2#X@\/MT0D&,'@!"7P`P``
+M"<*-3"08#[=!`L'@"B4`_```"<(/MT$$P>`0)0``/P`)P@^W00;!X!8E``#`
+M#PG"B9-LH@``N@$```")T(/$+%M>7UW#4X/L"(M<)!"#NP0O````=!N#[`S_
+MLP0O``#H_/___\>#!"\```````"#Q!"#NW at 9````=!N#[`S_LW at 9``#H_/__
+M_\>#>!D```````"#Q!"#Q`A;PXUV`%93@^P0BW0D((M<)!QHA````.C\____
+MB8-X&0``@\00A<!U",<&`@```.MPQX,(+P````$``(/L#&@``0``Z/S___^)
+M at P0O``"#Q!"%P'4(QP8"````ZT3'@\@6``!H:P$`QX/,%@``(%T!`,>#T!8`
+M`,AB`0#'@]06``#\70$`QX/8%@``0&`!`,>#W!8```AJ`0"X`0```(UV`(/$
+M!%M>PP`````````````X!P``2P<``$L'``!5!P``4`@``%\'``!I!P``>0<`
+M`%`(``")!P``G0<``.X'``"U!P``R@<``-P'``#]!P`````%``H`%``>`#(`
+M1@!5`%H`7P!D````"@`4`!X`*``R`#P`1@!0`%H`9`#"````Q````,4```#0
+M````V@```.0```#"````PP```-0```#R````#0$``"@!````````3D].10``
+M`````/\!1D-#```````0`$@#34M+``````!``(@!151320`````W`#H#````
+M`````````````````/\!``"?````H@````$````!`````0``````$`"H````
+MJP````$````!`````0````@``P"Z````O0````$``````````0````P``P#%
+M````R`````$``````````0```"``1P#0````TP```````````````````#,`
+M,`#=````X`````$``````````0```"0`(0#H````ZP````$````!`````0``
+M`"@`.0#U````^`````$``````````0```!\`,````0```P$```$````!````
+M`0```#```P`.`0``$0$```$``````````0```'```P`9`0``'`$```$`````
+M`````0```#@`,``D`0``)P$```$``````````0```%0`50`O`0``,@$```$`
+M```!`````0```$0`50`Y`0``/`$```$````!`````0```$P`"`!#`0``1@$`
+M`````````````````&``4@!-`0``4`$```$````!`````0```&0`-`!B`0``
+M90$```$``````````0```'P`(`!N`0``<0$```$````!`````0```)@`6`!X
+M`0``>P$````````!`````````)P`4@"!`0``A`$```$````!`````0```*H`
+M$`"*`0``C0$```$``````````0```+P``P"6`0``F0$```$``````````0``
+M`+\`-@"D`0``IP$```$``````````0```,0`-P"O`0``L@$```$````!````
+M`0```,L`-@"Y`0``O`$``````````````````-``-P#+`0``S@$```$`````
+M`````0```-8`$`#6`0``V0$```$````!`````0```-H``P#L`0``[P$`````
+M`````````````#(#`P#W`0``^@$```$``````````0```-X``P```@```P(`
+M``$``````````0```.D`-P`/`@``$@(```$``````````0```/8`-P`:`@``
+M'0(```$``````````0```/H`-@`E`@``*`(```$``````````0```/\`-@`O
+M`@``,@(```$``````````0````P!,``]`@``0`(```$````!`````0```!0!
+M-P!(`@``2P(```$``````````0```"P!`P!3`@``5@(```$``````````0``
+M`$`!$`!=`@``8`(```$````!`````0```%0!`P!J`@``;0(```$`````````
+M`0```%@!(0!V`@``>0(```$````!`````0```%P!-0"#`@``A@(```$`````
+M`````0```&`!-P".`@``D0(```$``````````0```&0!`P"9`@``G`(```$`
+M`````````0```&@!`P"B`@``I0(```$``````````0```&P!4@"O`@``L@(`
+M``$````!`````0```'0!-P"W`@``N@(```$``````````0```'@!`P#"`@``
+MQ0(```$``````````0```'P!-P#,`@``SP(```$``````````0```(@!0`#5
+M`@``V`(```$``````````````(D!00#>`@``X0(```$``````````````(H!
+M2`#H`@``ZP(```$``````````````(L!0P#R`@``]0(```$`````````````
+M`(P!2@#\`@``_P(```$``````````````(T!2P`&`P``"0,```$`````````
+M`````)`!`P`0`P``$P,```$``````````0```(X!`P`:`P``'0,```$`````
+M`````0```)@!10`H`P``*P,```$````!`````0```)H!10`W`P``.@,```$`
+M`````````````)L!20!)`P``3`,```$``````````````)X!`P!<`P``7P,`
+M``$``````````0```*P!`P!F`P``:0,```$``````````0```*8!`P!P`P``
+M<P,```$``````````0```+8!-0![`P``?@,```$``````````0```+@!-P",
+M`P``CP,```$``````````0```+H!-P"9`P``G`,```$``````````0```+X!
+M(0"G`P``J@,```$````!`````0```"<#`P"P`P``LP,```$``````````0``
+M`,H!`P"]`P``P`,``````````````````.0!$`#)`P``S`,```$````!````
+M`0```.P!,`#3`P``U@,```$````!`````0```/@!`P#=`P``X`,```$`````
+M`````0```!`"-P#H`P``ZP,```$``````````0```"H"(@#W`P``^@,```$`
+M`````````0```$("-P`&!```"00```$``````````0`````"`P`0!```$P0`
+M``$``````````0```$H"`P`8!```&P0```$``````````0```$\"$``D!```
+M)P0```$````!`````0```%P"`P`N!```,00```$``````````0```&`"$0`V
+M!```.00```$````!`````0```&@"-P!%!```2`0```$``````````0```&P"
+M-P!/!```4 at 0```$``````````0```'8"$`!;!```7 at 0```$````!`````0``
+M`'H"`P!J!```;00```$``````````0```(("`P!S!```=@0```$`````````
+M`0```(,"`P!^!```@00```$``````````0```*H"`P"(!```BP0```$`````
+M`````0```+X"0@"8!```FP0```$````!`````0```+\"-@"E!```J`0```$`
+M`````````0```,$"-P"X!```NP0```$``````````0```,8"-P#$!```QP0`
+M``$````!`````0```-0"-P#4!```UP0```$``````````0```/`"-P#=!```
+MX`0```$``````````0```/0"-0#G!```Z at 0```$``````````0```/@"`P#V
+M!```^00```$``````````0```)X`1P#_!````@4```$````!`````0```/P"
+M10`)!0``#`4```$````!`````0````P#,``5!0``&`4```$``````````0``
+M`!0#-@`J!0``+04```$``````````0```!@#-@`U!0``.`4```$`````````
+M`0```"0#`P`_!0``0 at 4```$``````````0```!`#`P!*!0``304```$`````
+M`````0```#H#-P!B!0``904```$``````````0```$@#$`!T!0``=P4```$`
+M```!`````0```%H#10"%!0``B`4```$``````````0```%P#.@"0!0``DP4`
+M``$````!`````0```%X#5@">!0``H04```$``````````0```,`"`P"K!0``
+MK at 4```$``````````0```'<#`P"W!0``N at 4```$``````````0```,P"`P#`
+M!0``PP4```$``````````0````````````````#_`?\!`P"8`9D!!P"8`3`+
+M"`"8`3`,(``@`1`*(0`@`9D!(@`@`3`,.@!@`1`*-P`P`9D!-0`P`ID!-@`P
+M`YD!,``P!)D!.0`P!9D!-``P!ID!,@`P`YD!,0`P`YD!$0`0`9D!$``0`1`*
+M4@!0`9D!10!0`ID!1P!0`YD!0@!0!)D!6`!0!9D!50!0`3`,5@!0`C`,20!0
+M`E`-0`!``4`*00!``4`*0P!``D`*2`!``1`*2@!``4`*2P!``4`*8`!@`&``
+M80!A`&$`8@!B`&(`8P!C`&,`9`!D`&0`90!E`&4`9@!F`&8`9P!G`&<`:`!H
+M`&@`:0!I`&D`:@!J`&H`````````````````````````````````````````
+M_P$"`!```!1$%@4&<1;!%@4&````````````````4`$!`!``<1;!%AX`````
+M````````````````````4`(!`!``<1:M%A<`````````````````````````
+M4`,"`!``H!3(%!$&<1:M%AX&````````````````4`0"`!``/!1X%!0`<1;!
+M%A0`````````````````4`4"`!``<1;!%A$`````````````````````````
+M,`$"`#``/!3(%!0`?!5$%AL`````````````````,`(!`#``/!1X%!(`````
+M````````````````````,`,!`#``/!3(%!0`````````````````````````
+M,`0!`#``/!3(%!(`````````````````````````,`4!`#``/!1X%`\`````
+M````````````````````,`8"`#``/!2@%!<`?!5$%AX`````````````````
+M$`$#`!``C!3(%!<&/!1X%!$&<1;!%AX&````````(`$"`!``/!3(%!<&<1;!
+M%AX&````````````````8`$$`!``/!1X%!$&C!3(%!0&?!5$%A0&<1;!%AX&
+M0`$!`$``,A1N%!<`````````````````````````0`(#`$``,A1N%!<`L!/8
+M$Q<`.!-T$Q<```````````#_`0(`$P`*%"86!08>%!(6!08`````````````
+M```0`0,`$P""%*H4%P9:%%H4$0:`%J at 6'@8```````!@`0,`$P""%*H4%P9:
+M%%H4$0:`%J at 6'@8```````!@`00`$P!X%*`4%P90%%`4$0:%%JT6'@:D%1P6
+M'@80"@$`$0``````````````````````````````````````````````_P$#
+M`/\`"`E$"04&A0F%"04&T`FL"@4&````````$`H!`!0`A0F%"10&````````
+M````````````````,`H!`#0`A0F%"10&````````````````````````,`L!
+M`#0`A0F%"10&````````````````````````,`P!`#0`A0F%"10&````````
+M````````````````0`H!`$0`A0F%"10&````````````````````````F0$!
+M`#0`A0F%"10&``````````````````````````````````````````#_`00`
+M_P`("40)!09L":@)!0:T";0)!0;0":P*!090#0(`_P!L":@)%``("40)%```
+M```````````````P"@$`_P"9":@)%``````````````````````````P"P$`
+M,0"`"8H)%``````````````````````````P#`$`,0!L":@)'@``````````
+M```````````````0"@$`$0!L"9X)&P8```````````````````````!`"@(`
+M00!L":@)%`"T";0)%`````````````````"9`0$`,0!L":@)%```````````
+M````````````````````_P``````````````````````````````````````
+M``````````````````!@`````0`````````*````.`$``$`!``!(`0``:`$`
+M`'`!``!X`0``@`$``(@!``"H`0``L`$``&8````!``````````D````X`0``
+M0`$``%`!``!H`0``<`$``'@!``"``0``D`$``+`!````````9P````$`````
+M````"0```#@!``!``0``2`$``&@!``!P`0``>`$``(`!``"(`0``L`$`````
+M``!H`````0`````````)````.`$``$`!``!(`0``:`$``'`!``!X`0``@`$`
+M`(@!``"P`0```````&$```````````````@````X`0``0`$``$@!``!H`0``
+M@`$``(@!``"H`0``L`$`````````````8@````(````L`0``"@```#@!``!`
+M`0``2`$``&@!``!P`0``>`$``(`!``"(`0``J`$``+`!``!C`````0``````
+M```(````.`$``$`!``!(`0``:`$``'`!``!X`0``B`$``+`!````````````
+M`&0```````````````<````X`0``6`$``&@!``!P`0``>`$``)@!``"P`0``
+M````````````````90``````````````!0```#@!``!(`0``:`$``(@!``"P
+M`0````````````````````````````!I```````````````(````.`$``%@!
+M``!H`0``<`$``'@!``"``0``F`$``+`!`````````````&H`````````````
+M``8````X`0``2`$``&@!``"``0``B`$``+`!````````````````````````
+M````````````````````````````````8`````$`````````"0```#@!``!`
+M`0``4`$``&@!``!P`0``>`$``(`!``"0`0``L`$```````!F`````0``````
+M```)````.`$``$`!``!0`0``:`$``'`!``!X`0``@`$``)`!``"P`0``````
+M`&<````!``````````D````X`0``0`$``$@!``!H`0``<`$``'@!``"``0``
+MD`$``+`!````````:`````$`````````"0```#@!``!``0``2`$``&@!``!P
+M`0``>`$``(`!``"0`0``L`$```````!A```````````````'````.`$``$`!
+M``!0`0``:`$``(`!``"0`0``L`$``````````````````&(````"````+`$`
+M``D````X`0``0`$``%`!``!H`0``<`$``'@!``"``0``D`$``+`!````````
+M8P````$`````````"````#@!``!``0``4`$``&@!``!P`0``>`$``)`!``"P
+M`0````````````!D```````````````'````.`$``&`!``!H`0``<`$``'@!
+M``"@`0``L`$``````````````````&4```````````````4````X`0``8`$`
+M`&@!``"@`0``L`$`````````````````````````````:0``````````````
+M"````#@!``!@`0``:`$``'`!``!X`0``@`$``*`!``"P`0````````````!J
+M```````````````&````.`$``&`!``!H`0``@`$``*`!``"P`0``````````
+M`````````````````````````````````````````````$`!B`%``0``N`$`
+M`!`!2`-0`0``P@$``#`!$`)``0``S@$```(`4`$!`$`!!`"@``@`P``@`-``
+MN#P``)@\``"J/```N#P``)X\``"X/```N#P``+@\``"D/```N#P``+@\``"X
+M/```N#P``+@\``"X/```N#P``+@\``"X/```N#P``+@\``"X/```N#P``+@\
+M``"X/```N#P``+@\``"X/```N#P``+@\``"X/```N#P``+@\``"P/```BCT`
+M`)$]``"\/0``F#T``+P]``"\/0``O#T``+P]``"?/0``O#T``*8]``"M/0``
+MC!2@%+04R!0``'P5D!6D%;@5S!7@%?05"!8<%C`61!8`````0`$"`,P6``#6
+M%@``````````````````````````````````````````````````8`$Z`$`!
+M``#P%@``,`$``$`!````````,`(``$`!````````,`,``$`!````````,`0`
+M`$`!````````,`4``$`!````````,`8``$`!````````,`H``$`!````````
+M,`L``$`!````````,`P``$`!````````:`!H`$`!````````0`)#`$`!````
+M````0`I#`*``````````0`I#`,``````````0`%!`$`!````````0`I!`*``
+M````````0`I!`,``````````0`!#`$$`2`!*`$L````````````D`9 at 9`"D1
+M!```````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````X%4`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````"8``!'````")@````````,F```IHZ$"1"8````
+MX#(]%)@``&L'```<F````````""8`````@("))@```X.```HF````0(""BR8
+M``#\;P,`,)@````````TF```#@X``#B8```'````/)@````!`@!`F`````!C
+MB428``"<%G(32)@``#.V&`!,F```/&&$$E"8``#@N. at -5)@``%E(!P!8F```
+MNKZ`?ER8``!>9CHQ8)@```@=``!DF````,X!`&B8``"009I`<)@```\```!T
+MF```@````'B8```$`````)D````````$F0````````B9````````#)D`````
+M@``0F0```P````";````````!)L``"`````(FP``$`````R;```P````$)L`
+M``@````4FP``*````!B;```H````')L```0````@FP``)````"2;```4````
+M*)L``#0````LFP``#````#";```L````-)L```(````XFP``(@```#R;```2
+M````0)L``#(```!$FP``"@```$B;```J````3)L```$```!0FP``(0```%2;
+M```1````6)L``#$```!<FP``"0```&";```I````9)L```4```!HFP``)0``
+M`&R;```5````<)L``#4```!TFP``#0```'B;```M````?)L```,```"`FP``
+M(P```(2;```3````B)L``#,```",FP``"P```)";```K````E)L```<```"8
+MFP``)P```)R;```7````H)L``#<```"DFP``#P```*B;```O````K)L``"\`
+M``"PFP``+P```+2;```O````N)L``"\```"\FP``+P```,";```O````Q)L`
+M`"\```#(FP``+P```,R;```O````T)L``"\```#4FP``+P```-B;```O````
+MW)L``"\```#@FP``+P```.2;```O````Z)L``"\```#LFP``+P```/";```O
+M````])L``"\```#XFP``+P```/R;```O`````)H``!T````$F@``70````B:
+M``"=````#)H``-T````0F@``'0$``!2:```A````&)H``&$````<F@``H0``
+M`"":``#A````))H``#$````HF@``<0```"R:``"Q````,)H``!P````TF@``
+M7````#B:```I````/)H``&D```!`F@``J0```$2:```@````2)H``!D```!,
+MF@``60```%":``"9````5)H``#````!8F@``!0```%R:```E````8)H``&4`
+M``!DF@``I0```&B:```H````;)H``&@```!PF@``'P```'2:```>````>)H`
+M`!@```!\F@``6````(":``"8````A)H```,```"(F@``!````(R:``!$````
+MD)H``(0```"4F@``$P```)B:```2````G)H``%(```"@F@``D@```*2:``#2
+M````J)H``"L```"LF@``*@```+":``!J````M)H``*H```"XF@``&P```+R:
+M```:````P)H``%H```#$F@``F@```,B:``#:````S)H```8```#0F@``!@``
+M`-2:```&````V)H```8```#<F@``!@```.":```&````Y)H```8```#HF@``
+M!@```.R:```&````\)H```8```#TF@``!@```/B:```&````_)H```8```#4
+MF```(````,R8```$````R)@```8!!@"<F```;0```,"8````````T)@``!0`
+M```)"0```````@(&!@8&!@8"`@````,`!@`)`#P`````````````````````
+M``````````````````````````#P````S`````````````````````KB````
+M(```"@(```$8```!````0!@$````````````````````````````````````
+M````!017&0`I$00`````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````-R(````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````!L"8\)M`D`````````````````
+M```````````````````````@````(````$````!````````&````!@````8`
+M,````!4````5````'0```!4```!`$```#_PO``_\+P`?_"\`#_PO`$00```/
+M_"\`#_PO`!_\+P`/_"\`2!````_\+P`/_"\`'_PO``_\+P!,$```#_PO``_\
+M+P`?_"\`#_PO`%`0```/_"\`#_PO`!_\+P`/_"\`5!````_\+P`/_"\`'_PO
+M``_\+P!8$```#_PO``_\+P`?_"\`#_PO`%P0```/_"\`#_PO`!_\+P`/_"\`
+M8!````_\+P`/_"\`'_PO``_\+P!D$```#_PO``_\+P`?_"\`#_PO`'`0``!H
+M`0``X`$``+@!``!H`0``,!```#`"``#@`0``L````#`"``"P$```F`T``(`1
+M``!('P``F`T``/`0``#@H```:$`!`(!8``#@H```%(`````$``0`"``(`#``
+M(``$``0<@```IX^-#L^/C0Z5CV`!IX^-#@28`````````P``````````````
+M()@````"`@(``@("``(!`@`"`@(DF```#@X```X.```'!P``#@X``"B8```!
+M``(*`0`""@```04!``(*-)@```X.```.#@``#@X```X.```XF```!P````<`
+M```+````"P```$28``"<%G(3I19R$Z at 6<A.<%G(32)@``&>Z&`!GNA@`:;H8
+M`&FZ&`!0F```X+0H#."T*`S at M"@,X+0H#%B8```N#8!^+ at V`?BX-P'XN#8!^
+M7)@``%Y=-S%>73<Q7ETZ,5Y=-S%@F```$+T``!"]```XO0``$+T``&28````
+MS@$``,X!``#.`0``S@$`%)D``!`G```0)P``?!4``!`G```8F0``D`$``)`!
+M``"$````D`$``$29```@$.!O(!#@;R`)X&\@$.!O@*$``/\4_P7_%/\%_Q3_
+M!?\9_P74F```$````!0````0````$``````````,`````````"@```"<G(2$
+M+````'Q\?'PT````!0```$``````````1`````@```!(````"````$P````0
+M````4`````````!4````'P`````(````````!`@````````("`````````P(
+M````````$`@````````4"````````!@(````````'`@````````@"```````
+M`"0(````````,!(````````$@`````````B`````````#(`````````8@```
+M`````"2`````````*(```#`````L@```__\'`#"```#___\!-(```#$````X
+M@````````#R`````````0(````````!$@````@```$B`````````5(``````
+M``!8@`````````B8````````#)@``).0A"T0F````.`R?128``!K#P``')@`
+M```````LF```_F\"`#"8````````/)@````!`@!`F```>@%J($R8```\8802
+M5)@``%D(``!HF```D$&:0&R8``"!L`P%<)@```\```!TF```@````'B8```,
+M`````)D````````$F0````````B9````````#)D`````@``0F0```0```!R9
+M```J"0``()D````````DF0``!8H%`"B9```!````+)D````````PF0``````
+M`#29````````.)D````````\F0``/P```$"9```$````2)D```````!,F0``
+M`````%"9````````5)D``$SQ4%U8F0``&````%R9``".:DL`A*$``/\%_P:(
+MH0``_P?_!XRA``#_"/\(D*$``/\)_PF4H0``_PK_"IBA``#_"_\+G*$``/\,
+M_PR at H0``_PW_#:2A``#_#O\/J*$``/\2_Q*LH0``_Q/_%+"A``#_%?\6M*$`
+M`/\7_QFXH0``_QK_&[RA``#_'?\>P*$``/\@_R/$H0``_R7_)\BA``#_*?\L
+MS*$``/\O_S'0H0``_S3_-]2A``#_.O\ZV*$``/\Z_SK<H0``_SK_.N"A``#_
+M.O\ZY*$``/\Z_SKHH0``_SK_.NRA``#_.O\Z\*$``/\Z_SKTH0``_SK_.OBA
+M``#_.O\Z_*$``/\Z_SH`FP````````2;```@````")L``!`````,FP``,```
+M`!";```(````%)L``"@````8FP``!````!R;```D````()L``!0````DFP``
+M-````"B;```,````+)L``"P````PFP```@```#2;```B````.)L``!(````\
+MFP``,@```$";```*````1)L``"H```!(FP``!@```$R;```F````4)L``!8`
+M``!4FP``-@```%B;```.````7)L``"X```!@FP```0```&2;```A````:)L`
+M`!$```!LFP``,0```'";```)````=)L``"D```!XFP``!0```'R;```E````
+M@)L``!4```"$FP``-0```(B;```-````C)L``"T```"0FP```P```)2;```C
+M````F)L``!,```"<FP``,P```*";```+````I)L``"L```"HFP``*P```*R;
+M```K````L)L``"L```"TFP``*P```+B;```K````O)L``"L```#`FP``*P``
+M`,2;```K````R)L``"L```#,FP``*P```-";```K````U)L``"L```#8FP``
+M*P```-R;```K````X)L``"L```#DFP``*P```.B;```K````[)L``"L```#P
+MFP``*P```/2;```K````^)L```(```#\FP``%@```-28```@````V)@``&@0
+M8`````````````":``"I`0````````2:``#I`0``0`````B:```I````@```
+M``R:``!I````4`$``!":``"9`0``D`$``!2:``#9`0``T`$``!B:```9````
+M$````!R:``!9````1````"":``"9````A````"2:``"E`0``2`$``"B:``#E
+M`0``B`$``"R:```E````R`$``#":``#(`0``%````#2:```(````0@```#B:
+M``!(````@@```#R:``"(````>`$``$":``"8`0``N`$``$2:``#8`0``^`$`
+M`$B:```8````$@```$R:``!8````4@```%":``"8````D@```%2:``"D`0``
+M?`$``%B:``#D`0``O`$``%R:```D````_`$``&":``!D````"@```&2:``"D
+M````2@```&B:``#D````B@```&R:```*`0``6@$``'":``!*`0``F@$``'2:
+M``"*`0``V@$``'B:``#*`0``#@```'R:```*````3@```(":``!*````C@``
+M`(2:``"*````7@$``(B:``"Z`0``G@$``(R:``#Z`0``W@$``)":```Z````
+M"0```)2:``!Z````20```)B:``"&`0``B0```)R:``#&`0``>0$``*":```&
+M````N0$``*2:``!&````^0$``*B:``"&````.0```*R:``#&````>0```+":
+M``#&````N0```+2:``#&````O0$``+B:``#&````_0$``+R:``#&````/0``
+M`,":``#&````?0```,2:``#&````O0```,B:``#&````_0```,R:``#&````
+M_0```-":``#&````_0```-2:``#&````_0```-B:``#&````_0```-R:``#&
+M````_0```.":``#&````_0```.2:``#&````_0```.B:``#&````_0```.R:
+M``#&````_0```/":``#&````_0```/2:``#&````_0```/B:``#&````_0``
+M`/R:``#&````_0````$```!&````8`````$```!&````80````$```!&````
+M8@````$```!&````8P````$```!&````9`````$```!&````90````$```!&
+M````9@````$```!&````9P````$```!&````:`````$```!&````:0````$`
+M``!&````:@````$```!&````:P````$```!&````;`````$```!&````=```
+M``$```!&````=`````$```!&````=`````$```!&````=`````$```!&````
+M=`````$```!&````=`````$```!&````=`````$```!&````=`````$```!&
+M````=0````$```!&````=@````$```!&````=P````$```!&````>`````$`
+M``!&````>0````$```!&````>@````$```!&````>P````$```!&````?```
+M``$```!&````?0````$```!&````?@````$```!&````?P````$```!&````
+M@`````$```!$````?`````$```!&````B`````$```!&````C`````$```!&
+M````D`````$```!&````E`````$```!&````F`````$```!&````G`````$`
+M``!&````H`````$```!&````I`````$```!&````J`````$```!&````K```
+M``$```!&````L`````$```!&````M``````"!P````,`!@`)`#P```!CNP``
+M8[L``(V[``"-NP``M[L``+>[``#ANP``X;L`````````````"0````0````$
+M``$``0`!``8```!&1S@`````````````````!`````$``0`$````1D<W````
+M``````````````,``0`!``$``P```$9'-@`````````````````$```````!
+M``$```!&1S4`````````````````!``!``$`````````1D<T````````````
+M``````0````!````_O___T9',P`````````````````#``$``0```/W___]&
+M1S(`````````````````!`````````#\____1D<Q``````````````````(`
+M`0`!````^O___T9',```````````````````````````````````````````
+M````````````%!!4&0`I$00`````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!L"8\)M`D("6P)M`G)
+M____R?___\G____)____PO____+____R____\O____+____T____P/___\#_
+M___`____P/___[K___^R____LO___[+___^R____L/___RWB``#FX0``\^$`
+M`&SB```,X@``7^(````````````````````````@````(````$````!`````
+M```&````!@````8`?.\``-#N```.[P``'>\``"3O```S[P``?.\``'SO``!\
+M[P``?.\``'SO``!\[P``?.\``'SO``!\[P``?.\``%KO````````````````
+M````````````````````````0!````_\+P`/_"\`'_PO``_\+P`/_"\`1!``
+M``_\+P`/_"\`'_PO``_\+P`/_"\`2!````_\+P`/_"\`'_PO``_\+P`/_"\`
+M3!````_\+P`/_"\`'_PO``_\+P`/_"\`4!````_\+P`/_"\`'_PO``_\+P`/
+M_"\`5!````_\+P`/_"\`'_PO``_\+P`/_"\`6!````_\+P`/_"\`'_PO``_\
+M+P`/_"\`7!````_\+P`/_"\`'_PO``_\+P`/_"\`8!````_\+P`/_"\`'_PO
+M``_\+P`/_"\`9!````_\+P`/_"\`'_PO``_\+P`/_"\`,!```#`"``#@`0``
+ML````&`!``#@`0``<!```&@!``#@`0``N`$``(P!``#@`0``L!```&`.``"`
+M$0``'!\``#@^``"`$0``\!```."@``!H0`$`@%@``."P``!H0`$`%(```.@#
+MZ`/@!N`&(`0@!$`(0`C@!N`&!)@````````#```````````````#````()@`
+M```"`@(``@("``(!`@`"`@(``@("))@```X.```.#@``!P<```X.```.#@``
+M1)@``!P6<A,E''(3*!=R$Z(6<A,E''(38)@``!"=```0G0``&)T``!B=```0
+MG0``9)@```#.`0``S@$``,X!``#.`0``S@$`:)@``)!!FD"009I`D$&:0)!!
+MFD"009I`&)D``+@!``"X`0``A`````@!``"X`0``))D```6*!1`%B at 40!8H%
+M$`6*!1`%B at 40,*(```````````````````@!```````````````````,````
+M`````#0````%````0`````````!$````"````$@````(````3````!````!0
+M`````````%0````?``````@````````$"`````````@(````````#`@`````
+M```0"````````!0(````````&`@````````<"````````"`(````````)`@`
+M``````!P$@```````#@0````````>!````````"X$````````/@0````````
+M.!$```````!X$0```````+ at 1````````^!$````````X$@```````'@2````
+M````N!(```````#X$@```````#@3````````>!,```````"X$P```````/@3
+M````````.!0```````!X%````````+ at 4````````^!0````````X%0``````
+M`'@5````````N!4```````#X%0```````#@6````````>!8```````"X%@``
+M`````/@6````````.!<```````!X%P```````+ at 7````````^!<````````\
+M$````````'P0````````O!````````#\$````````#P1````````?!$`````
+M``"\$0```````/P1````````/!(```````!\$@```````+P2````````_!(`
+M```````\$P```````'P3````````O!,```````#\$P```````#P4````````
+M?!0````````$@`````````B`````````#(`````````8@````````""`````
+M````)(`````````H@```,````"R```#__P<`,(```/___P$T@```,0```#B`
+M````````/(````````!(@````````%2`````````6(````````!<@```_\?_
+M_\"````:,((JQ(```.`!W`7(@```$"=`'\R``````/0!T(```!P>``#4@```
+MJJH"`-B```!550`"W(````````#@@```_____^2```#__P``Z(````````#L
+M@````````/"`````````](````````#X@````````/R```"(`````(<`````
+M```$AP``C`````B'``#D````#(<``-4"```0AP```````!2'````````&(<`
+M`*`````<AP``R0$``""'```L````)(<``"P````HAP``,````"R'```\````
+M,(<``"P````TAP``+````#B'```P````/(<``#P```!`AP```````$2'````
+M````2(<```````!,AP```````%"'````````5(<```````!8AP```````%R'
+M````````8(<``-4```!DAP``WP```&B'```"`0``;(<``#H!``!PAP``=0``
+M`'2'``!_````>(<``*(```!\AP````````"!```"``$`!($```$````(@0``
+MP`````R!````````$($``&@!```4 at 0```````,"'`````0(#Q(<```0%!@?(
+MAP``"`D*"\R'```,#0X/T(<``!`1$A/4AP``%!46%]B'```8&1H;W(<``!P=
+M'A_ at AP````$"`^2'```$!08'Z(<```@)"@OLAP``#`T.#_"'```0$1(3](<`
+M`!05%A?XAP``&!D:&_R'```<'1X?")@````````,F```&8Z$K1"8````X"A]
+M%)@``&N?"IP<F````````$"8``!Z`6H at 5)@``%D(````F0````````29````
+M````")D````````,F0````"``!"9```!````')D``(`,```@F0`````0!2B9
+M```!````+)D```0````TF0``(B`?'CB9```-#`L*/)D``#\```!(F0``$K*`
+MDE29``"(X5!=6)D``/\```!<F0``CFI+`&B9``#.`P``<)D``!6U+QEXF0``
+M`0```'R9````````$*(``#-C@``4H@``$&P0`!BB``!@0)P`(*(``,8PB`$D
+MH@````0``"RB````````-*(``"`@("`XH@``("`@($"B```@"DDX1*(``+9[
+M``!(H@``_#__#P";````````*)L```P````XFP``$@```&2;```A````C)L`
+M`"T```"<FP``,P```````P`&``D`/```````````````````````````````
+M```````````)````!`````0``0`!``$````````````&````1D<X````````
+M``````````0````!``$````````````$````1D<W``````````````````,`
+M`0`!``$````````````#````1D<V``````````````````0```````$`````
+M```````!````1D<U``````````````````0``0`!````````````````````
+M1D<T``````````````````0````!``````````````#^____1D<S````````
+M``````````,``0`!``````````````#]____1D<R``````````````````0`
+M``````````````````#\____1D<Q``````````````````(``0`!````````
+M``````#Z____1D<P````````````````````````````````````````````
+M```````````````````````````````````````````(`````0````,`````
+M```````````````&````1D<W``````````````````(`````````````````
+M````````1D<V``````````````````$```````````````````#]____1D<U
+M``````````````````````````````````````#Z____1D<T````````````
+M`````````0`!``````````````#X____1D<S`````````````````````0`!
+M`````0`!``````#V____1D<R`````````````````````0````$``0`!````
+M``#S____1D<Q`````````````````````0````$``0````$```#P____1D<P
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````````(````$
+M````!@````@````*````#`````X````0````8"\!`%HK`0!$+`$`^RT!`&4N
+M`0#H+@$`87(U,C$R06YI4&]L;```````````````,````!6````5@```%8``
+M`!6````5@```'(```*>/C1+/#X@)E0_@!*L/X!+/#X@)*)@```$``@H!``(*
+M``$!!0$``@H!``(*-)@```X.```.#@``#@X```X.```.#@``.)@```<````'
+M````"P````L````+````2)@``%K:&`!:VA@`:<H8`&G*&`!IRA@`4)@``."T
+MZ`W at M.@-X+3H#>"TZ`W at M.@-6)@``"X-@'XN#8!^+DWH?BY-Z'XN#8!^7)@`
+M`%YF-S%>9C<Q7F8W,5YF-S%>83<Q;)@``(&P#`6!L`P%@;`,!8"P#`6`L`P%
+M%)D``!`G```0)P``?!4``/@J```0)P``1)D``"`0N/<@$+CW(`VX]R`0N/<@
+M$+CW#*(``&I!+&1J02QD:D%`9&I!0&1J04!D'*(```J`@Q@*@(,8"H!S&`J`
+M at Q@*@(,8`````````````````````#`2````````+)@``/XO`@`\F`````$"
+M`$R8```\8802,)D``(-(``!`F0``!````'29````````^)D``!@````$H@``
+M``````BB``"(9S[0**(``+4!```\H@``KXG($P2;```@````")L``!`````,
+MFP``,````!";```(````%)L``"@````8FP``!````!R;```D````()L``!0`
+M```DFP``-````"R;```L````,)L```(````TFP``(@```#R;```R````0)L`
+M``H```!$FP``*@```$B;```&````3)L``"8```!0FP``%@```%2;```V````
+M6)L```X```!<FP``+@```&";```!````:)L``!$```!LFP``,0```'";```)
+M````=)L``"D```!XFP``!0```'R;```E````@)L``!4```"$FP``-0```(B;
+M```-````D)L```,```"4FP``(P```)B;```3````H)L```L```"DFP``*P``
+M`*B;```K````K)L``"L```"PFP``*P```+2;```K````N)L``"L```"\FP``
+M*P```,";```K````Q)L``"L```#(FP``*P```,R;```K````T)L``"L```#4
+MFP``*P```-B;```K````W)L``"L```#@FP``*P```.2;```K````Z)L``"L`
+M``#LFP``*P```/";```K````])L``"L```#XFP```@```/R;```6````````
+M`````````````````)R8`````````````````````````````)R8````````
+M`````````````````````)R8`````````````````````````````)R8````
+M`````````````````````````)R8`````````````````````````````)R8
+M`````````````````````````````)R8````````````````````````````
+M`)R8`````````````````````````````)R8````````````````````````
+M`````)R8`````````````````````````````)R8````````````````````
+M`````````)R8`````#@````X````.````#@````X`)R8````````````````
+M`````````````)R8`````````````````````````````)R8````````````
+M`,````"`````@````)R8``#Y``0`^0`$`/\`!`#]``0`_0`$`-28````````
+M``````0````$````!````````````````)H``*D!````````!)H``.D!``!`
+M````")H``"D```"`````#)H``&D```!0`0``$)H``)D!``"0`0``%)H``-D!
+M``#0`0``&)H``!D````0````')H``%D```!$````()H``)D```"$````))H`
+M`*4!``!(`0``*)H``.4!``"(`0``+)H``"4```#(`0``,)H``,@!```4````
+M-)H```@```!"````.)H``$@```""````/)H``(@```!X`0``0)H``)@!``"X
+M`0``1)H``-@!``#X`0``2)H``!@````2````3)H``%@```!2````4)H``)@`
+M``"2````5)H``*0!``!\`0``6)H``.0!``"\`0``7)H``"0```#\`0``8)H`
+M`&0````*````9)H``*0```!*````:)H``.0```"*````;)H```H!``!:`0``
+M<)H``$H!``":`0``=)H``(H!``#:`0``>)H``,H!```.````?)H```H```!.
+M````@)H``$H```".````A)H``(H```!>`0``B)H``+H!``">`0``C)H``/H!
+M``#>`0``D)H``#H````)````E)H``'H```!)````F)H``(8!``")````G)H`
+M`,8!``!Y`0``H)H```8```"Y`0``I)H``$8```#Y`0``J)H``(8````Y````
+MK)H``,8```!Y````L)H``,8```"Y````M)H``,8```"]`0``N)H``,8```#]
+M`0``O)H``,8````]````P)H``,8```!]````Q)H``,8```"]````R)H``,8`
+M``#]````S)H``,8```#]````T)H``,8```#]````U)H``,8```#]````V)H`
+M`,8```#]````W)H``,8```#]````X)H``,8```#]````Y)H``,8```#]````
+MZ)H``,8```#]````[)H``,8```#]````\)H``,8```#]````])H``,8```#]
+M````^)H``,8```#]````_)H``,8```#]````U)@``"````#4F```$````!0`
+M```0````$````!0```#8F```:!!@`&@08`!H$&``:!!@`&@08```````````
+M`)R8`````````````````````````````)R8````````````````````````
+M`````)R8`````````````````````````````)R8````````````````````
+M`````````)R8`````````````````````````````)R8```````0````$```
+M`!`````0````$)R8```````$````!`````0````$````!)R8````````````
+M`````````````````)R8`````````````````````````````)R8````````
+M`````````````````````)R8``````````````````H``````````)R8``#`
+M`#@`@``X`,``.`+``#@`P``X`)R8```&``(`!@`"``8````&``(`!@`"`)R8
+M``")````B0```(D```")````B0```)R8``"@````H````*````"@````H```
+M`)R8```'``0`!P`$``<`!``'``0`!P`$`-28```:````&@```!H````:````
+M&@``````````````G)@``$````!(````0````$````!`````G)@``!`````0
+M````$````!`````0````G)@```@````(````"`````@````(````G)@``$\`
+M``!/````3P```$\```!/````G)@``/$```#Q````80```/$```#Q````G)@`
+M`$^0``!/D```3)```$^0``!/D```G)@``%H2``!:$@``FA(``%H2``!:$@``
+MS)@```X````.````#P````X````.`````0```$8```!@`````0```$8```!A
+M`````0```$8```!B`````0```$8```!C`````0```$8```!D`````0```$8`
+M``!E`````0```$8```!F`````0```$8```!G`````0```$8```!H`````0``
+M`$8```!I`````0```$8```!J`````0```$8```!K`````0```$8```!L````
+M`0```$8```!T`````0```$8```!T`````0```$8```!T`````0```$8```!T
+M`````0```$8```!T`````0```$8```!T`````0```$8```!T`````0```$8`
+M``!T`````0```$8```!U`````0```$8```!V`````0```$8```!W`````0``
+M`$8```!X`````0```$8```!Y`````0```$8```!Z`````0```$8```![````
+M`0```$8```!\`````0```$8```!]`````0```$8```!^`````0```$8```!_
+M`````0```$8```"``````0```$0```!\`````0```$8```"(`````0```$8`
+M``",`````0```$8```"0`````0```$8```"4`````0```$8```"8`````0``
+M`$8```"<`````0```$8```"@`````0```$8```"D`````0```$8```"H````
+M`0```$8```"L`````0```$8```"P`````0```$8```"T````E3H!`'@Z`0!\
+M.@$`@#H!`)`Z`0"0.@$`A#H!`(@Z`0``````````````````````````````
+M```P````%8```!6````5@```%8```!6````<@```IY.-$L\3B`F5$^`$JQ/@
+M$L\3B`DHF````0`""@$``@H``0(%`0`""@$``@HTF```#@X```X.```.#@``
+M#@X```X.```XF```!P````<````+````"P````L```!(F```;=H8`&W:&`!U
+MRA@`=<H8`'7*&`!0F```X+3H#>"TZ`W at M.@-X+3H#>"TZ`U8F```+ at V`?BX-
+M@'XN#>A^+ at WH?BX-@'Y<F```7F8W,5YF-S%>9C<Q7F8W,5YF-S%LF```@;`,
+M!8&P#`6!L`P%@;`,!8&P#`44F0``T`<``-`'``!,!```F`@``-`'``!$F0``
+M(!"X]R`0N/<0#;CW$!"X]Q`0N/<$H@`````````````(````"`````@````(
+MH@``B&>^UHAGOM:(9S[0B&<^T(AG/M`,H@``0`$L9$`!+&1 at P4)D8,%"9&#!
+M0F0<H@``"H"#&`J`@Q@*@',8"H"#&`J`@Q at P$@```````"R8``#^+P(`/)@`
+M```!`@!,F```/&&$$C"9``""2```0)D```0```!TF0```````"BB``"U`0``
+M/*(``*^)R!,$FP```0````B;```"````#)L```,````0FP``!````!2;```%
+M````&)L```@````<FP``"0```"";```*````))L```L````LFP``#0```#";
+M```0````-)L``!$````\FP``$P```$";```4````1)L``!4```!(FP``&```
+M`$R;```9````4)L``!H```!4FP``&P```%B;```<````7)L``!T```!@FP``
+M(````&B;```B````;)L``",```!PFP``)````'2;```E````>)L``"@```!\
+MFP``*0```(";```J````A)L``"L```"(FP``+````)";```P````E)L``#$`
+M``"8FP``,@```*";```T````I)L``#4```"HFP``-0```*R;```U````L)L`
+M`#4```"TFP``-0```+B;```U````O)L``#4```#`FP``-0```,2;```U````
+MR)L``#4```#,FP``-0```-";```U````U)L``#4```#8FP``-0```-R;```U
+M````X)L``#4```#DFP``-0```.B;```U````[)L``#4```#PFP``-0```/2;
+M```U````^)L``!````#\FP``&@```````````````)H```<````'````!)H`
+M`$<```!'````")H``(<```"'````#)H``*`!``"@`0``$)H``.`!``#@`0``
+M%)H``"`````@````&)H``&````!@````')H``*$!``"A`0``()H``.$!``#A
+M`0``))H``"$````A````*)H``&$```!A````+)H``&(!``!B`0``,)H``*(!
+M``"B`0``-)H``.(!``#B`0``.)H``"(````B````/)H``&(```!B````0)H`
+M`&,!``!C`0``1)H``*,!``"C`0``2)H``.,!``#C`0``3)H``",````C````
+M4)H``&,```!C````5)H``(0!``"$`0``6)H``,0!``#$`0``7)H```0````$
+M````8)H``.H!```+````9)H``"H```!+````:)H``&H```"+````;)H``*H`
+M``"L`0``<)H``*L!``#L`0``=)H``.L!```L````>)H``"L````2````?)H`
+M`&L```!2````@)H``*L```"2````A)H``*P!``"3`0``B)H``.P!``#3`0``
+MC)H``"P````3````D)H``#H```!3````E)H``'H```"3````F)H``+H```"4
+M`0``G)H``+L!``#4`0``H)H``/L!```4````I)H``#L````Z````J)H``'L`
+M``!Z````K)H``+L```"Z````L)H``+P!``"[`0``M)H``/P!``#[`0``N)H`
+M`#P````[````O)H``'P```![````P)H``+P```"[````Q)H``/P```"\`0``
+MR)H``/P```#\`0``S)H``/P````\````T)H``/P```!\````U)H``/P```"\
+M````V)H``/P```#\````W)H``/P```#\````X)H``/P```#\````Y)H``/P`
+M``#\````Z)H``/P```#\````[)H``/P```#\````\)H``/P```#\````])H`
+M`/P```#\````^)H``/P```#\````_)H``/P```#\````U)@``"````#0F```
+M"`0&`P@$!P,(!`8#"`0&`P@$!P/<F```P,"@`,#`H`#`P.``P,#@`,#`X```
+M`````````)R8```````/````#P````\````/````#YR8````````````````
+M`````````````)R8`````(````"`````@````(````"``)R8`````"H````J
+M````*@```"H````J`)R8``````$````!`````0````$````!`)R8````````
+M`````````````````````)R8`````!@````8````&````!@````8`)R8````
+M`&````!@````;@```&X```!N`)R8`````,<```#'````QP```,<```#'`)R8
+M`````$L```!+````2P```$L```!+`)R8`````$@$``!(!```2`0``$@$``!(
+M!)R8`````$P```!,````3````$P```!,`)R8`````.0```#D````Y````.0`
+M``#D`)R8`````````````````````````````)R8`````/P```#\````_```
+M`/P```#\`)R8`````/\```#_````_P```/\```#_`)R8`````#\$```_!```
+M/P0``#\$```_!)R8``````P````,````#`````P````,`)R8`````!D"```9
+M`@``&0(``!D"```9`IR8`````"0````D````)````"0````D`)R8`````+0`
+M``"T````M````+0```"T`)R8`````)D```"9````F0```)D```"9`)R8````
+M`%````!0````4````%````!0`)R8`````"H````J````*@```"H````J`)R8
+M`````!(````2````$@```!(````2`)R8`````#+````RP```,L```#+````R
+MP)R8`````'0!``!T`0``=`$``'0!``!T`9R8`````!$````1````$0```!$`
+M```1`)R8`````"B&```HA@``*(8``"B&```HAIR8`````(0Q``"$,0``A#$`
+M`(0Q``"$,9R8``"``/(`@`#R`(``\@"``/(`@`#R`)R8```9`"<`&0`G`!D`
+M)P`9`"<`&0`G`)R8```#`````P````,````#`````P```)R8````````````
+M`````````````````)R8``"R````L@```+(```"R````L@```)R8``"$(+``
+MA""P`(0 at L`"$(+``A""P`)R8``"D)4$`I"5!`*0E00"D)4$`I"5!`)R8```@
+MDA$`()(1`""2$0`@DA$`()(1`)R8````2!H``$@:``!(&@``2!H``$@:`-B8
+M```P`@L`,`(+`#`""P`P`@L`,`(+`)R8``"4````E````)0```"4````E```
+M`)R8``"1````D0```)$```"1````D0```)R8```2````$@```!(````2````
+M$@```)R8``"`````@````(````"`````@````)R8``#9````V0```-D```#9
+M````V0```)R8``!@````8````&````!@````8````)R8``#P````\````/``
+M``#P````\````)R8``"B````H@```*(```"B````H@```)R8``!2````4@``
+M`%(```!2````4@```)R8``#4````U````-0```#4````U````)R8``#,%```
+MS!0``,P4``#,%```S!0``)R8``",!```C`0``(P$``",!```C`0``,28```#
+M`````P````,````#`````P```.A*`0#M2@$`U$H!`-A*`0#H2@$`Z$H!`-Q*
+M`0#@2@$````````````P````%0```!4````5````%0```!4````<@```IY.-
+M$L\3B`F5$^`$JQ/@$L\3B`DHF````0`""@$``@H```(%`0`""@$``@HTF```
+M``X````.````#@````X````.```XF````@````(````*````"@````H```!(
+MF```;=H8`&W:&`!D:AH`9&H:`&1J&@!0F```X+3H#>"TZ`W:L. at -VK"8#-JP
+MZ`U8F```+ at V`?BX-@'XN#>A^+ at W(?BX-@'Y<F```7F8W,5YF-S%>9C<Q7F`Y
+M,5YF-S%LF```@;`,!8&P#`6!L`P%@;`,!8&P#`44F0``T`<``-`'``!,!```
+MF`@``-`'``!$F0```!"X]P`0N/<`#;CW`!"X]P`0N/<$H@``````````````
+M```````````````(H@``B&>^UHAGOM:(9S[0B&<^T(AG/M`,H@``0`$L`$`!
+M+`!`P4(`0,%"`$#!0@`<H@``"H"#&`J`@Q@*@&,8"H"#&`J`@Q at P$@``X`,`
+M`&"````/````&($````````<@0```````""!````````)($````````H at 0``
+M`````"R!````````,($````````T at 0```````#B!````````/($```````!`
+M at 0``J```@$2!````````+)@```"@```\F`````0@`$R8```\(X02<)@``!\`
+M``!TF```@````'B8```.````@)D```````"$F0````"``J"9````````X)D`
+M``````#DF0``JJJJJNB9``!X9$8\[)D``*H```#PF0``#````/29``#_````
+M^)D``!0````HH@``M0D``#RB``"OB<B33*(```$```!0H@```*```%2B````
+M````6*(``(!3QPQ<H@```0\/#V"B```!#VE?9*(``!&*00!HH@```````&RB
+M``!JP3`,<*(``"`(@@!TH@``JGP;`'BB``#..><<?*(``,X!%P4`HP`````!
+M&`2C```")@,P"*,```8^!T@,HP``"DP+5A"C```/8!ID%*,``!MN3W at 8HP``
+M6GR/AARC``!;AL^.(*,```^73YTDHP``CZ'/I2BC```?KU^U+*,``)^YW[TP
+MHP``/\=_S32C``"_T?_5.*,````````\HP```````$"C````````1*,`````
+M``!(HP``____/TRC``#___\_4*,``/___S]4HP``__\#`%BC```?JJAY7*,`
+M``]";`9 at HP``!R(H#V2C``"%%F`7:*,```01@!]LHP```PR at -W"C``"#",0_
+M=*,```,(P%=XHP``@@;87WRC``""!.!_@*,``+I[/'^$HP``\'\P\P2;```!
+M````")L```(````,FP```P```!";```$````%)L```4````8FP``"````!R;
+M```)````()L```H````DFP``"P```"R;```-````,)L``!`````TFP``$0``
+M`#R;```3````0)L``!0```!$FP``%0```$B;```8````3)L``!D```!0FP``
+M&@```%2;```;````6)L``!P```!<FP``'0```&";```@````:)L``"(```!L
+MFP``(P```'";```D````=)L``"4```!XFP``*````'R;```I````@)L``"H`
+M``"$FP``*P```(B;```L````D)L``#````"4FP``,0```)B;```R````H)L`
+M`#0```"DFP``-0```*B;```U````K)L``#4```"PFP``-0```+2;```U````
+MN)L``#4```"\FP``-0```,";```U````Q)L``#4```#(FP``-0```,R;```U
+M````T)L``#4```#4FP``-0```-B;```U````W)L``#4```#@FP``-0```.2;
+M```U````Z)L``#4```#LFP``-0```/";```U````])L``#4```#XFP``$```
+M`/R;```:`````````````````````````````````````)H`````````````
+M!)H```````!`````")H```````"`````#)H```````"!`0``$)H```````#!
+M`0``%)H````````!````&)H```````!!````')H```````"!````()H`````
+M``!H`0``))H```````"H`0``*)H```````#H`0``+)H````````H````,)H`
+M``````!H````-)H```````")`0``.)H```````#)`0``/)H````````)````
+M0)H```````!)````1)H```````")````2)H```````"0`0``3)H```````#0
+M`0``4)H````````0````5)H```````!0````6)H```````"0````7)H`````
+M``"1`0``8)H```````#1`0``9)H````````1````:)H```````!1````;)H`
+M``````"1````<)H```````!X`0``=)H```````"X`0``>)H```````#X`0``
+M?)H````````X````@)H```````!X````A)H```````"9`0``B)H```````#9
+M`0``C)H````````9````D)H```````!9````E)H```````"9````F)H`````
+M``#9````G)H```````#Y````H)H```````#Y````I)H```````#Y````J)H`
+M``````#Y````K)H```````#Y````L)H```````#Y````M)H```````#Y````
+MN)H```````#Y````O)H```````#Y````P)H```````#Y````Q)H```````#Y
+M````R)H```````#Y````S)H```````#Y````T)H```````#Y````U)H`````
+M``#Y````V)H```````#Y````W)H```````#Y````X)H```````#Y````Y)H`
+M``````#Y````Z)H```````#Y````[)H```````#Y````\)H```````#Y````
+M])H```````#Y````^)H```````#Y````_)H```````#Y````U)@``"````#0
+MF```"!0``@@4`0((%``""!0``@@4`0+<F```P""@`,`@H`#`(.``P"#@`,`@
+MX````````````)R8``````#P````\````/````#P````\)R8````````````
+M`````````````````)R8```````#`````P````,````#`````YR8````````
+M`````````````````````)R8`````````````````````````````)R8````
+M`````````````````````````)R8`````````````````````````````)R8
+M`````````````````````````````)R8`````$!```!`0```0$```$!```!`
+M0)R8``````5E```%90``!64```5E```%99R8````````````````````````
+M`````)R8`````````````````````````````)R8`````$(```!"````0@``
+M`$(```!"`)R8`````+4```"U````M0```+4```"U`)R8``````,````#````
+M`P````,````#`)R8`````/<```#W````]P```/<```#W`)R8`````)T```"=
+M````G0```)T```"=`)R8`````"(````B````(@```"(````B`)R8`````"($
+M```B!```(@0``"($```B!)R8```8`",`&``C`!@`(P`8`",`&``C`)R8````
+M`"@````H`&``*`!@`"@`8``H`)R8``#``%``P`!0`,,`4`##`%``PP!0`)R8
+M``!_``0`?P`$`'\`!`!_``0`?P`$`)R8``!8!```6`0``%@$``!8!```6`0`
+M`)R8`````````````````````````````)R8````P````,````#`````P```
+M`,```-B8```P`D``,`)``#`"0``P`D``,`)`````````````````````````
+M`````````)R8````9````&0```!D````9````&0``)R8````"`````@````(
+M````"`````@``,R8```.````#@````X````.````#@```/1B`0#Y8@$`Y&(!
+M`.AB`0#T8@$`]&(!`.QB`0#P8@$`05(U,C$P`$%2-3(Q,0!!4C4R,3(`4D8U
+M,3$Q`%)&-3$Q,@!21C(T,3,`071H97)O<R`R,S$S(%=I4V]#`$%T:&5R;W,@
+M-3(Q,B`H1E!'02D`071H97)O<R`U,C$Q("A&4$=!*0!!=&AE<F]S(#4R,3``
+M071H97)O<R`U,C$Q`$%T:&5R;W, at -3,Q,B!7:5-O0P!!=&AE<F]S(#4R,3(`
+M1$(`1$5"54<`3D$`3D]?0T]53E1265]3150`04P`04Q"04Y)00!$6@!!3$=%
+M4DE!`$%2`$%21T5.5$E.00!!30!!4DU%3DE!`$%5`$%54U1204Q)00!!5`!!
+M55-44DE!`$%:`$%:15)"04E*04X`0D@`0D%(4D%)3@!"60!"14Q!4E53`$)%
+M`$)%3$=)54T`0EH`0D5,25I%`$)/`$)/3%9)00!"4@!"4D%:24P`0DX`0E)5
+M3D5)($1!4E534T%,04T`0D<`0E5,1T%224$`0T$`0T%.041!`$-,`$-(24Q%
+M`$-.`$-(24Y!`$-/`$-/3$]-0DE!`$-2`$-/4U1!(%))0T$`2%(`0U)/051)
+M00!#60!#65!255,`0UH`0UI%0T@@4D5054),24,`1$L`1$5.34%22P!$3P!$
+M3TU)3DE#04X at 4D5054),24,`14,`14-5041/4@!%1P!%1UE05`!35@!%3"!3
+M04Q6041/4@!%10!%4U1/3DE!`$9)`$9)3DQ!3D0`1E(`1E)!3D-%`$8R`$92
+M04Y#15]215,`1T4`1T5/4D=)00!$10!'15)-04Y9`$=2`$=2145#10!'5`!'
+M54%414U!3$$`2$X`2$].1%5205,`2$L`2$].1R!+3TY'`$A5`$A53D=!4ED`
+M25,`24-%3$%.1`!)3@!)3D1)00!)1`!)3D1/3D5324$`25(`25)!3@!)10!)
+M4D5,04Y$`$E,`$E34D%%3`!)5`!)5$%,60!*4`!*05!!3@!*,0!*05!!3C$`
+M2C(`2D%004XR`$HS`$I!4$%.,P!*-`!*05!!3C0`2C4`2D%004XU`$I/`$I/
+M4D1!3@!+6@!+05I!2TA35$%.`$M0`$Y/4E1(($M/4D5!`$M2`$M/4D5!(%)%
+M4%5"3$E#`$LR`$M/4D5!(%)%4%5"3$E#,@!+5P!+55=!250`3%8`3$%45DE!
+M`$Q"`$Q%0D%.3TX`3$D`3$E%0TA414Y35$5)3@!,5`!,251(54%.24$`3%4`
+M3%5814U"3U521P!-3P!-04-!50!-2P!-04-%1$].24$`35D`34%,05E324$`
+M35@`345824-/`$U#`$U/3D%#3P!-00!-3U)/0T-/`$Y,`$Y%5$A%4DQ!3D13
+M`$Y:`$Y%5R!:14%,04Y$`$Y/`$Y/4E=!60!/30!/34%.`%!+`%!!2TE35$%.
+M`%!!`%!!3D%-00!010!015)5`%!(`%!(24Q)4%!)3D53`%!,`%!/3$%.1`!0
+M5`!03U)454=!3`!04@!054525$\@4DE#3P!100!1051!4@!23P!23TU!3DE!
+M`%)5`%)54U-)00!300!3055$22!!4D%"24$`4T<`4TE.1T%03U)%`%-+`%-,
+M3U9!2R!215!50DQ)0P!320!33$]614Y)00!:00!33U542"!!1E))0T$`15,`
+M4U!!24X`4T4`4U=%1$5.`$-(`%-7251:15),04Y$`%-9`%-94DE!`%17`%1!
+M25=!3@!42`!42$%)3$%.1`!45`!44DE.241!1"`F(%1/0D%'3P!43@!454Y)
+M4TE!`%12`%154DM%60!500!52U)!24Y%`$%%`%5.251%1"!!4D%"($5-25)!
+M5$53`$="`%5.251%1"!+24Y'1$]-`%53`%5.251%1"!35$%415,`55D`55)5
+M1U5!60!56@!56D)%2TE35$%.`%9%`%9%3D5:545,00!63@!62454($Y!30!9
+M10!914U%3@!:5P!:24U"04)710`E<SH@<&AY0VYT,2`P>"5X+"!R97-E='1I
+M;F<@8V]U;G1E<B!V86QU92!T;R`P>"5X"@``)7,Z('!H>4-N=#(@,'@E>"P@
+M<F5S971T:6YG(&-O=6YT97(@=F%L=64@=&\@,'@E>`H````````P+CDN,30N
+M.0``````````!P````X````5````'````",`````````````````````````
+M````````````````)!.($Q$`B!/L$Q$`'A2"%`\`@A3F%!(`7A5=%A0`71;!
+M%A0`P1;:%A<`C!2@%+04R!0\%%`49!1X%```,A1&%%H4;A0``&P)A0F>"8H)
+MJ`D``&P)A0F>"8H)``!L"84)G at F*"0``;`F%"9X)B at D``'$6A1:9%JT6P18`
+M`%H4 at A2J%```@!:H%@``?!60%:05N!7,%>`5]!4(%AP6,!9$%@``<0EV"7L)
+M@`F/"90)F0FC"0``<0EV"7L)@`F/"90)F0D``'$)=@E["8`)CPF4"9D)``!Q
+M"78)>PF`"8\)E`F9"0``M`D``(4)```\%%`49!1X%``````!````:@````$`
+M``!\````!````(8````$````D@````0```"<````!````*8````!````L```
+M``(```"\`````@```,0````!````R@````0```#B````!````/0````$````
+M!`$```0````4`0``!````"0!```@````*`$``#(41A1:%&X4``!:%((4JA2`
+M%J at 6``!\%9`5I!6X%<P5X!7T%0 at 6'!8P%D06````````````````````````
+M`````````````&````"H"0```0```*,)```!````M`D```$```!G````J`D`
+M``$```"C"0```0```+0)```!````80```*@)```!````HPD```$```"T"0``
+M`0```&4```"H"0```0```*,)```!````M`D```$```!*````J`D```````"C
+M"0```````+0)```!````2P```*@)```!````HPD```$```"T"0```0```&H`
+M``"H"0```0```*,)```!````M`D```$`````````````````````````````
+M````````````"```````````````````````````````````````````````
+M`0)P%PL`C````````0(H(P\`$@```````0+ at +@H`F`(``````0)01 at X`)`(`
+M`````0+`70D`L`0``````0*@C`T`2`0``````0*`NP@`8`0``````0+PT at P`
+M;`0`````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````@```````````````````````````````````````````````$#
+M<!<+`(P```````$#*",/`!(```````$#X"X*`)@"``````$#4$8.`"0"````
+M``$#P%T)`+`$``````$#H(P-`$@$``````$#@+L(`&`$``````$#\-(,`&P$
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````(```````````````````````````````````````````````!`G`7
+M"P",```````!`B at C#P`2```````!`N`N"@"8`@`````!`E!&#@`D`@`````!
+M`L!="0"P!``````!`J",#0!(!``````!`H"["`!@!``````!`O#2#`!L!```
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````"````````````````````````````````````````````````0-P%PL`
+MC````````0,H(P\`$@```````0/@+ at H`F`(``````0-01 at X`)`(``````0/`
+M70D`L`0``````0. at C`T`2`0``````0.`NP@`8`0``````0/PT at P`;`0`````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``0```````````````````````````````````````````````$`Z`,+`((`
+M``````$`T`<*!(0!``````$`?!4)!(L!``````$`^"H(!)8!````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````$
+MH@`````````````(H@``1D8^4$9&/E`,H@``;$&`9&Q!@&00H@```Z"9`0.@
+MF0$4H@``$-9,!!#63`08H@``0`"`$T``@!,<H@``8`#@&V``X!L at H@``"H!3
+M#`J`4PPDH@``.]\4`#O?%``HH@``M0$``+4!```LH@``(````"````"<F```
+M``````````"<F`````````````"<F`````````````"<F`````````````"<
+MF`````````````"<F`````````````"<F`````````````"<F```````````
+M``"<F`````````````"<F`````````````"<F`````````````"<F``````X
+M````.`"<F`````````````"<F`````````````"<F`````````````"<F```
+M^0`$`/D`!`#4F`````````0`````````````````````````G)@`````````
+M````G)@`````````````G)@`````````````G)@`````````````G)@`````
+M````````G)@``````!`````0G)@```````0````$G)@`````````````G)@`
+M````````````G)@`````````````G)@````````````*G)@``(``.`"``#@"
+MG)@```8``@`&````G)@``)(```"2````G)@``*````"@````G)@```<`!``'
+M``0`U)@``!H````:````G)@``$@```!(````G)@``!`````0````G)@```@`
+M```(````G)@```\````/````G)@``/(```!B````G)@``$^0``!,D```G)@`
+M`%H2``":$@``S)@```X````/```````````````````````````````(````
+M```````````````````````````````````````````!`G`7"P",```````!
+M`B at C#P`2```````!`N`N"@"8`@`````!`E!&#@`D`@`````!`L!="0"P!```
+M```!`J",#0!(!``````!`H"["`!@!``````!`O#2#`!L!```````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````"```````
+M`````````````````````````````````````````0-P%PL`C````````0,H
+M(P\`$@```````0/@+ at H`F`(``````0-01 at X`)`(``````0/`70D`L`0`````
+M`0. at C`T`2`0``````0.`NP@`8`0``````0/PT at P`;`0`````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````````````0`````````
+M``````````````````````````````````````$`Z`,;`((```````$`T`<:
+M!(0!``````$`?!49!(L!``````$`^"H8!)8!````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M```````````````````````````````````````````````,````````````
+M```````````````````````````````````!`.@#&P`"```````!`-`'&@0$
+M`0`````!`'P5&00+`@`````!`/@J&`06`P```````G`7"P`,!````````B at C
+M#P`2!``````!`N`N"@`8!@`````!`E!&#@`D!@`````!`L!="0`P"``````!
+M`J",#0!("``````!`H"["`!@"``````!`O#2#`!L"```````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````#```````````````
+M`````````````````````````````````03T`0<`@0```````03H`P(`BP$`
+M`````030!P8`E@(``````02X"P$`E@,``````0)P%PL`C`0``````0(H(P\`
+M$@0``````0+ at +@H`F`8``````0)01 at X`)`8``````0+`70D`L`@``````0*@
+MC`T`2`@``````0*`NP@`8`@``````0+PT at P`;`@`````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!'0T,Z("A'3E4I(#,N
+M-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E
+M94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P
+M,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```
+M1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.
+M52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR
+M(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)3
+M1%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P
+M-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#
+M.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@
+M,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&
+M<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@
+M,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X
+M``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H
+M1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT
+M+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E
+M0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P
+M-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'
+M0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5
+M*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@
+M6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$
+M72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W
+M,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z
+M("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S
+M+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R
+M965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R
+M,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@`
+M`$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'
+M3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N
+M,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"
+M4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T
+M,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#
+M0SH@*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I
+M(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;
+M1G)E94)31%T@,C`P-#`W,C@``$=#0SH@*$=.52D@,RXT+C(@6T9R965"4T1=
+M(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N-"XR(%M&<F5E0E-$72`R,#`T,#<R
+M.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E94)31%T@,C`P-#`W,C@``$=#0SH@
+M*$=.52D@,RXT+C(@6T9R965"4T1=(#(P,#0P-S(X``!'0T,Z("A'3E4I(#,N
+M-"XR(%M&<F5E0E-$72`R,#`T,#<R.```1T-#.B`H1TY5*2`S+C0N,B!;1G)E
+M94)31%T@,C`P-#`W,C@``"YS>6UT86(`+G-T<G1A8@`N<VAS=')T86(`+G)E
+M;"YT97AT`"YR96PN<F]D871A`"YR;V1A=&$N<W1R,2XQ`"YR;V1A=&$N<W1R
+M,2XT`"YR96PN9&%T80`N8G-S`"YC;VUM96YT````````````````````````
+M`````````````````````````````````!\````!````!@`````````T````
+M8FP!````````````!``````````;````"0``````````````9#("`/`6```,
+M`````0````0````(````*0````$````"`````````*!L`0`(=P``````````
+M```@`````````"4````)``````````````!420(`$!D```P````#````!```
+M``@````Q`````0```#(`````````J.,!`,P%``````````````$````!````
+M0`````$````R`````````'3I`0!G```````````````$`````0```%,````!
+M`````P````````#@Z0$`Y!8`````````````(`````````!/````"0``````
+M````````9&("`+`````,````!P````0````(````60````@````#````````
+M`.```@"P`0`````````````@`````````%X````!``````````````#@``(`
+MRP8``````````````0`````````1`````P``````````````JP<"`&<`````
+M``````````$``````````0````(``````````````$0*`@"@%P``#0````@`
+M```$````$`````D````#``````````````#D(0(`@!```````````````0``
+M`````````````````````````````````````````````P`!````````````
+M``````,``P`````````````````#``4``````````````````P`&````````
+M``````````,`!P`````````````````#``D``````````````````P`*``$`
+M``#0B0``J````!(``0`,````1'L```8````2``$`%P```)#O``"[````$@`!
+M`"(```!P80``=````!(``0`M````_.4``(\````2``$`.````'17``#;````
+M$@`!`$,```"H,P$`Y````!(``0!.````X`(``*0!```1``<`60```#CS```C
+M````$@`!`&0```!H60``,0```!(``0!O````E$X``#P````2``$`>@```(3L
+M```E````$@`!`(4```!4Z0``$P```!(``0"0````V-\```P````2``$`FP``
+M`/C-``"Z````$@`!`*8```!\U@``Y0```!(``0"Q````7`@``#P````2``$`
+MO````(3"``"6````$@`!`,<````TK0``BP```!(``0#2````')L```L````2
+M``$`W0```!A_```E````$@`!`.@````@\@``!@```!(``0#S````C,T```8`
+M```2``$`_@```$C>```7````$@`!``D!``!8_@``>P```!(``0`4`0``#`8`
+M`'4````2``$`'P$```R-```&````$@`!`"H!``#()@$`=0```!(``0`U`0``
+M"((```8````2``$`0`$``"B;```/````$@`!`$L!``!`#@``I`$``!$`!P!6
+M`0``/.L``'0````2``$`80$```R```!N````$@`!`&P!``"\`@``+````!(`
+M`0!W`0``N"@!`%@````2``$`@@$``*BK```A````$@`!`(T!````````````
+M`!````"N`0``B%X``$$````2``$`N0$``/3$``"6````$@`!`,0!``!88```
+M)````!(``0#/`0``Z)(``$@````2``$`V@$``!R6``!X````$@`!`.4!``#(
+MQ0``.0```!(``0#P`0``$((``&$````2``$`^P$``,A=```S````$@`!``8"
+M``"(>P``.0```!(``0`1`@``9)@``$H````2``$`'`(``+#K```@````$@`!
+M`"<"``"\Z@``8````!(``0`R`@``Z/X``#<````2``$`/0(``+C.``!7````
+M$@`!`$@"``!@60``!@```!(``0!3`@``T.L``'@````2``$`7@(```A0``#(
+M!0``$@`!`&D"```(EP``'@$``!(``0!T`@``J.<``"8````2``$`?P(``&@"
+M``!3````$@`!`(H"```<ZP``(````!(``0"5`@``O!<!``(!```2``$`H`(`
+M`!CR```&````$@`!`*L"``!@W0``+````!(``0"V`@``G,\``/$````2``$`
+MP0(```!@```9````$@`!`,P"``!(C0``FP```!(``0#7`@``O)$``'L````2
+M``$`X@(``*1Z```A````$@`!`.T"``!,'`$`B````!(``0#X`@``A'```/(!
+M```2``$``P,``*AB```!````$@`!``X#``#4'`$`2````!(``0`9`P``X'P`
+M`#4"```2``$`)`,``!@!``!0`0``$@`!`#,#``"L"0$`Y@(``!(``0`^`P``
+M*/(``(<````2``$`20,``-R6```1````$@`!`%0#```4&P$`.`$``!(``0!?
+M`P``X.T``,X````2``$`:@,``+SR```/````$@`!`'4#````E```#@```!(`
+M`0"``P``!,8``-\````2``$`BP,``(S%```Y````$@`!`)8#```(IP``4P``
+M`!(``0"A`P``T%4``!`````2``$`K`,``!Q@```[````$@`!`+<#``#H`@``
+M*`$``!(``0#-`P``(`,!`"$````2``$`V`,``'2"``"6````$@`!`.,#``!0
+M80``(````!(``0#N`P``Y(L``!8````2``$`^0,``-3^```2````$@`!``0$
+M``"@LP``*@```!(``0`/!```G.$``((#```2``$`&@0``"C?```+````$@`!
+M`"4$```X6@``B````!(``0`P!```Q(H``#@````2``$`.P0``-PJ`0`]````
+M$@`!`$8$```8X```^````!(``0!1!```P)8```$````2``$`7`0``"!>``!E
+M````$@`!`&<$```PD0``BP```!(``0!R!```E,0``%\````2``$`?00``)#0
+M``"G!0``$@`!`(@$``!L&@$`I@```!(``0"3!```-.D```8````2``$`G at 0`
+M`$"0```A````$@`!`*D$``#4\P``5P```!(``0"T!```;"4!`/4````2``$`
+MOP0``)B<``!%````$@`!`,H$```X70``*0```!(``0#5!```2.P``",````2
+M``$`X`0``#C6``!!````$@`!`.L$```,S```'P```!(``0#V!```$"D!`#X`
+M```2``$``04``!#R```&````$@`!``P%`````````````!`````9!0``@'P`
+M`&`````2``$`)`4``!AG``!%````$@`!`"\%``"P*0$`"P```!(``0`Z!0``
+M!.@``'<````2``$`104``&P$``#%````$@`!`%8%``!4'P$`\P,``!(``0!A
+M!0``?&```$0````2``$`;`4``+"```"/````$@`!`'<%``!HZ0``$0```!(`
+M`0""!0``C#0!`'$````2``$`C04``)!F``!7````$@`!`)@%```4C0``,0``
+M`!(``0"C!0``%*D``"`!```2``$`K at 4``,AZ``!Y````$@`!`+D%````$```
+MI`$``!$`!P#$!0``K/0``#@````2``$`SP4``$SP```L````$@`!`-H%``!X
+MB@``2@```!(``0#E!0``X)P``.4````2``$`\`4``*SL```T`0``$@`!`/L%
+M```,E0``7````!(``0`&!@``R)8``!,````2``$`$08``+#N``#@````$@`!
+M`!P&``!D'0$``0$``!(``0`G!@``'"L!`%0$```2``$`, at 8``"B8```Z````
+M$@`!`#T&``#$6```"P```!(``0!(!@``_(H``.@````2``$`4P8``!S;``!*
+M````$@`!`%X&``#LY@``)P```!(``0!I!@``,&,``"0````2``$`=`8``,!K
+M`0"B````$@`!`'\&``"XZ```.````!(``0"*!@``P!$``*0!```1``<`E08`
+M`(S,``!E````$@`!`*`&```XFP``#P```!(``0"K!@``8&<``"T!```2``$`
+MM at 8``#SE```A````$@`!`,$&```4YP``E````!(``0#,!@``U%8``&@````2
+M``$`UP8``, at I`0`3`0``$@`!`.(&```P;P``%````!(``0#M!@``>&4```\`
+M```2``$`^`8``!Q?``"J````$@`!``,'``#X9@``'0```!(``0`.!P``C.8`
+M`%\````2``$`&0<``+```0#Q`0``$@`!`"0'``#TS```!@```!(``0`O!P``
+MX",!`%L````2``$`.@<```1E```C````$@`!`$4'``!@!@``I`$``!$`!P!0
+M!P``P%H``(P!```2``$`6P<``$Q[```Y````$@`!`&8'``#H9@``#@```!(`
+M`0!Q!P`````````````0````D0<``)!H```P!@``$@`!`)P'````````"0``
+M`!$`!P"L!P``-`4``'4````2``$`O0<``#B2```J````$@`!`,@'``!DUP``
+M4@```!(``0#3!P``X.<``"(````2``$`W@<``*QB```!````$@`!`.D'``"X
+ME@``!@```!(``0#T!P``G)H```L````2``$`_P<``.2-``"(````$@`!``H(
+M``"4Z0``'@$``!(``0`5"```W&0```8````2``$`(`@`````````````$```
+M`"\(``"0D@``,P```!(``0`Z"```_/$``!0````2``$`10@``.3Q```6````
+M$@`!`%`(``!HVP``.````!(``0!;"```P&X``&X````2``$`9@@``(B5``!T
+M````$@`!`'$(```\Z0``%P```!(``0!\"```--\``*(````2``$`AP@``&SL
+M```7````$@`!`)((``#8DP``)@```!(``0"="```5/0``%8````2``$`J`@`
+M`"`R`0"%`0``$@`!`+,(``!LC@``N`$``!(``0"^"````&8``!<````2``$`
+MR0@``,"0``!`````$@`!`-0(``"L=```'P```!(``0#?"```?/,``%<````2
+M``$`Z@@``&22```L````$@`!`/4(``!8DP``@````!(``0``"0``R%\``"8`
+M```2``$`"PD``&`C`0`?````$@`!`!8)`````````````!`````E"0``:%P`
+M`"$````2``$`,`D``*#E``!9````$@`!`#L)```@"```I`$``!$`!P!&"0``
+M=)P```X````2``$`40D``/R+``!@````$@`!`%P)``#D````,P```!(``0!J
+M"0``)%T``!(````2``$`=0D``*`$``"D`0``$0`'`(`)```<G```5P```!(`
+M`0"+"0``7(P```L````2``$`E at D```P````<````$0`'`*@)``#,7@``'```
+M`!(``0"S"0``Y-\``#0````2``$`O at D``%AD``!X````$@`!`,D)```P-@$`
+M#`(``!(``0#4"0``T&0```L````2``$`WPD```CS```M````$@`!`.H)``"8
+M20``F0```!(``0#U"0``2",!`!8````2``$```H``,2;``!7````$@`!``L*
+M``"4E@``(P```!(``0`6"@``W/(``"D````2``$`(0H``"S,```>````$@`!
+M`"P*```<'0$`2````!(``0`W"@``_)4``"`````2``$`0 at H``$"!```&````
+M$@`!`$T*``#$"`$`L0```!(``0!8"@``:(P``*(````2``$`8PH``)3-``!A
+M````$@`!`&X*``#@"0``I`$``!$`!P!Y"@``Z&4``!<````2``$`A`H``+PI
+M`0`*````$@`!`(\*``!H6```7````!(``0":"@``*&4``$,````2``$`I0H`
+M`,3'```M!```$@`!`+`*``#D8@``2@```!(``0"["@``L)H```8````2``$`
+MQ at H``*#;```X`0``$@`!`-$*``"$G```$@```!(``0#<"@``L$L``#<````2
+M``$`YPH``.3=``!B````$@`!`/(*``"8"@``;P```!(``0#]"@``J)H```8`
+M```2``$`"`L``!`'``!+`0``$@`!`!,+```TW0``*@```!(``0`>"P``!&(`
+M`'@````2``$`*0L``&QE```+````$@`!`#0+``#DE```*````!(``0`_"P``
+M(!8!`'8````2``$`2 at L``)Q=```L````$@`!`%4+``!X"0$`-````!(``0!@
+M"P``$`0``%L````2``$`:PL``&#E``!`````$@`!`'8+`````````````!``
+M``"&"P``S+,``(0!```2``$`D0L``%!8```6````$@`!`)P+``!D)@$`8@``
+M`!(``0"G"P``_%T``"0````2``$`L at L``!#A``",````$@`!`+T+``"@E```
+M1````!(``0#("P``_,P``(X````2``$`TPL`````````````$````/,+``"$
+M!@``B@```!(``0#^"P``C%P``$T````2``$`"0P``-Q<``!`````$@`!`!0,
+M````D0``+P```!(``0`?#```G%D``)L````2``$`*@P``#"3```G````$@`!
+M`#4,``"XUP``H0(``!(``0!`#```\)8``!4````2``$`2PP``.3T```1`0``
+M$@`!`%8,``!,7```'````!(``0!A#``````````````0````;PP``"20```<
+M````$@`!`'H,``#\"```4P```!(``0"%#```L&(``#(````2``$`D`P``*2;
+M```?````$@`!`)L,``"PF```+````!(``0"F#```O&\``,@````2``$`L0P`
+M`!AF```?````$@`!`+P,``#H7@``-````!(``0#'#```(.4``!P````2``$`
+MT at P``,SR```/````$@`!`-T,``#$D@``)````!(``0#H#```P-T``"0````2
+M``$`\PP``+@$`0`+!```$@`!`/X,``#`8```+@```!(``0`)#0``\%\```X`
+M```2``$`%`T``,":``!;````$@`!`!\-```\)`$`+0$``!(``0`J#0``$)0`
+M`!D````2``$`-0T``$B!``#`````$@`!`$`-``!DD```60```!(``0!+#0``
+MQ)8```$````2``$`5 at T``,1[``"[````$@`!`&$-``"`$P``I`$``!$`!P!L
+M#0``/%<``#@````2``$`=PT``-!8``".````$@`!`((-``",W0``,P```!(`
+M`0"-#0``G"@!`!L````2``$`F`T``+3J```&````$@`!`*,-``!HE```.```
+M`!(``0"N#0``N)H```8````2``$`N0T``&`5`0#`````$@`!`,0-```0F@``
+MC````!(``0#/#0``R)T``"L)```2``$`V at T``$`5``"D`0``$0`'`.4-``!D
+M70``-0```!(``0#P#0``K`4``%T````2``$`^PT``$`G`0!:`0``$@`!``8.
+M``#<'@$`=@```!(``0`1#@``Y&0``!X````2``$`'`X``(AE```/````$@`!
+M`"<.``!HE0``(````!(``0`R#@``L/(```L````2``$`/0X``)AE``!-````
+M$@`!`$@.``#D80``(````!(``0!3#@``Y(<``!`````2``$`7 at X``%`)``!'
+M`0``$@`!`&D.``"TS@```0```!(``0!T#@``0'\``$H````2``$`?PX``'2;
+M```7````$@`!`(H.``"@8@``!@```!(``0"5#@``6,<``&L````2``$`H`X`
+M`%SS```?````$@`!`*L.````1P$`H@```!(``0"V#@``?&(``",````2``$`
+MP0X``%A/``!`````$@`!`,P.``"T10``X@,``!(``0#B#@``+/0``"<````2
+M``$`[0X``#AF``!7````$@`!`/@.``#X]0``7@@``!(``0`##P``8-X``,8`
+M```2``$`#@\``$S,```]````$@`!`!D/``",FP``%P```!(``0`D#P``C'\`
+M`'T````2``$`+P\``&@X``!\````$@`!`#H/``!$;P``=P```!(``0!%#P``
+M2)L``"D````2``$`4`\``/2F```2````$@`!`%L/```\5@``E@```!(``0!F
+M#P``#(,```$````2``$`<0\``/!@``!@````$@`!`'P/``"8P0``/P```!(`
+M`0"'#P``</$``'0````2``$`D@\``/#H``!$````$@`!`)T/```LE```.P``
+M`!(``0"H#P``:(,``'H$```2``$`LP\``'!W``"Y`@``$@`!`+X/``!\Z0``
+M%0```!(``0#)#P``],L``!8````2``$`U`\``'1<`0"J````$@`!`-\/``"P
+M3```D````!(``0#J#P``?.@``#L````2``$`]0\``-C<``!9````$@`!```0
+M```@_P``C@$``!(``0`+$```@",!`!X````2``$`%A```%S:``"^````$@`!
+M`"$0`````````````!`````P$```'%T```8````2``$`.Q```'R````S````
+M$@`!`$80``#0YP``#@```!(``0!1$```7*<``+@!```2``$`7!```-!.``"&
+M````$@`!`'40``"@(P$`/0```!(``0``>GHP,#5B-3 at T-@!Z>C`P,F1B,3%C
+M`'IZ,#)D8F8X-S4`>GHP,F1B8C%B,@!Z>C`P,F1B-#8P`'IZ,&(W8C at S-C@`
+M>GHP-6(X8S8U80!Z>C`P-6(T,&%D`'IZ,#5B.30R960`>GHP9#DT8V0P-`!Z
+M>C`P,3EF-#<P`'IZ,#)D8F4W,V0`>GHP,#5B-CDR8@!Z>C!B-F0R,S(U`'IZ
+M,#`U8C at U,V0`>GHP,#$V9#DR90!Z>C!C9F(Q.#<U`'IZ,#`R9&(S,6,`>GHP
+M,F1A8F)A8P!Z>C`P,&(V8S8X`'IZ,#`P8C9B9C@`>GHP,F1B-6)F,`!Z>C`Q
+M-F4Q9&9D`'IZ,&(W,&4Y.&,`>GHP-F4P9C9C,@!Z>C`P-C=D,F(Q`'IZ,&(V
+M8V$S,C4`>GHP,#5B-F-F9`!Z>C`Q-F4P9&9D`'IZ,#`P8C9F-C@`>GHP,#5B
+M-#AA9`!Z>C`R9&)F,6(R`'IZ,#`U8C at X8V(`>GHP,S-E8SAB9@!Z>C!D8C1C
+M.31D`'IZ,#)D8C-B86,`871H7VAA;%]D;6%?8F5A8V]N7W)E<W!O;G-E7W1I
+M;64`>GHP,&(V9C$V,0!Z>C!D8S4X-S$X`'IZ,&1B,#DQ,&4`>GHP,&(V93(W
+M80!Z>C`R9&)B86(R`'IZ,&(V9#%E9F8`>GHP,#5B-C1F9`!Z>C`P,&(V9#4R
+M`'IZ,&(V8SEE9F8`>GHP,F1B9#@W-0!Z>C`R9&(S,6(R`'IZ,#!B-S`Y,C,`
+M>GHP,#!B-F0Q9`!Z>C`P8C9D-#4P`'IZ,&(V8S(S,C4`>GHP,F1B9&%B,@!Z
+M>C`P,#5B-C,R`'IZ,#9D9C)F-3<`>GHP,#$V9&$U,`!Z>C`P,#8W9#,R`'IZ
+M,#!B-F0Y,C,`>GHP,#)D8C4Q8P!Z>C`Q-F1A-V8P`'IZ,#$V9&-B9F4`>GHP
+M,#$V9&5C,0!Z>C`P,39D9C)C`'IZ,&(X,30V,3(`>GHP,39D.3DR,`!Z>C`R
+M9&(Q8F%C`'IZ,&1C-S at W,3@`>GHP-6(W,#%E,`!Z>C`P,&(V8C$P`'IZ,&(W
+M,#EE9F8`>GHP,39D9C at Y8@!A=&A?:&%L7V%T=&%C:`!Z>C`V93$T9&0Q`'IZ
+M,#$V9&%B,F0`>GHP,F1B,3,U9`!Z>C!E,&(V9C(S`'IZ,#$V93(Y,60`>GHP
+M,#!B-F9E.`!Z>C`P,39D.30X`'IZ,#$V93(P.6(`>GHP8C<P,65F9@!Z>C`P
+M,&(V8SED`'IZ,#`P-6(U83(`>GHP,F1B,3<P80!A=&A?:&%L7V-O;7!U=&5T
+M>'1I;64`>GHP,F1B-6)A8P!Z>C`P-6(X,3-D`'IZ,#!B-F,Y,C,`>GHP8C9C
+M93DX8P!Z>C`P-6(W9CED`'IZ,&(W,#%D,#(`>GHP-F4P-#8P90!Z>C`R9&(U
+M-S8S`'IZ,#9F,C,R8V,`>GHP93`W.#,W.0!Z>C`P,F1A9C8R`'IZ,&(X-30V
+M,3(`>GHP,#!B-F(Y,`!Z>C`Q-F0Y969A`'IZ,#`R9&(R-C``>GHP,39D93`U
+M,0!Z>C`P,#5B-F(R`'IZ,#5B-V8Q,30`>GHP,S0W-#9B-`!Z>C`R9&)D9&9D
+M`'IZ,#`X968S,#D`>GHP,&(W,3<W9`!Z>C`P8C<P9C-D`'IZ,#$V9#@Y,C``
+M>GHP,F1B,6%B,@!Z>C`P,#5B-C(R`'IZ,#`P8C9F-S@`>GHP8C9B-#DU,`!Z
+M>C`P-6(W-C,P`&%T:%]H86Q?9G)E90!Z>C`U8C<Y8C%B`'IZ,#!B-S`W,V0`
+M>GHP8C5F8V$R-@!Z>C`Q-F1F8F8Q`&%T:%]H86Q?;6AZ,FEE964`>GHP,39E
+M,3 at Y8@!Z>C!D8S,T.31D`'IZ,#`R9&,V,&(`>GHP,F1B,S,U9`!Z>C!B-F,U
+M,V$T`'IZ,&8X968S,#@`>GHP9&$T8V0P-`!Z>C`U8C<T,64P`'IZ,#)D83EB
+M86,`>GHP,39E8SEA9`!Z>C`P8C<Q-S-D`'IZ,#$V9#ED-#$`>GHP9&,Q,F,Q
+M9@!Z>C`P-6(X-&9D`'IZ,#`U8CAA8F0`>GHP,&(W,#$R,P!Z>C`P-6(V-3)B
+M`'IZ,#)D8C,X-S4`>GHP,39E,S`Y8@!Z>C`P-6(V,&(R`'IZ,#)D8C$X-S4`
+M>GHP,F1B,3<V,P!Z>C!B-V8X,S8X`'IZ,&1C,S)C,68`>GHP,&(W,#$V,0!Z
+M>C`Q-F0W9#0Q`'IZ,#`Q-F-D,S(`>GHP9&(T.3$P90!Z>C`P-6(T.&5D`'IZ
+M,#`R9&,X,&(`>GHP,F1B,S-E9`!Z>C`P-6(X,&9D`'IZ,#)D8F9D9F0`>GHP
+M,39E-C0X80!Z>C!D8F8R8S%F`'IZ,#`R9&%F9C(`>GHP,#5B-S<Y9`!Z>C`P
+M,&(V964X`'IZ,#$V930T.&$`>GHP,&(V9C9B8P!Z>C`Q-F1A.3(P`'IZ,#5B
+M-S at Q93``>GHP93(W,V$T9`!Z>C`P,F1C83!B`'IZ,#)D8C%B9C``>GHP,#5B
+M-#1A9`!Z>C`V9&4T-C!E`'IZ,&(V9CEE9F8`>GHP,&(V8S9B8P!A=&A?:&%L
+M7V%D9&ET:6]N86Q?<W=B85]B86-K;V9F`'IZ,#`P,F1B.3(`871H7VAA;%]V
+M97)S:6]N`&%T:%]H86Q?:65E93)M:'H`>GHP,F1B-SEA90!Z>C!B-F-F,#`Y
+M`'IZ,#`Q-F4Q,F,`>GHP,#$V9#EE-`!Z>C`P-6(U8V(R`'IZ,#$V9&$R-3$`
+M>GHP-F9A,S)C8P!Z>C`V93`R9C4W`'IZ,#`U8C9E,S``871H7VAA;%]M86QL
+M;V,`>GHP,#!B-F1D,@!Z>C`U8C9C8F0U`'IZ,#$V9&(R-3$`>GHP93$W.#,W
+M.0!Z>C`Q-F1D,#4Q`'IZ,#)D8F0Q8C(`>GHP9&$Y8S1F-@!Z>C`R9&,Q-S8S
+M`'IZ,#$V9&8P-3$`>GHP,#$V9#DU,`!Z>C`P8C<P-F)C`'IZ,#`Q-F0X-S(`
+M>GHP-F1F-#8P90!Z>C`U8CAC,F5D`'IZ,#9F9C$W93<`>GHP8C9F.60P,@!Z
+M>C!D-&5F,S`X`'IZ,#$V9&)B9F4`>GHP,39E-30X80!Z>C`P,39D.#4P`'IZ
+M,#`P8C9F9C@`871H7VAA;%]M96UC<'D`>GHP,F1B8F1F9`!Z>C`V9&(U96(V
+M`'IZ,#$V96(Y860`>GHP,&(V8V5B8P!Z>C!D.6(X8C<Y`&%T:%]H86Q?<')O
+M8F4`>GHP,#)D8C`V,`!Z>C`Q-F5A.6%D`'IZ,#`P968S,#D`>GHP,F1B,S<V
+M,P!A=&A?:&%L7V)U:6QD;W!T<P!Z>C`R9&%F,S5D`'IZ,&1B-&-D,#0`>GHP
+M,39D9C(U,0!Z>C`P,&(V8S$P`'IZ,#$V9#DR-3$`>GHP8C<R,6%E9`!Z>C`V
+M-V-F8V%E`'IZ,#`P8C9C9C@`>GHP8V-E9C,P.`!Z>C`R9&%F86(R`'IZ,#5B
+M.3)A960`>GHP,#5B.&-C8@!Z>C!B-F0Y969F`'IZ,#)D8C$Q8C(`>GHP93$W
+M,V$T9`!Z>C`R9&)F9F(Q`'IZ,#)D8F8W-C,`>GHP,#5B-CAF9`!Z>C`P-6(T
+M-&5D`'IZ,&(W,3%A960`>GHP8C5F8V$S-`!Z>C!D.&(X8C<Y`'IZ,#$V9#AB
+M,F0`>GHP,39E,#@Y8@!Z>C`R9&)B.#<U`'IZ,#$V9#DW9C``>GHP8C at S.#,V
+M.`!Z>C`P8C9F96)C`'IZ,&4Y.3DS,3,`>GHP,&(V96$W80!Z>C`S,V4X9F)F
+M`'IZ,#`U8C<R,S``>GHP8V9A-3 at W-0!Z>C`R9&(Y.6%E`'IZ,#)D8CEA8C(`
+M>GHP,#!B-F)E.`!Z>C!D.3EC-&8V`'IZ,&1E9#AA-3<`>GHP,39D86)F90!Z
+M>C!B-S`Y9#`R`'IZ,#$Y9C-A,#$`>GHP-S`W,3=E-P!A=&A?:&%L7VUE;7IE
+M<F\`>GHP-F4P-&1D,0!Z>C!B-CAE.3AC`'IZ,#$V93)D9F0`>GHP,#!B-F0R
+M,@!Z>C`W,#(S,F-C`'IZ,&1C-30Y-&0`>GHP,&(W,&8W9`!A=&A?:&%L7W-W
+M7V)E86-O;E]R97-P;VYS95]T:6UE`'IZ,#EF-CED,F4`>GHP,#5B-C4W80!Z
+M>C`V9C<Q-V4W`'IZ,#9D835E8C8`>GHP8C=D-#8Q,@!Z>C`P8C9F.38Q`'IZ
+M,&(V8C!C.3``>GHP,F1B9#,U9`!Z>C`P-6(X.&9D`'IZ,#)D869D9F0`871H
+M7VAA;%]D96QA>0!Z>C`R9&(Q9&9D`'IZ,#8W9#)F-#$`>GHP,F1A9C at W-0!Z
+M>C!B.#`W-S0Q`'IZ,#$V9#AD-#$`>GHP,#)D864Y,@!Z>C!B-V,W-S0Q`'IZ
+M,#)D8F(S-60`>GHP,F1B,V1F9`!Z>C`R9&(U,V5D`'IZ,#`P8C9D83(`>GHP
+M,#!B-F4R,@!Z>C`U8C at P,#0Q`'IZ,&0X.6,T9C8`>GHP,#$V9#@T.`!Z>C`Q
+M-F0Y8C)D`'IZ,&4S-S-A-&0`>GHP,#$V93`R8P!Z>C`P8C<P-S=D`'IZ,#`U
+M8C8Y-V$`>GHP,#$V9&%E-`!Z>C`Q-F4Q,#EB`'IZ,#`U8C1A,F0`>GHP9&8W
+M.#,W.0!Z>C`R9&)D-S8S`'IZ,#`P8C9E-3(`>GHP-6(V8S9D8@!Z>C!C835F
+M,S(Y`'IZ,&1B,CDQ,&4`>GHP,F1B,V)F,`!Z>C`V9#<X831C`'IZ,#$V93`R
+M-3$`>GHP,#`R9&)B,@!Z>C`P,F1D.#9D`'IZ,#)D8C4Y864`>GHP,#8W9#(R
+M,0!Z>C`P-6(X.3-D`'IZ,#5B.#%B,6(`>GHP,39D.#=F,`!Z>C`R9&(Q,V5D
+M`'IZ,#!B-F0Q,C,`>GHP,#!B-F-E.`!Z>C`U8CAA865D`'IZ,#)D868Q8C(`
+M>GHP,#`U8C5E,@!Z>C`S,V5B9F)F`'IZ,#5B-C at V9&(`>GHP,#!B-F5F.`!Z
+M>C!B-S$Y865D`'IZ,#`U8C4X8C(`>GHP-6(W9&(Q8@!Z>C!B.#0W-S0Q`'IZ
+M,#`Q-F0W,S(`>GHP,F1A9&%B,@!Z>C!F-&1E,S`W`&%T:%]H86Q?:6YI=%]C
+M:&%N;F5L<P!Z>C`P8C9D-F)C`'IZ,&,T968S,#@`>GHP,#`R9&)D,@!Z>C!D
+M86(X8C<Y`'IZ,#$V93!B.#<`>GHP-6(Y,#)E9`!Z>C!D8S,X-S$X`'IZ,&%A
+M,F5E8S$`>GHP,#!B-F,Q9`!Z>C`U8CAE865D`'IZ,#`U8C=B.60`>GHP,#5B
+M-30T-@!Z>C`U8C8T-F1B`'IZ,#!B-F8Y,C,`>GHP9&1D.&$U-P!Z>C`Q-F4Q
+M,C4Q`'IZ,&1C-S0Y-&0`>GHP,F1B,S<P80!Z>C`P,#5B-C<R`'IZ,#9D9C1D
+M9#$`>GHP,F1B9C,U9`!Z>C`P,&(V8S<X`'IZ,#`Q-F0X,S(`>GHP,V4Y-S,U
+M,P!Z>C`R9&(U-S!A`'IZ,#`U8C9D-V$`>GHP,#)D8C(Y,@!Z>C`P-6(Y,&-B
+M`'IZ,#`U8C5C-#8`871H7VAA;%]P<FEN=&8`>GHP-F0Y-65B-@!Z>C`Q-F1F
+M8C at W`'IZ,#`Q-F1A-#@`>GHP,#)D8C`Y,@!A=&A?:&%L7V=E='=I<F5L97-S
+M;6]D97,`>GHP,39E,6(X-P!Y`````0,``)$````!`P``L`````$#``"_````
+M`0,``,4````!`P``S0````$#``#5`````0,``,T!```";0``Y`$```)H`0#\
+M`0```CT``)X"```"'0$`X`8```)<``#V!@```EP``#0'```!`@``'PL```$"
+M```R"P```0(``-,.```"O@``FA,```(*`0#@'P```@H!`.$J```!`@``\RH`
+M``$"```..0```0(``#XY```!`@``F3D```$"``#..0```0(``#XZ```!`@``
+M8#H```$"``!R.@```0(``.HZ```!`@``]#H```$"```*.P```0(``!0[```!
+M`@``.3L```$"``!!.P```0(``'$\```!`@``E#P```$"```:/0```0(``&L]
+M```!`@``ACT```$"```=/@```0(``"4^```!`@``F3X```$"``"G/@```0(`
+M`*\^```!`@``V#X```$"```9/P```0(``"$_```!`@``?3\```$"``#1/P``
+M`0(``.$_```!`@``#4````$"```40````0(``&5````!`@``>D````$"``"#
+M0````0(``)%````!`@``M$````$"``"]0````0(``#U!```!!0``7D$```$%
+M``!F00```04``!E"```!`@``(D(```$"``!?0@```0(``(-"```!`@``W4(`
+M``$"``#T0@```0(```Y#```!`@``148```)X`0"Z1@```0(``,!&```!`@``
+M#4D```$"```<20```0$``*U*```!`@``:TP```*T``#D3````04``.U,```!
+M!0``,4T```$%``#`30```0(``'Q/```!`@``'U````*^``!`4````0(``$90
+M```"U```Q%````(C`0!"40```BD``'!1```"%`$`HE$```(4`0#140```A0!
+M`"%2```"%`$`75(```(4`0`L50```A0!`*95```">@``V%4```)Z``!C5@``
+M`2P``'=6```!$@$`^E8```(=`0`Z7````F4``&!<```"U```?%P```+4``"M
+M7````D```$1=```"#@$`6%T```)J``![7@```C8!`#=?```"U```J%\```)$
+M`0"Y7P```JP```QA```"(0``+V$```)!`0!H80```B$``()A```"(0``N&$`
+M``)!`0#\80```B$``!9B```"(0``3&(```)!`0"78@```B$``-=B```"``$`
+M(V,```(8``!,8P```A\!`+9C```"'0$`W6,```(=`0`V90```=P``$5E```!
+M#P``764```(3`0#*90```AT!`/5H```"(P$`@6L```*G```W;````0(``&QL
+M```":0$`CFT```(=`0"R;0```D```+]M```"B```Z&T```).```';@```G0`
+M`#IN```"8P$`66X```(+``!X;@```@(!`%1O```"X@``B6\```(=`0"E;P``
+M`AT!`-%O```"X@``)'````(=`0!-<````AT!`%EP```"X@``O7````(=`0#=
+M<````AT!``=Q```"'0$`>'$```(=`0"8<0```AT!`,5Q```"'0$`Z7$```)`
+M```5<@```H@``"ER```"'0$`-G(```*(``"A<@```AT!`+1R```"0```+W8`
+M``$"``"`=P```0(``(IW```"U```E'<```$"``">=P```M0``/MW```"*0``
+M(W@```(I``"G>0```BD``,-Y```"*0``W'D```(I```]>@```N8``'!Z```"
+M*0``[GH```)````->P```AT!`!-[```"20``>7L```)+`0"U>P```OX``"]\
+M```""@$`;GP```*O``"7?P```I4``*A_```"-@$`VG\```(V`0#W?P```C8!
+M`/F````"'0$`*8$```)```!?@P```BD``'F#```"O@``FH,```$"``"@@P``
+M`M0``!6$```"=P$`5X0```(=`0#NA````AT!`"F%```",0$`A(4```(Q`0#!
+MA0```C$!`"2&```!`@``:88```)=`0"FA@```AT!`,Z&```"'0$`"8<```(Q
+M`0`]AP```F0!`&"'```",0$`P8<```)*`0#LAP```GH``,6(```"'0$`58D`
+M``+-```GB@```2P``#N*```!$@$`CXH```):`0"@B@```AT!`!60```"NP``
+M.)````+4``!4D````M0``)&0```"0```3I$```(Q`0#(D0```B@!`-R1```"
+M,```ZY$```+0```,D@```B@!`"Z2```"OP``*),```+P``!,DP```0(``'.3
+M```"U```!)4```+P```HE0```B$``$>5```"00$`@)4```(A``":E0```B$`
+M`-"5```"00$`%)8```(A```NE@```B$``&26```"00$`KY8```(A``!7F```
+M`@`!`*.8```"&```S)@```(?`0`[F0```AT!`&&9```"'0$`Y9H```&M``#M
+MF@```?(``/6:```!UP``#9L```(3`0!AFP```D```$B>```"Y@``29\```)W
+M`0!BH````BD``'*@```"*0``@*````$%``":H````04``,6@```!!0``UZ``
+M``$%```"H0```0(``!6A```!`@``+:$```$"``!!H0```0(``%FA```!`@``
+M7J$```$"```9I````@L!`-.D```"'0$`]Z0```)````'I0```AH``(2E```"
+M]P``(:8```)K```QI@```D@``&2F```"CP``@Z8```+$``"BI@```C$``!BG
+M```"/0$`2*<```(=`0!QIP```CT!`!*H```"'0$`4:@```(=`0"KJ````AT!
+M`."H```"'0$`[*@```(]`0#:J0```AH``.ZI```"'0$`^*D```(:``!>J@``
+M`AT!`'&J```"0```JJH```)_``#"J@```0(``,^J```"*0``X*H```$"```.
+MJP```BD``#BK```"*0``2JP```(=`0!GK````BL``%"M```"5`$`@*T```$"
+M```CKP```04``$2O```!!0``8:\```$%```IL````04``#VP```"*0``6;``
+M``$%``!JL````BD``(>P```"*0``F;````$%``"JL````BD``,>P```"*0``
+MU[````$%``#YL````04``!&Q```!!0``$[(```(^``#JM````A(``&^U```!
+M!@``=+4```(*`0"!M0```08``(:U```""@$`B[4```$&``"CM0```08``+*U
+M```!!@``N;4```$&``#"M0```08``-"U```!!@``Z+4```$&``#_M0```08`
+M``RV```!!@``';8```$&``!!M@```08``&2V```!!@``<K8```$&``!\M@``
+M`08``.VV```!!@``+K<```$&``"RMP```08``,"W```!!@``YK<```$&```)
+MN````08``"&X```!!@``)K@```$&``!/N````08``%2X```!!@``TK@```$&
+M``#?N````FT!`">Y```"_```7[L```$"``";O````0(``(N]```"8@``I,$`
+M``$"``"ZP0```0(``%O#```!`@``GL,```$"``"QPP```0(``-K#```!`@``
+M],,```$"```6Q````0(``"G$```!`@```,4```+>```1Q0```O```$[%```"
+M\```>L4```+P``"YQ0```DL!`/7%```"_@``B\8```(*`0#2Q@```E@``&O*
+M```!$@$`<<H```$L``"!R@```;$``+/,```"6@$`Q,P```(=`0`'SP```BD`
+M`"7/```!`@``-L\```$"``!#SP```0(``%#/```!`@``J\\```*^``#5SP``
+M`0(``-O/```"U```K]````)%``#/T````F4!`._0```"<`$`:=$```(7``"3
+MT0```CH``#_2```";P$`IM(```)O`0#6T@```F\!`!O3```";P$`:M,```)O
+M`0#5TP```0(``-K3```!`@``)=0```)=`0!NU````AT!`';4```".@``EM0`
+M``(=`0#+U````F\!`/O4```"&0$`)M4```*:``!+U0```FP!`%[5```"4@$`
+M<M4```(%`0"8U0```F\!`/'5```"HP``&=8```)V``!"U@```F<``&C6```"
+M90$`<-8```)Z``!-UP```AT!`+/:```!+```Q]H```$2`0`SVP```GD!`$3;
+M```"'0$`!=T```)````\W@```L8``.+A```!`@``NN,```(/`0`&Y0```@\!
+M`#3E```"U```4.4```+4```(YP```0(``"_G```"U```V.H```(A``#[Z@``
+M`D$!`#3K```"(0``3NL```(A``"$ZP```D$!`,CK```"(0``XNL```(A```8
+M[````D$!`&/L```"(0``>^P```(@``"W[````A,``.OM```"$P``S.X```$"
+M``"$[P```@`!`#_P```"&```:/````(?`0";\````AT!`,'P```"'0$`:?(`
+M``$F``!Q\@```9P``'CR```!-`$`@?(```&)``"(\@```3\!`*'R```"$P$`
+M]?(```)````D\P```BH``"SS```"R@``4_,```)X``!W]0```H0``%;V```"
+M90$`<_8```+F```S]P```E0``#OW```"0@``2/<```)P`0!%^````0(``%?X
+M```!`@``<?@```(=`0"'^````0(``*+X```!`@``N/@```(=`0#F^````E,`
+M``KZ```"4```6/H```+O``!H^@```BH!``O[```"(```//P```(=`0``_0``
+M`H```%[]```"#```;OT```*H``"`_0```D```)K]```"C@``S?T```(X``#L
+M_0```B<```O^```"/```^/X```)E`0!/_P```F4!`&<``0`"'0$`A@`!``(=
+M`0#%``$``N8``,X"`0`"'0$`X0(!``)````$`P$``F4!`,X#`0`"70``I`0!
+M``(I``#G!0$``KP``*0)`0`"4```R at D!``(*`0#;"0$``@H!``X*`0`!`@``
+MRPP!``)M`0#W#`$``A(``(P.`0`"_```CA$!``+4``#_$@$``CP!`#T4`0`"
+M/`$`/!8!``$"``!,%@$``0(``&T6`0`!`@``>18!``$"``#S&`$``0(``/T8
+M`0`!`@``@1H!``(I```\&P$``E$``'4;`0`"7```HQL!``)1``#I&P$``EP`
+M`"0<`0`"7```91P!``+&``#`'`$``L8```@=`0`"2P$`4!T!``+^```+'@$`
+M`@H!`%(>`0`"30``UR$!``$2`0#=(0$``2P``.LA`0`!L0``!R0!``)Y`0`8
+M)`$``AT!`(TD`0`"Q@``4B4!``+&``#)*`$``F8!`"LI`0`"9@$``RH!``(*
+M`0"Z*@$``BH``/(J`0`">```5BL!``$"``#Q+@$``0(``!$P`0`"9@$`:3`!
+M``*3``"(,`$``I,``,8P`0`"DP``T#`!``*3``#P,`$``I,```HQ`0`"DP``
+M(3$!``*3```P,0$``@@!`$LQ`0`"DP``7C$!``*3``"I,0$``I,``.HQ`0`"
+MDP``^3$!``((`0`4,@$``I,``&DR`0`"DP``<S(!``*3``!],@$``I,``(HR
+M`0`"DP``E#(!``*3``"=,@$``E8!`*HR`0`"R0``SS(!``*3``#G,@$``I,`
+M``$S`0`"DP``%S,!``*3```O,P$``I,``$8S`0`"5@$`4S,!``+)``"-,P$`
+M`E8!`)HS`0`"R0``VS,!``)F`0!#-0$``I,``%PU`0`"DP``>#4!``*3``"4
+M-0$``I,``+`U`0`"DP``I#8!``)F`0#1-@$``0(``-8V`0`!!```W#8!``)S
+M`0`--P$``0(``!(W`0`!!```&#<!``)S`0!=.`$``0(``&\X`0`!`@``AC@!
+M``(=`0"B.`$``0(``*DX`0`!`@``O#@!``(=`0#9.`$``0(``.(X`0`!`@``
+M]3@!``(=`0`B.0$``G\``#HY`0`!`@``1SD!``(I``!8.0$``0(``+PY`0`"
+M*0``ZCD!``(I``!R.@$``0(``/@\`0`!`@``+ST!``)N``!"/0$``FX``%D]
+M`0`!`@``<ST!``(=`0"-/0$``0(``)0]`0`!`@``JCT!``(=`0#+/0$``0(`
+M`-T]`0`!`@``]ST!``(=`0`8/@$``0(``"H^`0`!`@``1#X!``(=`0!9/@$`
+M`0(``*$^`0`";@``N#X!``)N``#4/@$``FX``.D^`0`";@``_CX!``)N```3
+M/P$``FX``"D_`0`!`@``0S\!``(=`0!=/P$``0(``(P_`0`";@``H3\!``)N
+M``"Y/P$``0(``-,_`0`"'0$`D4$!``(*`0"Z1`$``CP!`)I&`0`"/`$`PT8!
+M``)Z``#G1@$``GH``!-'`0`"O@``/T<!``*^``!>1P$``0$``&A'`0`!`0``
+M<D<!``$!``!\1P$``0$``(9'`0`!`0``D$<!``$!``#%1P$``0(``-='`0`!
+M`@``[D<!``(=`0`*2`$``0(``!%(`0`!`@``)$@!``(=`0!!2`$``0(``$I(
+M`0`!`@``74@!``(=`0`J20$``BD``*A)`0`"*0``MDD!``(I``#\20$``BD`
+M``I*`0`"*0``4DH!``(I``!@2@$``BD``,Y*`0`!`@``$DT!``$"```J30$`
+M`0(``$E-`0`!`@``:$T!``$"``"630$``FX``+!-`0`";@``RTT!``)N``#Z
+M30$``FX``!A.`0`";@``-DX!``)N``!43@$``FX``').`0`";@``D$X!``)N
+M``"X3@$``FX``-!.`0`";@``\$X!``)N```(3P$``FX``$1/`0`";@``54\!
+M``)N``!F3P$``FX``'I/`0`";@``GD\!``)N``"O3P$``FX``,!/`0`";@``
+MU$\!``)N``#H3P$``FX``/I/`0`!`@``/%`!``)N``!:4`$``FX``&Q0`0`!
+M`@``A5`!``(=`0"=4`$``0(``+=0`0`"'0$`SU`!``$"``#I4`$``AT!``%1
+M`0`!`@``&U$!``(=`0`Y40$``0(``%91`0`"'0$`BE,!``+4``"?4P$``M0`
+M`/)3`0`"U```)50!``+4```Z5`$``M0``(U4`0`"U```N%0!``+4``#-5`$`
+M`M0``"!5`0`"U```-UP!``)Z``!;7`$``GH``(=<`0`"O@``LUP!``*^``#<
+M7`$``0$``.9<`0`!`0``\%P!``$!``#Z7`$``0$```1=`0`!`0``#ET!``$!
+M``!!70$``0(``%-=`0`!`@``;5T!``(=`0"&70$``0(``(U=`0`!`@``HUT!
+M``(=`0#%70$``0(``,Y=`0`!`@``Y%T!``(=`0"R7@$``BD``#!?`0`"*0``
+M/E\!``(I``"$7P$``BD``))?`0`"*0``VE\!``(I``#H7P$``BD``.)@`0`!
+M`@``^F`!``$"```980$``0(``#AA`0`!`@``9F$!``)N``!^80$``FX``)!A
+M`0`!`@``LF$!``$"``#+80$``AT!`.-A`0`!`@``_6$!``(=`0`58@$``0(`
+M`"]B`0`"'0$`26(!``$"``!C8@$``AT!`(!B`0`!`@``FF(!``(=`0#>8@$`
+M`0(``(-K`0`">@``IVL!``)Z``#3:P$``KX``/]K`0`"O@``'FP!``$!```H
+M;`$``0$``#)L`0`!`0``/&P!``$!``!&;`$``0$``%!L`0`!`0````````$!
+M```$`````0$```@````!`0``#`````$!```0`````0$``!0````!`0``&```
+M``$!```<`````0$``"`````!`0``)`````$!```H`````0$``"P````!`0``
+M,`````$!```T`````0$``#@````!`0``/`````$!``#D`````0,``.@````!
+M`P``_`````$#`````0```0,``!0!```!`P``&`$```$#```L`0```0,``#`!
+M```!`P``1`$```$#``!(`0```0,``%P!```!`P``8`$```$#``!T`0```0,`
+M`'@!```!`P``C`$```$#``"0`0```0,``*0!```!`P``J`$```$#``"\`0``
+M`0,``,`!```!`P``U`$```$#``#8`0```0,``.P!```!`P``\`$```$#```$
+M`@```0,```@"```!`P``'`(```$#```@`@```0,``#0"```!`P``.`(```$#
+M``!,`@```0,``%`"```!`P``9`(```$#``!H`@```0,``'P"```!`P``@`(`
+M``$#``"4`@```0,``)@"```!`P``K`(```$#``"P`@```0,``,0"```!`P``
+MR`(```$#``#<`@```0,``.`"```!`P``]`(```$#``#X`@```0,```P#```!
+M`P``$`,```$#```D`P```0,``"@#```!`P``/`,```$#``!``P```0,``%0#
+M```!`P``6`,```$#``!L`P```0,``'`#```!`P``A`,```$#``"(`P```0,`
+M`)P#```!`P``H`,```$#``"T`P```0,``+@#```!`P``S`,```$#``#0`P``
+M`0,``.0#```!`P``Z`,```$#``#\`P```0,````$```!`P``%`0```$#```8
+M!````0,``"P$```!`P``,`0```$#``!$!````0,``$@$```!`P``7`0```$#
+M``!@!````0,``'0$```!`P``>`0```$#``",!````0,``)`$```!`P``I`0`
+M``$#``"H!````0,``+P$```!`P``P`0```$#``#4!````0,``-@$```!`P``
+M[`0```$#``#P!````0,```0%```!`P``"`4```$#```<!0```0,``"`%```!
+M`P``-`4```$#```X!0```0,``$P%```!`P``4`4```$#``!D!0```0,``&@%
+M```!`P``?`4```$#``"`!0```0,``)0%```!`P``F`4```$#``"L!0```0,`
+M`+`%```!`P``Q`4```$#``#(!0```0,``-P%```!`P``X`4```$#``#T!0``
+M`0,``/@%```!`P``#`8```$#```0!@```0,``"0&```!`P``*`8```$#```\
+M!@```0,``$`&```!`P``5`8```$#``!8!@```0,``&P&```!`P``<`8```$#
+M``"$!@```0,``(@&```!`P``G`8```$#``"@!@```0,``+0&```!`P``N`8`
+M``$#``#,!@```0,``-`&```!`P``Y`8```$#``#H!@```0,``/P&```!`P``
+M``<```$#```4!P```0,``!@'```!`P``+`<```$#```P!P```0,``$0'```!
+M`P``2`<```$#``!<!P```0,``&`'```!`P``=`<```$#``!X!P```0,``(P'
+M```!`P``D`<```$#``"D!P```0,``*@'```!`P``O`<```$#``#`!P```0,`
+M`-0'```!`P``V`<```$#``#L!P```0,``/`'```!`P``!`@```$#```("```
+M`0,``!P(```!`P``(`@```$#```T"````0,``#@(```!`P``3`@```$#``!0
+M"````0,``&0(```!`P``:`@```$#``!\"````0,``(`(```!`P``E`@```$#
+M``"8"````0,``*P(```!`P``L`@```$#``#$"````0,``,@(```!`P``W`@`
+M``$#``#@"````0,``/0(```!`P``^`@```$#```,"0```0,``!`)```!`P``
+M)`D```$#```H"0```0,``#P)```!`P``0`D```$#``!4"0```0,``%@)```!
+M`P``;`D```$#``!P"0```0,``(0)```!`P``B`D```$#``"<"0```0,``*`)
+M```!`P``M`D```$#``"X"0```0,``,P)```!`P``T`D```$#``#D"0```0,`
+M`.@)```!`P``_`D```$#````"@```0,``!0*```!`P``&`H```$#```L"@``
+M`0,``#`*```!`P``1`H```$#``!("@```0,``%P*```!`P``8`H```$#``!T
+M"@```0,``'@*```!`P``C`H```$#``"0"@```0,``*0*```!`P``J`H```$#
+M``"\"@```0,``,`*```!`P``U`H```$#``#8"@```0,``.P*```!`P``\`H`
+M``$#```$"P```0,```@+```!`P``\!````$%``#T$````04``/@0```!!0``
+M_!````$%````$0```04```01```!!0``"!$```$%```,$0```04``!`1```!
+M!0``%!$```$%```H$0```04``"P1```!!0``,!$```$%```T$0```04``#@1
+M```!!0``/!$```$%``!`$0```04``$01```!!0``2!$```$%``!@$0```04`
+M`&01```!!0``:!$```$%``!L$0```04``'`1```!!0``=!$```$%``!X$0``
+M`04``'P1```!!0``@!$```$%``"8$0```04``)P1```!!0``H!$```$%``"D
+M$0```04``*@1```!!0``K!$```$%``"P$0```04``+01```!!0``N!$```$%
+M``#0$0```04``-01```!!0``V!$```$%``#<$0```04``.`1```!!0``Y!$`
+M``$%``#H$0```04``.P1```!!0```!(```$%```($@```04```P2```!!0``
+M$!(```$%```4$@```04``!@2```!!0``'!(```$%```@$@```04``"02```!
+M!0``*!(```$%```L$@```04``$`2```!!0``1!(```$%``!($@```04``$P2
+M```!!0``4!(```$%``!4$@```04``%@2```!!0``7!(```$%``!X$@```04`
+M`'P2```!!0``@!(```$%``"$$@```04``(@2```!!0``C!(```$%``"0$@``
+M`04``+`2```!!0``M!(```$%``"X$@```04``+P2```!!0``P!(```$%``#H
+M$@```04``.P2```!!0``\!(```$%``#T$@```04``/@2```!!0``_!(```$%
+M````$P```04```03```!!0``(!,```$%```D$P```04``"@3```!!0``+!,`
+M``$%```P$P```04``#03```!!0``<!,```$%``!T$P```04``'@3```!!0``
+M?!,```$%``"`$P```04``(03```!!0``B!,```$%``",$P```04``)`3```!
+M!0``J!,```$%``"L$P```04``+`3```!!0``M!,```$%``"X$P```04``+P3
+M```!!0``P!,```$%``#$$P```04``, at 3```!!0``X!,```$%``#D$P```04`
+M`. at 3```!!0``[!,```$%``#P$P```04``/03```!!0``^!,```$%``#\$P``
+M`04````4```!!0``&!0```$%```<%````04``"`4```!!0``)!0```$%```H
+M%````04``"P4```!!0``,!0```$%```T%````04``#@4```!!0``4!0```$%
+M``!4%````04``%@4```!!0``7!0```$%``!@%````04``&04```!!0``:!0`
+M``$%``"`%````04``(@4```!!0``C!0```$%``"0%````04``)04```!!0``
+MF!0```$%``"<%````04``*`4```!!0``I!0```$%``"H%````04``,`4```!
+M!0``Q!0```$%``#(%````04``,P4```!!0``T!0```$%``#4%````04``- at 4
+M```!!0``W!0```$%``#X%````04``/P4```!!0```!4```$%```$%0```04`
+M``@5```!!0``#!4```$%```0%0```04``#`5```!!0``-!4```$%```X%0``
+M`04``#P5```!!0``0!4```$%``!H%0```04``&P5```!!0``<!4```$%``!T
+M%0```04``'@5```!!0``?!4```$%``"`%0```04``(05```!!0``H!4```$%
+M``"D%0```04``*@5```!!0``K!4```$%``"P%0```04``+05```!!0``Z!4`
+M``$%``#T%0```04````6```!!0``&!8```$!```<%@```0$``"`6```!`0``
+M)!8```$!```H%@```0$``"P6```!`0``,!8```$!```T%@```0$``#@6```!
+M`0``/!8```$!``!`%@```0$``$06```!`0``2!8```$!``!,%@```0$``%`6
+M```!`0``5!8```$!``!8%@```0$``%P6```!`0``8!8```$!``!D%@```0$`
+M`&@6```!`0``;!8```$!``!P%@```0$``'06```!`0``>!8```$!``!\%@``
+M`0$``(`6```!`0``A!8```$!``"(%@```0$``(P6```!`0``D!8```$!``"4
+M%@```0$``)@6```!`0``G!8```$!``"@%@```0$``*06```!`0``J!8```$!
+M``"L%@```0$``+`6```!`0``M!8```$!``"X%@```0$``+P6```!`0``P!8`
+M``$!``#$%@```0$``, at 6```!`0``]!8```$"``#X%@```0(``"@7```!`@``
+M*!@```'V```L&````5H``#`8```!L@``-!@```&D```X&````<,``#P8```!
+M2P``0!@```'.``!$&````5P!`$@8```!,P$`3!@```&O``!0&````34``%08
+M```!>P``6!@```%.``!<&````1P``&`8```!30$`9!@```%U`0!H&````2@`
+M`&P8```!@@``<!@```'N``!T&````3`!`'@8```!)```?!@```$S``"`&```
+M`5X``(08```!8@$`B!@```$#`0",&````:4``)`8```!10$`E!@```%(`0"8
+M&````?,``)P8```!S```H!@```$D`0"D&````84``*@8```!5P$`K!@```&P
+M``"P&````:<``+08```!?```N!@```&?``"\&````4P``,`8```!N```Q!@`
+M``$@`0#(&````?@``,P8```!F0``T!@```$<`0#4&````=4``- at 8```!%0$`
+MW!@```$M``#@&````:8``.08```!0`$`Z!@```$&`0#L&````0X!`/`8```!
+M-```]!@```%J``#X&````2P!`/P8```!T@```!D```%&```$&0```2\```@9
+M```!@0``#!D```$)```0&0```>```!09```!)0$`&!D```%C`0`<&0```5\`
+M`"`9```!"P``)!D```%)`0`H&0```0(!`"P9```!4P$`,!D```$[```T&0``
+M`1<!`#@9```!$0``/!D```&N``!`&0```64``$09```!X@``2!D```'D``!,
+M&0```;T``%`9```!1`$`5!D```&L``!8&0```6$!`%P9```!#0``8!D```$U
+M`0!D&0```:(``&@9```!#`$`;!D```'U``!P&0```94``'09```!-@$`>!D`
+M``$!``!\&0```2L!`(`9```!=`$`A!D```$4`0",&0```4`!`)`9```!!@$`
+ME!D```$.`0"8&0```30``)P9```!:@``Z"4```$M`0#L)0```4H!`/`E```!
+M/@$`]"4```%@`0#X)0```6P``/PE```!AP```"8```%B```$)@```2X```@F
+M```!5P``#"8```%8```0)@```3(``!0F```!4`$`&"8```'W```<)@```6L!
+M`"`F```!=P``)"8```%:`0`H)@```>L``"PF```!G0``,"8```&J```T)@``
+M`1$!`#@F```!'@``/"8```'Q``!`)@```18``$0F```!3`$`2"8```$;``!,
+M)@```24``%`F```!G@``5"8```%?`0!8)@```4X!`%PF```!6P$`8"8```$A
+M`0!D)@```=T``&@F```!Z```;"8```'8``!P)@```?L``'0F```!<P``>"8`
+M``&-``!\)@```6D``(`F```!,@$`A"8```&4``"()@```38``(PF```!(@$`
+MD"8```$>`0"4)@```7```)@F```!S0``G"8```$8`0"@)@```=$``*0F```!
+MM0``J"8```'0``"L)@```2@!`+`F```!OP``M"8```$P``"X)@```58``+PF
+M```!R```P"8```$O`0#$)@```3H!`, at F```!$`$`S"8```$9``#0)@```5(`
+M`-0F```!&@$`V"8```&/``#<)@```48!`.`F```!Q```Y"8```'M``#H)@``
+M`3$``.PF```!Z0``\"8```$B``#T)@```4<``/@F```!A@``_"8```'+````
+M)P```;L```0G```!/0$`""<```&Z```,)P```?\``!`G```!^0``%"<```$[
+M`0`8)P```0@``!PG```!E@``("<```%F```D)P```8P``"@G```!8```+"<`
+M``'9```P)P```=X``#0G```!\```."<```$!```\)P```00!`$`G```!%@$`
+M1"<```$Q`0!,)P```;4``%`G```!T```5"<```$H`0!8)P```;\``%PG```!
+M,```6#L```$!``!<.P```0$``&`[```!`0``9#L```$!``!H.P```0$``&P[
+M```!`0``<#L```$!``!T.P```0$``,@\```!40``S#P```%V``#0/````5@!
+M`-0\```!80``V#P```''``#</````:D``.`\```!!P$`Y#P```%*``#H/```
+M`9(``.P\```!30``\#P```'L``#T/````4,!`/@\```!@```_#P```'G````
+M/0```=,```0]```!>0$`"#T```%Q`0`,/0```:L``!`]```!+@$`%#T```%R
+M```8/0```0T!`!P]```!(P``(#T```%"`0`D/0```3@!`"@]```!1P$`+#T`
+M``%5```P/0```2<!`#0]```!Z@``.#T```'E```\/0```1```$`]```!40$`
+M1#T```%Q``!(/0```<\``$P]```!5@$`4#T```')``!4/0```8H``%@]```!
+M&P$`7#T```'C``!@/0```0X``&0]```!D0``:#T```$*``!L/0```8L``'`]
+M```!)@$`=#T```&@``!X/0```0D!`'P]```!F```@#T```&A``"$/0```0$!
+M`(@]```!1```C#T```$I`0"0/0```3<!`)0]```!_0``F#T```%V`0"</0``
+M`3\``*`]```!MP``I#T```&;``"H/0```68!`*P]```!0@``L#T```&#``"T
+M/0```6H!`+@]```!.```O#T```%!``#`/0```2<``,0]```!-P``R#T```$\
+M``#,/0```74``-`]```!%0``U#T```%H``#8/0```>$``-P]```!8P``X#T`
+M``$/`0#D/0```64!`.@]```!P0``[#T```%Y``#P/0```4,``/0]```!'0``
+M^#T```%R`0#\/0```?H````^```!P@``!#X```&7```(/@```1\```P^```!
+M60$`$#X```%D```4/@```<8``!@^```!M@``'#X```'%```@/@```=8``"0^
+M```!;P$`+#X```$!`0`P/@```40``#0^```!*0$`.#X```$W`0`\/@```?T`
+M`/Q$```!`0```$4```$!```$10```0$```A%```!`0``#$4```$!```010``
+M`0$``$!%```!`0``1$4```$!``!(10```0$``$Q%```!`0``4$4```$!``!4
+M10```0$``%A%```!`0``7$4```$!``!@10```0$``&1%```!`0``:$4```$!
+M``!L10```0$``'!%```!`0``=$4```$!``!X10```0$``'Q%```!`0``@$4`
+M``$!``!P40```0$``'11```!`0``>%$```$!``!\40```0$``(!1```!`0``
+MA%$```$!``"H7@```0$``*Q>```!`0``L%X```$!``"T7@```0$``+A>```!
+M`0``O%X```$!``#`7@```0$``,1>```!`0``N&H```$!``"\:@```0$``,!J
+M```!`0``Q&H```$!``#(:@```0$``,QJ```!`0``T&H```$!``#4:@```0$`
+M`.AV```!`0``['8```$!``#P=@```0$``/1V```!`0``^'8```$!``#\=@``
+M`0$```!W```!`0``!'<```$!```,`````0,``!`````!`P``%`````$#```8
+M`````0,``!P````!`P``(`````$#```\`0```04``$0!```!!0``3`$```$%
+M``!4`0```04``%P!```!!0``9`$```$%``!L`0```04``'0!```!!0``?`$`
+M``$%``"$`0```04``(P!```!!0``E`$```$%``"<`0```04``*0!```!!0``
+0K`$```$%``"T`0```04`````
+`
+end
diff -N -u -r src.preview/sys/contrib/dev/ath/public/i386-elf.inc src/sys/contrib/dev/ath/public/i386-elf.inc
--- src.preview/sys/contrib/dev/ath/public/i386-elf.inc	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/public/i386-elf.inc	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+# $Id: i386-elf.inc,v 1.1.1.1 2004/12/08 18:06:08 sam Exp $
+#
+
+#
+# Compilation configuration for building i386-elf.
+# This assumes the build platform is also i386-elf.
+#
+
+#
+ifndef TOOLPREFIX
+TOOLPREFIX=
+endif
+#
+CC=	${TOOLPREFIX}gcc
+LD=	${TOOLPREFIX}ld
+STRIP=	${TOOLPREFIX}strip
+OBJCOPY=${TOOLPREFIX}objcopy
+NM=	${TOOLPREFIX}nm
+
+COPTS+=	-DAH_BYTE_ORDER=AH_LITTLE_ENDIAN
+ifndef CONFIG_FRAME_POINTER
+COPTS+=	-fomit-frame-pointer
+endif
diff -N -u -r src.preview/sys/contrib/dev/ath/public/i386-elf.opt_ah.h src/sys/contrib/dev/ath/public/i386-elf.opt_ah.h
--- src.preview/sys/contrib/dev/ath/public/i386-elf.opt_ah.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/public/i386-elf.opt_ah.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,6 @@
+#define AH_SUPPORT_AR5210	1
+#define AH_SUPPORT_AR5211	1
+#define AH_SUPPORT_AR5212	1
+#define AH_SUPPORT_5111	1
+#define AH_SUPPORT_5112	1
+#define AH_SUPPORT_2413	1
diff -N -u -r src.preview/sys/contrib/dev/ath/version.h src/sys/contrib/dev/ath/version.h
--- src.preview/sys/contrib/dev/ath/version.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/contrib/dev/ath/version.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $Id: version.h,v 1.1.1.6 2004/12/08 18:04:16 sam Exp $
+ */
+#define	ATH_HAL_VERSION	"0.9.14.9"
diff -N -u -r src.preview/sys/dev/netif/Makefile src/sys/dev/netif/Makefile
--- src.preview/sys/dev/netif/Makefile	2005-08-31 18:55:28.000000000 -0400
+++ src/sys/dev/netif/Makefile	2005-09-26 10:54:04.000000000 -0400
@@ -1,8 +1,8 @@
 # $DragonFly: src/sys/dev/netif/Makefile,v 1.14 2005/08/31 22:55:28 swildner Exp $
 #
 
-SUBDIR= an ar aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue lge lnc \
-	mii_layer my nge nv owi pcn ray re rl sbni sbsh sf sis sk \
-	sr ste ti tl tx txp vr vx wb wi xe xl
+SUBDIR= an ar ath aue axe bfe bge cue dc ed em ep fwe fxp gx kue lge lnc \
+	mii_layer my nge nv pcn re rl sbni sbsh sf sis sk sr ste ti tl \
+        tx txp vr vx wb xe xl
 
 .include <bsd.subdir.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/Makefile src/sys/dev/netif/ath/Makefile
--- src.preview/sys/dev/netif/ath/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,49 @@
+#
+# Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+# $FreeBSD: src/sys/modules/ath/Makefile,v 1.1 2003/06/23 17:02:43 sam Exp $
+#
+
+
+KMOD=	if_ath
+SRCS=	if_ath.c if_ath_pci.c
+SRCS+=	opt_bdg.h device_if.h bus_if.h pci_if.h opt_inet.h
+
+HAL=	${.CURDIR}/../../../contrib/dev/ath
+CFLAGS+=  -g -I. -I${HAL}/freebsd -I${HAL}
+
+SUBDIR= ath_rate ath_hal
+
+.include <bsd.kmod.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/ath_hal/Makefile src/sys/dev/netif/ath/ath_hal/Makefile
--- src.preview/sys/dev/netif/ath/ath_hal/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_hal/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,70 @@
+#
+# Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+# $FreeBSD: /repoman/r/ncvs/src/sys/modules/ath_hal/Makefile,v 1.3 2004/12/08 17:38:37 sam Exp $
+#
+
+#
+# Hardware Access Layer (HAL) for the Atheros Wireless NIC driver.
+#
+# This module contains the hardware-specific bits for the network
+# interface driver.  It is built as a separate module to simplify
+# maintenance and isolate the bits that are not (currently) distributed
+# in source form.
+#
+HAL= ${.CURDIR}/../../../../contrib/dev/ath
+
+.PATH: ${HAL}/freebsd
+
+KMOD=	ath_hal
+SRCS=	ah_osdep.c
+SRCS+=	bus_if.h device_if.h pci_if.h ah_if.h opt_ah.h
+OBJS=	hal.o
+MFILES=	kern/bus_if.m kern/device_if.m bus/pci/pci_if.m \
+	contrib/dev/ath/freebsd/ah_if.m
+
+CFLAGS+= -I. -I../../.. -I${HAL}/freebsd -I${HAL}
+
+.if defined(HAL_SRC)
+.include "${HAL}/freebsd/Makefile.inc"
+.else
+
+hal.o:	${HAL}/public/i386-elf.hal.o.uu
+	uudecode -p < ${HAL}/public/i386-elf.hal.o.uu > ${.TARGET}
+opt_ah.h: ${HAL}/public/i386-elf.opt_ah.h
+	cp ${HAL}/public/i386-elf.opt_ah.h ${.TARGET}
+.endif
+
+.include <bsd.kmod.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/Makefile src/sys/dev/netif/ath/ath_rate/Makefile
--- src.preview/sys/dev/netif/ath/ath_rate/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,7 @@
+# $DragonFly: src/sys/dev/netif/Makefile,v 1.13 2005/03/06 18:25:08 dillon Exp $
+#
+
+SUBDIR= ammr onoe sample
+
+.include <bsd.subdir.mk>
+
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/ammr/Makefile src/sys/dev/netif/ath/ath_rate/ammr/Makefile
--- src.preview/sys/dev/netif/ath/ath_rate/ammr/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/ammr/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,48 @@
+#
+# Copyright (c) 2004 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+# $FreeBSD: /repoman/r/ncvs/src/sys/modules/ath_rate_amrr/Makefile,v 1.1 2004/12/08 17:38:37 sam Exp $
+#
+
+.PATH: ${.CURDIR}/../../dev/ath/ath_rate/amrr
+
+KMOD=	ath_rate_ammr
+SRCS=	amrr.c
+SRCS+=	opt_bdg.h device_if.h bus_if.h pci_if.h opt_inet.h
+
+HAL= ${.CURDIR}/../../../../../contrib/dev/ath
+CFLAGS+= -I. -I${HAL}/freebsd -I${HAL}
+
+.include <bsd.kmod.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/ammr/amrr.c src/sys/dev/netif/ath/ath_rate/ammr/amrr.c
--- src.preview/sys/dev/netif/ath/ath_rate/ammr/amrr.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/ammr/amrr.c	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,545 @@
+/*-
+ * Copyright (c) 2004 INRIA
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * AMRR rate control. See:
+ * http://www-sop.inria.fr/rapports/sophia/RR-5208.html
+ * "IEEE 802.11 Rate Adaptation: A Practical Approach" by
+ *    Mathieu Lacage, Hossein Manshaei, Thierry Turletti
+ */
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h> 
+#include <netinet/if_ether.h>
+#endif
+
+#include "../../if_athvar.h"
+#include "../../if_athrate.h"
+#include "amrr.h"
+#include "contrib/dev/ath/ah_desc.h"
+
+#define	AMRR_DEBUG
+#ifdef AMRR_DEBUG
+#define	DPRINTF(sc, _fmt, ...) do {					\
+	if (0)					\
+		printf(_fmt, __VA_ARGS__);				\
+} while (0)
+#else
+#define	DPRINTF(sc, _fmt, ...)
+#endif
+
+static	int ath_rateinterval = 1000;		/* rate ctl interval (ms)  */
+static	int ath_rate_max_success_threshold = 10;
+static	int ath_rate_min_success_threshold = 1;
+
+static void	ath_ratectl(void *);
+static void	ath_rate_update(struct ath_softc *, struct ieee80211_node *,
+			int rate);
+static void	ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
+static void	ath_rate_ctl(void *, struct ieee80211_node *);
+
+void
+ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+{
+	/* NB: assumed to be zero'd by caller */
+	ath_rate_update(sc, &an->an_node, 0);
+}
+
+void
+ath_rate_node_cleanup(struct ath_softc *sc __unused,
+        struct ath_node *an __unused)
+{
+}
+
+void
+ath_rate_findrate(struct ath_softc *sc __unused, struct ath_node *an,
+	int shortPreamble, size_t frameLen __unused,
+	u_int8_t *rix, int *try0, u_int8_t *txrate)
+{
+	struct amrr_node *amn = ATH_NODE_AMRR(an);
+
+	*rix = amn->amn_tx_rix0;
+	*try0 = amn->amn_tx_try0;
+	if (shortPreamble)
+		*txrate = amn->amn_tx_rate0sp;
+	else
+		*txrate = amn->amn_tx_rate0;
+}
+
+void
+ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,
+	struct ath_desc *ds, int shortPreamble __unused,
+	u_int8_t rix __unused)
+{
+	struct amrr_node *amn = ATH_NODE_AMRR(an);
+
+	ath_hal_setupxtxdesc(sc->sc_ah, ds
+		, amn->amn_tx_rate1sp, amn->amn_tx_try1	/* series 1 */
+		, amn->amn_tx_rate2sp, amn->amn_tx_try2	/* series 2 */
+		, amn->amn_tx_rate3sp, amn->amn_tx_try3	/* series 3 */
+	);
+}
+
+void
+ath_rate_tx_complete(struct ath_softc *sc __unused,
+	struct ath_node *an, const struct ath_desc *ds, const struct ath_desc *ds0 __unused)
+{
+	struct amrr_node *amn = ATH_NODE_AMRR(an);
+	int sr = ds->ds_txstat.ts_shortretry;
+	int lr = ds->ds_txstat.ts_longretry;
+	int retry_count = sr + lr;
+
+	amn->amn_tx_try0_cnt++;
+	if (retry_count == 1) {
+		amn->amn_tx_try1_cnt++;
+	} else if (retry_count == 2) {
+		amn->amn_tx_try1_cnt++;
+		amn->amn_tx_try2_cnt++;
+	} else if (retry_count == 3) {
+		amn->amn_tx_try1_cnt++;
+		amn->amn_tx_try2_cnt++;
+		amn->amn_tx_try3_cnt++;
+	} else if (retry_count > 3) {
+		amn->amn_tx_try1_cnt++;
+		amn->amn_tx_try2_cnt++;
+		amn->amn_tx_try3_cnt++;
+		amn->amn_tx_failure_cnt++;
+	}
+}
+
+void
+ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+{
+	if (isnew)
+		ath_rate_ctl_start(sc, &an->an_node);
+}
+
+static void 
+node_reset (struct amrr_node *amn)
+{
+	amn->amn_tx_try0_cnt = 0;
+	amn->amn_tx_try1_cnt = 0;
+	amn->amn_tx_try2_cnt = 0;
+	amn->amn_tx_try3_cnt = 0;
+	amn->amn_tx_failure_cnt = 0;
+  	amn->amn_success = 0;
+  	amn->amn_recovery = 0;
+  	amn->amn_success_threshold = ath_rate_min_success_threshold;
+}
+
+
+/**
+ * The code below assumes that we are dealing with hardware multi rate retry
+ * I have no idea what will happen if you try to use this module with another
+ * type of hardware. Your machine might catch fire or it might work with
+ * horrible performance...
+ */
+static void
+ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
+{
+	struct ath_node *an = ATH_NODE(ni);
+	struct amrr_node *amn = ATH_NODE_AMRR(an);
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	u_int8_t rix;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	DPRINTF(sc, "%s: set xmit rate for %6D to %dM\n",
+	    __func__, (ni->ni_macaddr), ":",
+	    ni->ni_rates.rs_nrates > 0 ?
+		(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+
+	ni->ni_txrate = rate;
+	/* XXX management/control frames always go at the lowest speed */
+	an->an_tx_mgtrate = rt->info[0].rateCode;
+	an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble;
+	/*
+	 * Before associating a node has no rate set setup
+	 * so we can't calculate any transmit codes to use.
+	 * This is ok since we should never be sending anything
+	 * but management frames and those always go at the
+	 * lowest hardware rate.
+	 */
+	if (ni->ni_rates.rs_nrates > 0) {
+		amn->amn_tx_rix0 = sc->sc_rixmap[
+					       ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+		amn->amn_tx_rate0 = rt->info[amn->amn_tx_rix0].rateCode;
+		amn->amn_tx_rate0sp = amn->amn_tx_rate0 |
+			rt->info[amn->amn_tx_rix0].shortPreamble;
+		if (sc->sc_mrretry) {
+			amn->amn_tx_try0 = 1;
+			amn->amn_tx_try1 = 1;
+			amn->amn_tx_try2 = 1;
+			amn->amn_tx_try3 = 1;
+			if (--rate >= 0) {
+				rix = sc->sc_rixmap[
+						    ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+				amn->amn_tx_rate1 = rt->info[rix].rateCode;
+				amn->amn_tx_rate1sp = amn->amn_tx_rate1 |
+					rt->info[rix].shortPreamble;
+			} else {
+				amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0;
+			}
+			if (--rate >= 0) {
+				rix = sc->sc_rixmap[
+						    ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+				amn->amn_tx_rate2 = rt->info[rix].rateCode;
+				amn->amn_tx_rate2sp = amn->amn_tx_rate2 |
+					rt->info[rix].shortPreamble;
+			} else {
+				amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0;
+			}
+			if (rate > 0) {
+				/* NB: only do this if we didn't already do it above */
+				amn->amn_tx_rate3 = rt->info[0].rateCode;
+				amn->amn_tx_rate3sp =
+					an->an_tx_mgtrate | rt->info[0].shortPreamble;
+			} else {
+				amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0;
+			}
+		} else {
+			amn->amn_tx_try0 = ATH_TXMAXTRY;
+			/* theorically, these statements are useless because
+			 *  the code which uses them tests for an_tx_try0 == ATH_TXMAXTRY
+			 */
+			amn->amn_tx_try1 = 0;
+			amn->amn_tx_try2 = 0;
+			amn->amn_tx_try3 = 0;
+			amn->amn_tx_rate1 = amn->amn_tx_rate1sp = 0;
+			amn->amn_tx_rate2 = amn->amn_tx_rate2sp = 0;
+			amn->amn_tx_rate3 = amn->amn_tx_rate3sp = 0;
+		}
+	}
+	node_reset (amn);
+}
+
+/*
+ * Set the starting transmit rate for a node.
+ */
+static void
+ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+	struct ieee80211com *ic = &sc->sc_ic;
+	int srate;
+
+	KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
+	if (ic->ic_fixed_rate == -1) {
+		/*
+		 * No fixed rate is requested. For 11b start with
+		 * the highest negotiated rate; otherwise, for 11g
+		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
+		 */
+		srate = ni->ni_rates.rs_nrates - 1;
+		if (sc->sc_curmode != IEEE80211_MODE_11B) {
+			/*
+			 * Scan the negotiated rate set to find the
+			 * closest rate.
+			 */
+			/* NB: the rate set is assumed sorted */
+			for (; srate >= 0 && RATE(srate) > 72; srate--)
+				;
+			KASSERT(srate >= 0, ("bogus rate set"));
+		}
+	} else {
+		/*
+		 * A fixed rate is to be used; ic_fixed_rate is an
+		 * index into the supported rate set.  Convert this
+		 * to the index into the negotiated rate set for
+		 * the node.  We know the rate is there because the
+		 * rate set is checked when the station associates.
+		 */
+		const struct ieee80211_rateset *rs =
+			&ic->ic_sup_rates[ic->ic_curmode];
+		int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
+		/* NB: the rate set is assumed sorted */
+		srate = ni->ni_rates.rs_nrates - 1;
+		for (; srate >= 0 && RATE(srate) != r; srate--)
+			;
+		KASSERT(srate >= 0,
+			("fixed rate %d not in rate set", ic->ic_fixed_rate));
+	}
+	ath_rate_update(sc, ni, srate);
+#undef RATE
+}
+
+static void
+ath_rate_cb(void *arg, struct ieee80211_node *ni)
+{
+	ath_rate_update(ni->ni_ic->ic_ifp->if_softc, ni, (int)(uintptr_t) arg);
+}
+
+/*
+ * Reset the rate control state for each 802.11 state transition.
+ */
+void
+ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+{
+	struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+
+	if (state == IEEE80211_S_INIT) {
+		callout_stop(&asc->timer);
+		return;
+	}
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * Reset local xmit state; this is really only
+		 * meaningful when operating in station mode.
+		 */
+		ni = ic->ic_bss;
+		if (state == IEEE80211_S_RUN) {
+			ath_rate_ctl_start(sc, ni);
+		} else {
+			ath_rate_update(sc, ni, 0);
+		}
+	} else {
+		/*
+		 * When operating as a station the node table holds
+		 * the AP's that were discovered during scanning.
+		 * For any other operating mode we want to reset the
+		 * tx rate state of each node.
+		 */
+		ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, 0);
+		ath_rate_update(sc, ic->ic_bss, 0);
+	}
+	if (ic->ic_fixed_rate == -1 && state == IEEE80211_S_RUN) {
+		int interval;
+		/*
+		 * Start the background rate control thread if we
+		 * are not configured to use a fixed xmit rate.
+		 */
+		interval = ath_rateinterval;
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			interval /= 2;
+		callout_reset(&asc->timer, (interval * hz) / 1000,
+			ath_ratectl, &sc->sc_if);
+	}
+}
+
+/* 
+ * Examine and potentially adjust the transmit rate.
+ */
+static void
+ath_rate_ctl(void *arg, struct ieee80211_node *ni)
+{
+	struct ath_softc *sc = arg;
+	struct amrr_node *amn = ATH_NODE_AMRR(ATH_NODE (ni));
+	int old_rate;
+
+#define is_success(amn) \
+(amn->amn_tx_try1_cnt  < (amn->amn_tx_try0_cnt/10))
+#define is_enough(amn) \
+(amn->amn_tx_try0_cnt > 10)
+#define is_failure(amn) \
+(amn->amn_tx_try1_cnt > (amn->amn_tx_try0_cnt/3))
+#define is_max_rate(ni) \
+((ni->ni_txrate + 1) >= ni->ni_rates.rs_nrates)
+#define is_min_rate(ni) \
+(ni->ni_txrate == 0)
+
+	old_rate = ni->ni_txrate;
+  
+  	DPRINTF (sc, "cnt0: %d cnt1: %d cnt2: %d cnt3: %d -- threshold: %d\n",
+		 amn->amn_tx_try0_cnt,
+		 amn->amn_tx_try1_cnt,
+		 amn->amn_tx_try2_cnt,
+		 amn->amn_tx_try3_cnt,
+		 amn->amn_success_threshold);
+  	if (is_success (amn) && is_enough (amn)) {
+		amn->amn_success++;
+		if (amn->amn_success == amn->amn_success_threshold &&
+  		    !is_max_rate (ni)) {
+  			amn->amn_recovery = 1;
+  			amn->amn_success = 0;
+  			ni->ni_txrate++;
+			DPRINTF (sc, "increase rate to %d\n", ni->ni_txrate);
+  		} else {
+			amn->amn_recovery = 0;
+		}
+  	} else if (is_failure (amn)) {
+  		amn->amn_success = 0;
+  		if (!is_min_rate (ni)) {
+  			if (amn->amn_recovery) {
+  				/* recovery failure. */
+  				amn->amn_success_threshold *= 2;
+  				amn->amn_success_threshold = min (amn->amn_success_threshold,
+								  (u_int)ath_rate_max_success_threshold);
+ 				DPRINTF (sc, "decrease rate recovery thr: %d\n", amn->amn_success_threshold);
+  			} else {
+  				/* simple failure. */
+ 				amn->amn_success_threshold = ath_rate_min_success_threshold;
+ 				DPRINTF (sc, "decrease rate normal thr: %d\n", amn->amn_success_threshold);
+  			}
+			amn->amn_recovery = 0;
+  			ni->ni_txrate--;
+   		} else {
+			amn->amn_recovery = 0;
+		}
+
+   	}
+	if (is_enough (amn) || old_rate != ni->ni_txrate) {
+		/* reset counters. */
+		amn->amn_tx_try0_cnt = 0;
+		amn->amn_tx_try1_cnt = 0;
+		amn->amn_tx_try2_cnt = 0;
+		amn->amn_tx_try3_cnt = 0;
+		amn->amn_tx_failure_cnt = 0;
+	}
+	if (old_rate != ni->ni_txrate) {
+		ath_rate_update(sc, ni, ni->ni_txrate);
+	}
+}
+
+static void
+ath_ratectl(void *arg)
+{
+	struct ifnet *ifp = arg;
+	struct ath_softc *sc = ifp->if_softc;
+	struct amrr_softc *asc = (struct amrr_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int interval;
+
+	if (ifp->if_flags & IFF_RUNNING) {
+		sc->sc_stats.ast_rate_calls++;
+
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			ath_rate_ctl(sc, ic->ic_bss);	/* NB: no reference */
+		else 
+			ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
+	}
+	interval = ath_rateinterval;
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		interval /= 2;
+	callout_reset(&asc->timer, (interval * hz) / 1000,
+		ath_ratectl, &sc->sc_if);
+}
+
+static void
+ath_rate_sysctlattach(struct ath_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = &sc->sysctl_ctx;
+	struct sysctl_oid *tree = sc->sysctl_tree;
+
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rate_interval", CTLFLAG_RW, &ath_rateinterval, 0,
+		"rate control: operation interval (ms)");
+	/* XXX bounds check values */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"max_sucess_threshold", CTLFLAG_RW,
+		&ath_rate_max_success_threshold, 0, "");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"min_sucess_threshold", CTLFLAG_RW,
+		&ath_rate_min_success_threshold, 0, "");
+}
+
+struct ath_ratectrl *
+ath_rate_attach(struct ath_softc *sc)
+{
+	struct amrr_softc *asc;
+
+	asc = malloc(sizeof(struct amrr_softc), M_DEVBUF, M_NOWAIT|M_ZERO);
+	if (asc == NULL)
+		return NULL;
+	asc->arc.arc_space = sizeof(struct amrr_node);
+	callout_init(&asc->timer);
+	ath_rate_sysctlattach(sc);
+
+	return &asc->arc;
+}
+
+void
+ath_rate_detach(struct ath_ratectrl *arc)
+{
+	struct amrr_softc *asc = (struct amrr_softc *) arc;
+
+	callout_stop(&asc->timer);
+	free(asc, M_DEVBUF);
+}
+
+/*
+ * Module glue.
+ */
+static int
+amrr_modevent(module_t mod __unused, int type, void *unused __unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		printf("ath_rate: <AMRR rate control algorithm> version 0.1\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t amrr_mod = {
+	"ath_rate",
+	amrr_modevent,
+	0
+};
+DECLARE_MODULE(ath_rate, amrr_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(ath_rate, 1);
+MODULE_DEPEND(ath_rate, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/ammr/amrr.h src/sys/dev/netif/ath/ath_rate/ammr/amrr.h
--- src.preview/sys/dev/netif/ath/ath_rate/ammr/amrr.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/ammr/amrr.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,77 @@
+/*-
+ * Copyright (c) 2004 INRIA
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/dev/ath/ath_rate/amrr/amrr.h,v 1.2 2004/12/31 22:41:45 sam Exp $
+ */
+
+#ifndef _DEV_ATH_RATE_AMRR_H
+#define _DEV_ATH_RATE_AMRR_H
+
+/* per-device state */
+struct amrr_softc {
+	struct ath_ratectrl arc;	/* base state */
+	struct callout timer;		/* periodic timer */
+};
+
+/* per-node state */
+struct amrr_node {
+  	/* AMRR statistics for this node */
+  	u_int           amn_tx_try0_cnt;
+  	u_int           amn_tx_try1_cnt;
+  	u_int           amn_tx_try2_cnt;
+  	u_int           amn_tx_try3_cnt;
+  	u_int           amn_tx_failure_cnt; 
+        /* AMRR algorithm state for this node */
+  	u_int           amn_success_threshold;
+  	u_int           amn_success;
+  	u_int           amn_recovery;
+	/* rate index et al. */
+	u_int8_t	amn_tx_rix0;	/* series 0 rate index */
+	u_int8_t	amn_tx_rate0;	/* series 0 h/w rate */
+	u_int8_t	amn_tx_rate1;	/* series 1 h/w rate */
+	u_int8_t	amn_tx_rate2;	/* series 2 h/w rate */
+	u_int8_t	amn_tx_rate3;	/* series 3 h/w rate */
+	u_int8_t	amn_tx_rate0sp;	/* series 0 short preamble h/w rate */
+	u_int8_t	amn_tx_rate1sp;	/* series 1 short preamble h/w rate */
+	u_int8_t	amn_tx_rate2sp;	/* series 2 short preamble h/w rate */
+	u_int8_t	amn_tx_rate3sp;	/* series 3 short preamble h/w rate */
+	u_int8_t	amn_tx_try0;	/* series 0 try count */
+  	u_int           amn_tx_try1;    /* series 1 try count */
+  	u_int           amn_tx_try2;    /* series 2 try count */
+  	u_int           amn_tx_try3;    /* series 3 try count */
+};
+#define	ATH_NODE_AMRR(an)	((struct amrr_node *)&an[1])
+#endif /* _DEV_ATH_RATE_AMRR_H */
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/onoe/Makefile src/sys/dev/netif/ath/ath_rate/onoe/Makefile
--- src.preview/sys/dev/netif/ath/ath_rate/onoe/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/onoe/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2004 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+#
+
+KMOD=	ath_rate_onoe
+SRCS=	onoe.c
+SRCS+=	opt_bdg.h device_if.h bus_if.h pci_if.h opt_inet.h
+
+HAL= ${.CURDIR}/../../../../../contrib/dev/ath
+CFLAGS+=  -I. -I${HAL}/freebsd -I${HAL}
+
+.include <bsd.kmod.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/onoe/onoe.c src/sys/dev/netif/ath/ath_rate/onoe/onoe.c
--- src.preview/sys/dev/netif/ath/ath_rate/onoe/onoe.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/onoe/onoe.c	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,539 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * Atsushi Onoe's rate control algorithm.
+ */
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h> 
+#include <netinet/if_ether.h>
+#endif
+
+#include "../../if_athvar.h"
+#include "../../if_athrate.h"
+#include "onoe.h"
+#include "contrib/dev/ath/ah_desc.h"
+
+#define	ONOE_DEBUG
+#ifdef ONOE_DEBUG
+enum {
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+};
+#define	DPRINTF(sc, _fmt, ...) do {				\
+	if (sc->sc_debug & ATH_DEBUG_RATE)			\
+		printf(_fmt, __VA_ARGS__);			\
+} while (0)
+#else
+#define	DPRINTF(sc, _fmt, ...)
+#endif
+
+/*
+ * Default parameters for the rate control algorithm.  These are
+ * all tunable with sysctls.  The rate controller runs periodically
+ * (each ath_rateinterval ms) analyzing transmit statistics for each
+ * neighbor/station (when operating in station mode this is only the AP).
+ * If transmits look to be working well over a sampling period then
+ * it gives a "raise rate credit".  If transmits look to not be working
+ * well than it deducts a credit.  If the credits cross a threshold then
+ * the transmit rate is raised.  Various error conditions force the
+ * the transmit rate to be dropped.
+ *
+ * The decision to issue/deduct a credit is based on the errors and
+ * retries accumulated over the sampling period.  ath_rate_raise defines
+ * the percent of retransmits for which a credit is issued/deducted.
+ * ath_rate_raise_threshold defines the threshold on credits at which
+ * the transmit rate is increased.
+ *
+ * XXX this algorithm is flawed.
+ */
+static	int ath_rateinterval = 1000;		/* rate ctl interval (ms)  */
+static	int ath_rate_raise = 10;		/* add credit threshold */
+static	int ath_rate_raise_threshold = 10;	/* rate ctl raise threshold */
+
+static void	ath_ratectl(void *);
+static void	ath_rate_update(struct ath_softc *, struct ieee80211_node *,
+			int rate);
+static void	ath_rate_ctl_start(struct ath_softc *, struct ieee80211_node *);
+static void	ath_rate_ctl(void *, struct ieee80211_node *);
+
+void
+ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+{
+	/* NB: assumed to be zero'd by caller */
+	ath_rate_update(sc, &an->an_node, 0);
+}
+
+void
+ath_rate_node_cleanup(struct ath_softc *sc __unused,
+	struct ath_node *an __unused)
+{
+}
+
+void
+ath_rate_findrate(struct ath_softc *sc __unused, struct ath_node *an,
+	int shortPreamble, size_t frameLen __unused,
+	u_int8_t *rix, int *try0, u_int8_t *txrate)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	*rix = on->on_tx_rix0;
+	*try0 = on->on_tx_try0;
+	if (shortPreamble)
+		*txrate = on->on_tx_rate0sp;
+	else
+		*txrate = on->on_tx_rate0;
+}
+
+void
+ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,
+	struct ath_desc *ds, int shortPreamble __unused,
+	u_int8_t rix __unused)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	ath_hal_setupxtxdesc(sc->sc_ah, ds
+		, on->on_tx_rate1sp, 2	/* series 1 */
+		, on->on_tx_rate2sp, 2	/* series 2 */
+		, on->on_tx_rate3sp, 2	/* series 3 */
+	);
+}
+
+void
+ath_rate_tx_complete(struct ath_softc *sc __unused,
+	struct ath_node *an, const struct ath_desc *ds, const struct ath_desc *ds0)
+{
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+
+	if (ds->ds_txstat.ts_status == 0)
+		on->on_tx_ok++;
+	else
+		on->on_tx_err++;
+	on->on_tx_retr += ds->ds_txstat.ts_shortretry
+			+ ds->ds_txstat.ts_longretry;
+}
+
+void
+ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+{
+	if (isnew)
+		ath_rate_ctl_start(sc, &an->an_node);
+}
+
+static void
+ath_rate_update(struct ath_softc *sc, struct ieee80211_node *ni, int rate)
+{
+	struct ath_node *an = ATH_NODE(ni);
+	struct onoe_node *on = ATH_NODE_ONOE(an);
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	u_int8_t rix;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+#if 0
+	if ( !sc || !an || !on || !rt || !rt->info ) {
+		printf("ath_rate_update: %p %p %p %p %p\n",
+			sc, an, on, rt, (rt) ? rt->info : 0 );         
+		return;
+	}
+
+	DPRINTF(sc, "%s: set xmit rate for %s to %dM\n",
+	    __func__, /* ether_sprintf(ni->ni_macaddr) */ "xx",
+	    ni->ni_rates.rs_nrates > 0 ?
+		(ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL) / 2 : 0);
+#endif
+
+	ni->ni_txrate = rate;
+	/* XXX management/control frames always go at the lowest speed */
+	an->an_tx_mgtrate = rt->info[0].rateCode;
+	an->an_tx_mgtratesp = an->an_tx_mgtrate | rt->info[0].shortPreamble;
+	/*
+	 * Before associating a node has no rate set setup
+	 * so we can't calculate any transmit codes to use.
+	 * This is ok since we should never be sending anything
+	 * but management frames and those always go at the
+	 * lowest hardware rate.
+	 */
+	if (ni->ni_rates.rs_nrates == 0)
+		goto done;
+	on->on_tx_rix0 = sc->sc_rixmap[
+		ni->ni_rates.rs_rates[rate] & IEEE80211_RATE_VAL];
+	on->on_tx_rate0 = rt->info[on->on_tx_rix0].rateCode;
+	
+	on->on_tx_rate0sp = on->on_tx_rate0 |
+		rt->info[on->on_tx_rix0].shortPreamble;
+	if (sc->sc_mrretry) {
+		/*
+		 * Hardware supports multi-rate retry; setup two
+		 * step-down retry rates and make the lowest rate
+		 * be the ``last chance''.  We use 4, 2, 2, 2 tries
+		 * respectively (4 is set here, the rest are fixed
+		 * in the xmit routine).
+		 */
+		on->on_tx_try0 = 1 + 3;		/* 4 tries at rate 0 */
+		if (--rate >= 0) {
+			rix = sc->sc_rixmap[
+				ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+			on->on_tx_rate1 = rt->info[rix].rateCode;
+			on->on_tx_rate1sp = on->on_tx_rate1 |
+				rt->info[rix].shortPreamble;
+		} else {
+			on->on_tx_rate1 = on->on_tx_rate1sp = 0;
+		}
+		if (--rate >= 0) {
+			rix = sc->sc_rixmap[
+				ni->ni_rates.rs_rates[rate]&IEEE80211_RATE_VAL];
+			on->on_tx_rate2 = rt->info[rix].rateCode;
+			on->on_tx_rate2sp = on->on_tx_rate2 |
+				rt->info[rix].shortPreamble;
+		} else {
+			on->on_tx_rate2 = on->on_tx_rate2sp = 0;
+		}
+		if (rate > 0) {
+			/* NB: only do this if we didn't already do it above */
+			on->on_tx_rate3 = rt->info[0].rateCode;
+			on->on_tx_rate3sp =
+				an->an_tx_mgtrate | rt->info[0].shortPreamble;
+		} else {
+			on->on_tx_rate3 = on->on_tx_rate3sp = 0;
+		}
+	} else {
+		on->on_tx_try0 = ATH_TXMAXTRY;	/* max tries at rate 0 */
+		on->on_tx_rate1 = on->on_tx_rate1sp = 0;
+		on->on_tx_rate2 = on->on_tx_rate2sp = 0;
+		on->on_tx_rate3 = on->on_tx_rate3sp = 0;
+	}
+done:
+	on->on_tx_ok = on->on_tx_err = on->on_tx_retr = on->on_tx_upper = 0;
+}
+
+/*
+ * Set the starting transmit rate for a node.
+ */
+static void
+ath_rate_ctl_start(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+	struct ieee80211com *ic = &sc->sc_ic;
+	int srate;
+
+	KASSERT(ni->ni_rates.rs_nrates > 0, ("no rates"));
+	if (ic->ic_fixed_rate == -1) {
+		/*
+		 * No fixed rate is requested. For 11b start with
+		 * the highest negotiated rate; otherwise, for 11g
+		 * and 11a, we start "in the middle" at 24Mb or 36Mb.
+		 */
+		srate = ni->ni_rates.rs_nrates - 1;
+		if (sc->sc_curmode != IEEE80211_MODE_11B) {
+			/*
+			 * Scan the negotiated rate set to find the
+			 * closest rate.
+			 */
+			/* NB: the rate set is assumed sorted */
+			for (; srate >= 0 && RATE(srate) > 72; srate--)
+				;
+			KASSERT(srate >= 0, ("bogus rate set"));
+		}
+	} else {
+		/*
+		 * A fixed rate is to be used; ic_fixed_rate is an
+		 * index into the supported rate set.  Convert this
+		 * to the index into the negotiated rate set for
+		 * the node.  We know the rate is there because the
+		 * rate set is checked when the station associates.
+		 */
+		const struct ieee80211_rateset *rs =
+			&ic->ic_sup_rates[ic->ic_curmode];
+		int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
+		/* NB: the rate set is assumed sorted */
+		srate = ni->ni_rates.rs_nrates - 1;
+		for (; srate >= 0 && RATE(srate) != r; srate--)
+			;
+		KASSERT(srate >= 0,
+			("fixed rate %d not in rate set", ic->ic_fixed_rate));
+	}
+	ath_rate_update(sc, ni, srate);
+#undef RATE
+
+static void
+ath_rate_cb(void *arg, struct ieee80211_node *ni)
+{
+    struct ath_softc *sc = arg;
+        ath_rate_update(sc, ni, 0);
+}
+
+/*
+ * Reset the rate control state for each 802.11 state transition.
+ */
+void
+ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+{
+	struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+
+	if (state == IEEE80211_S_INIT) {
+		callout_stop(&osc->timer);
+		return;
+	}
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * Reset local xmit state; this is really only
+		 * meaningful when operating in station mode.
+		 */
+		ni = ic->ic_bss;
+		if (state == IEEE80211_S_RUN) {
+			ath_rate_ctl_start(sc, ni);
+		} else {
+			ath_rate_update(sc, ni, 0);
+		}
+	} else {
+		/*
+		 * When operating as a station the node table holds
+		 * the AP's that were discovered during scanning.
+		 * For any other operating mode we want to reset the
+		 * tx rate state of each node.
+		 */
+		ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_cb, sc);
+		ath_rate_update(sc, ic->ic_bss, 0);
+	}
+	if (ic->ic_fixed_rate == -1 && state == IEEE80211_S_RUN) {
+		int interval;
+		/*
+		 * Start the background rate control thread if we
+		 * are not configured to use a fixed xmit rate.
+		 */
+		interval = ath_rateinterval;
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			interval /= 2;
+		callout_reset(&osc->timer, (interval * hz) / 1000,
+			ath_ratectl, &sc->sc_if);
+	}
+}
+
+/* 
+ * Examine and potentially adjust the transmit rate.
+ */
+static void
+ath_rate_ctl(void *arg, struct ieee80211_node *ni)
+{
+	struct ath_softc *sc = arg;
+	struct onoe_node *on = ATH_NODE_ONOE(ATH_NODE(ni));
+	struct ieee80211_rateset *rs = &ni->ni_rates;
+	int dir = 0, nrate, enough;
+
+	/*
+	 * Rate control
+	 * XXX: very primitive version.
+	 */
+	enough = (on->on_tx_ok + on->on_tx_err >= 10);
+
+	/* no packet reached -> down */
+	if (on->on_tx_err > 0 && on->on_tx_ok == 0)
+		dir = -1;
+
+	/* all packets needs retry in average -> down */
+	if (enough && on->on_tx_ok < on->on_tx_retr)
+		dir = -1;
+
+	/* no error and less than rate_raise% of packets need retry -> up */
+	if (enough && on->on_tx_err == 0 &&
+	    on->on_tx_retr < (on->on_tx_ok * ath_rate_raise) / 100)
+		dir = 1;
+
+#if 0
+	DPRINTF(sc, "%s: ok %d err %d retr %d upper %d dir %d\n",
+		/* ether_sprintf(ni->ni_macaddr) */ "xx",
+		on->on_tx_ok, on->on_tx_err, on->on_tx_retr,
+		on->on_tx_upper, dir);
+#endif
+
+	nrate = ni->ni_txrate;
+	switch (dir) {
+	case 0:
+		if (enough && on->on_tx_upper > 0)
+			on->on_tx_upper--;
+		break;
+	case -1:
+		if (nrate > 0) {
+			nrate--;
+			sc->sc_stats.ast_rate_drop++;
+		}
+		on->on_tx_upper = 0;
+		break;
+	case 1:
+		/* raise rate if we hit rate_raise_threshold */
+		if (++on->on_tx_upper < ath_rate_raise_threshold)
+			break;
+		on->on_tx_upper = 0;
+		if (nrate + 1 < rs->rs_nrates) {
+			nrate++;
+			sc->sc_stats.ast_rate_raise++;
+		}
+		break;
+	}
+
+	if (nrate != ni->ni_txrate) {
+#if 0
+		DPRINTF(sc, "%s: %dM -> %dM (%d ok, %d err, %d retr)\n",
+		    __func__,
+		    (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL) / 2,
+		    (rs->rs_rates[nrate] & IEEE80211_RATE_VAL) / 2,
+		    on->on_tx_ok, on->on_tx_err, on->on_tx_retr);
+#endif
+		ath_rate_update(sc, ni, nrate);
+	} else if (enough)
+		on->on_tx_ok = on->on_tx_err = on->on_tx_retr = 0;
+}
+
+static void
+ath_ratectl(void *arg)
+{
+	struct ifnet *ifp = arg;
+	struct ath_softc *sc = ifp->if_softc;
+	struct onoe_softc *osc = (struct onoe_softc *) sc->sc_rc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	int interval;
+
+	if (ifp->if_flags & IFF_RUNNING) {
+		sc->sc_stats.ast_rate_calls++;
+
+		if (ic->ic_opmode == IEEE80211_M_STA)
+			ath_rate_ctl(sc, ic->ic_bss);	/* NB: no reference */
+		else
+			ieee80211_iterate_nodes(&ic->ic_sta, ath_rate_ctl, sc);
+	}
+	interval = ath_rateinterval;
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		interval /= 2;
+	callout_reset(&osc->timer, (interval * hz) / 1000,
+		ath_ratectl, &sc->sc_if);
+}
+
+static void
+ath_rate_sysctlattach(struct ath_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = &sc->sysctl_ctx;
+	struct sysctl_oid *tree = sc->sysctl_tree;
+
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rate_interval", CTLFLAG_RW, &ath_rateinterval, 0,
+		"rate control: operation interval (ms)");
+	/* XXX bounds check values */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rate_raise", CTLFLAG_RW, &ath_rate_raise, 0,
+		"rate control: retry threshold to credit rate raise (%%)");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rate_raise_threshold", CTLFLAG_RW, &ath_rate_raise_threshold,0,
+		"rate control: # good periods before raising rate");
+}
+
+struct ath_ratectrl *
+ath_rate_attach(struct ath_softc *sc)
+{
+	struct onoe_softc *osc;
+
+	osc = malloc(sizeof(struct onoe_softc), M_DEVBUF, M_NOWAIT|M_ZERO);
+	if (osc == NULL)
+		return NULL;
+	osc->arc.arc_space = sizeof(struct onoe_node);
+	callout_init(&osc->timer);
+	ath_rate_sysctlattach(sc);
+
+	return &osc->arc;
+}
+
+void
+ath_rate_detach(struct ath_ratectrl *arc)
+{
+	struct onoe_softc *osc = (struct onoe_softc *) arc;
+
+	callout_stop(&osc->timer);
+	free(osc, M_DEVBUF);
+}
+
+/*
+ * Module glue.
+ */
+static int
+onoe_modevent(module_t mod __unused, int type, void *unused __unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		printf("ath_rate: <Atsushi Onoe's rate control algorithm>\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t onoe_mod = {
+	"ath_rate",
+	onoe_modevent,
+	0
+};
+DECLARE_MODULE(ath_rate, onoe_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(ath_rate, 1);
+MODULE_DEPEND(ath_rate, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/onoe/onoe.h src/sys/dev/netif/ath/ath_rate/onoe/onoe.h
--- src.preview/sys/dev/netif/ath/ath_rate/onoe/onoe.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/onoe/onoe.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/dev/ath/ath_rate/onoe/onoe.h,v 1.2 2004/12/31 22:41:45 sam Exp $
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_RATE_ONOE_H
+#define _DEV_ATH_RATE_ONOE_H
+
+/* per-device state */
+struct onoe_softc {
+	struct ath_ratectrl arc;	/* base state */
+	struct callout timer;		/* periodic timer */
+};
+
+/* per-node state */
+struct onoe_node {
+	u_int		on_tx_ok;	/* tx ok pkt */
+	u_int		on_tx_err;	/* tx !ok pkt */
+	u_int		on_tx_retr;	/* tx retry count */
+	int		on_tx_upper;	/* tx upper rate req cnt */
+	u_int8_t	on_tx_rix0;	/* series 0 rate index */
+	u_int8_t	on_tx_try0;	/* series 0 try count */
+	u_int8_t	on_tx_rate0;	/* series 0 h/w rate */
+	u_int8_t	on_tx_rate1;	/* series 1 h/w rate */
+	u_int8_t	on_tx_rate2;	/* series 2 h/w rate */
+	u_int8_t	on_tx_rate3;	/* series 3 h/w rate */
+	u_int8_t	on_tx_rate0sp;	/* series 0 short preamble h/w rate */
+	u_int8_t	on_tx_rate1sp;	/* series 1 short preamble h/w rate */
+	u_int8_t	on_tx_rate2sp;	/* series 2 short preamble h/w rate */
+	u_int8_t	on_tx_rate3sp;	/* series 3 short preamble h/w rate */
+};
+#define	ATH_NODE_ONOE(an)	((struct onoe_node *)&an[1])
+#endif /* _DEV_ATH_RATE_ONOE_H */
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/sample/Makefile src/sys/dev/netif/ath/ath_rate/sample/Makefile
--- src.preview/sys/dev/netif/ath/ath_rate/sample/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/sample/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,46 @@
+#
+# Copyright (c) 2004 Sam Leffler, Errno Consulting
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer,
+#    without modification.
+# 2. Redistributions in binary form must reproduce at minimum a disclaimer
+#    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+#    redistribution must be conditioned upon including a substantially
+#    similar Disclaimer requirement for further binary redistribution.
+# 3. Neither the names of the above-listed copyright holders nor the names
+#    of any contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# Alternatively, this software may be distributed under the terms of the
+# GNU General Public License ("GPL") version 2 as published by the Free
+# Software Foundation.
+#
+# NO WARRANTY
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+# THE POSSIBILITY OF SUCH DAMAGES.
+#
+# $FreeBSD: /repoman/r/ncvs/src/sys/modules/ath_rate_amrr/Makefile,v 1.1 2004/12/08 17:38:37 sam Exp $
+#
+
+KMOD=	ath_rate
+SRCS=	sample.c
+SRCS+=	opt_bdg.h device_if.h bus_if.h pci_if.h opt_inet.h
+
+HAL=	${.CURDIR}/../../../../../contrib/dev/ath
+CFLAGS+= -g -I. -I${HAL}/freebsd -I${HAL}
+
+.include <bsd.kmod.mk>
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/sample/sample.c src/sys/dev/netif/ath/ath_rate/sample/sample.c
--- src.preview/sys/dev/netif/ath/ath_rate/sample/sample.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/sample/sample.c	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,855 @@
+/*-
+ * Copyright (c) 2005 John Bicket
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * John Bicket's SampleRate control algorithm.
+ */
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#include <sys/socket.h>
+ 
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>		/* XXX for ether_sprintf */
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h> 
+#include <netinet/if_ether.h>
+#endif
+
+#include "../../if_athvar.h"
+#include "../../if_athrate.h"
+#include "sample.h"
+#include "contrib/dev/ath/ah_desc.h"
+
+#define	SAMPLE_DEBUG
+#ifdef SAMPLE_DEBUG
+enum {
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+};
+#define	DPRINTF(sc, _fmt, ...) do {				\
+	if (sc->sc_debug & ATH_DEBUG_RATE)			\
+		printf(_fmt, __VA_ARGS__);			\
+} while (0)
+#else
+#define	DPRINTF(sc, _fmt, ...)
+#endif
+
+/*
+ * This file is an implementation of the SampleRate algorithm
+ * in "Bit-rate Selection in Wireless Networks"
+ * (http://www.pdos.lcs.mit.edu/papers/jbicket-ms.ps)
+ *
+ * SampleRate chooses the bit-rate it predicts will provide the most
+ * throughput based on estimates of the expected per-packet
+ * transmission time for each bit-rate.  SampleRate periodically sends
+ * packets at bit-rates other than the current one to estimate when
+ * another bit-rate will provide better performance. SampleRate
+ * switches to another bit-rate when its estimated per-packet
+ * transmission time becomes smaller than the current bit-rate's.
+ * SampleRate reduces the number of bit-rates it must sample by
+ * eliminating those that could not perform better than the one
+ * currently being used.  SampleRate also stops probing at a bit-rate
+ * if it experiences several successive losses.
+ *
+ * The difference between the algorithm in the thesis and the one in this
+ * file is that the one in this file uses a ewma instead of a window.
+ *
+ */
+
+#define STALE_FAILURE_TIMEOUT_MS 10000
+
+static void	ath_rate_ctl_reset(struct ath_softc *, struct ieee80211_node *);
+
+static __inline int size_to_bin(int size) 
+{
+	int x = 0;
+	for (x = 0; x < NUM_PACKET_SIZE_BINS; x++) {
+		if (size <= packet_size_bins[x]) {
+			return x;
+		}
+	}
+	return NUM_PACKET_SIZE_BINS-1;
+}
+static __inline int bin_to_size(int ndx) {
+	return packet_size_bins[ndx];
+}
+
+static __inline int rate_to_ndx(struct sample_node *sn, int rate) {
+	int x = 0;
+	for (x = 0; x < sn->num_rates; x++) {
+		if (sn->rates[x].rate == rate) {
+			return x;
+		}      
+	}
+	return -1;
+}
+
+/*
+ * Setup rate codes for management/control frames.  We force
+ * all such frames to the lowest rate.
+ */
+static void
+ath_rate_setmgtrates(struct ath_softc *sc, struct ath_node *an)
+{
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+
+	/* setup rates for management frames */
+	/* XXX management/control frames always go at lowest speed */
+	an->an_tx_mgtrate = rt->info[0].rateCode;
+	an->an_tx_mgtratesp = an->an_tx_mgtrate
+			    | rt->info[0].shortPreamble;
+}
+
+void
+ath_rate_node_init(struct ath_softc *sc, struct ath_node *an)
+{
+	DPRINTF(sc, "%s:\n", __func__);
+	/* NB: assumed to be zero'd by caller */
+	ath_rate_setmgtrates(sc, an);
+}
+
+void
+ath_rate_node_cleanup(struct ath_softc *sc, struct ath_node *an __unused)
+{
+	DPRINTF(sc, "%s:\n", __func__);
+}
+
+
+/*
+ * returns the ndx with the lowest average_tx_time,
+ * or -1 if all the average_tx_times are 0.
+ */
+static __inline int best_rate_ndx(struct sample_node *sn, int size_bin, 
+				  int require_acked_before)
+{
+	int x = 0;
+        int best_rate_ndx_val = 0;
+        int best_rate_tt = 0;
+        for (x = 0; x < sn->num_rates; x++) {
+		int tt = sn->stats[size_bin][x].average_tx_time;
+		if (tt <= 0 || (require_acked_before && 
+				!sn->stats[size_bin][x].packets_acked)) {
+			continue;
+		}
+		if (!best_rate_tt || best_rate_tt > tt) {
+			best_rate_tt = tt;
+			best_rate_ndx_val = x;
+		}
+        }
+        return (best_rate_tt) ? best_rate_ndx_val : -1;
+}
+
+/*
+ * pick a ndx s.t. the perfect_tx_time
+ * is less than the best bit-rate's average_tx_time
+ * and the ndx has not had four successive failures.
+ */
+static __inline int pick_sample_ndx(struct sample_node *sn, int size_bin) 
+{
+	int x = 0;
+	int current_ndx = 0;
+	unsigned current_tt = 0;
+	
+	current_ndx = sn->current_rate[size_bin];
+	if (current_ndx < 0) {
+		/* no successes yet, send at the lowest bit-rate */
+		return 0;
+	}
+	
+	current_tt = sn->stats[size_bin][current_ndx].average_tx_time;
+
+	for (x = 0; x < sn->num_rates; x++) {
+		int ndx = (sn->last_sample_ndx[size_bin] + 1 + x) % sn->num_rates;
+		/* 
+		 * clear any stale stuff out.
+		 */
+		if (ticks - sn->stats[size_bin][ndx].last_tx > ((hz * STALE_FAILURE_TIMEOUT_MS)/1000)) {
+			sn->stats[size_bin][ndx].average_tx_time = sn->stats[size_bin][ndx].perfect_tx_time;
+			sn->stats[size_bin][ndx].successive_failures = 0;
+			sn->stats[size_bin][ndx].tries = 0;
+			sn->stats[size_bin][ndx].total_packets = 0;
+			sn->stats[size_bin][ndx].packets_acked = 0;
+		}
+
+		if (ndx != current_ndx &&
+		    sn->stats[size_bin][ndx].perfect_tx_time < current_tt &&
+		    sn->stats[size_bin][ndx].successive_failures < 4) {
+			sn->last_sample_ndx[size_bin] = ndx;
+			return ndx;
+		}
+	}
+	return current_ndx;
+}
+
+void
+ath_rate_findrate(struct ath_softc *sc, struct ath_node *an,
+		  int shortPreamble, size_t frameLen,
+		  u_int8_t *rix, int *try0, u_int8_t *txrate)
+{
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
+	struct ieee80211com *ic = &sc->sc_ic;
+	int ndx, size_bin, mrr, best_ndx;
+	unsigned average_tx_time;
+
+	mrr = sc->sc_mrretry && !(ic->ic_flags & IEEE80211_F_USEPROT) && 
+		!(frameLen > ic->ic_rtsthreshold);
+	size_bin = size_to_bin(frameLen);
+	best_ndx = best_rate_ndx(sn, size_bin, !mrr);
+
+	if (best_ndx >= 0) {
+		average_tx_time = sn->stats[size_bin][best_ndx].average_tx_time;
+	} else {
+		average_tx_time = 0;
+	}
+	if (sn->static_rate_ndx != -1) {
+		ndx = sn->static_rate_ndx;
+		*try0 = ATH_TXMAXTRY;
+	} else {
+		ndx = 0;
+		*try0 = mrr ? 2 : ATH_TXMAXTRY;
+
+		DPRINTF(sc, "%s: %6D size %d mrr %d packets_sent %d best_ndx %d "
+			"sample tt %d packets since %d\n"
+			, __func__, an->an_node.ni_macaddr, ":"
+			, packet_size_bins[size_bin]
+			, mrr
+			, sn->packets_sent[size_bin]
+			, best_ndx
+			, sn->sample_tt[size_bin]
+			, sn->packets_since_sample[size_bin]
+		);
+		if (!sn->packets_sent[size_bin]) {
+			/* no packets sent */
+			if (best_ndx == -1) {
+				ndx = sn->num_rates - 1;
+				if (sc->sc_curmode != IEEE80211_MODE_11B) {
+					for (; ndx >= 0 && sn->rates[ndx].rate > 72; ndx--)
+						;
+						
+				}
+			} else {
+				ndx = best_ndx;
+			}
+		} else if (best_ndx == -1) {
+			/* no packet has succeeded yet */
+			if (mrr) {
+				/* 
+				 * no packet has succeeded, try the
+				 * highest bitrate that hasn't failed 
+				 */  
+				for (ndx = sn->num_rates-1; ndx >= 0; ndx--) {
+					if (sn->stats[size_bin][ndx].successive_failures == 0) {
+						break;
+					}
+				}
+			} else {
+				ndx = sn->num_rates - 1;
+				if (sc->sc_curmode != IEEE80211_MODE_11B) {
+					for (; ndx >= 0 && sn->rates[ndx].rate > 72; ndx--)
+						;
+						
+				}
+			}
+		} else if (sn->sample_tt[size_bin] < (sn->packets_since_sample[size_bin]*ssc->ath_sample_rate/100) * average_tx_time &&
+			   sn->packets_since_sample[size_bin] > 15) {
+			/*
+			 * we want to limit the time measuring the performance
+			 * of other bit-rates to ath_sample_rate% of the
+			 * total transmission time.
+			 */
+			ndx = pick_sample_ndx(sn, size_bin);
+			if (ndx != sn->current_rate[size_bin]) {
+				DPRINTF(sc, "%s: %6D size %d last sample tt %d sampling %d packets since %d\n",
+					__func__,
+					an->an_node.ni_macaddr, ":",
+					packet_size_bins[size_bin],
+					sn->sample_tt[size_bin], 
+					sn->rates[ndx].rate,
+					sn->packets_since_sample[size_bin]);
+				sn->current_sample_ndx[size_bin] = ndx;
+			} else {
+				sn->current_sample_ndx[size_bin] = -1;
+			}
+			sn->packets_since_sample[size_bin] = 0;
+
+		} else {
+			sn->packets_since_sample[size_bin]++;
+			/*
+			 * don't switch bit-rates every packet.  only
+			 * switch during the first few packets we send
+			 * or after 100 packets, or if the current
+			 * bit-rate begins to perform twice as bad as
+			 * another one.
+			 */
+			if (sn->packets_sent[size_bin] < 20 ||
+			    ticks - ((hz*2000)/1000) > sn->jiffies_since_switch[size_bin] ||
+			    average_tx_time * 2 < sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time ) {
+				if (sn->packets_sent[size_bin] > 20) {
+					DPRINTF(sc, "%s: %6D size %d switch rate %d (%d/%d) -> %d (%d/%d) after %d packets mmr %d\n",
+						__func__,
+						an->an_node.ni_macaddr, ":",
+						packet_size_bins[size_bin],
+						sn->rates[sn->current_rate[size_bin]].rate,
+						sn->stats[size_bin][sn->current_rate[size_bin]].average_tx_time,
+						sn->stats[size_bin][sn->current_rate[size_bin]].perfect_tx_time,
+						sn->rates[best_ndx].rate,
+						sn->stats[size_bin][best_ndx].average_tx_time,
+						sn->stats[size_bin][best_ndx].perfect_tx_time,
+						sn->packets_since_switch[size_bin],
+						mrr);
+				}
+				sn->packets_since_switch[size_bin] = 0;
+				sn->current_rate[size_bin] = best_ndx;
+				sn->jiffies_since_switch[size_bin] = ticks;
+			}
+			ndx = sn->current_rate[size_bin];
+			sn->packets_since_switch[size_bin]++;
+		}
+		
+	}
+
+	if (ndx < 0) {
+		ndx = 0;
+	}
+	*rix = sn->rates[ndx].rix;
+	if (shortPreamble) {
+		*txrate = sn->rates[ndx].shortPreambleRateCode;
+	} else {
+		*txrate = sn->rates[ndx].rateCode;
+	}
+	sn->packets_sent[size_bin]++;
+	an->an_node.ni_txrate = ndx;
+}
+
+void
+ath_rate_setupxtxdesc(struct ath_softc *sc, struct ath_node *an,
+		      struct ath_desc *ds, int shortPreamble, u_int8_t rix __unused)
+{
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	int rateCode = -1;
+	int frame_size, size_bin, best_ndx, ndx;
+
+	frame_size = ds->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
+	KASSERT(frame_size != 0, ("no frame size"));
+	size_bin = size_to_bin(frame_size);
+	best_ndx = best_rate_ndx(sn, size_bin, 0);
+
+	if (best_ndx == -1 || !sn->stats[size_bin][best_ndx].packets_acked) {
+		/* 
+		 * no packet has succeeded, so also try at the
+		 * lowest bitate.
+		 */
+		ndx = 0;
+	} else {
+		/*
+		 * we're trying a different bit-rate, and it could be lossy, 
+		 * so if it fails try at the best bit-rate.
+		 */
+		ndx = best_ndx;
+	}
+	KASSERT(0 <= ndx && ndx < IEEE80211_RATE_MAXSIZE,
+		("invalid ndx %d", ndx));
+	if (shortPreamble) {
+		rateCode = sn->rates[ndx].shortPreambleRateCode;
+	} else {
+		rateCode = sn->rates[ndx].rateCode;
+	}
+	ath_hal_setupxtxdesc(sc->sc_ah, ds
+			     , rateCode, 3	        /* series 1 */
+			     , sn->rates[0].rateCode, 3	/* series 2 */
+			     , 0, 0	                /* series 3 */
+			     );
+}
+
+static void
+update_stats(struct ath_softc *sc, struct ath_node *an, 
+		  int frame_size,
+		  int ndx0, int tries0,
+		  int ndx1, int tries1,
+		  int ndx2, int tries2,
+		  int ndx3, int tries3,
+		  int short_tries, int tries, int status)
+{
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	struct sample_softc *ssc = ATH_SOFTC_SAMPLE(sc);
+	int tt = 0;
+	int tries_so_far = 0;
+	int size_bin = 0;
+	int size = 0;
+	int rate = 0;
+
+	size_bin = size_to_bin(frame_size);
+	size = bin_to_size(size_bin);
+	rate = sn->rates[ndx0].rate;
+
+	tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx0].rix, 
+					short_tries-1, 
+					MIN(tries0, tries) - 1);
+	tries_so_far += tries0;
+	if (tries1 && tries0 < tries) {
+		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx1].rix, 
+						short_tries-1, 
+						MIN(tries1 + tries_so_far, tries) - tries_so_far - 1);
+	}
+	tries_so_far += tries1;
+
+	if (tries2 && tries0 + tries1 < tries) {
+		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx2].rix, 
+					       short_tries-1, 
+						MIN(tries2 + tries_so_far, tries) - tries_so_far - 1);
+	}
+
+	tries_so_far += tries2;
+
+	if (tries3 && tries0 + tries1 + tries2 < tries) {
+		tt += calc_usecs_unicast_packet(sc, size, sn->rates[ndx3].rix, 
+						short_tries-1, 
+						MIN(tries3 + tries_so_far, tries) - tries_so_far - 1);
+	}
+#ifdef SAMPLE_DEBUG
+	if (short_tries + tries > 3 || status) {
+		DPRINTF(sc, "%s: %6D size %d rate %d ndx %d tries (%d/%d) tries0 %d tt %d avg_tt %d perfect_tt %d status %d\n", 
+			__func__, an->an_node.ni_macaddr, ":",
+			size, 
+			rate, ndx0, short_tries, tries, tries0, tt, 
+			sn->stats[size_bin][ndx0].average_tx_time,
+			sn->stats[size_bin][ndx0].perfect_tx_time,
+			status);
+	}
+#endif /* SAMPLE_DEBUG */
+	if (sn->stats[size_bin][ndx0].total_packets < (100 / (100 - ssc->ath_smoothing_rate))) {
+		/* just average the first few packets */
+		int avg_tx = sn->stats[size_bin][ndx0].average_tx_time;
+		int packets = sn->stats[size_bin][ndx0].total_packets;
+		sn->stats[size_bin][ndx0].average_tx_time = (tt+(avg_tx*packets))/(packets+1);
+	} else {
+		/* use a ewma */
+		sn->stats[size_bin][ndx0].average_tx_time = 
+			((sn->stats[size_bin][ndx0].average_tx_time * ssc->ath_smoothing_rate) + 
+			 (tt * (100 - ssc->ath_smoothing_rate))) / 100;
+	}
+	
+	if (status) {
+		/* 
+		 * this packet failed - count this as a failure
+		 * for larger packets also, since we assume
+		 * if a small packet fails at a lower bit-rate 
+		 * then a larger one will also.
+		 */
+		int y;
+		for (y = size_bin; y < NUM_PACKET_SIZE_BINS; y++) {
+			sn->stats[y][ndx0].successive_failures++;
+			sn->stats[y][ndx0].last_tx = ticks;
+		}
+	} else {
+		sn->stats[size_bin][ndx0].packets_acked++;
+		sn->stats[size_bin][ndx0].successive_failures = 0;
+	}
+	sn->stats[size_bin][ndx0].tries += tries;
+	sn->stats[size_bin][ndx0].last_tx = ticks;
+	sn->stats[size_bin][ndx0].total_packets++;
+
+
+	if (ndx0 == sn->current_sample_ndx[size_bin]) {
+		DPRINTF(sc, "%s: %6D size %d sample rate %d tries (%d/%d) tt %d avg_tt (%d/%d) status %d\n", 
+			__func__, an->an_node.ni_macaddr, ":",
+			size, rate, short_tries, tries, tt, 
+			sn->stats[size_bin][ndx0].average_tx_time,
+			sn->stats[size_bin][ndx0].perfect_tx_time,
+			status);
+		sn->sample_tt[size_bin] = tt;
+		sn->current_sample_ndx[size_bin] = -1;
+	}
+}
+
+void
+ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
+	const struct ath_desc *ds, const struct ath_desc *ds0)
+{
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	const struct ar5212_desc *ads = (const struct ar5212_desc *)&ds->ds_ctl0;
+	int final_rate, short_tries, long_tries, frame_size;
+	int ndx = -1;
+
+	final_rate = sc->sc_hwmap[ds->ds_txstat.ts_rate &~ HAL_TXSTAT_ALTRATE].ieeerate;
+	short_tries = ds->ds_txstat.ts_shortretry + 1;
+	long_tries = ds->ds_txstat.ts_longretry + 1;
+	frame_size = ds0->ds_ctl0 & 0x0fff; /* low-order 12 bits of ds_ctl0 */
+	if (frame_size == 0)		    /* NB: should not happen */
+		frame_size = 1500;
+
+	if (sn->num_rates <= 0) {
+		DPRINTF(sc, "%s: %6D size %d status %d rate/try %d/%d "
+			"no rates yet\n", 
+			__func__, an->an_node.ni_macaddr, ":",
+			bin_to_size(size_to_bin(frame_size)),
+			ds->ds_txstat.ts_status,
+			short_tries, long_tries);
+		return;
+	}
+
+	if (sc->sc_mrretry && ds->ds_txstat.ts_status) {
+		/* this packet failed */
+		DPRINTF(sc, "%s: %6D size %d rate/try %d/%d %d/%d %d/%d %d/%d status %s retries (%d/%d)\n", 
+			__func__,
+			an->an_node.ni_macaddr, ":",
+			bin_to_size(size_to_bin(frame_size)),
+			sc->sc_hwmap[ads->xmit_rate0].ieeerate,
+				ads->xmit_tries0,
+			sc->sc_hwmap[ads->xmit_rate1].ieeerate,
+				ads->xmit_tries1,
+			sc->sc_hwmap[ads->xmit_rate2].ieeerate,
+				ads->xmit_tries2,
+			sc->sc_hwmap[ads->xmit_rate3].ieeerate,
+				ads->xmit_tries3,
+			ds->ds_txstat.ts_status ? "FAIL" : "OK",
+			short_tries, 
+			long_tries);
+	}
+
+	if (!(ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)) {
+		/* only one rate was used */
+		ndx = rate_to_ndx(sn, final_rate);
+		DPRINTF(sc, "%s: %6D size %d status %d rate/try %d/%d/%d\n", 
+			__func__, an->an_node.ni_macaddr, ":",
+			bin_to_size(size_to_bin(frame_size)),
+			ds->ds_txstat.ts_status,
+			ndx, short_tries, long_tries);
+		if (ndx >= 0 && ndx < sn->num_rates) {
+			update_stats(sc, an, frame_size, 
+				     ndx, long_tries,
+				     0, 0,
+				     0, 0,
+				     0, 0,
+				     short_tries, long_tries, ds->ds_txstat.ts_status);
+		}
+	} else {
+		int rate0, tries0, ndx0;
+		int rate1, tries1, ndx1;
+		int rate2, tries2, ndx2;
+		int rate3, tries3, ndx3;
+		int finalTSIdx = ads->final_ts_index;
+
+		/*
+		 * Process intermediate rates that failed.
+		 */
+
+		rate0 = sc->sc_hwmap[ads->xmit_rate0].ieeerate;
+		tries0 = ads->xmit_tries0;
+		ndx0 = rate_to_ndx(sn, rate0);
+
+		if ( ndx0 < 0 ) /* garbage rate, ignore */
+			ndx0 = tries0 = rate0 = 0;
+		
+		rate1 = sc->sc_hwmap[ads->xmit_rate1].ieeerate;
+		tries1 = ads->xmit_tries1;
+		ndx1 = rate_to_ndx(sn, rate1);
+
+		if ( ndx1 < 0 ) /* garbage rate, ignore */
+			ndx1 = tries1 = rate1 = 0;
+		
+		rate2 = sc->sc_hwmap[ads->xmit_rate2].ieeerate;
+		tries2 = ads->xmit_tries2;
+		ndx2 = rate_to_ndx(sn, rate2);
+
+		if ( ndx2 < 0 ) /* garbage rate, ignore */
+			ndx2 = tries2 = rate2 = 0;
+		
+		rate3 = sc->sc_hwmap[ads->xmit_rate3].ieeerate;
+		tries3 = ads->xmit_tries3;
+		ndx3 = rate_to_ndx(sn, rate3);
+
+		if ( ndx3 < 0 ) /* garbage rate, ignore */
+			ndx3 = tries3 = rate3 = 0;
+		
+#if 1
+		DPRINTF(sc, "%s: %6D size %d finaltsidx %d tries %d status %d rate/try %d/%d %d/%d %d/%d %d/%d\n", 
+			__func__, an->an_node.ni_macaddr, ":",
+			bin_to_size(size_to_bin(frame_size)),
+			finalTSIdx,
+			long_tries, 
+			ds->ds_txstat.ts_status,
+			rate0, tries0,
+			rate1, tries1,
+			rate2, tries2,
+			rate3, tries3);
+#endif
+
+		if (tries0) {
+			update_stats(sc, an, frame_size, 
+				     ndx0, tries0, 
+				     ndx1, tries1, 
+				     ndx2, tries2, 
+				     ndx3, tries3, 
+				     short_tries, ds->ds_txstat.ts_longretry + 1, 
+				     ds->ds_txstat.ts_status);
+		}
+		
+		if (tries1 && finalTSIdx > 0) {
+			update_stats(sc, an, frame_size, 
+				     ndx1, tries1, 
+				     ndx2, tries2, 
+				     ndx3, tries3, 
+				     0, 0, 
+				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0, 
+				     ds->ds_txstat.ts_status);
+		}
+
+		if (tries2 && finalTSIdx > 1) {
+			update_stats(sc, an, frame_size, 
+				     ndx2, tries2, 
+				     ndx3, tries3, 
+				     0, 0,
+				     0, 0,
+				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0 - tries1, 
+				     ds->ds_txstat.ts_status);
+		}
+
+		if (tries3 && finalTSIdx > 2) {
+			update_stats(sc, an, frame_size, 
+				     ndx3, tries3, 
+				     0, 0,
+				     0, 0,
+				     0, 0,
+				     short_tries, ds->ds_txstat.ts_longretry + 1 - tries0 - tries1 - tries2, 
+				     ds->ds_txstat.ts_status);
+		}
+	}
+}
+
+void
+ath_rate_newassoc(struct ath_softc *sc, struct ath_node *an, int isnew)
+{
+	DPRINTF(sc, "%s: %6D isnew %d\n", __func__,
+		an->an_node.ni_macaddr, ":", isnew);
+	if (isnew)
+		ath_rate_ctl_reset(sc, &an->an_node);
+}
+
+/*
+ * Initialize the tables for a node.
+ */
+static void
+ath_rate_ctl_reset(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+#define	RATE(_ix)	(ni->ni_rates.rs_rates[(_ix)] & IEEE80211_RATE_VAL)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_node *an = ATH_NODE(ni);
+	struct sample_node *sn = ATH_NODE_SAMPLE(an);
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	int x, y, srate;
+
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+        sn->static_rate_ndx = -1;
+	if (ic->ic_fixed_rate != -1) {
+		/*
+		 * A fixed rate is to be used; ic_fixed_rate is an
+		 * index into the supported rate set.  Convert this
+		 * to the index into the negotiated rate set for
+		 * the node.  We know the rate is there because the
+		 * rate set is checked when the station associates.
+		 */
+		const struct ieee80211_rateset *rs =
+			&ic->ic_sup_rates[ic->ic_curmode];
+		int r = rs->rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL;
+		/* NB: the rate set is assumed sorted */
+		srate = ni->ni_rates.rs_nrates - 1;
+		for (; srate >= 0 && RATE(srate) != r; srate--)
+			;
+		KASSERT(srate >= 0,
+			("fixed rate %d not in rate set", ic->ic_fixed_rate));
+                sn->static_rate_ndx = srate;
+	}
+
+        DPRINTF(sc, "%s: %6D size 1600 rate/tt", __func__, ni->ni_macaddr, ":");
+
+	sn->num_rates = ni->ni_rates.rs_nrates;
+        for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+		sn->rates[x].rate = ni->ni_rates.rs_rates[x] & IEEE80211_RATE_VAL;
+		sn->rates[x].rix = sc->sc_rixmap[sn->rates[x].rate];
+		sn->rates[x].rateCode = rt->info[sn->rates[x].rix].rateCode;
+		sn->rates[x].shortPreambleRateCode = 
+			rt->info[sn->rates[x].rix].rateCode | 
+			rt->info[sn->rates[x].rix].shortPreamble;
+
+		DPRINTF(sc, " %d/%d", sn->rates[x].rate,
+			calc_usecs_unicast_packet(sc, 1600, sn->rates[x].rix, 
+						  0,0));
+	}
+	DPRINTF(sc, "%s\n", "");
+	
+	/* set the visible bit-rate to the lowest one available */
+	ni->ni_txrate = 0;
+	sn->num_rates = ni->ni_rates.rs_nrates;
+	
+	for (y = 0; y < NUM_PACKET_SIZE_BINS; y++) {
+		int size = bin_to_size(y);
+		sn->packets_sent[y] = 0;
+		sn->current_sample_ndx[y] = -1;
+		sn->last_sample_ndx[y] = 0;
+		
+		for (x = 0; x < ni->ni_rates.rs_nrates; x++) {
+			sn->stats[y][x].successive_failures = 0;
+			sn->stats[y][x].tries = 0;
+			sn->stats[y][x].total_packets = 0;
+			sn->stats[y][x].packets_acked = 0;
+			sn->stats[y][x].last_tx = 0;
+			
+			sn->stats[y][x].perfect_tx_time = 
+				calc_usecs_unicast_packet(sc, size, 
+							  sn->rates[x].rix,
+							  0, 0);
+			sn->stats[y][x].average_tx_time = sn->stats[y][x].perfect_tx_time;
+		}
+	}
+#undef RATE
+}
+
+static void
+rate_cb(void *arg, struct ieee80211_node *ni)
+{
+	struct ath_softc *sc = arg;
+
+	ath_rate_newassoc(sc, ATH_NODE(ni), 1);
+}
+
+/*
+ * Reset the rate control state for each 802.11 state transition.
+ */
+void
+ath_rate_newstate(struct ath_softc *sc, enum ieee80211_state state)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	if (state == IEEE80211_S_RUN) {
+		if (ic->ic_opmode != IEEE80211_M_STA) {
+			/*
+			 * Sync rates for associated stations and neighbors.
+			 */
+			ieee80211_iterate_nodes(&ic->ic_sta, rate_cb, sc);
+		}
+		ath_rate_newassoc(sc, ATH_NODE(ic->ic_bss), 1);
+	}
+}
+
+static void
+ath_rate_sysctlattach(struct ath_softc *sc, struct sample_softc *osc)
+{
+	struct sysctl_ctx_list *ctx = &sc->sysctl_ctx;
+	struct sysctl_oid *tree = sc->sysctl_tree;
+	crit_enter();
+	/* XXX bounds check [0..100] */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"smoothing_rate", CTLFLAG_RW, &osc->ath_smoothing_rate, 0,
+		"rate control: retry threshold to credit rate raise (%%)");
+	/* XXX bounds check [2..100] */
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"sample_rate", CTLFLAG_RW, &osc->ath_sample_rate,0,
+		"rate control: # good periods before raising rate");
+	crit_exit();
+}
+
+struct ath_ratectrl *
+ath_rate_attach(struct ath_softc *sc)
+{
+	struct sample_softc *osc;
+	
+	DPRINTF(sc, "%s:\n", __func__);
+	osc = malloc(sizeof(struct sample_softc), M_DEVBUF, M_NOWAIT|M_ZERO);
+	if (osc == NULL)
+		return NULL;
+	osc->arc.arc_space = sizeof(struct sample_node);
+	osc->ath_smoothing_rate = 95;	/* ewma percentage (out of 100) */
+	osc->ath_sample_rate = 10;	/* send a different bit-rate 1/X packets */
+	ath_rate_sysctlattach(sc, osc);
+	return &osc->arc;
+}
+
+void
+ath_rate_detach(struct ath_ratectrl *arc)
+{
+	struct sample_softc *osc = (struct sample_softc *) arc;
+	
+	free(osc, M_DEVBUF);
+}
+
+/*
+ * Module glue.
+ */
+static int
+sample_modevent(module_t mod __unused, int type, void *unused __unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		if (bootverbose)
+			printf("ath_rate: version 1.2 <SampleRate bit-rate selection algorithm>\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t sample_mod = {
+	"ath_rate",
+	sample_modevent,
+	0
+};
+DECLARE_MODULE(ath_rate, sample_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(ath_rate, 1);
+MODULE_DEPEND(ath_rate, ath_hal, 1, 1, 1);	/* Atheros HAL */
+MODULE_DEPEND(ath_rate, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/dev/netif/ath/ath_rate/sample/sample.h src/sys/dev/netif/ath/ath_rate/sample/sample.h
--- src.preview/sys/dev/netif/ath/ath_rate/sample/sample.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/ath_rate/sample/sample.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 2005 John Bicket
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_RATE_SAMPLE_H
+#define _DEV_ATH_RATE_SAMPLE_H
+
+/* per-device state */
+struct sample_softc {
+	struct ath_ratectrl arc;	/* base state */
+	int	ath_smoothing_rate;	/* ewma percentage (out of 100) */
+	int	ath_sample_rate;	/* send a different bit-rate 1/X packets */
+};
+#define	ATH_SOFTC_SAMPLE(sc)	((struct sample_softc *)sc->sc_rc)
+
+struct rate_info {
+	int rate;
+	int rix;
+	int rateCode;
+	int shortPreambleRateCode;
+};
+
+
+struct rate_stats {	
+	int successive_failures;
+	int tries;
+	int total_packets;
+	int packets_acked;
+	unsigned perfect_tx_time; /* transmit time for 0 retries */
+	int last_tx;
+};
+
+/*
+ * for now, we track performance for three different packet
+ * size buckets
+ */
+#define NUM_PACKET_SIZE_BINS 3
+static int packet_size_bins[NUM_PACKET_SIZE_BINS] = {250, 1600, 3000};
+
+/* per-node state */
+struct sample_node {
+	int static_rate_ndx;
+	int num_rates;
+
+	struct rate_info rates[IEEE80211_RATE_MAXSIZE];
+	
+	struct rate_stats stats[NUM_PACKET_SIZE_BINS][IEEE80211_RATE_MAXSIZE];
+	int last_sample_ndx[NUM_PACKET_SIZE_BINS];
+
+	int current_sample_ndx[NUM_PACKET_SIZE_BINS];       
+	int packets_sent[NUM_PACKET_SIZE_BINS];
+
+	int current_rate[NUM_PACKET_SIZE_BINS];
+	int packets_since_switch[NUM_PACKET_SIZE_BINS];
+	unsigned jiffies_since_switch[NUM_PACKET_SIZE_BINS];
+
+	int packets_since_sample[NUM_PACKET_SIZE_BINS];
+	unsigned sample_tt[NUM_PACKET_SIZE_BINS];
+};
+#define	ATH_NODE_SAMPLE(an)	((struct sample_node *)&an[1])
+
+#ifndef MIN
+#define	MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+#define	MAX(a,b)	((a) > (b) ? (a) : (b))
+#endif
+
+#define WIFI_CW_MIN 31
+#define WIFI_CW_MAX 1023
+
+struct ar5212_desc {
+	/*
+	 * tx_control_0
+	 */
+	u_int32_t	frame_len:12;
+	u_int32_t	reserved_12_15:4;
+	u_int32_t	xmit_power:6;
+	u_int32_t	rts_cts_enable:1;
+	u_int32_t	veol:1;
+	u_int32_t	clear_dest_mask:1;
+	u_int32_t	ant_mode_xmit:4;
+	u_int32_t	inter_req:1;
+	u_int32_t	encrypt_key_valid:1;
+	u_int32_t	cts_enable:1;
+
+	/*
+	 * tx_control_1
+	 */
+	u_int32_t	buf_len:12;
+	u_int32_t	more:1;
+	u_int32_t	encrypt_key_index:7;
+	u_int32_t	frame_type:4;
+	u_int32_t	no_ack:1;
+	u_int32_t	comp_proc:2;
+	u_int32_t	comp_iv_len:2;
+	u_int32_t	comp_icv_len:2;
+	u_int32_t	reserved_31:1;
+
+	/*
+	 * tx_control_2
+	 */
+	u_int32_t	rts_duration:15;
+	u_int32_t	duration_update_enable:1;
+	u_int32_t	xmit_tries0:4;
+	u_int32_t	xmit_tries1:4;
+	u_int32_t	xmit_tries2:4;
+	u_int32_t	xmit_tries3:4;
+
+	/*
+	 * tx_control_3
+	 */
+	u_int32_t	xmit_rate0:5;
+	u_int32_t	xmit_rate1:5;
+	u_int32_t	xmit_rate2:5;
+	u_int32_t	xmit_rate3:5;
+	u_int32_t	rts_cts_rate:5;
+	u_int32_t	reserved_25_31:7;
+
+	/*
+	 * tx_status_0
+	 */
+	u_int32_t	frame_xmit_ok:1;
+	u_int32_t	excessive_retries:1;
+	u_int32_t	fifo_underrun:1;
+	u_int32_t	filtered:1;
+	u_int32_t	rts_fail_count:4;
+	u_int32_t	data_fail_count:4;
+	u_int32_t	virt_coll_count:4;
+	u_int32_t	send_timestamp:16;
+
+	/*
+	 * tx_status_1
+	 */
+	u_int32_t	done:1;
+	u_int32_t	seq_num:12;
+	u_int32_t	ack_sig_strength:8;
+	u_int32_t	final_ts_index:2;
+	u_int32_t	comp_success:1;
+	u_int32_t	xmit_antenna:1;
+	u_int32_t	reserved_25_31_x:7;
+} __packed;
+
+/*
+ * Calculate the transmit duration of a frame.
+ */
+static unsigned calc_usecs_unicast_packet(struct ath_softc *sc,
+				int length, 
+				int rix, int short_retries, int long_retries) {
+	const HAL_RATE_TABLE *rt = sc->sc_currates;
+	
+	/* pg 205 ieee.802.11.pdf */
+	unsigned t_slot = 20;
+	unsigned t_difs = 50; 
+	unsigned t_sifs = 10; 
+	struct ieee80211com *ic = &sc->sc_ic;
+	int tt = 0;
+	int x = 0;
+	int cw = WIFI_CW_MIN;
+	int cix = rt->info[rix].controlRate;
+	int rts = 0;
+	int cts = 0;
+	
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	if (rt->info[rix].phy == IEEE80211_T_OFDM) {
+		t_slot = 9;
+		t_sifs = 9;
+		t_difs = 28;
+	}
+
+	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+	    rt->info[rix].phy == IEEE80211_T_OFDM) {
+		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+			rts = 1;
+		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+			cts = 1;
+
+		cix = rt->info[sc->sc_protrix].controlRate;
+
+	}
+
+	if (length > ic->ic_rtsthreshold) {
+		rts = 1;
+	}
+
+	if (rts || cts) {
+		int ctsrate = rt->info[cix].rateCode;
+		int ctsduration = 0;
+		ctsrate |= rt->info[cix].shortPreamble;
+		if (rts)		/* SIFS + CTS */
+			ctsduration += rt->info[cix].spAckDuration;
+
+		ctsduration += ath_hal_computetxtime(sc->sc_ah,
+						     rt, length, rix, AH_TRUE);
+
+		if (cts)	/* SIFS + ACK */
+			ctsduration += rt->info[cix].spAckDuration;
+
+		tt += (short_retries + 1) * ctsduration;
+	}
+	tt += t_difs;
+	tt += (long_retries+1)*(t_sifs + rt->info[cix].spAckDuration);
+	tt += (long_retries+1)*ath_hal_computetxtime(sc->sc_ah, rt, length, 
+						rix, AH_TRUE);
+	for (x = 0; x <= short_retries + long_retries; x++) {
+		cw = MIN(WIFI_CW_MAX, (cw + 1) * 2);
+		tt += (t_slot * cw/2);
+	}
+	return tt;
+}
+#endif /* _DEV_ATH_RATE_SAMPLE_H */
diff -N -u -r src.preview/sys/dev/netif/ath/if_ath.c src/sys/dev/netif/ath/if_ath.c
--- src.preview/sys/dev/netif/ath/if_ath.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/if_ath.c	2005-09-23 11:57:00.000000000 -0400
@@ -0,0 +1,5115 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * DragonFly Port - Copyright (c) 2005 Andrew Atrens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * Driver for the Atheros Wireless LAN controller.
+ *
+ * This software is derived from work of Atsushi Onoe; his contribution
+ * is greatly appreciated.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/thread.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+#include <sys/callout.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+
+#include <sys/proc.h>
+#include <sys/ucred.h>
+#include <sys/thread2.h>
+
+#include <bus/pci/pcireg.h>
+#include <bus/pci/pcivar.h>
+
+#include <machine/bus.h>
+ 
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#ifndef IFQ_SET_MAXLEN
+#include <net/ifq_var.h>
+#endif
+#include <net/if_llc.h>
+
+
+#include <netproto/802_11/ieee80211_var.h>
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_radiotap.h>
+#include <netproto/802_11/if_wavelan_ieee.h>
+
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h> 
+#include <netinet/if_ether.h>
+#endif
+
+#define	AR_DEBUG
+#include "if_athvar.h"
+#include "../../../contrib/dev/ath/ah_desc.h"
+#include "../../../contrib/dev/ath/ah_devid.h"
+
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)							\
+	((u_int16_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)							\
+	((u_int32_t)							\
+	 ((((u_int8_t *)(p))[0]      ) | (((u_int8_t *)(p))[1] <<  8) |	\
+	  (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)))
+
+enum {
+	ATH_LED_TX,
+	ATH_LED_RX,
+	ATH_LED_POLL,
+};
+
+static void	ath_init(void *);
+static void	ath_stop_locked(struct ifnet *);
+static void	ath_stop(struct ifnet *);
+static void	ath_start(struct ifnet *);
+static int	ath_reset(struct ifnet *);
+static int	ath_media_change(struct ifnet *);
+static void	ath_watchdog(struct ifnet *);
+static int	ath_ioctl(struct ifnet *, u_long, caddr_t,  struct ucred *cr);
+static void	ath_fatal_proc(void *, int);
+static void	ath_rxorn_proc(void *, int);
+static void	ath_bmiss_proc(void *, int);
+static int	ath_key_alloc(struct ieee80211com *,
+			const struct ieee80211_key *,
+			ieee80211_keyix *, ieee80211_keyix *);
+static int	ath_key_delete(struct ieee80211com *,
+			const struct ieee80211_key *);
+static int	ath_key_set(struct ieee80211com *, const struct ieee80211_key *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+static void	ath_key_update_begin(struct ieee80211com *);
+static void	ath_key_update_end(struct ieee80211com *);
+static void	ath_mode_init(struct ath_softc *);
+static void	ath_setslottime(struct ath_softc *);
+static void	ath_updateslot(struct ifnet *);
+static int	ath_beaconq_setup(struct ath_hal *);
+static int	ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+static void	ath_beacon_setup(struct ath_softc *, struct ath_buf *);
+static void	ath_beacon_proc(void *, int);
+static void	ath_bstuck_proc(void *, int);
+static void	ath_beacon_free(struct ath_softc *);
+static void	ath_beacon_config(struct ath_softc *);
+static void	ath_descdma_cleanup(struct ath_softc *sc,
+			struct ath_descdma *, ath_bufhead *);
+static int	ath_desc_alloc(struct ath_softc *);
+static void	ath_desc_free(struct ath_softc *);
+static struct ieee80211_node *ath_node_alloc(struct ieee80211_node_table *);
+static void	ath_node_free(struct ieee80211_node *);
+static u_int8_t	ath_node_getrssi(const struct ieee80211_node *);
+static int	ath_rxbuf_init(struct ath_softc *, struct ath_buf *);
+static void	ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
+			struct ieee80211_node *ni,
+			int subtype, int rssi, u_int32_t rstamp);
+static void	ath_setdefantenna(struct ath_softc *, u_int);
+static void	ath_rx_proc(void *, int);
+static struct ath_txq *ath_txq_setup(struct ath_softc*, int qtype, int subtype);
+static int	ath_tx_setup(struct ath_softc *, int, int);
+static int	ath_wme_update(struct ieee80211com *);
+static void	ath_tx_cleanupq(struct ath_softc *, struct ath_txq *);
+static void	ath_tx_cleanup(struct ath_softc *);
+static int	ath_tx_start(struct ath_softc *, struct ieee80211_node *,
+			     struct ath_buf *, struct mbuf *);
+static void	ath_tx_proc_q0(void *, int);
+static void	ath_tx_proc_q0123(void *, int);
+static void	ath_tx_proc(void *, int);
+static int	ath_chan_set(struct ath_softc *, struct ieee80211_channel *);
+static void	ath_draintxq(struct ath_softc *);
+static void	ath_stoprecv(struct ath_softc *);
+static int	ath_startrecv(struct ath_softc *);
+static void	ath_chan_change(struct ath_softc *, struct ieee80211_channel *);
+static void	ath_next_scan(void *);
+static void	ath_calibrate(void *);
+static int	ath_newstate(struct ieee80211com *, enum ieee80211_state, int);
+static void	ath_setup_stationkey(struct ieee80211_node *);
+static void	ath_newassoc(struct ieee80211_node *, int);
+static int	ath_getchannels(struct ath_softc *, u_int cc,
+			HAL_BOOL outdoor, HAL_BOOL xchanmode);
+static void	ath_led_event(struct ath_softc *, int);
+static void	ath_update_txpow(struct ath_softc *);
+
+static int	ath_rate_setup(struct ath_softc *, u_int mode);
+static void	ath_setcurmode(struct ath_softc *, enum ieee80211_phymode);
+
+static void	ath_sysctlattach(struct ath_softc *);
+static void	ath_bpfattach(struct ath_softc *);
+static void	ath_announce(struct ath_softc *);
+
+SYSCTL_DECL(_hw_ath);
+
+/* XXX validate sysctl values */
+
+static	int ath_dwelltime = 200;		/* 5 channels/second */
+SYSCTL_INT(_hw_ath, OID_AUTO, dwell, CTLFLAG_RW, &ath_dwelltime,
+	    0, "channel dwell time (ms) for AP/station scanning");
+TUNABLE_INT("hw.ath.dwell", &ath_dwelltime);
+
+static	int ath_calinterval = 30;		/* calibrate every 30 secs */
+SYSCTL_INT(_hw_ath, OID_AUTO, calibrate, CTLFLAG_RW, &ath_calinterval,
+	    0, "chip calibration interval (secs)");
+TUNABLE_INT("hw.ath.calibrate", &ath_calinterval);
+
+static	int ath_outdoor = AH_TRUE;		/* outdoor operation */
+SYSCTL_INT(_hw_ath, OID_AUTO, outdoor, CTLFLAG_RD, &ath_outdoor,
+	    0, "outdoor operation");
+TUNABLE_INT("hw.ath.outdoor", &ath_outdoor);
+static	int ath_xchanmode = AH_TRUE;		/* extended channel use */
+SYSCTL_INT(_hw_ath, OID_AUTO, xchanmode, CTLFLAG_RD, &ath_xchanmode,
+	    0, "extended channel mode");
+TUNABLE_INT("hw.ath.xchanmode", &ath_xchanmode);
+static	int ath_countrycode = CTRY_DEFAULT;	/* country code */
+TUNABLE_INT("hw.ath.countrycode", &ath_countrycode);
+
+static	int ath_regdomain = 0;			/* regulatory domain */
+TUNABLE_INT("hw.ath.regdomain", &ath_regdomain);
+
+#ifdef AR_DEBUG
+
+static  int ath_debug = 0;
+TUNABLE_INT("hw.ath.debug", &ath_debug);
+
+enum {
+	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
+	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
+	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
+	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
+	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
+	ATH_DEBUG_BEACON 	= 0x00000080,	/* beacon handling */
+	ATH_DEBUG_WATCHDOG 	= 0x00000100,	/* watchdog timeout */
+	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
+	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
+	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
+	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
+	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
+	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
+	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
+	ATH_DEBUG_LED		= 0x00100000,	/* led management */
+	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
+	ATH_DEBUG_ANY		= 0xffffffff
+};
+#define	IFF_DUMPPKTS(sc, m) \
+	((sc->sc_debug & (m)) || \
+	    (sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+
+#if 0
+#define	DPRINTF(sc, m, fmt, ...) do {				\
+	if (sc->sc_debug & (m))					\
+		printf(fmt, __VA_ARGS__);			\
+} while (0)
+#endif
+#define	KEYPRINTF(sc, ix, hk, mac) do {				\
+	if (sc->sc_debug & ATH_DEBUG_KEYCACHE)			\
+		ath_keyprint(__func__, ix, hk, mac);		\
+} while (0)
+static	void ath_printrxbuf(struct ath_buf *bf, int);
+static	void ath_printtxbuf(struct ath_buf *bf, int);
+
+
+#define	DPRINTF(_sc,_m,X)	if (_sc->sc_debug & _m) printf X
+#else
+#define	IFF_DUMPPKTS(sc, m) \
+ 	((sc->sc_ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2))
+#define	DPRINTF(_m, X)
+#define	KEYPRINTF(sc, k, ix, mac)
+#endif
+
+
+int
+ath_attach(u_int16_t devid, struct ath_softc *sc)
+{
+	struct ifnet *ifp;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = NULL;
+	HAL_STATUS status;
+	int error = 0;
+	unsigned i;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: devid 0x%x\n", __func__, devid));
+
+	ifp = sc->sc_ifp = &sc->sc_if;
+	if (ifp == NULL) {
+		device_printf(sc->sc_dev, "can not if_alloc()\n");
+		error = ENOSPC;
+		goto bad;
+	}
+
+	/* set these up early for if_printf use */
+	if_initname(ifp, device_get_name(sc->sc_dev),
+		device_get_unit(sc->sc_dev));
+
+	ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, &status);
+	if (ah == NULL) {
+		if_printf(ifp, "unable to attach hardware; HAL status %u\n",
+			status);
+		error = ENXIO;
+		goto bad;
+	}
+	if (ah->ah_abi != HAL_ABI_VERSION) {
+		if_printf(ifp, "HAL ABI mismatch detected (0x%x != 0x%x)\n",
+			ah->ah_abi, HAL_ABI_VERSION);
+		error = ENXIO;
+		goto bad;
+	}
+	if_printf(ifp, "mac %d.%d phy %d.%d",
+		ah->ah_macVersion, ah->ah_macRev,
+		ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
+	if (ah->ah_analog5GhzRev)
+		printf(" 5ghz radio %d.%d",
+			ah->ah_analog5GhzRev >> 4, ah->ah_analog5GhzRev & 0xf);
+	if (ah->ah_analog2GhzRev)
+		printf(" 2ghz radio %d.%d",
+			ah->ah_analog2GhzRev >> 4, ah->ah_analog2GhzRev & 0xf);
+	printf("\n");
+	sc->sc_ah = ah;
+	sc->sc_invalid = 0;	/* ready to go, enable interrupt handling */
+
+	/*
+	 * Check if the MAC has multi-rate retry support.
+	 * We do this by trying to setup a fake extended
+	 * descriptor.  MAC's that don't have support will
+	 * return false w/o doing anything.  MAC's that do
+	 * support it will return true w/o doing anything.
+	 */
+	sc->sc_mrretry = ath_hal_setupxtxdesc(ah, NULL, 0,0, 0,0, 0,0);
+
+	/*
+	 * Check if the device has hardware counters for PHY
+	 * errors.  If so we need to enable the MIB interrupt
+	 * so we can act on stat triggers.
+	 */
+	if (ath_hal_hwphycounters(ah))
+		sc->sc_needmib = 1;
+
+	/*
+	 * Get the hardware key cache size.
+	 */
+	sc->sc_keymax = ath_hal_keycachesize(ah);
+	if (sc->sc_keymax > sizeof(sc->sc_keymap) * NBBY) {
+		if_printf(ifp,
+			"Warning, using only %zu of %u key cache slots\n",
+			sizeof(sc->sc_keymap) * NBBY, sc->sc_keymax);
+	}
+	/*
+	 * Reset the key cache since some parts do not
+	 * reset the contents on initial power up.
+	 */
+	for (i = 0; i < sc->sc_keymax; i++)
+		ath_hal_keyreset(ah, i);
+	/*
+	 * Mark key cache slots associated with global keys
+	 * as in use.  If we knew TKIP was not to be used we
+	 * could leave the +32, +64, and +32+64 slots free.
+	 * XXX only for splitmic.
+	 */
+	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+		setbit(sc->sc_keymap, i);
+		setbit(sc->sc_keymap, i+32);
+		setbit(sc->sc_keymap, i+64);
+		setbit(sc->sc_keymap, i+32+64);
+	}
+
+        /*
+         * Check if the device has hardware counters for PHY
+         * errors.  If so we need to enable the MIB interrupt
+         * so we can act on stat triggers.
+         */
+        if (ath_hal_hwphycounters(ah))
+                sc->sc_needmib = 1;
+
+	/*
+	 * Collect the channel list using the default country
+	 * code and including outdoor channels.  The 802.11 layer
+	 * is resposible for filtering this list based on settings
+	 * like the phy mode.
+	 */
+	error = ath_getchannels(sc, ath_countrycode, ath_outdoor, ath_xchanmode);
+	if (error != 0)
+		goto bad;
+	/*
+	 * Copy these back; they are set as a side effect
+	 * of constructing the channel list.
+	 */
+	ath_hal_getregdomain(ah, &ath_regdomain);
+	ath_hal_getcountrycode(ah, &ath_countrycode);
+
+	sysctl_ctx_init(&sc->sysctl_ctx);
+
+	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
+				SYSCTL_STATIC_CHILDREN(_hw),
+				OID_AUTO,
+				device_get_nameunit(sc->sc_dev),
+				CTLFLAG_RD,
+				0, "");
+
+	/*
+	 * Setup rate tables for all potential media types.
+	 */
+	ath_rate_setup(sc, IEEE80211_MODE_11A);
+	ath_rate_setup(sc, IEEE80211_MODE_11B);
+	ath_rate_setup(sc, IEEE80211_MODE_11G);
+	ath_rate_setup(sc, IEEE80211_MODE_TURBO_A);
+	ath_rate_setup(sc, IEEE80211_MODE_TURBO_G);
+	/* NB: setup here so ath_rate_update is happy */
+	ath_setcurmode(sc, IEEE80211_MODE_11A);
+
+	/*
+	 * Allocate tx+rx descriptors and populate the lists.
+	 */
+	error = ath_desc_alloc(sc);
+	if (error != 0) {
+		if_printf(ifp, "failed to allocate descriptors: %d\n", error);
+		goto bad;
+	}
+	callout_init(&sc->sc_scan_ch);
+	callout_init(&sc->sc_cal_ch);
+
+	ATH_TXBUF_LOCK_INIT(sc);
+
+	TASK_INIT(&sc->sc_rxtask, 0, ath_rx_proc, sc);
+	TASK_INIT(&sc->sc_rxorntask, 0, ath_rxorn_proc, sc);
+	TASK_INIT(&sc->sc_fataltask, 0, ath_fatal_proc, sc);
+	TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc);
+	TASK_INIT(&sc->sc_bstucktask, 0, ath_bstuck_proc, sc);
+
+	/*
+	 * Allocate hardware transmit queues: one queue for
+	 * beacon frames and one data queue for each QoS
+	 * priority.  Note that the hal handles reseting
+	 * these queues at the needed time.
+	 *
+	 * XXX PS-Poll
+	 */
+	sc->sc_bhalq = ath_beaconq_setup(ah);
+	if (sc->sc_bhalq == (u_int) -1) {
+		if_printf(ifp, "unable to setup a beacon xmit queue!\n");
+		error = EIO;
+		goto bad2;
+	}
+	sc->sc_cabq = ath_txq_setup(sc, HAL_TX_QUEUE_CAB, 0);
+	if (sc->sc_cabq == NULL) {
+		if_printf(ifp, "unable to setup CAB xmit queue!\n");
+		error = EIO;
+		goto bad2;
+	}
+	/* NB: insure BK queue is the lowest priority h/w queue */
+	if (!ath_tx_setup(sc, WME_AC_BK, HAL_WME_AC_BK)) {
+		if_printf(ifp, "unable to setup xmit queue for %s traffic!\n",
+			ieee80211_wme_acnames[WME_AC_BK]);
+		error = EIO;
+		goto bad2;
+	}
+	if (!ath_tx_setup(sc, WME_AC_BE, HAL_WME_AC_BE) ||
+	    !ath_tx_setup(sc, WME_AC_VI, HAL_WME_AC_VI) ||
+	    !ath_tx_setup(sc, WME_AC_VO, HAL_WME_AC_VO)) {
+		/* 
+		 * Not enough hardware tx queues to properly do WME;
+		 * just punt and assign them all to the same h/w queue.
+		 * We could do a better job of this if, for example,
+		 * we allocate queues when we switch from station to
+		 * AP mode.
+		 */
+		if (sc->sc_ac2q[WME_AC_VI] != NULL)
+			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_VI]);
+		if (sc->sc_ac2q[WME_AC_BE] != NULL)
+			ath_tx_cleanupq(sc, sc->sc_ac2q[WME_AC_BE]);
+		sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK];
+		sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK];
+		sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK];
+	}
+
+	/* 
+	 * Special case certain configurations.  Note the
+	 * CAB queue is handled by these specially so don't
+	 * include them when checking the txq setup mask.
+	 */
+	switch (sc->sc_txqsetup &~ (1<<sc->sc_cabq->axq_qnum)) {
+	case 0x01:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0, sc);
+		break;
+	case 0x0f:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc_q0123, sc);
+		break;
+	default:
+		TASK_INIT(&sc->sc_txtask, 0, ath_tx_proc, sc);
+		break;
+	}
+
+	/*
+	 * Setup rate control.  Some rate control modules
+	 * call back to change the anntena state so expose
+	 * the necessary entry points.
+	 * XXX maybe belongs in struct ath_ratectrl?
+	 */
+	sc->sc_setdefantenna = ath_setdefantenna;
+	sc->sc_rc = ath_rate_attach(sc);
+	if (sc->sc_rc == NULL) {
+		error = EIO;
+		goto bad2;
+	}
+
+	sc->sc_blinking = 0;
+	sc->sc_ledstate = 1;
+	sc->sc_ledon = 0;			/* low true */
+	sc->sc_ledidle = (2700*hz)/1000;	/* 2.7sec */
+	callout_init(&sc->sc_ledtimer);
+	/*
+	 * Auto-enable soft led processing for IBM cards and for
+	 * 5211 minipci cards.  Users can also manually enable/disable
+	 * support with a sysctl.
+	 */
+	sc->sc_softled = (/*devid == AR5212_DEVID_IBM || devid == AR5211_DEVID*/0);
+	if (sc->sc_softled) {
+		ath_hal_gpioCfgOutput(ah, sc->sc_ledpin);
+		ath_hal_gpioset(ah, sc->sc_ledpin, !sc->sc_ledon);
+	}
+
+	ifp->if_softc = sc;
+	ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
+	ifp->if_start = ath_start;
+	ifp->if_watchdog = ath_watchdog;
+	ifp->if_ioctl = ath_ioctl;
+	ifp->if_init = (void(*)(void *)) ath_init;
+#ifdef IFQ_SET_MAXLEN
+	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+#else
+	ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN);
+#endif
+
+
+	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+
+#ifdef IFQ_SET_MAXLEN
+	IFQ_SET_READY(&ifp->if_snd);
+#else
+	ifq_set_ready(&ifp->if_snd);
+#endif
+
+	ic->ic_ifp = ifp;
+	ic->ic_reset = ath_reset;
+	ic->ic_newassoc = ath_newassoc;
+	ic->ic_updateslot = ath_updateslot;
+	ic->ic_wme.wme_update = ath_wme_update;
+	/* XXX not right but it's not used anywhere important */
+	ic->ic_phytype = IEEE80211_T_OFDM;
+	ic->ic_opmode = IEEE80211_M_STA;
+	ic->ic_caps =
+		  IEEE80211_C_IBSS		/* ibss, nee adhoc, mode */
+		| IEEE80211_C_HOSTAP		/* hostap mode */
+		| IEEE80211_C_MONITOR		/* monitor mode */
+		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
+		| IEEE80211_C_SHSLOT		/* short slot */
+		| IEEE80211_C_WPA		/* capable of WPA1+WPA2 */
+		;
+	/*
+	 * Query the hal to figure out h/w crypto support.
+	 */
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_WEP))
+		ic->ic_caps |= IEEE80211_C_WEP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_OCB))
+		ic->ic_caps |= IEEE80211_C_AES;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_AES_CCM))
+		ic->ic_caps |= IEEE80211_C_AES_CCM;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_CKIP))
+		ic->ic_caps |= IEEE80211_C_CKIP;
+	if (ath_hal_ciphersupported(ah, HAL_CIPHER_TKIP)) {
+		ic->ic_caps |= IEEE80211_C_TKIP;
+		/*
+		 * Check if h/w does the MIC and/or whether the
+		 * separate key cache entries are required to
+		 * handle both tx+rx MIC keys.
+		 */
+		if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
+			ic->ic_caps |= IEEE80211_C_TKIPMIC;
+		if (ath_hal_tkipsplit(ah))
+			sc->sc_splitmic = 1;
+	}
+	sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
+	sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
+	/*
+	 * TPC support can be done either with a global cap or
+	 * per-packet support.  The latter is not available on
+	 * all parts.  We're a bit pedantic here as all parts
+	 * support a global cap.
+	 */
+	if (ath_hal_hastpc(ah) || ath_hal_hastxpowlimit(ah))
+		ic->ic_caps |= IEEE80211_C_TXPMGT;
+
+	/*
+	 * Mark WME capability only if we have sufficient
+	 * hardware queues to do proper priority scheduling.
+	 */
+	if (sc->sc_ac2q[WME_AC_BE] != sc->sc_ac2q[WME_AC_BK])
+		ic->ic_caps |= IEEE80211_C_WME;
+	/*
+	 * Check for frame bursting capability.
+	 */
+	if (ath_hal_hasbursting(ah))
+		ic->ic_caps |= IEEE80211_C_BURST;
+
+	/*
+	 * Indicate we need the 802.11 header padded to a
+	 * 32-bit boundary for 4-address and QoS frames.
+	 */
+	ic->ic_flags |= IEEE80211_F_DATAPAD;
+
+	/*
+	 * Query the hal about antenna support.
+	 */
+	sc->sc_defant = ath_hal_getdefantenna(ah);
+
+	/*
+	 * Not all chips have the VEOL support we want to
+	 * use with IBSS beacons; check here for it.
+	 */
+	sc->sc_hasveol = ath_hal_hasveol(ah);
+
+	/* get mac address from hardware */
+	ath_hal_getmac(ah, ic->ic_myaddr);
+
+	/* call MI attach routine. */
+	ieee80211_ifattach(ic);
+	/* override default methods */
+	ic->ic_node_alloc = ath_node_alloc;
+	sc->sc_node_free = ic->ic_node_free;
+	ic->ic_node_free = ath_node_free;
+	ic->ic_node_getrssi = ath_node_getrssi;
+	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
+	ic->ic_recv_mgmt = ath_recv_mgmt;
+	sc->sc_newstate = ic->ic_newstate;
+	ic->ic_newstate = ath_newstate;
+	ic->ic_crypto.cs_max_keyix = sc->sc_keymax;
+	ic->ic_crypto.cs_key_alloc = ath_key_alloc;
+	ic->ic_crypto.cs_key_delete = ath_key_delete;
+	ic->ic_crypto.cs_key_set = ath_key_set;
+	ic->ic_crypto.cs_key_update_begin = ath_key_update_begin;
+	ic->ic_crypto.cs_key_update_end = ath_key_update_end;
+	/* complete initialization */
+	ieee80211_media_init(ic, ath_media_change, ieee80211_media_status);
+
+	ath_bpfattach(sc);
+	/*
+	 * Setup dynamic sysctl's now that country code and
+	 * regdomain are available from the hal.
+	 */
+	ath_sysctlattach(sc);
+
+	if (bootverbose)
+		ieee80211_announce(ic);
+	ath_announce(sc);
+	return 0;
+bad2:
+	ath_tx_cleanup(sc);
+	ath_desc_free(sc);
+bad:
+	if (ah)
+		ath_hal_detach(ah);
+#if notyet
+	if (ifp != NULL)
+		if_free(ifp);
+#endif
+	sc->sc_invalid = 1;
+	return error;
+}
+
+int
+ath_detach(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags %x\n", __func__, ifp->if_flags));
+
+	ath_stop(ifp);
+	bpfdetach(ifp);
+	/* 
+	 * NB: the order of these is important:
+	 * o call the 802.11 layer before detaching the hal to
+	 *   insure callbacks into the driver to delete global
+	 *   key cache entries can be handled
+	 * o reclaim the tx queue data structures after calling
+	 *   the 802.11 layer as we'll get called back to reclaim
+	 *   node state and potentially want to use them
+	 * o to cleanup the tx queues the hal is called, so detach
+	 *   it last
+	 * Other than that, it's straightforward...
+	 */
+	ieee80211_ifdetach(&sc->sc_ic);
+	ath_rate_detach(sc->sc_rc);
+	ath_desc_free(sc);
+	ath_tx_cleanup(sc);
+	ath_hal_detach(sc->sc_ah);
+#if notyet
+	if_free(ifp);
+#endif
+
+	return 0;
+}
+
+void
+ath_suspend(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags %x\n", __func__, ifp->if_flags));
+
+	ath_stop(ifp);
+}
+
+void
+ath_resume(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags %x\n", __func__, ifp->if_flags));
+
+	if (ifp->if_flags & IFF_UP) {
+		ath_init(ifp); /* XXX fix me (sc) */
+		if (ifp->if_flags & IFF_RUNNING)
+			ath_start(ifp);
+	}
+	if (sc->sc_softled) {
+		ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
+		ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+	}
+}
+
+void
+ath_shutdown(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags %x\n", __func__, ifp->if_flags));
+
+	ath_stop(ifp);
+}
+
+/*
+ * Interrupt handler.  Most of the actual processing is deferred.
+ */
+void
+ath_intr(void *arg)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_INT status;
+
+	if (sc->sc_invalid) {
+		/*
+		 * The hardware is not ready/present, don't touch anything.
+		 * Note this can happen early on if the IRQ is shared.
+		 */
+		DPRINTF(sc, ATH_DEBUG_ANY, ("%s: invalid; ignored\n", __func__));
+		return;
+	}
+	if (!ath_hal_intrpend(ah))		/* shared irq, not for us */
+		return;
+	if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP)) {
+		DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags 0x%x\n",
+			__func__, ifp->if_flags));
+		ath_hal_getisr(ah, &status);	/* clear ISR */
+		ath_hal_intrset(ah, 0);		/* disable further intr's */
+		return;
+	}
+	/*
+	 * Figure out the reason(s) for the interrupt.  Note
+	 * that the hal returns a pseudo-ISR that may include
+	 * bits we haven't explicitly enabled so we mask the
+	 * value to insure we only process bits we requested.
+	 */
+	ath_hal_getisr(ah, &status);		/* NB: clears ISR too */
+	DPRINTF(sc, ATH_DEBUG_INTR, ("%s: mask 0x%x status 0x%x\n", __func__, sc->sc_imask, status));
+	status &= sc->sc_imask;			/* discard unasked for bits */
+	DPRINTF(sc, ATH_DEBUG_INTR, ("%s: masked status 0x%x\n", __func__, status));
+	if (status & HAL_INT_FATAL) {
+		/*
+		 * Fatal errors are unrecoverable.  Typically
+		 * these are caused by DMA errors.  Unfortunately
+		 * the exact reason is not (presently) returned
+		 * by the hal.
+		 */
+		sc->sc_stats.ast_hardware++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		taskqueue_enqueue(taskqueue_swi, &sc->sc_fataltask);
+	} else if (status & HAL_INT_RXORN) {
+		sc->sc_stats.ast_rxorn++;
+		ath_hal_intrset(ah, 0);		/* disable intr's until reset */
+		taskqueue_enqueue(taskqueue_swi, &sc->sc_rxorntask);
+	} else {
+		if (status & HAL_INT_SWBA) {
+			/*
+			 * Software beacon alert--time to send a beacon.
+			 * Handle beacon transmission directly; deferring
+			 * this is too slow to meet timing constraints
+			 * under load.
+			 */
+			DPRINTF(sc, ATH_DEBUG_BEACON,("SWBA\n"));
+			ath_beacon_proc(sc, 0);
+		}
+		if (status & HAL_INT_RXEOL) {
+			/*
+			 * NB: the hardware should re-read the link when
+			 *     RXE bit is written, but it doesn't work at
+			 *     least on older hardware revs.
+			 */
+			sc->sc_stats.ast_rxeol++;
+			sc->sc_rxlink = NULL;
+		}
+		if (status & HAL_INT_TXURN) {
+			sc->sc_stats.ast_txurn++;
+			/* bump tx trigger level */
+			ath_hal_updatetxtriglevel(ah, AH_TRUE);
+		}
+		if (status & HAL_INT_RX)
+			taskqueue_enqueue(taskqueue_swi, &sc->sc_rxtask);
+		if (status & HAL_INT_TX)
+			taskqueue_enqueue(taskqueue_swi, &sc->sc_txtask);
+		if (status & HAL_INT_BMISS) {
+			sc->sc_stats.ast_bmiss++;
+			taskqueue_enqueue(taskqueue_swi, &sc->sc_bmisstask);
+		}
+		if (status & HAL_INT_MIB) {
+			sc->sc_stats.ast_mib++;
+			/*
+			 * Disable interrupts until we service the MIB
+			 * interrupt; otherwise it will continue to fire.
+			 */
+			ath_hal_intrset(ah, 0);
+			/*
+			 * Let the hal handle the event.  We assume it will
+			 * clear whatever condition caused the interrupt.
+			 */
+			ath_hal_mibevent(ah,
+				&ATH_NODE(sc->sc_ic.ic_bss)->an_halstats);
+			ath_hal_intrset(ah, sc->sc_imask);
+		}
+	}
+}
+
+static void
+ath_fatal_proc(void *arg, int pending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+
+	device_printf(sc->sc_dev, "hardware error; resetting\n");
+	ath_reset(ifp);
+}
+
+static void
+ath_rxorn_proc(void *arg, int pending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+
+	device_printf(sc->sc_dev, "rx FIFO overrun; resetting\n");
+	ath_reset(ifp);
+}
+
+static void
+ath_bmiss_proc(void *arg, int pending)
+{
+	struct ath_softc *sc = arg;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: pending %u\n", __func__, pending));
+	KASSERT(ic->ic_opmode == IEEE80211_M_STA,
+		("unexpect operating mode %u", ic->ic_opmode));
+	if (ic->ic_state == IEEE80211_S_RUN) {
+		/*
+		 * Rather than go directly to scan state, try to
+		 * reassociate first.  If that fails then the state
+		 * machine will drop us into scanning after timing
+		 * out waiting for a probe response.
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1);
+	}
+}
+
+static u_int
+ath_chan2flags(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const u_int modeflags[] = {
+		0,			/* IEEE80211_MODE_AUTO */
+		CHANNEL_A,		/* IEEE80211_MODE_11A */
+		CHANNEL_B,		/* IEEE80211_MODE_11B */
+		CHANNEL_PUREG,		/* IEEE80211_MODE_11G */
+		0,			/* IEEE80211_MODE_FH */
+		CHANNEL_T,		/* IEEE80211_MODE_TURBO_A */
+		CHANNEL_108G		/* IEEE80211_MODE_TURBO_G */
+	};
+	enum ieee80211_phymode mode = ieee80211_chan2mode(ic, chan);
+
+	KASSERT(mode < N(modeflags), ("unexpected phy mode %u", mode));
+	KASSERT(modeflags[mode] != 0, ("mode %u undefined", mode));
+	return modeflags[mode];
+#undef N
+}
+
+static void
+ath_init(void *arg)
+{
+	struct ath_softc *sc = (struct ath_softc *) arg;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_STATUS status;
+	ATH_LOCK_INFO;
+	crit_enter();
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: if_flags 0x%x\n",
+		__func__, ifp->if_flags));
+
+	ATH_LOCK(sc);
+	/*
+	 * Stop anything previously setup.  This is safe
+	 * whether this is the first time through or not.
+	 */
+	ath_stop_locked(ifp);
+
+	/*
+	 * The basic interface to setting the hardware in a good
+	 * state is ``reset''.  On return the hardware is known to
+	 * be powered up and with interrupts disabled.  This must
+	 * be followed by initialization of the appropriate bits
+	 * and then setup of the interrupt mask.
+	 */
+	sc->sc_curchan.channel = ic->ic_curchan->ic_freq;
+	sc->sc_curchan.channelFlags = ath_chan2flags(ic, ic->ic_curchan);
+	if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_FALSE, &status)) {
+		if_printf(ifp, "unable to reset hardware; hal status %u\n",
+			status);
+		goto done;
+	}
+
+	/*
+	 * This is needed only to setup initial state
+	 * but it's best done after a reset.
+	 */
+	ath_update_txpow(sc);
+	/*
+	 * Likewise this is set during reset so update
+	 * state cached in the driver.
+	 */
+	sc->sc_diversity = ath_hal_getdiversity(ah);
+
+	/*
+	 * Setup the hardware after reset: the key cache
+	 * is filled as needed and the receive engine is
+	 * set going.  Frame transmit is handled entirely
+	 * in the frame output path; there's nothing to do
+	 * here except setup the interrupt mask.
+	 */
+	if (ath_startrecv(sc) != 0) {
+		if_printf(ifp, "unable to start recv logic\n");
+		goto done;
+	}
+
+	/*
+	 * Enable interrupts.
+	 */
+	sc->sc_imask = HAL_INT_RX | HAL_INT_TX
+		  | HAL_INT_RXEOL | HAL_INT_RXORN
+		  | HAL_INT_FATAL | HAL_INT_GLOBAL;
+	/*
+	 * Enable MIB interrupts when there are hardware phy counters.
+	 * Note we only do this (at the moment) for station mode.
+	 */
+	if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA)
+		sc->sc_imask |= HAL_INT_MIB;
+	ath_hal_intrset(ah, sc->sc_imask);
+
+	ifp->if_flags |= IFF_RUNNING;
+	ic->ic_state = IEEE80211_S_INIT;
+
+	/*
+	 * The hardware should be ready to go now so it's safe
+	 * to kick the 802.11 state machine as it's likely to
+	 * immediately call back to us to send mgmt frames.
+	 */
+	ath_chan_change(sc, ic->ic_curchan);
+	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+		if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+	} else
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+done:
+	ATH_UNLOCK(sc);
+	crit_exit();
+}
+
+static void
+ath_stop_locked(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, ("%s: invalid %u if_flags 0x%x\n",
+		__func__, sc->sc_invalid, ifp->if_flags));
+
+	ATH_LOCK_ASSERT(sc);
+	if (ifp->if_flags & IFF_RUNNING) {
+		/*
+		 * Shutdown the hardware and driver:
+		 *    reset 802.11 state machine
+		 *    turn off timers
+		 *    disable interrupts
+		 *    turn off the radio
+		 *    clear transmit machinery
+		 *    clear receive machinery
+		 *    drain and release tx queues
+		 *    reclaim beacon resources
+		 *    power down hardware
+		 *
+		 * Note that some of this work is not possible if the
+		 * hardware is gone (invalid).
+		 */
+		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+		ifp->if_flags &= ~IFF_RUNNING;
+		ifp->if_timer = 0;
+		if (!sc->sc_invalid) {
+			if (sc->sc_softled) {
+				callout_stop(&sc->sc_ledtimer);
+				ath_hal_gpioset(ah, sc->sc_ledpin,
+					!sc->sc_ledon);
+				sc->sc_blinking = 0;
+			}
+			ath_hal_intrset(ah, 0);
+		}
+		ath_draintxq(sc);
+		if (!sc->sc_invalid) {
+			ath_stoprecv(sc);
+			ath_hal_phydisable(ah);
+		} else
+			sc->sc_rxlink = NULL;
+#ifdef IFQ_PURGE
+		IFQ_PURGE(&ifp->if_snd);
+#else
+		ifq_purge(&ifp->if_snd);
+#endif
+		ath_beacon_free(sc);
+	}
+}
+
+static void
+ath_stop(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	ATH_LOCK_INFO;
+	crit_enter();
+
+	ATH_LOCK(sc);
+	ath_stop_locked(ifp);
+	if (!sc->sc_invalid) {
+		/*
+		 * Set the chip in full sleep mode.  Note that we are
+		 * careful to do this only when bringing the interface
+		 * completely to a stop.  When the chip is in this state
+		 * it must be carefully woken up or references to
+		 * registers in the PCI clock domain may freeze the bus
+		 * (and system).  This varies by chip and is mostly an
+		 * issue with newer parts that go to sleep more quickly.
+		 */
+		ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP, 0);
+	}
+	ATH_UNLOCK(sc);
+	crit_exit();
+}
+
+/*
+ * Reset the hardware w/o losing operational state.  This is
+ * basically a more efficient way of doing ath_stop, ath_init,
+ * followed by state transitions to the current 802.11
+ * operational state.  Used to recover from various errors and
+ * to reset or reload hardware state.
+ */
+static int
+ath_reset(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_channel *c;
+	HAL_STATUS status;
+
+	/*
+	 * Convert to a HAL channel description with the flags
+	 * constrained to reflect the current operating mode.
+	 */
+	c = ic->ic_curchan;
+	sc->sc_curchan.channel = c->ic_freq;
+	sc->sc_curchan.channelFlags = ath_chan2flags(ic, c);
+
+	ath_hal_intrset(ah, 0);		/* disable interrupts */
+	ath_draintxq(sc);		/* stop xmit side */
+	ath_stoprecv(sc);		/* stop recv side */
+	/* NB: indicate channel change so we do a full reset */
+	if (!ath_hal_reset(ah, ic->ic_opmode, &sc->sc_curchan, AH_TRUE, &status))
+		if_printf(ifp, "%s: unable to reset hardware; hal status %u\n",
+			__func__, status);
+	ath_update_txpow(sc);		/* update tx power state */
+	sc->sc_diversity = ath_hal_getdiversity(ah);
+	if (ath_startrecv(sc) != 0)	/* restart recv */
+		if_printf(ifp, "%s: unable to start recv logic\n", __func__);
+	/*
+	 * We may be doing a reset in response to an ioctl
+	 * that changes the channel so update any state that
+	 * might change as a result.
+	 */
+	ath_chan_change(sc, c);
+	if (ic->ic_state == IEEE80211_S_RUN)
+		ath_beacon_config(sc);	/* restart beacons */
+
+	ath_start(ifp);			/* restart xmit */
+	return 0;
+}
+
+static void
+ath_start(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni;
+	struct ath_buf *bf;
+	struct mbuf *m;
+	struct ieee80211_frame *wh;
+	struct ether_header *eh;
+	ATH_TXBUF_LOCK_INFO;
+
+	if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid)
+		return;
+	for (;;) {
+		/*
+		 * Grab a TX buffer and associated resources.
+		 */
+		ATH_TXBUF_LOCK(sc);
+		bf = STAILQ_FIRST(&sc->sc_txbuf);
+		if (bf != NULL)
+			STAILQ_REMOVE_HEAD(&sc->sc_txbuf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+		if (bf == NULL) {
+			DPRINTF(sc, ATH_DEBUG_ANY, ("%s: out of xmit buffers\n",
+				__func__));
+			sc->sc_stats.ast_tx_qstop++;
+			ifp->if_flags |= IFF_OACTIVE;
+			break;
+		}
+		/*
+		 * Poll the management queue for frames; they
+		 * have priority over normal data frames.
+		 */
+		IF_DEQUEUE(&ic->ic_mgtq, m);
+		if (m == NULL) {
+			/*
+			 * No data frames go out unless we're associated.
+			 */
+			if (ic->ic_state != IEEE80211_S_RUN) {
+				DPRINTF(sc, ATH_DEBUG_ANY,
+					("%s: ignore data packet, state %u\n",
+					__func__, ic->ic_state));
+				sc->sc_stats.ast_tx_discard++;
+				ATH_TXBUF_LOCK(sc);
+				STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+				ATH_TXBUF_UNLOCK(sc);
+				break;
+			}
+#ifdef IFQ_DEQUEUE
+			IFQ_DEQUEUE(&ifp->if_snd, m);	/* XXX: LOCK */
+#else
+			m = ifq_dequeue(&ifp->if_snd);	/* XXX: LOCK */
+#endif
+			if (m == NULL) {
+				ATH_TXBUF_LOCK(sc);
+				STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+				ATH_TXBUF_UNLOCK(sc);
+				break;
+			}
+			/* 
+			 * Find the node for the destination so we can do
+			 * things like power save and fast frames aggregation.
+			 */
+			if (m->m_len < sizeof(struct ether_header) &&
+			   (m = m_pullup(m, sizeof(struct ether_header))) == NULL) {
+				ic->ic_stats.is_tx_nobuf++;	/* XXX */
+				ni = NULL;
+				goto bad;
+			}
+			eh = mtod(m, struct ether_header *);
+			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+			if (ni == NULL) {
+				/* NB: ieee80211_find_txnode does stat+msg */
+				m_freem(m);
+				goto bad;
+			}
+			if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+			    (m->m_flags & M_PWR_SAV) == 0) {
+				/*
+				 * Station in power save mode; pass the frame
+				 * to the 802.11 layer and continue.  We'll get
+				 * the frame back when the time is right.
+				 */
+				ieee80211_pwrsave(ic, ni, m);
+				goto reclaim;
+			}
+			/* calculate priority so we can find the tx queue */
+			if (ieee80211_classify(ic, m, ni)) {
+				DPRINTF(sc, ATH_DEBUG_XMIT,
+					("%s: discard, classification failure\n",
+					__func__));
+				m_freem(m);
+				goto bad;
+			}
+			ifp->if_opackets++;
+			BPF_MTAP(ifp, m);
+			/*
+			 * Encapsulate the packet in prep for transmission.
+			 */
+			m = ieee80211_encap(ic, m, ni);
+			if (m == NULL) {
+				DPRINTF(sc, ATH_DEBUG_ANY,
+					("%s: encapsulation failure\n",
+					__func__));
+				sc->sc_stats.ast_tx_encap++;
+				goto bad;
+			}
+		} else {
+			/*
+			 * Hack!  The referenced node pointer is in the
+			 * rcvif field of the packet header.  This is
+			 * placed there by ieee80211_mgmt_output because
+			 * we need to hold the reference with the frame
+			 * and there's no other way (other than packet
+			 * tags which we consider too expensive to use)
+			 * to pass it along.
+			 */
+			ni = (struct ieee80211_node *) m->m_pkthdr.rcvif;
+			m->m_pkthdr.rcvif = NULL;
+
+			wh = mtod(m, struct ieee80211_frame *);
+			if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+			    IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+				/* fill time stamp */
+				u_int64_t tsf;
+				u_int32_t *tstamp;
+
+				tsf = ath_hal_gettsf64(ah);
+				/* XXX: adjust 100us delay to xmit */
+				tsf += 100;
+				tstamp = (u_int32_t *)&wh[1];
+				tstamp[0] = htole32(tsf & 0xffffffff);
+				tstamp[1] = htole32(tsf >> 32);
+			}
+			sc->sc_stats.ast_tx_mgmt++;
+		}
+
+		if (ath_tx_start(sc, ni, bf, m)) {
+	bad:
+			ifp->if_oerrors++;
+	reclaim:
+			ATH_TXBUF_LOCK(sc);
+			STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+			ATH_TXBUF_UNLOCK(sc);
+			if (ni != NULL)
+				ieee80211_free_node(ni);
+			continue;
+		}
+
+		sc->sc_tx_timer = 5;
+		ifp->if_timer = 1;
+	}
+}
+
+static int
+ath_media_change(struct ifnet *ifp)
+{
+#define	IS_UP(ifp) \
+	((ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING))
+	int error;
+
+	error = ieee80211_media_change(ifp);
+	if (error == ENETRESET) {
+		if (IS_UP(ifp))
+			ath_init(ifp->if_softc);	/* XXX lose error */
+		error = 0;
+	}
+	return error;
+#undef IS_UP
+}
+
+#ifdef AR_DEBUG
+static void
+ath_keyprint(const char *tag, u_int ix,
+	const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	static const char *ciphers[] = {
+		"WEP",
+		"AES-OCB",
+		"AES-CCM",
+		"CKIP",
+		"TKIP",
+		"CLR",
+	};
+	size_t i, n;
+
+	printf("%s: [%02u] %-7s ", tag, ix, ciphers[hk->kv_type]);
+	for (i = 0, n = hk->kv_len; i < n; i++)
+		printf("%02x", hk->kv_val[i]);
+	printf(" mac %6D", (mac), ":");
+	if (hk->kv_type == HAL_CIPHER_TKIP) {
+		printf(" mic ");
+		for (i = 0; i < sizeof(hk->kv_mic); i++)
+			printf("%02x", hk->kv_mic[i]);
+	}
+	printf("\n");
+}
+#endif
+
+/*
+ * Set a TKIP key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP.
+ */
+static int
+ath_keyset_tkip(struct ath_softc *sc, const struct ieee80211_key *k,
+	HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+#define	IEEE80211_KEY_XR	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)
+	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	struct ath_hal *ah = sc->sc_ah;
+
+	KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
+		("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
+	KASSERT(sc->sc_splitmic, ("key cache !split"));
+	if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
+		/*
+		 * TX key goes at first index, RX key at the rx index.
+		 * The hal handles the MIC keys at index+64.
+		 */
+		memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+		if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
+			return 0;
+
+		memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
+		/* XXX delete tx key on failure? */
+		return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+	} else if (k->wk_flags & IEEE80211_KEY_XR) {
+		/*
+		 * TX/RX key goes at first index.
+		 * The hal handles the MIC keys are index+64.
+		 */
+		memcpy(hk->kv_mic, k->wk_flags & IEEE80211_KEY_XMIT ?
+			k->wk_txmic : k->wk_rxmic, sizeof(hk->kv_mic));
+		KEYPRINTF(sc, k->wk_keyix, hk, mac);
+		return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+	}
+	return 0;
+#undef IEEE80211_KEY_XR
+}
+
+/*
+ * Set a net80211 key into the hardware.  This handles the
+ * potential distribution of key state to multiple key
+ * cache slots for TKIP with hardware MIC support.
+ */
+static int
+ath_keyset(struct ath_softc *sc, const struct ieee80211_key *k,
+	const u_int8_t mac0[IEEE80211_ADDR_LEN],
+	struct ieee80211_node *bss)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	static const u_int8_t ciphermap[] = {
+		HAL_CIPHER_WEP,		/* IEEE80211_CIPHER_WEP */
+		HAL_CIPHER_TKIP,	/* IEEE80211_CIPHER_TKIP */
+		HAL_CIPHER_AES_OCB,	/* IEEE80211_CIPHER_AES_OCB */
+		HAL_CIPHER_AES_CCM,	/* IEEE80211_CIPHER_AES_CCM */
+		(u_int8_t) -1,		/* 4 is not allocated */
+		HAL_CIPHER_CKIP,	/* IEEE80211_CIPHER_CKIP */
+		HAL_CIPHER_CLR,		/* IEEE80211_CIPHER_NONE */
+	};
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	u_int8_t gmac[IEEE80211_ADDR_LEN];
+	const u_int8_t *mac;
+	HAL_KEYVAL hk;
+
+	memset(&hk, 0, sizeof(hk));
+	/*
+	 * Software crypto uses a "clear key" so non-crypto
+	 * state kept in the key cache are maintained and
+	 * so that rx frames have an entry to match.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+		KASSERT(cip->ic_cipher < N(ciphermap),
+			("invalid cipher type %u", cip->ic_cipher));
+		hk.kv_type = ciphermap[cip->ic_cipher];
+		hk.kv_len = k->wk_keylen;
+		memcpy(hk.kv_val, k->wk_key, k->wk_keylen);
+	} else
+		hk.kv_type = HAL_CIPHER_CLR;
+
+	if ((k->wk_flags & IEEE80211_KEY_GROUP) && sc->sc_mcastkey) {
+		/*
+		 * Group keys on hardware that supports multicast frame
+		 * key search use a mac that is the sender's address with
+		 * the high bit set instead of the app-specified address.
+		 */
+		IEEE80211_ADDR_COPY(gmac, bss->ni_macaddr);
+		gmac[0] |= 0x80;
+		mac = gmac;
+	} else
+		mac = mac0;
+
+	if (hk.kv_type == HAL_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+	    sc->sc_splitmic) {
+		return ath_keyset_tkip(sc, k, &hk, mac);
+	} else {
+		KEYPRINTF(sc, k->wk_keyix, &hk, mac);
+		return ath_hal_keyset(ah, k->wk_keyix, &hk, mac);
+	}
+#undef N
+}
+
+/*
+ * Allocate tx/rx key slots for TKIP.  We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static u_int16_t
+key_alloc_2pair(struct ath_softc *sc,
+	ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	u_int i, keyix;
+
+	KASSERT(sc->sc_splitmic, ("key cache !split"));
+	/* XXX could optimize */
+	for (i = 0; i < N(sc->sc_keymap)/4; i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots in this byte are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1) {
+		again:
+				keyix++;
+				b >>= 1;
+			}
+			/* XXX IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV */
+			if (isset(sc->sc_keymap, keyix+32) ||
+			    isset(sc->sc_keymap, keyix+64) ||
+			    isset(sc->sc_keymap, keyix+32+64)) {
+				/* full pair unavailable */
+				/* XXX statistic */
+				if (keyix == (i+1)*NBBY) {
+					/* no slots were appropriate, advance */
+					continue;
+				}
+				goto again;
+			}
+			setbit(sc->sc_keymap, keyix);
+			setbit(sc->sc_keymap, keyix+64);
+			setbit(sc->sc_keymap, keyix+32);
+			setbit(sc->sc_keymap, keyix+32+64);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				("%s: key pair %u,%u %u,%u\n",
+				__func__, keyix, keyix+64,
+				keyix+32, keyix+32+64));
+			*txkeyix = keyix;
+			*rxkeyix = keyix+32;
+			return 1;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s: out of pair space\n", __func__));
+	return 0;
+#undef N
+}
+
+/*
+ * Allocate a single key cache slot.
+ */
+static int
+key_alloc_single(struct ath_softc *sc,
+	ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	u_int i, keyix;
+
+	/* XXX try i,i+32,i+64,i+32+64 to minimize key pair conflicts */
+	for (i = 0; i < N(sc->sc_keymap); i++) {
+		u_int8_t b = sc->sc_keymap[i];
+		if (b != 0xff) {
+			/*
+			 * One or more slots are free.
+			 */
+			keyix = i*NBBY;
+			while (b & 1)
+				keyix++, b >>= 1;
+			setbit(sc->sc_keymap, keyix);
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s: key %u\n",
+				__func__, keyix));
+			*txkeyix = *rxkeyix = keyix;
+			return 1;
+		}
+	}
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s: out of space\n", __func__));
+	return 0;
+#undef N
+}
+
+/*
+ * Allocate one or more key cache slots for a uniacst key.  The
+ * key itself is needed only to identify the cipher.  For hardware
+ * TKIP with split cipher+MIC keys we allocate two key cache slot
+ * pairs so that we can setup separate TX and RX MIC keys.  Note
+ * that the MIC key for a TKIP key at slot i is assumed by the
+ * hardware to be at slot i+64.  This limits TKIP keys to the first
+ * 64 entries.
+ */
+static int
+ath_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+	ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	/*
+	 * Group key allocation must be handled specially for
+	 * parts that do not support multicast key cache search
+	 * functionality.  For those parts the key id must match
+	 * the h/w key index so lookups find the right key.  On
+	 * parts w/ the key search facility we install the sender's
+	 * mac address (with the high bit set) and let the hardware
+	 * find the key w/o using the key id.  This is preferred as
+	 * it permits us to support multiple users for adhoc and/or
+	 * multi-station operation.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_GROUP) && !sc->sc_mcastkey) {
+		if (!(&ic->ic_nw_keys[0] <= k &&
+		      k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+			/* should not happen */
+			DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+				("%s: bogus group key\n", __func__));
+			return 0;
+		}
+		/*
+		 * XXX we pre-allocate the global keys so
+		 * have no way to check if they've already been allocated.
+		 */
+		*keyix = *rxkeyix = k - ic->ic_nw_keys;
+		return 1;
+	}
+
+	/*
+	 * We allocate two pair for TKIP when using the h/w to do
+	 * the MIC.  For everything else, including software crypto,
+	 * we allocate a single entry.  Note that s/w crypto requires
+	 * a pass-through slot on the 5211 and 5212.  The 5210 does
+	 * not support pass-through cache entries and we map all
+	 * those requests to slot 0.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		return key_alloc_single(sc, keyix, rxkeyix);
+	} else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
+		return key_alloc_2pair(sc, keyix, rxkeyix);
+	} else {
+		return key_alloc_single(sc, keyix, rxkeyix);
+	}
+}
+
+/*
+ * Delete an entry in the key cache allocated by ath_key_alloc.
+ */
+static int
+ath_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	u_int keyix = k->wk_keyix;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s: delete key %u\n", __func__, keyix));
+
+	ath_hal_keyreset(ah, keyix);
+	/*
+	 * Handle split tx/rx keying required for TKIP with h/w MIC.
+	 */
+	if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+	    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic)
+		ath_hal_keyreset(ah, keyix+32);		/* RX key */
+	if (keyix >= IEEE80211_WEP_NKID) {
+		/*
+		 * Don't touch keymap entries for global keys so
+		 * they are never considered for dynamic allocation.
+		 */
+		clrbit(sc->sc_keymap, keyix);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
+		    (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
+		    sc->sc_splitmic) {
+			clrbit(sc->sc_keymap, keyix+64);	/* TX key MIC */
+			clrbit(sc->sc_keymap, keyix+32);	/* RX key */
+			clrbit(sc->sc_keymap, keyix+32+64);	/* RX key MIC */
+		}
+	}
+	return 1;
+}
+
+/*
+ * Set the key cache contents for the specified key.  Key cache
+ * slot(s) must already have been allocated by ath_key_alloc.
+ */
+static int
+ath_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	return ath_keyset(sc, k, mac, ic->ic_bss);
+}
+
+#define IF_LOCK(x)
+#define IF_UNLOCK(x)
+/*
+ * Block/unblock tx+rx processing while a key change is done.
+ * We assume the caller serializes key management operations
+ * so we only need to worry about synchronization with other
+ * uses that originate in the driver.
+ */
+static void
+ath_key_update_begin(struct ieee80211com *ic __unused)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s:\n", __func__));
+#if 0
+	tasklet_disable(&sc->sc_rxtq);
+	IF_LOCK(&ifp->if_snd);		/* NB: doesn't block mgmt frames */
+#endif
+}
+
+static void
+ath_key_update_end(struct ieee80211com *ic __unused)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+
+	DPRINTF(sc, ATH_DEBUG_KEYCACHE, ("%s:\n", __func__));
+#if 0
+	IF_UNLOCK(&ifp->if_snd);
+	tasklet_enable(&sc->sc_rxtq);
+#endif
+}
+
+/*
+ * Calculate the receive filter according to the
+ * operating mode and state:
+ *
+ * o always accept unicast, broadcast, and multicast traffic
+ * o maintain current state of phy error reception (the hal
+ *   may enable phy error frames for noise immunity work)
+ * o probe request frames are accepted only when operating in
+ *   hostap, adhoc, or monitor modes
+ * o enable promiscuous mode according to the interface state
+ * o accept beacons:
+ *   - when operating in adhoc mode so the 802.11 layer creates
+ *     node table entries for peers,
+ *   - when operating in station mode for collecting rssi data when
+ *     the station is otherwise quiet, or
+ *   - when scanning
+ */
+static u_int32_t
+ath_calcrxfilter(struct ath_softc *sc, enum ieee80211_state state)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ifnet *ifp = sc->sc_ifp;
+	u_int32_t rfilt;
+
+	rfilt = (ath_hal_getrxfilter(ah) & HAL_RX_FILTER_PHYERR)
+	      | HAL_RX_FILTER_UCAST | HAL_RX_FILTER_BCAST | HAL_RX_FILTER_MCAST;
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		rfilt |= HAL_RX_FILTER_PROBEREQ;
+	if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+	    (ifp->if_flags & IFF_PROMISC))
+		rfilt |= HAL_RX_FILTER_PROM;
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_IBSS ||
+	    state == IEEE80211_S_SCAN)
+		rfilt |= HAL_RX_FILTER_BEACON;
+	return rfilt;
+}
+
+static void
+ath_mode_init(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ifnet *ifp = sc->sc_ifp;
+	u_int32_t rfilt, mfilt[2], val;
+	u_int8_t pos;
+	struct ifmultiaddr *ifma;
+
+	/* configure rx filter */
+	rfilt = ath_calcrxfilter(sc, ic->ic_state);
+	ath_hal_setrxfilter(ah, rfilt);
+
+	/* configure operational mode */
+	ath_hal_setopmode(ah);
+
+	/*
+	 * Handle any link-level address change.  Note that we only
+	 * need to force ic_myaddr; any other addresses are handled
+	 * as a byproduct of the ifnet code marking the interface
+	 * down then up.
+	 *
+	 * XXX should get from lladdr instead of arpcom but that's more work
+	 */
+#ifndef IFP2AC
+#define IFP2AC(ifp) ((struct arpcom *)(ifp))
+#endif
+	IEEE80211_ADDR_COPY(ic->ic_myaddr, IFP2AC(ifp)->ac_enaddr);
+
+	ath_hal_setmac(ah, ic->ic_myaddr);
+
+	/* calculate and install multicast filter */
+	if ((ifp->if_flags & IFF_ALLMULTI) == 0) {
+		mfilt[0] = mfilt[1] = 0;
+		/* XXX ADDR_LOCK */
+		LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+			caddr_t dl;
+
+			/* calculate XOR of eight 6bit values */
+			dl = LLADDR((struct sockaddr_dl *) ifma->ifma_addr);
+			val = LE_READ_4(dl + 0);
+			pos = (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			val = LE_READ_4(dl + 3);
+			pos ^= (val >> 18) ^ (val >> 12) ^ (val >> 6) ^ val;
+			pos &= 0x3f;
+			mfilt[pos / 32] |= (1 << (pos % 32));
+		}
+		/* IF_ADDR_UNLOCK(ifp); XXX */
+	} else {
+		mfilt[0] = mfilt[1] = ~0;
+	}
+	ath_hal_setmcastfilter(ah, mfilt[0], mfilt[1]);
+	DPRINTF(sc, ATH_DEBUG_MODE, ("%s: RX filter 0x%x, MC filter %08x:%08x\n",
+		__func__, rfilt, mfilt[0], mfilt[1]));
+}
+
+static void
+ath_mbuf_load_cb(void *arg, bus_dma_segment_t *seg, int nseg, bus_size_t mapsize, int error __unused)
+{
+	struct ath_buf *bf = arg;
+
+	KASSERT(nseg <= ATH_MAX_SCATTER,
+		("ath_mbuf_load_cb: too many DMA segments %u", nseg));
+	bf->bf_mapsize = mapsize;
+	bf->bf_nseg = nseg;
+	bcopy(seg, bf->bf_segs, nseg * sizeof (seg[0]));
+}
+
+/*
+ * Set the slot time based on the current setting.
+ */
+static void
+ath_setslottime(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
+	else
+		ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
+	sc->sc_updateslot = OK;
+}
+
+/*
+ * Callback from the 802.11 layer to update the
+ * slot time based on the current setting.
+ */
+static void
+ath_updateslot(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	/*
+	 * When not coordinating the BSS, change the hardware
+	 * immediately.  For other operation we defer the change
+	 * until beacon updates have propagated to the stations.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+		sc->sc_updateslot = UPDATE;
+	else
+		ath_setslottime(sc);
+}
+
+/*
+ * Setup a h/w transmit queue for beacons.
+ */
+static int
+ath_beaconq_setup(struct ath_hal *ah)
+{
+	HAL_TXQ_INFO qi;
+
+	memset(&qi, 0, sizeof(qi));
+	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
+	/* NB: for dynamic turbo, don't enable any other interrupts */
+	qi.tqi_qflags = TXQ_FLAG_TXDESCINT_ENABLE;
+	return ath_hal_setuptxqueue(ah, HAL_TX_QUEUE_BEACON, &qi);
+}
+
+/*
+ * Setup the transmit queue parameters for the beacon queue.
+ */
+static int
+ath_beaconq_config(struct ath_softc *sc)
+{
+#define	ATH_EXPONENT_TO_VALUE(v)	((1<<(v))-1)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+
+	ath_hal_gettxqueueprops(ah, sc->sc_bhalq, &qi);
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/*
+		 * Always burst out beacon and CAB traffic.
+		 */
+		qi.tqi_aifs = ATH_BEACON_AIFS_DEFAULT;
+		qi.tqi_cwmin = ATH_BEACON_CWMIN_DEFAULT;
+		qi.tqi_cwmax = ATH_BEACON_CWMAX_DEFAULT;
+	} else {
+		struct wmeParams *wmep =
+			&ic->ic_wme.wme_chanParams.cap_wmeParams[WME_AC_BE];
+		/*
+		 * Adhoc mode; important thing is to use 2x cwmin.
+		 */
+		qi.tqi_aifs = wmep->wmep_aifsn;
+		qi.tqi_cwmin = 2*ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+		qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);
+	}
+
+	if (!ath_hal_settxqueueprops(ah, sc->sc_bhalq, &qi)) {
+		device_printf(sc->sc_dev, "unable to update parameters for "
+			"beacon hardware queue!\n");
+		return 0;
+	} else {
+		ath_hal_resettxqueue(ah, sc->sc_bhalq); /* push to h/w */
+		return 1;
+	}
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Allocate and setup an initial beacon frame.
+ */
+static int
+ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_buf *bf;
+	struct mbuf *m;
+	int error;
+
+	bf = STAILQ_FIRST(&sc->sc_bbuf);
+	if (bf == NULL) {
+		DPRINTF(sc, ATH_DEBUG_BEACON, ("%s: no dma buffers\n", __func__));
+		sc->sc_stats.ast_be_nombuf++;	/* XXX */
+		return ENOMEM;			/* XXX */
+	}
+	/*
+	 * NB: the beacon data buffer must be 32-bit aligned;
+	 * we assume the mbuf routines will return us something
+	 * with this alignment (perhaps should assert).
+	 */
+	m = ieee80211_beacon_alloc(ic, ni, &sc->sc_boff);
+	if (m == NULL) {
+		DPRINTF(sc, ATH_DEBUG_BEACON, ("%s: cannot get mbuf\n",
+			__func__));
+		sc->sc_stats.ast_be_nombuf++;
+		return ENOMEM;
+	}
+        error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m,
+                                     ath_mbuf_load_cb, bf,
+                                     BUS_DMA_NOWAIT);
+	if (error == 0) {
+		bf->bf_m = m;
+		bf->bf_node = ieee80211_ref_node(ni);
+	} else {
+		m_freem(m);
+	}
+	return error;
+}
+
+/*
+ * Setup the beacon frame for transmit.
+ */
+static void
+ath_beacon_setup(struct ath_softc *sc, struct ath_buf *bf)
+{
+#define	USE_SHPREAMBLE(_ic) \
+	(((_ic)->ic_flags & (IEEE80211_F_SHPREAMBLE | IEEE80211_F_USEBARKER))\
+		== IEEE80211_F_SHPREAMBLE)
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m = bf->bf_m;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_node *an = ATH_NODE(ni);
+	struct ath_desc *ds;
+	int flags, antenna;
+	u_int8_t rate;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON, ("%s: m %p len %u\n",
+		__func__, m, m->m_len));
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+
+	flags = HAL_TXDESC_NOACK;
+	if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) {
+		ds->ds_link = bf->bf_daddr;	/* self-linked */
+		flags |= HAL_TXDESC_VEOL;
+		/*
+		 * Let hardware handle antenna switching.
+		 */
+		antenna = sc->sc_txantenna;
+	} else {
+		ds->ds_link = 0;
+		/*
+		 * Switch antenna every 4 beacons.
+		 * XXX assumes two antenna
+		 */
+		antenna = (sc->sc_stats.ast_be_xmit & 4 ? 2 : 1);
+	}
+
+	KASSERT(bf->bf_nseg == 1,
+		("multi-segment beacon frame; nseg %u", bf->bf_nseg));
+	ds->ds_data = bf->bf_segs[0].ds_addr;
+	/*
+	 * Calculate rate code.
+	 * XXX everything at min xmit rate
+	 */
+	if (USE_SHPREAMBLE(ic))
+		rate = an ?  an->an_tx_mgtratesp
+			  : sc->sc_currates->info[0].shortPreamble;
+	else
+		rate = an ? an->an_tx_mgtrate
+			  : sc->sc_currates->info[0].rateCode;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON,("Beacon rate %d\n", rate));
+
+	if (ic->ic_rawbpf)
+		bpf_mtap(ic->ic_rawbpf, m);
+	if (sc->sc_drvbpf) {
+		sc->sc_tx_th.wt_flags = 0;
+		if (USE_SHPREAMBLE(ic))
+			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+		sc->sc_tx_th.wt_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
+		sc->sc_tx_th.wt_txpower = sc->tx_power;		/* XXX */
+		sc->sc_tx_th.wt_antenna = antenna;
+		bpf_ptap(sc->sc_drvbpf, m, &sc->sc_tx_th, sc->sc_tx_th_len);
+	}	
+ 
+	ath_hal_setuptxdesc(ah, ds
+		, m->m_len + IEEE80211_CRC_LEN	/* frame length */
+		, sizeof(struct ieee80211_frame)/* header length */
+		, HAL_PKT_TYPE_BEACON		/* Atheros packet type */
+		, ni->ni_txpower		/* txpower XXX */
+		, rate, 1			/* series 0 rate/tries */
+		, HAL_TXKEYIX_INVALID		/* no encryption */
+		, antenna			/* antenna mode */
+		, flags				/* no ack, veol for beacons */
+		, 0				/* rts/cts rate */
+		, 0				/* rts/cts duration */
+	);
+	/* NB: beacon's BufLen must be a multiple of 4 bytes */
+	ath_hal_filltxdesc(ah, ds
+		, roundup(m->m_len, 4)		/* buffer length */
+		, AH_TRUE			/* first segment */
+		, AH_TRUE			/* last segment */
+		, ds				/* first descriptor */
+	);
+#undef USE_SHPREAMBLE
+}
+
+/*
+ * Transmit a beacon frame at SWBA.  Dynamic updates to the
+ * frame contents are done as needed and the slot time is
+ * also adjusted based on current state.
+ */
+static void
+ath_beacon_proc(void *arg, int pending)
+{
+	struct ath_softc *sc = arg;
+	struct ath_buf *bf = STAILQ_FIRST(&sc->sc_bbuf);
+	struct ieee80211_node *ni = bf->bf_node;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct mbuf *m;
+	int ncabq, error, otherant;
+
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC, ("%s: pending %u\n",
+		__func__, pending));
+
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    bf == NULL || bf->bf_m == NULL) {
+		DPRINTF(sc, ATH_DEBUG_ANY, ("%s: ic_flags=%x bf=%p bf_m=%p\n",
+			__func__, ic->ic_flags, bf, bf ? bf->bf_m : NULL));
+		return;
+	}
+	/*
+	 * Check if the previous beacon has gone out.  If
+	 * not don't don't try to post another, skip this
+	 * period and wait for the next.  Missed beacons
+	 * indicate a problem and should not occur.  If we
+	 * miss too many consecutive beacons reset the device.
+	 */
+	if (ath_hal_numtxpending(ah, sc->sc_bhalq) != 0) {
+		sc->sc_bmisscount++;
+		DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+			("%s: missed %u consecutive beacons\n",
+			__func__, sc->sc_bmisscount));
+		if (sc->sc_bmisscount > 3)		/* NB: 3 is a guess */
+			taskqueue_enqueue(taskqueue_swi, &sc->sc_bstucktask);
+		return;
+	}
+	if (sc->sc_bmisscount != 0) {
+		DPRINTF(sc, ATH_DEBUG_BEACON,
+			("%s: resume beacon xmit after %u misses\n",
+			__func__, sc->sc_bmisscount));
+		sc->sc_bmisscount = 0;
+	}
+
+	/*
+	 * Update dynamic beacon contents.  If this returns
+	 * non-zero then we need to remap the memory because
+	 * the beacon frame changed size (probably because
+	 * of the TIM bitmap).
+	 */
+	m = bf->bf_m;
+	ncabq = ath_hal_numtxpending(ah, sc->sc_cabq->axq_qnum);
+	if (ieee80211_beacon_update(ic, bf->bf_node, &sc->sc_boff, m, ncabq)) {
+		/* XXX too conservative? */
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+        	error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m,
+               		                     ath_mbuf_load_cb, bf,
+                       		             BUS_DMA_NOWAIT);
+		if (error != 0) {
+			if_printf(ic->ic_ifp,
+			    "%s: bus_dmamap_load_mbuf failed, error %u\n",
+			    __func__, error);
+			return;
+		}
+	}
+
+	/*
+	 * Handle slot time change when a non-ERP station joins/leaves
+	 * an 11g network.  The 802.11 layer notifies us via callback,
+	 * we mark updateslot, then wait one beacon before effecting
+	 * the change.  This gives associated stations at least one
+	 * beacon interval to note the state change.
+	 */
+	/* XXX locking */
+	if (sc->sc_updateslot == UPDATE)
+		sc->sc_updateslot = COMMIT;	/* commit next beacon */
+	else if (sc->sc_updateslot == COMMIT)
+		ath_setslottime(sc);		/* commit change to h/w */
+
+	/*
+	 * Check recent per-antenna transmit statistics and flip
+	 * the default antenna if noticeably more frames went out
+	 * on the non-default antenna.
+	 * XXX assumes 2 anntenae
+	 */
+	otherant = sc->sc_defant & 1 ? 2 : 1;
+	if (sc->sc_ant_tx[otherant] > sc->sc_ant_tx[sc->sc_defant] + 2)
+		ath_setdefantenna(sc, otherant);
+	sc->sc_ant_tx[1] = sc->sc_ant_tx[2] = 0;
+
+	/*
+	 * Construct tx descriptor.
+	 */
+	ath_beacon_setup(sc, bf);
+
+	/*
+	 * Stop any current dma and put the new frame on the queue.
+	 * This should never fail since we check above that no frames
+	 * are still pending on the queue.
+	 */
+	if (!ath_hal_stoptxdma(ah, sc->sc_bhalq)) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			("%s: beacon queue %u did not stop?\n",
+			__func__, sc->sc_bhalq));
+	}
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+
+	/*
+	 * Enable the CAB queue before the beacon queue to
+	 * insure cab frames are triggered by this beacon.
+	 */
+	if (sc->sc_boff.bo_tim[4] & 1)		/* NB: only at DTIM */
+		ath_hal_txstart(ah, sc->sc_cabq->axq_qnum);
+	ath_hal_puttxbuf(ah, sc->sc_bhalq, bf->bf_daddr);
+	ath_hal_txstart(ah, sc->sc_bhalq);
+	DPRINTF(sc, ATH_DEBUG_BEACON_PROC,
+		("%s: TXDP[%u] = %p (%p)\n", __func__,
+		sc->sc_bhalq, (caddr_t)bf->bf_daddr, bf->bf_desc));
+
+	sc->sc_stats.ast_be_xmit++;
+}
+
+/*
+ * Reset the hardware after detecting beacons have stopped.
+ */
+static void
+ath_bstuck_proc(void *arg, int pending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+
+	if_printf(ifp, "stuck beacon; resetting (bmiss count %u)\n",
+		sc->sc_bmisscount);
+	ath_reset(ifp);
+}
+
+/*
+ * Reclaim beacon resources.
+ */
+static void
+ath_beacon_free(struct ath_softc *sc)
+{
+	struct ath_buf *bf;
+
+	STAILQ_FOREACH(bf, &sc->sc_bbuf, bf_list) {
+		if (bf->bf_m != NULL) {
+			bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+			m_freem(bf->bf_m);
+			bf->bf_m = NULL;
+		}
+		if (bf->bf_node != NULL) {
+			ieee80211_free_node(bf->bf_node);
+			bf->bf_node = NULL;
+		}
+	}
+}
+
+/*
+ * Configure the beacon and sleep timers.
+ *
+ * When operating as an AP this resets the TSF and sets
+ * up the hardware to notify us when we need to issue beacons.
+ *
+ * When operating in station mode this sets up the beacon
+ * timers according to the timestamp of the last received
+ * beacon and the current TSF, configures PCF and DTIM
+ * handling, programs the sleep registers so the hardware
+ * will wakeup in time to receive beacons, and configures
+ * the beacon miss handling so we'll receive a BMISS
+ * interrupt when we stop seeing beacons from the AP
+ * we've associated with.
+ */
+static void
+ath_beacon_config(struct ath_softc *sc)
+{
+#define	TSF_TO_TU(_h,_l)	(((_h) << 22) | ((_l) >> 10))
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ieee80211_node *ni = ic->ic_bss;
+	u_int32_t nexttbtt, intval;
+
+	/* extract tstamp from last beacon and convert to TU */
+	nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4),
+			     LE_READ_4(ni->ni_tstamp.data));
+	/* NB: the beacon interval is kept internally in TU's */
+	intval = ni->ni_intval & HAL_BEACON_PERIOD;
+	if (nexttbtt == 0)		/* e.g. for ap mode */
+		nexttbtt = intval;
+	else if (intval)		/* NB: can be 0 for monitor mode */
+		nexttbtt = roundup(nexttbtt, intval);
+	DPRINTF(sc, ATH_DEBUG_BEACON, ("%s: nexttbtt %u intval %u (%u)\n",
+		__func__, nexttbtt, intval, ni->ni_intval));
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		HAL_BEACON_STATE bs;
+		u_int64_t tsf;
+		u_int32_t tsftu;
+		int dtimperiod, dtimcount;
+		int cfpperiod, cfpcount;
+
+		/*
+		 * Setup dtim and cfp parameters according to
+		 * last beacon we received (which may be none).
+		 */
+		dtimperiod = ni->ni_dtim_period;
+		if (dtimperiod <= 0)		/* NB: 0 if not known */
+			dtimperiod = 1;
+		dtimcount = ni->ni_dtim_count;
+		if (dtimcount >= dtimperiod)	/* NB: sanity check */
+			dtimcount = 0;		/* XXX? */
+		cfpperiod = 1;			/* NB: no PCF support yet */
+		cfpcount = 0;
+#define	FUDGE	2
+		/*
+		 * Pull nexttbtt forward to reflect the current
+		 * TSF and calculate dtim+cfp state for the result.
+		 */
+		tsf = ath_hal_gettsf64(ah);
+		tsftu = TSF_TO_TU((u_int32_t)(tsf>>32), (u_int32_t)tsf) + FUDGE;
+		do {
+			nexttbtt += intval;
+			if (--dtimcount < 0) {
+				dtimcount = dtimperiod - 1;
+				if (--cfpcount < 0)
+					cfpcount = cfpperiod - 1;
+			}
+		} while (nexttbtt < tsftu);
+#undef FUDGE
+		memset(&bs, 0, sizeof(bs));
+		bs.bs_intval = intval;
+		bs.bs_nexttbtt = nexttbtt;
+		bs.bs_dtimperiod = dtimperiod*intval;
+		bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
+		bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
+		bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
+		bs.bs_cfpmaxduration = 0;
+#if 0
+		/*
+		 * The 802.11 layer records the offset to the DTIM
+		 * bitmap while receiving beacons; use it here to
+		 * enable h/w detection of our AID being marked in
+		 * the bitmap vector (to indicate frames for us are
+		 * pending at the AP).
+		 * XXX do DTIM handling in s/w to WAR old h/w bugs
+		 * XXX enable based on h/w rev for newer chips
+		 */
+		bs.bs_timoffset = ni->ni_timoff;
+#endif
+		/*
+		 * Calculate the number of consecutive beacons to miss
+		 * before taking a BMISS interrupt.  The configuration
+		 * is specified in ms, so we need to convert that to
+		 * TU's and then calculate based on the beacon interval.
+		 * Note that we clamp the result to at most 10 beacons.
+		 */
+		bs.bs_bmissthreshold = howmany(ic->ic_bmisstimeout, intval);
+		if (bs.bs_bmissthreshold > 10)
+			bs.bs_bmissthreshold = 10;
+		else if (bs.bs_bmissthreshold <= 0)
+			bs.bs_bmissthreshold = 1;
+
+		/*
+		 * Calculate sleep duration.  The configuration is
+		 * given in ms.  We insure a multiple of the beacon
+		 * period is used.  Also, if the sleep duration is
+		 * greater than the DTIM period then it makes senses
+		 * to make it a multiple of that.
+		 *
+		 * XXX fixed at 100ms
+		 */
+		bs.bs_sleepduration =
+			roundup(IEEE80211_MS_TO_TU(100), bs.bs_intval);
+		if (bs.bs_sleepduration > bs.bs_dtimperiod)
+			bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod);
+
+		DPRINTF(sc, ATH_DEBUG_BEACON, 
+			( "%s: intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n"
+			, __func__
+			, bs.bs_intval
+			, bs.bs_nexttbtt
+			, bs.bs_dtimperiod
+			, bs.bs_nextdtim
+			, bs.bs_bmissthreshold
+			, bs.bs_sleepduration
+			, bs.bs_cfpperiod
+			, bs.bs_cfpmaxduration
+			, bs.bs_cfpnext
+			, bs.bs_timoffset )
+		);
+		ath_hal_intrset(ah, 0);
+		ath_hal_beacontimers(ah, &bs);
+		sc->sc_imask |= HAL_INT_BMISS;
+		ath_hal_intrset(ah, sc->sc_imask);
+	} else {
+		ath_hal_intrset(ah, 0);
+		if (nexttbtt == intval)
+			intval |= HAL_BEACON_RESET_TSF;
+		if (ic->ic_opmode == IEEE80211_M_IBSS) {
+			/*
+			 * In IBSS mode enable the beacon timers but only
+			 * enable SWBA interrupts if we need to manually
+			 * prepare beacon frames.  Otherwise we use a
+			 * self-linked tx descriptor and let the hardware
+			 * deal with things.
+			 */
+			intval |= HAL_BEACON_ENA;
+			if (!sc->sc_hasveol)
+				sc->sc_imask |= HAL_INT_SWBA;
+			ath_beaconq_config(sc);
+		} else if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			/*
+			 * In AP mode we enable the beacon timers and
+			 * SWBA interrupts to prepare beacon frames.
+			 */
+			intval |= HAL_BEACON_ENA;
+			sc->sc_imask |= HAL_INT_SWBA;	/* beacon prepare */
+			ath_beaconq_config(sc);
+		}
+		ath_hal_beaconinit(ah, nexttbtt, intval);
+		sc->sc_bmisscount = 0;
+		ath_hal_intrset(ah, sc->sc_imask);
+		/*
+		 * When using a self-linked beacon descriptor in
+		 * ibss mode load it once here.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol)
+			ath_beacon_proc(sc, 0);
+	}
+#undef TSF_TO_TU
+}
+
+static void
+ath_load_cb(void *arg, bus_dma_segment_t *segs, int nsegs __unused, int error __unused)
+{
+	bus_addr_t *paddr = (bus_addr_t*) arg;
+	*paddr = segs->ds_addr;
+}
+
+static int
+ath_descdma_setup(struct ath_softc *sc,
+	struct ath_descdma *dd, ath_bufhead *head,
+	const char *name, int nbuf, int ndesc)
+{
+#define	DS2PHYS(_dd, _ds) \
+	((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_desc *ds;
+	struct ath_buf *bf;
+	int i, bsize, error;
+
+	DPRINTF(sc, ATH_DEBUG_RESET, ("%s: %s DMA: %u buffers %u desc/buf\n",
+	    __func__, name, nbuf, ndesc));
+
+	dd->dd_name = name;
+	dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc;
+
+	/*
+	 * Setup DMA descriptor area.
+	 */
+	error = bus_dma_tag_create(NULL,	/* parent */
+		       PAGE_SIZE, 0,		/* alignment, bounds */
+		       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+		       BUS_SPACE_MAXADDR,	/* highaddr */
+		       NULL, NULL,		/* filter, filterarg */
+		       dd->dd_desc_len,		/* maxsize */
+		       1,			/* nsegments */
+		       BUS_SPACE_MAXADDR,	/* maxsegsize */
+		       BUS_DMA_ALLOCNOW,	/* flags */
+		       &dd->dd_dmat);
+	if (error != 0) {
+		if_printf(ifp, "cannot allocate %s DMA tag\n", dd->dd_name);
+		return error;
+	}
+
+	/* allocate descriptors */
+	error = bus_dmamap_create(dd->dd_dmat, BUS_DMA_WAITOK, &dd->dd_dmamap);
+	if (error != 0) {
+		if_printf(ifp, "unable to create dmamap for %s descriptors, "
+			"error %u\n", dd->dd_name, error);
+		goto fail0;
+	}
+
+	error = bus_dmamem_alloc(dd->dd_dmat, (void**) &dd->dd_desc,
+				 BUS_DMA_WAITOK, &dd->dd_dmamap);
+	if (error != 0) {
+		if_printf(ifp, "unable to alloc memory for %u %s descriptors, "
+			"error %u\n", nbuf * ndesc, dd->dd_name, error);
+		goto fail1;
+	}
+
+	error = bus_dmamap_load(dd->dd_dmat, dd->dd_dmamap,
+				dd->dd_desc, dd->dd_desc_len,
+				ath_load_cb, &dd->dd_desc_paddr,
+				BUS_DMA_NOWAIT);
+	if (error != 0) {
+		if_printf(ifp, "unable to map %s descriptors, error %u\n",
+			dd->dd_name, error);
+		goto fail2;
+	}
+
+	ds = dd->dd_desc;
+	DPRINTF(sc, ATH_DEBUG_RESET, ("%s: %s DMA map: %p (%lu) -> %p (%lu)\n",
+	    __func__, dd->dd_name, ds, (u_long) dd->dd_desc_len,
+	    (caddr_t) dd->dd_desc_paddr, /*XXX*/ (u_long) dd->dd_desc_len));
+
+	/* allocate rx buffers */
+	bsize = sizeof(struct ath_buf) * nbuf;
+	bf = malloc(bsize, M_DEVBUF, M_WAITOK | M_ZERO);
+	if (bf == NULL) {
+		if_printf(ifp, "malloc of %s buffers failed, size %u\n",
+			dd->dd_name, bsize);
+		goto fail3;
+	}
+	dd->dd_bufptr = bf;
+
+	STAILQ_INIT(head);
+	for (i = 0; i < nbuf; i++, bf++, ds += ndesc) {
+		bf->bf_desc = ds;
+		bf->bf_daddr = DS2PHYS(dd, ds);
+		error = bus_dmamap_create(sc->sc_dmat, BUS_DMA_WAITOK,
+				&bf->bf_dmamap);
+		if (error != 0) {
+			if_printf(ifp, "unable to create dmamap for %s "
+				"buffer %u, error %u\n", dd->dd_name, i, error);
+			ath_descdma_cleanup(sc, dd, head);
+			return error;
+		}
+		STAILQ_INSERT_TAIL(head, bf, bf_list);
+	}
+	return 0;
+fail3:
+	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+fail2:
+	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+fail1:
+	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+fail0:
+	bus_dma_tag_destroy(dd->dd_dmat);
+	memset(dd, 0, sizeof(*dd));
+	return error;
+#undef DS2PHYS
+}
+
+static void
+ath_descdma_cleanup(struct ath_softc *sc,
+	struct ath_descdma *dd, ath_bufhead *head)
+{
+	struct ath_buf *bf;
+	struct ieee80211_node *ni;
+
+	bus_dmamap_unload(dd->dd_dmat, dd->dd_dmamap);
+	bus_dmamem_free(dd->dd_dmat, dd->dd_desc, dd->dd_dmamap);
+	bus_dmamap_destroy(dd->dd_dmat, dd->dd_dmamap);
+	bus_dma_tag_destroy(dd->dd_dmat);
+
+	STAILQ_FOREACH(bf, head, bf_list) {
+		if (bf->bf_m) {
+			m_freem(bf->bf_m);
+			bf->bf_m = NULL;
+		}
+		if (bf->bf_dmamap != NULL) {
+			bus_dmamap_destroy(sc->sc_dmat, bf->bf_dmamap);
+			bf->bf_dmamap = NULL;
+		}
+		bf->bf_node = NULL;
+		if (ni != NULL) {
+			/*
+			 * Reclaim node reference.
+			 */
+			ieee80211_free_node(ni);
+		}
+	}
+
+	STAILQ_INIT(head);
+	free(dd->dd_bufptr, M_DEVBUF);
+	memset(dd, 0, sizeof(*dd));
+}
+
+static int
+ath_desc_alloc(struct ath_softc *sc)
+{
+	int error;
+
+	error = ath_descdma_setup(sc, &sc->sc_rxdma, &sc->sc_rxbuf,
+			"rx", ATH_RXBUF, 1);
+	if (error != 0)
+		return error;
+
+	error = ath_descdma_setup(sc, &sc->sc_txdma, &sc->sc_txbuf,
+			"tx", ATH_TXBUF, ATH_TXDESC);
+	if (error != 0) {
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+		return error;
+	}
+
+	error = ath_descdma_setup(sc, &sc->sc_bdma, &sc->sc_bbuf,
+			"beacon", 1, 1);
+	if (error != 0) {
+		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+		return error;
+	}
+	return 0;
+}
+
+static void
+ath_desc_free(struct ath_softc *sc)
+{
+
+	if (sc->sc_bdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_bdma, &sc->sc_bbuf);
+	if (sc->sc_txdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_txdma, &sc->sc_txbuf);
+	if (sc->sc_rxdma.dd_desc_len != 0)
+		ath_descdma_cleanup(sc, &sc->sc_rxdma, &sc->sc_rxbuf);
+}
+
+static struct ieee80211_node *
+ath_node_alloc(struct ieee80211_node_table *nt)
+{
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	const size_t space = sizeof(struct ath_node) + sc->sc_rc->arc_space;
+	struct ath_node *an;
+
+	an = malloc(space, M_80211_NODE, M_WAITOK|M_ZERO);
+	if (an == NULL) {
+		/* XXX stat+msg */
+		return NULL;
+	}
+	an->an_avgrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+	an->an_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+	ath_rate_node_init(sc, an);
+
+	DPRINTF(sc, ATH_DEBUG_NODE, ("%s: an %p\n", __func__, an));
+	return &an->an_node;
+}
+
+static void
+ath_node_free(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+        struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	ath_rate_node_cleanup(sc, ATH_NODE(ni));
+	(*sc->sc_node_free)( ni);
+}
+
+static u_int8_t
+ath_node_getrssi(const struct ieee80211_node *ni)
+{
+#define	HAL_EP_RND(x, mul) \
+	((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
+	u_int32_t avgrssi = ATH_NODE_CONST(ni)->an_avgrssi;
+	int32_t rssi;
+
+	/*
+	 * When only one frame is received there will be no state in
+	 * avgrssi so fallback on the value recorded by the 802.11 layer.
+	 */
+	if (avgrssi != ATH_RSSI_DUMMY_MARKER)
+		rssi = HAL_EP_RND(avgrssi, HAL_RSSI_EP_MULTIPLIER);
+	else
+		rssi = ni->ni_rssi;
+	/* NB: theoretically we shouldn't need this, but be paranoid */
+	return rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
+#undef HAL_EP_RND
+}
+
+static int
+ath_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	int error;
+	struct mbuf *m;
+	struct ath_desc *ds;
+
+	m = bf->bf_m;
+	if (m == NULL) {
+		/*
+		 * NB: by assigning a page to the rx dma buffer we
+		 * implicitly satisfy the Atheros requirement that
+		 * this buffer be cache-line-aligned and sized to be
+		 * multiple of the cache line size.  Not doing this
+		 * causes weird stuff to happen (for the 5210 at least).
+		 */
+		m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
+		if (m == NULL) {
+			DPRINTF(sc, ATH_DEBUG_ANY,
+				("%s: no mbuf/cluster\n", __func__));
+			sc->sc_stats.ast_rx_nombuf++;
+			return ENOMEM;
+		}
+		bf->bf_m = m;
+		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+		error = bus_dmamap_load_mbuf(sc->sc_dmat,
+					     bf->bf_dmamap, m,
+					     ath_mbuf_load_cb, bf,
+					     BUS_DMA_WAITOK);
+		if (error != 0) {
+			DPRINTF(sc, ATH_DEBUG_ANY,
+				("%s: bus_dmamap_load_mbuf failed; error %d\n",
+				__func__, error));
+			sc->sc_stats.ast_rx_busdma++;
+			return error;
+		}
+		KASSERT(bf->bf_nseg == 1,
+			("multi-segment packet; nseg %u", bf->bf_nseg));
+	}
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREREAD);
+
+	/*
+	 * Setup descriptors.  For receive we always terminate
+	 * the descriptor list with a self-linked entry so we'll
+	 * not get overrun under high load (as can happen with a
+	 * 5212 when ANI processing enables PHY error frames).
+	 *
+	 * To insure the last descriptor is self-linked we create
+	 * each descriptor as self-linked and add it to the end.  As
+	 * each additional descriptor is added the previous self-linked
+	 * entry is ``fixed'' naturally.  This should be safe even
+	 * if DMA is happening.  When processing RX interrupts we
+	 * never remove/process the last, self-linked, entry on the
+	 * descriptor list.  This insures the hardware always has
+	 * someplace to write a new frame.
+	 */
+	ds = bf->bf_desc;
+	ds->ds_link = bf->bf_daddr;	/* link to self */
+	ds->ds_data = bf->bf_segs[0].ds_addr;
+	ath_hal_setuprxdesc(ah, ds
+		, m->m_len		/* buffer size */
+		, 0
+	);
+
+	if (sc->sc_rxlink != NULL)
+		*sc->sc_rxlink = bf->bf_daddr;
+	sc->sc_rxlink = &ds->ds_link;
+	return 0;
+}
+
+/*
+ * Extend 15-bit time stamp from rx descriptor to
+ * a full 64-bit TSF using the current h/w TSF.
+ */
+static __inline u_int64_t
+ath_extend_tsf(struct ath_hal *ah, u_int32_t rstamp)
+{
+	u_int64_t tsf;
+
+	tsf = ath_hal_gettsf64(ah);
+	if ((tsf & 0x7fff) < rstamp)
+		tsf -= 0x8000;
+	return ((tsf &~ 0x7fff) | rstamp);
+}
+
+/*
+ * Intercept management frames to collect beacon rssi data
+ * and to do ibss merges.
+ */
+static void
+ath_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
+	struct ieee80211_node *ni,
+	int subtype, int rssi, u_int32_t rstamp)
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	/*
+	 * Call up first so subsequent work can use information
+	 * potentially stored in the node (e.g. for ibss merge).
+	 */
+	sc->sc_recv_mgmt(ic, m, ni, subtype, rssi, rstamp);
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		/* update rssi statistics for use by the hal */
+		ATH_RSSI_LPF(ATH_NODE(ni)->an_halstats.ns_avgbrssi, rssi);
+		/* fall thru... */
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		if (ic->ic_opmode == IEEE80211_M_IBSS &&
+		    ic->ic_state == IEEE80211_S_RUN) {
+			u_int64_t tsf = ath_extend_tsf(sc->sc_ah, rstamp);
+			/*
+			 * Handle ibss merge as needed; check the tsf on the
+			 * frame before attempting the merge.  The 802.11 spec
+			 * says the station should change it's bssid to match
+			 * the oldest station with the same ssid, where oldest
+			 * is determined by the tsf.  Note that hardware
+			 * reconfiguration happens through callback to
+			 * ath_newstate as the state machine will go from
+			 * RUN -> RUN when this happens.
+			 */
+			if (le64toh(ni->ni_tstamp.tsf) >= tsf) {
+				DPRINTF(sc, ATH_DEBUG_STATE,
+				    ("ibss merge, rstamp %u tsf %ju "
+				    "tstamp %ju\n", rstamp, (uintmax_t)tsf,
+				    (uintmax_t)ni->ni_tstamp.tsf));
+				(void) ieee80211_ibss_merge(ni);
+			}
+		}
+		break;
+	}
+}
+
+/*
+ * Set the default antenna.
+ */
+static void
+ath_setdefantenna(struct ath_softc *sc, u_int antenna)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	/* XXX block beacon interrupts */
+	ath_hal_setdefantenna(ah, antenna);
+	if (sc->sc_defant != antenna)
+		sc->sc_stats.ast_ant_defswitch++;
+	sc->sc_defant = antenna;
+	sc->sc_rxotherant = 0;
+}
+
+static void
+ath_rx_proc(void *arg, int npending)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+	struct ath_softc *sc = arg;
+	struct ath_buf *bf;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_desc *ds;
+	struct mbuf *m;
+	struct ieee80211_node *ni;
+	struct ath_node *an;
+	size_t len, type;
+	u_int phyerr;
+	HAL_STATUS status;
+
+	NET_LOCK_GIANT();		/* XXX */
+
+	DPRINTF(sc, ATH_DEBUG_RX_PROC, ("%s: pending %u\n", __func__, npending));
+	do {
+		bf = STAILQ_FIRST(&sc->sc_rxbuf);
+		if (bf == NULL) {		/* NB: shouldn't happen */
+			if_printf(ifp, "ath_rx_proc: no buffer!\n");
+			break;
+		}
+		ds = bf->bf_desc;
+		if (ds->ds_link == bf->bf_daddr) {
+			/* NB: never process the self-linked entry at the end */
+			break;
+		}
+		m = bf->bf_m;
+		if (m == NULL) {		/* NB: shouldn't happen */
+			if_printf(ifp, "ath_rx_proc: no mbuf!\n");
+			continue;
+		}
+		/* XXX sync descriptor memory */
+		/*
+		 * Must provide the virtual address of the current
+		 * descriptor, the physical address, and the virtual
+		 * address of the next descriptor in the h/w chain.
+		 * This allows the HAL to look ahead to see if the
+		 * hardware is done with a descriptor by checking the
+		 * done bit in the following descriptor and the address
+		 * of the current descriptor the DMA engine is working
+		 * on.  All this is necessary because of our use of
+		 * a self-linked list to avoid rx overruns.
+		 */
+		status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RECV_DESC)
+			ath_printrxbuf(bf, status == HAL_OK); 
+#endif
+		if (status == HAL_EINPROGRESS)
+			break;
+		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
+		if (ds->ds_rxstat.rs_more) {
+			/*
+			 * Frame spans multiple descriptors; this
+			 * cannot happen yet as we don't support
+			 * jumbograms.  If not in monitor mode,
+			 * discard the frame.
+			 */
+			if (ic->ic_opmode != IEEE80211_M_MONITOR) {
+				sc->sc_stats.ast_rx_toobig++;
+				goto rx_next;
+			}
+			/* fall thru for monitor mode handling... */
+		} else if (ds->ds_rxstat.rs_status != 0) {
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_CRC)
+				sc->sc_stats.ast_rx_crcerr++;
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_FIFO)
+				sc->sc_stats.ast_rx_fifoerr++;
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_PHY) {
+				sc->sc_stats.ast_rx_phyerr++;
+				phyerr = ds->ds_rxstat.rs_phyerr & 0x1f;
+				sc->sc_stats.ast_rx_phy[phyerr]++;
+				goto rx_next;
+			}
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_DECRYPT) {
+				/*
+				 * Decrypt error.  If the error occurred
+				 * because there was no hardware key, then
+				 * let the frame through so the upper layers
+				 * can process it.  This is necessary for 5210
+				 * parts which have no way to setup a ``clear''
+				 * key cache entry.
+				 *
+				 * XXX do key cache faulting
+				 */
+				if (ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID)
+					goto rx_accept;
+				sc->sc_stats.ast_rx_badcrypt++;
+			}
+			if (ds->ds_rxstat.rs_status & HAL_RXERR_MIC) {
+				sc->sc_stats.ast_rx_badmic++;
+				/*
+				 * Do minimal work required to hand off
+				 * the 802.11 header for notifcation.
+				 */
+				/* XXX frag's and qos frames */
+				len = ds->ds_rxstat.rs_datalen;
+				if (len >= sizeof (struct ieee80211_frame)) {
+					bus_dmamap_sync(sc->sc_dmat,
+					    bf->bf_dmamap,
+					    BUS_DMASYNC_POSTREAD);
+					ieee80211_notify_michael_failure(ic,
+					    mtod(m, struct ieee80211_frame *),
+					    sc->sc_splitmic ?
+					        ds->ds_rxstat.rs_keyix-32 :
+					        ds->ds_rxstat.rs_keyix
+					);
+				}
+			}
+			ifp->if_ierrors++;
+			/*
+			 * Reject error frames, we normally don't want
+			 * to see them in monitor mode (in monitor mode
+			 * allow through packets that have crypto problems).
+			 */
+			if ((ds->ds_rxstat.rs_status &~
+				(HAL_RXERR_DECRYPT|HAL_RXERR_MIC)) ||
+			    sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR)
+				goto rx_next;
+		}
+rx_accept:
+		/*
+		 * Sync and unmap the frame.  At this point we're
+		 * committed to passing the mbuf somewhere so clear
+		 * bf_m; this means a new sk_buff must be allocated
+		 * when the rx descriptor is setup again to receive
+		 * another frame.
+		 */
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_POSTREAD);
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		bf->bf_m = NULL;
+
+		m->m_pkthdr.rcvif = ifp;
+		len = ds->ds_rxstat.rs_datalen;
+		m->m_pkthdr.len = m->m_len = len;
+
+		sc->sc_stats.ast_ant_rx[ds->ds_rxstat.rs_antenna]++;
+
+		if (sc->sc_drvbpf) {
+			u_int8_t rix;
+
+			/*
+			 * Discard anything shorter than an ack or cts.
+			 */
+			if (len < IEEE80211_ACK_LEN) {
+				DPRINTF(sc, ATH_DEBUG_RECV,
+					( "%s: runt packet %d\n",
+					__func__, len ));
+				sc->sc_stats.ast_rx_tooshort++;
+				m_freem(m);
+				goto rx_next;
+			}
+			rix = ds->ds_rxstat.rs_rate;
+			sc->sc_rx_th.wr_flags = sc->sc_hwmap[rix].rxflags;
+			sc->sc_rx_th.wr_rate = sc->sc_hwmap[rix].ieeerate;
+			sc->sc_rx_th.wr_antsignal = ds->ds_rxstat.rs_rssi;
+			sc->sc_rx_th.wr_antenna = ds->ds_rxstat.rs_antenna;
+			/* XXX TSF */
+
+#ifdef ATH_RAWBPF
+			bpf_ptap(sc->sc_drvbpf, m, &sc->sc_rx_th, sc->sc_rx_th_len);
+#endif
+		}
+
+		/*
+		 * From this point on we assume the frame is at least
+		 * as large as ieee80211_frame_min; verify that.
+		 */
+		if (len < IEEE80211_MIN_LEN) {
+			DPRINTF(sc, ATH_DEBUG_RECV, ("%s: short packet %d\n",
+				__func__, len));
+			sc->sc_stats.ast_rx_tooshort++;
+			m_freem(m);
+			goto rx_next;
+		}
+
+		if (IFF_DUMPPKTS(sc, ATH_DEBUG_RECV)) {
+			ieee80211_dump_pkt(mtod(m, caddr_t), len,
+				   sc->sc_hwmap[ds->ds_rxstat.rs_rate].ieeerate,
+				   ds->ds_rxstat.rs_rssi);
+		}
+
+		m_adj(m, -IEEE80211_CRC_LEN);
+
+		/*
+		 * Locate the node for sender, track state, and then
+		 * pass the (referenced) node up to the 802.11 layer
+		 * for its use.
+		 */
+		ni = ieee80211_find_rxnode_withkey(ic,
+			mtod(m, const struct ieee80211_frame_min *),
+			ds->ds_rxstat.rs_keyix == HAL_RXKEYIX_INVALID ?
+				IEEE80211_KEYIX_NONE : ds->ds_rxstat.rs_keyix);
+		/*
+		 * Track rx rssi and do any rx antenna management.
+		 */
+		an = ATH_NODE(ni);
+		ATH_RSSI_LPF(an->an_avgrssi, ds->ds_rxstat.rs_rssi);
+		/*
+		 * Send frame up for processing.
+		 */
+		type = ieee80211_input(ic, m, ni,
+			ds->ds_rxstat.rs_rssi, ds->ds_rxstat.rs_tstamp);
+		ieee80211_free_node(ni);
+		if (sc->sc_diversity) {
+			/*
+			 * When using fast diversity, change the default rx
+			 * antenna if diversity chooses the other antenna 3
+			 * times in a row.
+			 */
+			if (sc->sc_defant != ds->ds_rxstat.rs_antenna) {
+				if (++sc->sc_rxotherant >= 3)
+					ath_setdefantenna(sc,
+						ds->ds_rxstat.rs_antenna);
+			} else
+				sc->sc_rxotherant = 0;
+		}
+		if (sc->sc_softled) {
+			/*
+			 * Blink for any data frame.  Otherwise do a
+			 * heartbeat-style blink when idle.  The latter
+			 * is mainly for station mode where we depend on
+			 * periodic beacon frames to trigger the poll event.
+			 */
+			if (type == IEEE80211_FC0_TYPE_DATA) {
+				sc->sc_rxrate = ds->ds_rxstat.rs_rate;
+				ath_led_event(sc, ATH_LED_RX);
+			} else if (ticks - sc->sc_ledevent >= sc->sc_ledidle)
+				ath_led_event(sc, ATH_LED_POLL);
+		}
+rx_next:
+		STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
+	} while (ath_rxbuf_init(sc, bf) == 0);
+
+	/* rx signal state monitoring */
+	ath_hal_rxmonitor(ah, &ATH_NODE(ic->ic_bss)->an_halstats);
+
+	NET_UNLOCK_GIANT();		/* XXX */
+#undef PA2DESC
+}
+
+/*
+ * Setup a h/w transmit queue.
+ */
+static struct ath_txq *
+ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+	int qnum;
+
+	memset(&qi, 0, sizeof(qi));
+	qi.tqi_subtype = subtype;
+	qi.tqi_aifs = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmin = HAL_TXQ_USEDEFAULT;
+	qi.tqi_cwmax = HAL_TXQ_USEDEFAULT;
+	/*
+	 * Enable interrupts only for EOL and DESC conditions.
+	 * We mark tx descriptors to receive a DESC interrupt
+	 * when a tx queue gets deep; otherwise waiting for the
+	 * EOL to reap descriptors.  Note that this is done to
+	 * reduce interrupt load and this only defers reaping
+	 * descriptors, never transmitting frames.  Aside from
+	 * reducing interrupts this also permits more concurrency.
+	 * The only potential downside is if the tx queue backs
+	 * up in which case the top half of the kernel may backup
+	 * due to a lack of tx descriptors.
+	 */
+	qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE;
+	qnum = ath_hal_setuptxqueue(ah, qtype, &qi);
+	if (qnum == -1) {
+		/*
+		 * NB: don't print a message, this happens
+		 * normally on parts with too few tx queues
+		 */
+		return NULL;
+	}
+	if (qnum >= N(sc->sc_txq)) {
+		device_printf(sc->sc_dev,
+			"hal qnum %u out of range, max %zu!\n",
+			qnum, N(sc->sc_txq));
+		ath_hal_releasetxqueue(ah, qnum);
+		return NULL;
+	}
+	if (!ATH_TXQ_SETUP(sc, qnum)) {
+		struct ath_txq *txq = &sc->sc_txq[qnum];
+
+		txq->axq_qnum = qnum;
+		txq->axq_depth = 0;
+		txq->axq_intrcnt = 0;
+		txq->axq_link = NULL;
+		STAILQ_INIT(&txq->axq_q);
+		ATH_TXQ_LOCK_INIT(sc, txq);
+		sc->sc_txqsetup |= 1<<qnum;
+	}
+	return &sc->sc_txq[qnum];
+#undef N
+}
+
+/*
+ * Setup a hardware data transmit queue for the specified
+ * access control.  The hal may not support all requested
+ * queues in which case it will return a reference to a
+ * previously setup queue.  We record the mapping from ac's
+ * to h/w queues for use by ath_tx_start and also track
+ * the set of h/w queues being used to optimize work in the
+ * transmit interrupt handler and related routines.
+ */
+static int
+ath_tx_setup(struct ath_softc *sc, int ac, int haltype)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ath_txq *txq;
+
+	if (ac >= N(sc->sc_ac2q)) {
+		device_printf(sc->sc_dev, "AC %u out of range, max %zu!\n",
+			ac, N(sc->sc_ac2q));
+		return 0;
+	}
+	txq = ath_txq_setup(sc, HAL_TX_QUEUE_DATA, haltype);
+	if (txq != NULL) {
+		sc->sc_ac2q[ac] = txq;
+		return 1;
+	} else
+		return 0;
+#undef N
+}
+
+/*
+ * Update WME parameters for a transmit queue.
+ */
+static int
+ath_txq_update(struct ath_softc *sc, int ac)
+{
+#define	ATH_EXPONENT_TO_VALUE(v)	((1<<v)-1)
+#define	ATH_TXOP_TO_US(v)		(v<<5)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_txq *txq = sc->sc_ac2q[ac];
+	struct wmeParams *wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac];
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_TXQ_INFO qi;
+
+	ath_hal_gettxqueueprops(ah, txq->axq_qnum, &qi);
+	qi.tqi_aifs = wmep->wmep_aifsn;
+	qi.tqi_cwmin = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmin);
+	qi.tqi_cwmax = ATH_EXPONENT_TO_VALUE(wmep->wmep_logcwmax);	
+	qi.tqi_burstTime = ATH_TXOP_TO_US(wmep->wmep_txopLimit);
+
+	if (!ath_hal_settxqueueprops(ah, txq->axq_qnum, &qi)) {
+		device_printf(sc->sc_dev, "unable to update hardware queue "
+			"parameters for %s traffic!\n",
+			ieee80211_wme_acnames[ac]);
+		return 0;
+	} else {
+		ath_hal_resettxqueue(ah, txq->axq_qnum); /* push to h/w */
+		return 1;
+	}
+#undef ATH_TXOP_TO_US
+#undef ATH_EXPONENT_TO_VALUE
+}
+
+/*
+ * Callback from the 802.11 layer to update WME parameters.
+ */
+static int
+ath_wme_update(struct ieee80211com *ic)
+{
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	return !ath_txq_update(sc, WME_AC_BE) ||
+	    !ath_txq_update(sc, WME_AC_BK) ||
+	    !ath_txq_update(sc, WME_AC_VI) ||
+	    !ath_txq_update(sc, WME_AC_VO) ? EIO : 0;
+}
+
+/*
+ * Reclaim resources for a setup queue.
+ */
+static void
+ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
+{
+
+	ath_hal_releasetxqueue(sc->sc_ah, txq->axq_qnum);
+	ATH_TXQ_LOCK_DESTROY(txq);
+	sc->sc_txqsetup &= ~(1<<txq->axq_qnum);
+}
+
+/*
+ * Reclaim all tx queue resources.
+ */
+static void
+ath_tx_cleanup(struct ath_softc *sc)
+{
+	int i;
+
+	ATH_TXBUF_LOCK_DESTROY(sc);
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_cleanupq(sc, &sc->sc_txq[i]);
+}
+
+/*
+ * Defragment an mbuf chain, returning at most maxfrags separate
+ * mbufs+clusters.  If this is not possible NULL is returned and
+ * the original mbuf chain is left in it's present (potentially
+ * modified) state.  We use two techniques: collapsing consecutive
+ * mbufs and replacing consecutive mbufs by a cluster.
+ */
+static struct mbuf *
+ath_defrag(struct mbuf *m0, int how, int maxfrags)
+{
+	struct mbuf *m, *n, *n2, **prev;
+	u_int curfrags;
+
+	/*
+	 * Calculate the current number of frags.
+	 */
+	curfrags = 0;
+	for (m = m0; m != NULL; m = m->m_next)
+		curfrags++;
+	/*
+	 * First, try to collapse mbufs.  Note that we always collapse
+	 * towards the front so we don't need to deal with moving the
+	 * pkthdr.  This may be suboptimal if the first mbuf has much
+	 * less data than the following.
+	 */
+	m = m0;
+again:
+	for (;;) {
+		n = m->m_next;
+		if (n == NULL)
+			break;
+		if (/* (m->m_flags & M_RDONLY) == 0 && */ /* XXX */
+		    n->m_len < M_TRAILINGSPACE(m)) {
+			bcopy(mtod(n, void *), mtod(m, char *) + m->m_len,
+				n->m_len);
+			m->m_len += n->m_len;
+			m->m_next = n->m_next;
+			m_free(n);
+			if (--curfrags <= maxfrags)
+				return m0;
+		} else
+			m = n;
+	}
+	KASSERT(maxfrags > 1,
+		("maxfrags %u, but normal collapse failed", maxfrags));
+	/*
+	 * Collapse consecutive mbufs to a cluster.
+	 */
+	prev = &m0->m_next;		/* NB: not the first mbuf */
+	while ((n = *prev) != NULL) {
+		if ((n2 = n->m_next) != NULL &&
+		    n->m_len + n2->m_len < MCLBYTES) {
+			m = m_getcl(how, MT_DATA, 0);
+			if (m == NULL)
+				goto bad;
+			bcopy(mtod(n, void *), mtod(m, void *), n->m_len);
+			bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len,
+				n2->m_len);
+			m->m_len = n->m_len + n2->m_len;
+			m->m_next = n2->m_next;
+			*prev = m;
+			m_free(n);
+			m_free(n2);
+			if (--curfrags <= maxfrags)	/* +1 cl -2 mbufs */
+				return m0;
+			/*
+			 * Still not there, try the normal collapse
+			 * again before we allocate another cluster.
+			 */
+			goto again;
+		}
+		prev = &n->m_next;
+	}
+	/*
+	 * No place where we can collapse to a cluster; punt.
+	 * This can occur if, for example, you request 2 frags
+	 * but the packet requires that both be clusters (we
+	 * never reallocate the first mbuf to avoid moving the
+	 * packet header).
+	 */
+bad:
+	return NULL;
+}
+
+static int
+ath_tx_start(struct ath_softc *sc, struct ieee80211_node *ni, struct ath_buf *bf,
+    struct mbuf *m0)
+{
+#define	CTS_DURATION \
+	ath_hal_computetxtime(ah, rt, IEEE80211_ACK_LEN, cix, AH_TRUE)
+#define	updateCTSForBursting(_ah, _ds, _txq) \
+	ath_hal_updateCTSForBursting(_ah, _ds, \
+	    _txq->axq_linkbuf != NULL ? _txq->axq_linkbuf->bf_desc : NULL, \
+	    _txq->axq_lastdsWithCTS, _txq->axq_gatingds, \
+	    txopLimit, CTS_DURATION)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ifnet *ifp = sc->sc_ifp;
+	const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
+	int i, error, iswep, ismcast, keyix, hdrlen, pktlen, try0;
+	u_int8_t rix, txrate, ctsrate;
+	u_int8_t cix = 0xff;		/* NB: silence compiler */
+	struct ath_desc *ds, *ds0;
+	struct ath_txq *txq;
+	struct ieee80211_frame *wh;
+	u_int subtype, flags, ctsduration;
+	HAL_PKT_TYPE atype;
+	const HAL_RATE_TABLE *rt;
+	HAL_BOOL shortPreamble;
+	struct ath_node *an;
+	struct mbuf *m;
+	u_int pri;
+	ATH_LOCK_INFO;
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+	ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1);
+	hdrlen = ieee80211_anyhdrsize(wh);
+	/*
+	 * Packet length must not include any
+	 * pad bytes; deduct them here.
+	 */
+	pktlen = m0->m_pkthdr.len - (hdrlen & 3);
+
+	if (iswep) {
+		const struct ieee80211_cipher *cip;
+		struct ieee80211_key *k;
+
+		/*
+		 * Construct the 802.11 header+trailer for an encrypted
+		 * frame. The only reason this can fail is because of an
+		 * unknown or unsupported cipher/key type.
+		 */
+		k = ieee80211_crypto_encap(ic, ni, m0);
+		if (k == NULL) {
+			/*
+			 * This can happen when the key is yanked after the
+			 * frame was queued.  Just discard the frame; the
+			 * 802.11 layer counts failures and provides
+			 * debugging/diagnostics.
+			 */
+			m_freem(m0);
+			return EIO;
+		}
+		/*
+		 * Adjust the packet + header lengths for the crypto
+		 * additions and calculate the h/w key index.  When
+		 * a s/w mic is done the frame will have had any mic
+		 * added to it prior to entry so skb->len above will
+		 * account for it. Otherwise we need to add it to the
+		 * packet length.
+		 */
+		cip = k->wk_cipher;
+		hdrlen += cip->ic_header;
+		pktlen += cip->ic_header + cip->ic_trailer;
+		if ((k->wk_flags & IEEE80211_KEY_SWMIC) == 0)
+			pktlen += cip->ic_miclen;
+		keyix = k->wk_keyix;
+
+		/* packet header may have moved, reset our local pointer */
+		wh = mtod(m0, struct ieee80211_frame *);
+	} else if (ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+		/*
+		 * Use station key cache slot, if assigned.
+		 */
+		keyix = ni->ni_ucastkey.wk_keyix;
+		if (keyix == IEEE80211_KEYIX_NONE)
+			keyix = HAL_TXKEYIX_INVALID;
+	} else
+		keyix = HAL_TXKEYIX_INVALID;
+
+	pktlen += IEEE80211_CRC_LEN;
+
+	/*
+	 * Load the DMA map so any coalescing is done.  This
+	 * also calculates the number of descriptors we need.
+	 */
+	error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+				     ath_mbuf_load_cb, bf,
+				     BUS_DMA_WAITOK);
+	if (error == EFBIG) {
+		/* XXX packet requires too many descriptors */
+		bf->bf_nseg = ATH_TXDESC+1;
+	} else if (error != 0) {
+		sc->sc_stats.ast_tx_busdma++;
+		m_freem(m0);
+		return error;
+	}
+	/*
+	 * Discard null packets and check for packets that
+	 * require too many TX descriptors.  We try to convert
+	 * the latter to a cluster.
+	 */
+	if (bf->bf_nseg > ATH_TXDESC) {		/* too many desc's, linearize */
+		sc->sc_stats.ast_tx_linear++;
+		m = ath_defrag(m0, MB_DONTWAIT, ATH_TXDESC);
+		if (m == NULL) {
+			m_freem(m0);
+			sc->sc_stats.ast_tx_nombuf++;
+			return ENOMEM;
+		}
+		m0 = m;
+		error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+					     ath_mbuf_load_cb, bf,
+					     BUS_DMA_WAITOK);
+		if (error != 0) {
+			sc->sc_stats.ast_tx_busdma++;
+			m_freem(m0);
+			return error;
+		}
+		KASSERT(bf->bf_nseg <= ATH_TXDESC,
+		    ("too many segments after defrag; nseg %u", bf->bf_nseg));
+	} else if (bf->bf_nseg == 0) {		/* null packet, discard */
+		sc->sc_stats.ast_tx_nodata++;
+		m_freem(m0);
+		return EIO;
+	}
+	DPRINTF(sc, ATH_DEBUG_XMIT, ("%s: m %p len %u\n", __func__, m0, pktlen));
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, BUS_DMASYNC_PREWRITE);
+	bf->bf_m = m0;
+	bf->bf_node = ni;			/* NB: held reference */
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	/*
+	 * NB: the 802.11 layer marks whether or not we should
+	 * use short preamble based on the current mode and
+	 * negotiated parameters.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+		shortPreamble = AH_TRUE;
+		sc->sc_stats.ast_tx_shortpre++;
+	} else {
+		shortPreamble = AH_FALSE;
+	}
+
+	an = ATH_NODE(ni);
+	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
+	/*
+	 * Calculate Atheros packet type from IEEE80211 packet header,
+	 * setup for rate calculations, and select h/w transmit queue.
+	 */
+	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+	case IEEE80211_FC0_TYPE_MGT:
+		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
+			atype = HAL_PKT_TYPE_BEACON;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+			atype = HAL_PKT_TYPE_PROBE_RESP;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
+			atype = HAL_PKT_TYPE_ATIM;
+		else
+			atype = HAL_PKT_TYPE_NORMAL;	/* XXX */
+		rix = 0;			/* XXX lowest rate */
+		try0 = ATH_TXMAXTRY;
+		if (shortPreamble)
+			txrate = an->an_tx_mgtratesp;
+		else
+			txrate = an->an_tx_mgtrate;
+		/* NB: force all management frames to highest queue */
+		if (ni->ni_flags & IEEE80211_NODE_QOS) {
+			/* NB: force all management frames to highest queue */
+			pri = WME_AC_VO;
+		} else
+			pri = WME_AC_BE;
+		flags |= HAL_TXDESC_INTREQ;	/* force interrupt */
+		break;
+	case IEEE80211_FC0_TYPE_CTL:
+		atype = HAL_PKT_TYPE_PSPOLL;	/* stop setting of duration */
+		rix = 0;			/* XXX lowest rate */
+		try0 = ATH_TXMAXTRY;
+		if (shortPreamble)
+			txrate = an->an_tx_mgtratesp;
+		else
+			txrate = an->an_tx_mgtrate;
+		/* NB: force all ctl frames to highest queue */
+		if (ni->ni_flags & IEEE80211_NODE_QOS) {
+			/* NB: force all ctl frames to highest queue */
+			pri = WME_AC_VO;
+		} else
+			pri = WME_AC_BE;
+		flags |= HAL_TXDESC_INTREQ;	/* force interrupt */
+		break;
+	case IEEE80211_FC0_TYPE_DATA:
+		atype = HAL_PKT_TYPE_NORMAL;		/* default */
+		/*
+		 * Data frames; consult the rate control module.
+		 */
+		ath_rate_findrate(sc, an, shortPreamble, pktlen,
+			&rix, &try0, &txrate);
+		sc->sc_txrate = txrate;			/* for LED blinking */
+		/*
+		 * Default all non-QoS traffic to the background queue.
+		 */
+		if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+			pri = M_WME_GETAC(m0);
+			if (cap->cap_wmeParams[pri].wmep_noackPolicy) {
+				flags |= HAL_TXDESC_NOACK;
+				sc->sc_stats.ast_tx_noack++;
+			}
+		} else
+			pri = WME_AC_BE;
+		break;
+	default:
+		if_printf(ifp, "bogus frame type 0x%x (%s)\n",
+			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+		/* XXX statistic */
+		m_freem(m0);
+		return EIO;
+	}
+	txq = sc->sc_ac2q[pri];
+
+	/*
+	 * When servicing one or more stations in power-save mode
+	 * multicast frames must be buffered until after the beacon.
+	 * We use the CAB queue for that.
+	 */
+	if (ismcast && ic->ic_ps_sta) {
+		txq = sc->sc_cabq;
+		/* XXX? more bit in 802.11 frame header */
+	}
+
+	/*
+	 * Calculate miscellaneous flags.
+	 */
+	if (ismcast) {
+		flags |= HAL_TXDESC_NOACK;	/* no ack on broad/multicast */
+		sc->sc_stats.ast_tx_noack++;
+	} else if (pktlen > ic->ic_rtsthreshold) {
+		flags |= HAL_TXDESC_RTSENA;	/* RTS based on frame length */
+		cix = rt->info[rix].controlRate;
+		sc->sc_stats.ast_tx_rts++;
+	}
+
+	/*
+	 * If 802.11g protection is enabled, determine whether
+	 * to use RTS/CTS or just CTS.  Note that this is only
+	 * done for OFDM unicast frames.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+	    rt->info[rix].phy == IEEE80211_T_OFDM &&
+	    (flags & HAL_TXDESC_NOACK) == 0) {
+		/* XXX fragments must use CCK rates w/ protection */
+		if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+			flags |= HAL_TXDESC_RTSENA;
+		else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
+			flags |= HAL_TXDESC_CTSENA;
+		cix = rt->info[sc->sc_protrix].controlRate;
+		sc->sc_stats.ast_tx_protect++;
+	}
+
+	/*
+	 * Calculate duration.  This logically belongs in the 802.11
+	 * layer but it lacks sufficient information to calculate it.
+	 */
+	if ((flags & HAL_TXDESC_NOACK) == 0 &&
+	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) {
+		u_int16_t dur;
+		/*
+		 * XXX not right with fragmentation.
+		 */
+		if (shortPreamble)
+			dur = rt->info[rix].spAckDuration;
+		else
+			dur = rt->info[rix].lpAckDuration;
+		*(u_int16_t *)wh->i_dur = htole16(dur);
+	}
+
+	/*
+	 * Calculate RTS/CTS rate and duration if needed.
+	 */
+	ctsduration = 0;
+	if (flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
+		/*
+		 * CTS transmit rate is derived from the transmit rate
+		 * by looking in the h/w rate table.  We must also factor
+		 * in whether or not a short preamble is to be used.
+		 */
+		/* NB: cix is set above where RTS/CTS is enabled */
+		KASSERT(cix != 0xff, ("cix not setup"));
+		ctsrate = rt->info[cix].rateCode;
+		/*
+		 * Compute the transmit duration based on the frame
+		 * size and the size of an ACK frame.  We call into the
+		 * HAL to do the computation since it depends on the
+		 * characteristics of the actual PHY being used.
+		 *
+		 * NB: CTS is assumed the same size as an ACK so we can
+		 *     use the precalculated ACK durations.
+		 */
+		if (shortPreamble) {
+			ctsrate |= rt->info[cix].shortPreamble;
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].spAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_TRUE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[rix].spAckDuration;
+		} else {
+			if (flags & HAL_TXDESC_RTSENA)		/* SIFS + CTS */
+				ctsduration += rt->info[cix].lpAckDuration;
+			ctsduration += ath_hal_computetxtime(ah,
+				rt, pktlen, rix, AH_FALSE);
+			if ((flags & HAL_TXDESC_NOACK) == 0)	/* SIFS + ACK */
+				ctsduration += rt->info[rix].lpAckDuration;
+		}
+		/*
+		 * Must disable multi-rate retry when using RTS/CTS.
+		 */
+		try0 = ATH_TXMAXTRY;
+	} else
+		ctsrate = 0;
+
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+		ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
+			sc->sc_hwmap[txrate].ieeerate, -1);
+
+	if (ic->ic_rawbpf)
+		bpf_mtap(ic->ic_rawbpf, m0);
+	if (sc->sc_drvbpf) {
+		sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
+		if (iswep)
+			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+		sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
+		sc->sc_tx_th.wt_txpower = ni->ni_txpower;
+		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+		bpf_ptap(sc->sc_drvbpf, m0, &sc->sc_tx_th, sc->sc_tx_th_len);
+	}
+
+	/* 
+	 * Determine if a tx interrupt should be generated for
+	 * this descriptor.  We take a tx interrupt to reap
+	 * descriptors when the h/w hits an EOL condition or
+	 * when the descriptor is specifically marked to generate
+	 * an interrupt.  We periodically mark descriptors in this
+	 * way to insure timely replenishing of the supply needed
+	 * for sending frames.  Defering interrupts reduces system
+	 * load and potentially allows more concurrent work to be
+	 * done but if done to aggressively can cause senders to
+	 * backup.
+	 *
+	 * NB: use >= to deal with sc_txintrperiod changing
+	 *     dynamically through sysctl.
+	 */
+	if (flags & HAL_TXDESC_INTREQ) {
+		txq->axq_intrcnt = 0;
+	} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
+		flags |= HAL_TXDESC_INTREQ;
+		txq->axq_intrcnt = 0;
+	}
+
+	/*
+	 * Formulate first tx descriptor with tx controls.
+	 */
+	/* XXX check return value? */
+	ath_hal_setuptxdesc(ah, ds
+		, pktlen		/* packet length */
+		, hdrlen		/* header length */
+		, atype			/* Atheros packet type */
+		, ni->ni_txpower	/* txpower */
+		, txrate, try0		/* series 0 rate/tries */
+		, keyix			/* key cache index */
+		, sc->sc_txantenna	/* antenna mode */
+		, flags			/* flags */
+		, ctsrate		/* rts/cts rate */
+		, ctsduration		/* rts/cts duration */
+	);
+	bf->bf_flags = flags;
+	/*
+	 * Setup the multi-rate retry state only when we're
+	 * going to use it.  This assumes ath_hal_setuptxdesc
+	 * initializes the descriptors (so we don't have to)
+	 * when the hardware supports multi-rate retry and
+	 * we don't use it.
+	 */
+	if (try0 != ATH_TXMAXTRY)
+		ath_rate_setupxtxdesc(sc, an, ds, shortPreamble, rix);
+
+	/*
+	 * Fillin the remainder of the descriptor info.
+	 */
+	ds0 = ds;
+	for (i = 0; i < bf->bf_nseg; i++, ds++) {
+		ds->ds_data = bf->bf_segs[i].ds_addr;
+		if (i == bf->bf_nseg - 1)
+			ds->ds_link = 0;
+		else
+			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+		ath_hal_filltxdesc(ah, ds
+			, bf->bf_segs[i].ds_len	/* segment length */
+			, i == 0		/* first segment */
+			, i == bf->bf_nseg - 1	/* last segment */
+			, ds0			/* first descriptor */
+		);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			("%s: %d: %08x %08x %08x %08x %08x %08x\n",
+			__func__, i, ds->ds_link, ds->ds_data,
+			ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]));
+	}
+	/*
+	 * Insert the frame on the outbound list and
+	 * pass it on to the hardware.
+	 */
+	ATH_TXQ_LOCK(txq);
+	if (flags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) {
+		u_int32_t txopLimit = IEEE80211_TXOP_TO_US(
+			cap->cap_wmeParams[pri].wmep_txopLimit);
+		/*
+		 * When bursting, potentially extend the CTS duration
+		 * of a previously queued frame to cover this frame
+		 * and not exceed the txopLimit.  If that can be done
+		 * then disable RTS/CTS on this frame since it's now
+		 * covered (burst extension).  Otherwise we must terminate
+		 * the burst before this frame goes out so as not to
+		 * violate the WME parameters.  All this is complicated
+		 * as we need to update the state of packets on the
+		 * (live) hardware queue.  The logic is buried in the hal
+		 * because it's highly chip-specific.
+		 */
+		if (txopLimit != 0) {
+			sc->sc_stats.ast_tx_ctsburst++;
+			if (updateCTSForBursting(ah, ds0, txq) == 0) {
+				/*
+				 * This frame was not covered by RTS/CTS from
+				 * the previous frame in the burst; update the
+				 * descriptor pointers so this frame is now
+				 * treated as the last frame for extending a
+				 * burst.
+				 */
+				txq->axq_lastdsWithCTS = ds0;
+				/* set gating Desc to final desc */
+				txq->axq_gatingds =
+					(struct ath_desc *)txq->axq_link;
+			} else
+				sc->sc_stats.ast_tx_ctsext++;
+		}
+	}
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	if (txq->axq_link == NULL) {
+		ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			("%s: TXDP[%u] = %p (%p) depth %d\n", __func__,
+			txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc,
+			txq->axq_depth));
+	} else {
+		*txq->axq_link = bf->bf_daddr;
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			("%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
+			txq->axq_qnum, txq->axq_link,
+			(caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth));
+	}
+	txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+	/*
+	 * The CAB queue is started from the SWBA handler since
+	 * frames only go out on DTIM and to avoid possible races.
+	 */
+	if (txq != sc->sc_cabq)
+		ath_hal_txstart(ah, txq->axq_qnum);
+	ATH_TXQ_UNLOCK(txq);
+
+	return 0;
+#undef updateCTSForBursting
+#undef CTS_DURATION
+}
+
+/*
+ * Process completed xmit descriptors from the specified queue.
+ */
+static void
+ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_buf *bf;
+	struct ath_desc *ds, *ds0;
+	struct ieee80211_node *ni;
+	struct ath_node *an;
+	int sr, lr, pri;
+	HAL_STATUS status;
+	ATH_LOCK_INFO;
+	ATH_TXBUF_LOCK_INFO;
+
+	DPRINTF(sc, ATH_DEBUG_TX_PROC, ("%s: tx queue %u head %p link %p\n",
+		__func__, txq->axq_qnum,
+		(caddr_t)(uintptr_t) ath_hal_gettxbuf(sc->sc_ah, txq->axq_qnum),
+		txq->axq_link));
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+		txq->axq_intrcnt = 0;	/* reset periodic desc intr count */
+		bf = STAILQ_FIRST(&txq->axq_q);
+		if (bf == NULL) {
+			txq->axq_link = NULL;
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+		ds0 = &bf->bf_desc[0];
+		ds = &bf->bf_desc[bf->bf_nseg - 1];
+		status = ath_hal_txprocdesc(ah, ds);
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_XMIT_DESC)
+			ath_printtxbuf(bf, status == HAL_OK);
+#endif
+		if (status == HAL_EINPROGRESS) {
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+		if (ds0 == txq->axq_lastdsWithCTS)
+			txq->axq_lastdsWithCTS = NULL;
+		if (ds == txq->axq_gatingds)
+			txq->axq_gatingds = NULL;
+		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+		ATH_TXQ_UNLOCK(txq);
+
+		ni = bf->bf_node;
+		if (ni != NULL) {
+			an = ATH_NODE(ni);
+			if (ds->ds_txstat.ts_status == 0) {
+				u_int8_t txant = ds->ds_txstat.ts_antenna;
+				sc->sc_stats.ast_ant_tx[txant]++;
+				sc->sc_ant_tx[txant]++;
+				if (ds->ds_txstat.ts_rate & HAL_TXSTAT_ALTRATE)
+					sc->sc_stats.ast_tx_altrate++;
+				sc->sc_stats.ast_tx_rssi =
+					ds->ds_txstat.ts_rssi;
+				ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
+					ds->ds_txstat.ts_rssi);
+				pri = M_WME_GETAC(bf->bf_m);
+				if (pri >= WME_AC_VO)
+					ic->ic_wme.wme_hipri_traffic++;
+				ni->ni_inact = ni->ni_inact_reload;
+			} else {
+				if (ds->ds_txstat.ts_status & HAL_TXERR_XRETRY)
+					sc->sc_stats.ast_tx_xretries++;
+				if (ds->ds_txstat.ts_status & HAL_TXERR_FIFO)
+					sc->sc_stats.ast_tx_fifoerr++;
+				if (ds->ds_txstat.ts_status & HAL_TXERR_FILT)
+					sc->sc_stats.ast_tx_filtered++;
+			}
+			sr = ds->ds_txstat.ts_shortretry;
+			lr = ds->ds_txstat.ts_longretry;
+			sc->sc_stats.ast_tx_shortretry += sr;
+			sc->sc_stats.ast_tx_longretry += lr;
+			/*
+			 * Hand the descriptor to the rate control algorithm.
+			 */
+			if ((ds->ds_txstat.ts_status & HAL_TXERR_FILT) == 0 &&
+			    (bf->bf_flags & HAL_TXDESC_NOACK) == 0)
+				ath_rate_tx_complete(sc, an, ds, ds0);
+			/*
+			 * Reclaim reference to node.
+			 *
+			 * NB: the node may be reclaimed here if, for example
+			 *     this is a DEAUTH message that was sent and the
+			 *     node was timed out due to inactivity.
+			 */
+			ieee80211_free_node(ni);
+		}
+		bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap,
+		    BUS_DMASYNC_POSTWRITE);
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		m_freem(bf->bf_m);
+		bf->bf_m = NULL;
+		bf->bf_node = NULL;
+
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+	}
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for a single hardware transmit queue (e.g. 5210 and 5211).
+ */
+static void
+ath_tx_proc_q0(void *arg, int npending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+
+	ath_tx_processq(sc, &sc->sc_txq[0]);
+	ath_tx_processq(sc, sc->sc_cabq);
+	ifp->if_flags &= ~IFF_OACTIVE;
+	sc->sc_tx_timer = 0;
+
+	if (sc->sc_softled)
+		ath_led_event(sc, ATH_LED_TX);
+
+	ath_start(ifp);
+}
+
+/*
+ * Deferred processing of transmit interrupt; special-cased
+ * for four hardware queues, 0-3 (e.g. 5212 w/ WME support).
+ */
+static void
+ath_tx_proc_q0123(void *arg, int npending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+
+	/*
+	 * Process each active queue.
+	 */
+	ath_tx_processq(sc, &sc->sc_txq[0]);
+	ath_tx_processq(sc, &sc->sc_txq[1]);
+	ath_tx_processq(sc, &sc->sc_txq[3]);
+	ath_tx_processq(sc, sc->sc_cabq);
+
+	ifp->if_flags &= ~IFF_OACTIVE;
+	sc->sc_tx_timer = 0;
+
+	if (sc->sc_softled)
+		ath_led_event(sc, ATH_LED_TX);
+
+	ath_start(ifp);
+}
+
+/*
+ * Deferred processing of transmit interrupt.
+ */
+static void
+ath_tx_proc(void *arg, int npending __unused)
+{
+	struct ath_softc *sc = arg;
+	struct ifnet *ifp = sc->sc_ifp;
+	int i;
+
+	/*
+	 * Process each active queue.
+	 */
+	/* XXX faster to read ISR_S0_S and ISR_S1_S to determine q's? */
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_processq(sc, &sc->sc_txq[i]);
+
+	ifp->if_flags &= ~IFF_OACTIVE;
+	sc->sc_tx_timer = 0;
+
+	if (sc->sc_softled)
+		ath_led_event(sc, ATH_LED_TX);
+
+	ath_start(ifp);
+}
+
+static void
+ath_tx_draintxq(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_node *ni;
+	struct ath_buf *bf;
+	ATH_LOCK_INFO;
+	ATH_TXBUF_LOCK_INFO;
+
+	/*
+	 * NB: this assumes output has been stopped and
+	 *     we do not need to block ath_tx_tasklet
+	 */
+	for (;;) {
+		ATH_TXQ_LOCK(txq);
+		bf = STAILQ_FIRST(&txq->axq_q);
+		if (bf == NULL) {
+			txq->axq_link = NULL;
+			ATH_TXQ_UNLOCK(txq);
+			break;
+		}
+#if old
+		TAILQ_REMOVE(&sc->sc_txq, bf, bf_list);
+#else
+		ATH_TXQ_REMOVE_HEAD(txq, bf_list);
+#endif
+		ATH_TXQ_UNLOCK(txq);
+#ifdef AR_DEBUG
+		if (sc->sc_debug & ATH_DEBUG_RESET)
+			ath_printtxbuf(bf,
+				ath_hal_txprocdesc(ah, bf->bf_desc) == HAL_OK);
+#endif /* AR_DEBUG */
+		bus_dmamap_unload(sc->sc_dmat, bf->bf_dmamap);
+		m_freem(bf->bf_m);
+		bf->bf_m = NULL;
+		ni = bf->bf_node;
+		bf->bf_node = NULL;
+		if (ni != NULL) {
+			/*
+			 * Reclaim node reference.
+			 */
+			ieee80211_free_node(ni);
+		}
+		ATH_TXBUF_LOCK(sc);
+		STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+		ATH_TXBUF_UNLOCK(sc);
+	}
+}
+
+static void
+ath_tx_stopdma(struct ath_softc *sc, struct ath_txq *txq)
+{
+	struct ath_hal *ah = sc->sc_ah;
+
+	(void) ath_hal_stoptxdma(ah, txq->axq_qnum);
+	DPRINTF(sc, ATH_DEBUG_RESET, ("%s: tx queue [%u] %p, link %p\n",
+	    __func__, txq->axq_qnum,
+	    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, txq->axq_qnum),
+	    txq->axq_link));
+}
+
+/*
+ * Drain the transmit queues and reclaim resources.
+ */
+static void
+ath_draintxq(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ifnet *ifp = sc->sc_ifp;
+	int i;
+
+	/* XXX return value */
+	if (!sc->sc_invalid) {
+		/* don't touch the hardware if marked invalid */
+		(void) ath_hal_stoptxdma(ah, sc->sc_bhalq);
+		DPRINTF(sc, ATH_DEBUG_RESET,
+		    ("%s: beacon queue %p\n", __func__,
+		    (caddr_t)(uintptr_t) ath_hal_gettxbuf(ah, sc->sc_bhalq)));
+		for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+			if (ATH_TXQ_SETUP(sc, i))
+				ath_tx_stopdma(sc, &sc->sc_txq[i]);
+	}
+	for (i = 0; i < HAL_NUM_TX_QUEUES; i++)
+		if (ATH_TXQ_SETUP(sc, i))
+			ath_tx_draintxq(sc, &sc->sc_txq[i]);
+	ifp->if_flags &= ~IFF_OACTIVE;
+	sc->sc_tx_timer = 0;
+}
+
+/*
+ * Disable the receive h/w in preparation for a reset.
+ */
+static void
+ath_stoprecv(struct ath_softc *sc)
+{
+#define	PA2DESC(_sc, _pa) \
+	((struct ath_desc *)((caddr_t)(_sc)->sc_rxdma.dd_desc + \
+		((_pa) - (_sc)->sc_rxdma.dd_desc_paddr)))
+	struct ath_hal *ah = sc->sc_ah;
+
+	ath_hal_stoppcurecv(ah);	/* disable PCU */
+	ath_hal_setrxfilter(ah, 0);	/* clear recv filter */
+	ath_hal_stopdmarecv(ah);	/* disable DMA engine */
+	DELAY(3000);			/* long enough for 1 frame */
+#ifdef AR_DEBUG
+	if (sc->sc_debug & (ATH_DEBUG_RESET | ATH_DEBUG_FATAL)) {
+		struct ath_buf *bf;
+
+		printf("%s: rx queue %p, link %p\n", __func__,
+			(caddr_t)(uintptr_t) ath_hal_getrxbuf(ah), sc->sc_rxlink);
+		STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+			struct ath_desc *ds = bf->bf_desc;
+			HAL_STATUS status = ath_hal_rxprocdesc(ah, ds,
+				bf->bf_daddr, PA2DESC(sc, ds->ds_link));
+			if (status == HAL_OK || (sc->sc_debug & ATH_DEBUG_FATAL))
+				ath_printrxbuf(bf, status == HAL_OK);
+		}
+	}
+#endif
+	sc->sc_rxlink = NULL;		/* just in case */
+#undef PA2DESC
+}
+
+/*
+ * Enable the receive h/w following a reset.
+ */
+static int
+ath_startrecv(struct ath_softc *sc)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_buf *bf;
+
+	sc->sc_rxlink = NULL;
+	STAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) {
+		int error = ath_rxbuf_init(sc, bf);
+		if (error != 0) {
+			DPRINTF(sc, ATH_DEBUG_RECV,
+				("%s: ath_rxbuf_init failed %d\n",
+				__func__, error));
+			return error;
+		}
+	}
+
+	bf = STAILQ_FIRST(&sc->sc_rxbuf);
+	ath_hal_putrxbuf(ah, bf->bf_daddr);
+	ath_hal_rxena(ah);		/* enable recv descriptors */
+	ath_mode_init(sc);		/* set filters, etc. */
+	ath_hal_startpcurecv(ah);	/* re-enable PCU/DMA engine */
+	return 0;
+}
+
+/* 
+ * Update internal state after a channel change.
+ */
+static void
+ath_chan_change(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	enum ieee80211_phymode mode;
+	u_int16_t flags;
+
+	/*
+	 * Change channels and update the h/w rate map
+	 * if we're switching; e.g. 11a to 11b/g.
+	 */
+	mode = ieee80211_chan2mode(ic, chan);
+	if (mode != sc->sc_curmode)
+		ath_setcurmode(sc, mode);
+	/*
+	 * Update BPF state.  NB: ethereal et. al. don't handle
+	 * merged flags well so pick a unique mode for their use.
+	 */
+	if (IEEE80211_IS_CHAN_A(chan))
+		flags = IEEE80211_CHAN_A;
+	/* XXX 11g schizophrenia */
+	else if (IEEE80211_IS_CHAN_G(chan) ||
+	    IEEE80211_IS_CHAN_PUREG(chan))
+		flags = IEEE80211_CHAN_G;
+	else
+		flags = IEEE80211_CHAN_B;
+	if (IEEE80211_IS_CHAN_T(chan))
+		flags |= IEEE80211_CHAN_TURBO;
+	sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
+		htole16(chan->ic_freq);
+	sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
+		htole16(flags);
+}
+
+/*
+ * Set/change channels.  If the channel is really being changed,
+ * it's done by resetting the chip.  To accomplish this we must
+ * first cleanup any pending DMA, then restart stuff after a la
+ * ath_init.
+ */
+static int
+ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	HAL_CHANNEL hchan;
+
+	/*
+	 * Convert to a HAL channel description with
+	 * the flags constrained to reflect the current
+	 * operating mode.
+	 */
+	hchan.channel = chan->ic_freq;
+	hchan.channelFlags = ath_chan2flags(ic, chan);
+
+	DPRINTF(sc, ATH_DEBUG_RESET, ("%s: %u (%u MHz) -> %u (%u MHz)\n",
+	    __func__,
+	    ath_hal_mhz2ieee(sc->sc_curchan.channel,
+		sc->sc_curchan.channelFlags),
+	    	sc->sc_curchan.channel,
+	    ath_hal_mhz2ieee(hchan.channel, hchan.channelFlags), hchan.channel));
+	if (hchan.channel != sc->sc_curchan.channel ||
+	    hchan.channelFlags != sc->sc_curchan.channelFlags) {
+		HAL_STATUS status;
+
+		/*
+		 * To switch channels clear any pending DMA operations;
+		 * wait long enough for the RX fifo to drain, reset the
+		 * hardware at the new frequency, and then re-enable
+		 * the relevant bits of the h/w.
+		 */
+		ath_hal_intrset(ah, 0);		/* disable interrupts */
+		ath_draintxq(sc);		/* clear pending tx frames */
+		ath_stoprecv(sc);		/* turn off frame recv */
+		if (!ath_hal_reset(ah, ic->ic_opmode, &hchan, AH_TRUE, &status)) {
+			if_printf(ic->ic_ifp, "ath_chan_set: unable to reset "
+				"channel %u (%u Mhz)\n",
+				ieee80211_chan2ieee(ic, chan), chan->ic_freq);
+			return EIO;
+		}
+		sc->sc_curchan = hchan;
+		ath_update_txpow(sc);		/* update tx power state */
+		sc->sc_diversity = ath_hal_getdiversity(ah);
+
+		/*
+		 * Re-enable rx framework.
+		 */
+		if (ath_startrecv(sc) != 0) {
+			if_printf(ic->ic_ifp,
+				"ath_chan_set: unable to restart recv logic\n");
+			return EIO;
+		}
+
+		/*
+		 * Change channels and update the h/w rate map
+		 * if we're switching; e.g. 11a to 11b/g.
+		 */
+		ic->ic_ibss_chan = chan;
+		ath_chan_change(sc, chan);
+
+		/*
+		 * Re-enable interrupts.
+		 */
+		ath_hal_intrset(ah, sc->sc_imask);
+	}
+	return 0;
+}
+
+static void
+ath_next_scan(void *arg)
+{
+	struct ath_softc *sc = arg;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	if (ic->ic_state == IEEE80211_S_SCAN)
+		ieee80211_next_scan(ic);
+}
+
+/*
+ * Periodically recalibrate the PHY to account
+ * for temperature/environment changes.
+ */
+static void
+ath_calibrate(void *arg)
+{
+	struct ath_softc *sc = arg;
+	struct ath_hal *ah = sc->sc_ah;
+
+	sc->sc_stats.ast_per_cal++;
+
+	DPRINTF(sc, ATH_DEBUG_CALIBRATE, ( "%s: channel %u/%x\n",
+		__func__, sc->sc_curchan.channel, sc->sc_curchan.channelFlags ));
+
+	if (ath_hal_getrfgain(ah) == HAL_RFGAIN_NEED_CHANGE) {
+		/*
+		 * Rfgain is out of bounds, reset the chip
+		 * to load new gain values.
+		 */
+		sc->sc_stats.ast_per_rfgain++;
+		ath_reset(sc->sc_ifp);
+	}
+	if (!ath_hal_calibrate(ah, &sc->sc_curchan)) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			("%s: calibration of channel %u failed\n",
+			__func__, sc->sc_curchan.channel));
+		sc->sc_stats.ast_per_calfail++;
+	}
+	callout_reset(&sc->sc_cal_ch, ath_calinterval * hz, ath_calibrate, sc);
+}
+
+static int
+ath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ath_softc *sc = ifp->if_softc;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211_node *ni;
+	int i, error;
+	const u_int8_t *bssid;
+	u_int32_t rfilt;
+	static const HAL_LED_STATE leds[] = {
+	    HAL_LED_INIT,	/* IEEE80211_S_INIT */
+	    HAL_LED_SCAN,	/* IEEE80211_S_SCAN */
+	    HAL_LED_AUTH,	/* IEEE80211_S_AUTH */
+	    HAL_LED_ASSOC, 	/* IEEE80211_S_ASSOC */
+	    HAL_LED_RUN, 	/* IEEE80211_S_RUN */
+	};
+
+	DPRINTF(sc, ATH_DEBUG_STATE, ("%s: %s -> %s\n", __func__,
+		ieee80211_state_name[ic->ic_state],
+		ieee80211_state_name[nstate]));
+
+	callout_stop(&sc->sc_scan_ch);
+	callout_stop(&sc->sc_cal_ch);
+	ath_hal_setledstate(ah, leds[nstate]);	/* set LED */
+
+	if (nstate == IEEE80211_S_INIT) {
+		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+		/*
+		 * NB: disable interrupts so we don't rx frames.
+		 */
+		ath_hal_intrset(ah, sc->sc_imask &~ HAL_INT_GLOBAL);
+		/*
+		 * Notify the rate control algorithm.
+		 */
+		ath_rate_newstate(sc, nstate);
+		goto done;
+	}
+	ni = ic->ic_bss;
+	error = ath_chan_set(sc, ic->ic_curchan);
+	if (error != 0)
+		goto bad;
+	rfilt = ath_calcrxfilter(sc, nstate);
+	if (nstate == IEEE80211_S_SCAN)
+		bssid = ifp->if_broadcastaddr;
+	else
+		bssid = ni->ni_bssid;
+	ath_hal_setrxfilter(ah, rfilt);
+	DPRINTF(sc, ATH_DEBUG_STATE, ("%s: RX filter 0x%x bssid %6D\n",
+		 __func__, rfilt, (bssid), ":"));
+
+	if (nstate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA)
+		ath_hal_setassocid(ah, bssid, ni->ni_associd);
+	else
+		ath_hal_setassocid(ah, bssid, 0);
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		for (i = 0; i < IEEE80211_WEP_NKID; i++)
+			if (ath_hal_keyisvalid(ah, i))
+				ath_hal_keysetmac(ah, i, bssid);
+	}
+
+	/*
+	 * Notify the rate control algorithm so rates
+	 * are setup should ath_beacon_alloc be called.
+	 */
+	ath_rate_newstate(sc, nstate);
+
+	if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+		/* nothing to do */;
+	} else if (nstate == IEEE80211_S_RUN) {
+		DPRINTF(sc, ATH_DEBUG_STATE,
+			("%s(RUN): ic_flags=0x%08x iv=%d bssid=%6D "
+			"capinfo=0x%04x chan=%d\n"
+			 , __func__
+			 , ic->ic_flags
+			 , ni->ni_intval
+			 , (ni->ni_bssid), ":"
+			 , ni->ni_capinfo
+			 , ieee80211_chan2ieee(ic, ic->ic_curchan)));
+
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_HOSTAP:
+		case IEEE80211_M_IBSS:
+			/*
+			 * Allocate and setup the beacon frame.
+			 *
+			 * Stop any previous beacon DMA.  This may be
+			 * necessary, for example, when an ibss merge
+			 * causes reconfiguration; there will be a state
+			 * transition from RUN->RUN that means we may
+			 * be called with beacon transmission active.
+			 */
+			ath_hal_stoptxdma(ah, sc->sc_bhalq);
+			ath_beacon_free(sc);
+			error = ath_beacon_alloc(sc, ni);
+			if (error != 0)
+				goto bad;
+			break;
+		case IEEE80211_M_STA:
+			/*
+			 * Allocate a key cache slot to the station.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0 &&
+			    sc->sc_hasclrkey &&
+			    ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE)
+				ath_setup_stationkey(ni);
+			break;
+		default:
+			break;
+		}
+
+		/*
+		 * Configure the beacon and sleep timers.
+		 */
+		ath_beacon_config(sc);
+	} else {
+		ath_hal_intrset(ah,
+			sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS));
+		sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS);
+	}
+done:
+	/*
+	 * Invoke the parent method to complete the work.
+	 */
+	error = sc->sc_newstate(ic, nstate, arg);
+	/*
+	 * Finally, start any timers.
+	 */
+	if (nstate == IEEE80211_S_RUN) {
+		/* start periodic recalibration timer */
+		callout_reset(&sc->sc_cal_ch, ath_calinterval * hz,
+			ath_calibrate, sc);
+	} else if (nstate == IEEE80211_S_SCAN) {
+		/* start ap/neighbor scan timer */
+		callout_reset(&sc->sc_scan_ch, (ath_dwelltime * hz) / 1000,
+			ath_next_scan, sc);
+	}
+bad:
+	return error;
+}
+
+/*
+ * Allocate a key cache slot to the station so we can
+ * setup a mapping from key index to node. The key cache
+ * slot is needed for managing antenna state and for
+ * compression when stations do not use crypto.  We do
+ * it uniliaterally here; if crypto is employed this slot
+ * will be reassigned.
+ */
+static void
+ath_setup_stationkey(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+	ieee80211_keyix keyix, rxkeyix;
+
+	if (!ath_key_alloc(ic, &ni->ni_ucastkey, &keyix, &rxkeyix)) {
+		/*
+		 * Key cache is full; we'll fall back to doing
+		 * the more expensive lookup in software.  Note
+		 * this also means no h/w compression.
+		 */
+		/* XXX msg+statistic */
+	} else {
+		/* XXX locking? */
+		ni->ni_ucastkey.wk_keyix = keyix;
+		ni->ni_ucastkey.wk_rxkeyix = rxkeyix;
+		/* NB: this will create a pass-thru key entry */
+		ath_keyset(sc, &ni->ni_ucastkey, ni->ni_macaddr, ic->ic_bss);
+	}
+}
+
+/*
+ * Setup driver-specific state for a newly associated node.
+ * Note that we're called also on a re-associate, the isnew
+ * param tells us if this is the first time or not.
+ */
+static void
+ath_newassoc(struct ieee80211_node *ni, int isnew)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ath_softc *sc = ic->ic_ifp->if_softc;
+
+	ath_rate_newassoc(sc, ATH_NODE(ni), isnew);
+	if (isnew &&
+	    (ic->ic_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey) {
+		KASSERT(ni->ni_ucastkey.wk_keyix == IEEE80211_KEYIX_NONE,
+		    ("new assoc with a unicast key already setup (keyix %u)",
+		    ni->ni_ucastkey.wk_keyix));
+		ath_setup_stationkey(ni);
+	}
+}
+
+static int
+ath_getchannels(struct ath_softc *sc, u_int cc,
+	HAL_BOOL outdoor, HAL_BOOL xchanmode)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+	HAL_CHANNEL *chans;
+	int i, ix, nchan;
+
+	chans = malloc(IEEE80211_CHAN_MAX * sizeof(HAL_CHANNEL),
+			M_TEMP, M_WAITOK);
+	if (chans == NULL) {
+		if_printf(ifp, "unable to allocate channel table\n");
+		return ENOMEM;
+	}
+	if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+	    cc, HAL_MODE_ALL, outdoor, xchanmode)) {
+		u_int32_t rd;
+
+		ath_hal_getregdomain(ah, &rd);
+		if_printf(ifp, "unable to collect channel list from hal; "
+			"regdomain likely %u country code %u\n", rd, cc);
+		free(chans, M_TEMP);
+		return EINVAL;
+	}
+
+	/*
+	 * Convert HAL channels to ieee80211 ones and insert
+	 * them in the table according to their channel number.
+	 */
+	for (i = 0; i < nchan; i++) {
+		HAL_CHANNEL *c = &chans[i];
+		ix = ath_hal_mhz2ieee(c->channel, c->channelFlags);
+		if (ix > IEEE80211_CHAN_MAX) {
+			if_printf(ifp, "bad hal channel %u (%u/%x) ignored\n",
+				ix, c->channel, c->channelFlags);
+			continue;
+		}
+		/* NB: flags are known to be compatible */
+		if (ic->ic_channels[ix].ic_freq == 0) {
+			ic->ic_channels[ix].ic_freq = c->channel;
+			ic->ic_channels[ix].ic_flags = c->channelFlags;
+		} else {
+			/* channels overlap; e.g. 11g and 11b */
+			ic->ic_channels[ix].ic_flags |= c->channelFlags;
+		}
+	}
+	free(chans, M_TEMP);
+	return 0;
+}
+
+static void
+ath_led_done(void *arg)
+{
+	struct ath_softc *sc = arg;
+
+	sc->sc_blinking = 0;
+}
+
+/*
+ * Turn the LED off: flip the pin and then set a timer so no
+ * update will happen for the specified duration.
+ */
+static void
+ath_led_off(void *arg)
+{
+	struct ath_softc *sc = arg;
+
+	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, !sc->sc_ledon);
+	callout_reset(&sc->sc_ledtimer, sc->sc_ledoff, ath_led_done, sc);
+}
+
+/*
+ * Blink the LED according to the specified on/off times.
+ */
+static void
+ath_led_blink(struct ath_softc *sc, int on, int off)
+{
+	DPRINTF(sc, ATH_DEBUG_LED, ("%s: on %u off %u\n", __func__, on, off));
+	ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin, sc->sc_ledon);
+	sc->sc_blinking = 1;
+	sc->sc_ledoff = off;
+	callout_reset(&sc->sc_ledtimer, on, ath_led_off, sc);
+}
+
+static void
+ath_led_event(struct ath_softc *sc, int event)
+{
+
+	sc->sc_ledevent = ticks;	/* time of last event */
+	if (sc->sc_blinking)		/* don't interrupt active blink */
+		return;
+	switch (event) {
+	case ATH_LED_POLL:
+		ath_led_blink(sc, sc->sc_hwmap[0].ledon,
+			sc->sc_hwmap[0].ledoff);
+		break;
+	case ATH_LED_TX:
+		ath_led_blink(sc, sc->sc_hwmap[sc->sc_txrate].ledon,
+			sc->sc_hwmap[sc->sc_txrate].ledoff);
+		break;
+	case ATH_LED_RX:
+		ath_led_blink(sc, sc->sc_hwmap[sc->sc_rxrate].ledon,
+			sc->sc_hwmap[sc->sc_rxrate].ledoff);
+		break;
+	}
+}
+
+static void
+ath_update_txpow(struct ath_softc *sc)
+{
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int32_t txpow;
+
+	if (sc->sc_curtxpow != ic->ic_txpowlimit) {
+		ath_hal_settxpowlimit(ah, ic->ic_txpowlimit);
+		/* read back in case value is clamped */
+		ath_hal_gettxpowlimit(ah, &txpow);
+		ic->ic_txpowlimit = sc->sc_curtxpow = txpow;
+	}
+	/* 
+	 * Fetch max tx power level for status requests.
+	 */
+	ath_hal_getmaxtxpow(sc->sc_ah, &txpow);
+	ic->ic_bss->ni_txpower = txpow;
+}
+
+static int
+ath_rate_setup(struct ath_softc *sc, u_int mode)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	struct ieee80211com *ic = &sc->sc_ic;
+	const HAL_RATE_TABLE *rt;
+	struct ieee80211_rateset *rs;
+	int i, maxrates;
+
+	switch (mode) {
+	case IEEE80211_MODE_11A:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A);
+		break;
+	case IEEE80211_MODE_11B:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11B);
+		break;
+	case IEEE80211_MODE_11G:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11G);
+		break;
+	case IEEE80211_MODE_TURBO_A:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_TURBO);
+		break;
+	case IEEE80211_MODE_TURBO_G:
+		sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_108G);
+		break;
+	default:
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			("%s: invalid mode %u\n", __func__, mode));
+		return 0;
+	}
+	rt = sc->sc_rates[mode];
+	if (rt == NULL)
+		return 0;
+	if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+		DPRINTF(sc, ATH_DEBUG_ANY,
+			("%s: rate table too small (%u > %u)\n",
+			__func__, rt->rateCount, IEEE80211_RATE_MAXSIZE));
+		maxrates = IEEE80211_RATE_MAXSIZE;
+	} else
+		maxrates = rt->rateCount;
+	rs = &ic->ic_sup_rates[mode];
+	for (i = 0; i < maxrates; i++)
+		rs->rs_rates[i] = rt->info[i].dot11Rate;
+	rs->rs_nrates = maxrates;
+	return 1;
+}
+
+static void
+ath_setcurmode(struct ath_softc *sc, enum ieee80211_phymode mode)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	/* NB: on/off times from the Atheros NDIS driver, w/ permission */
+	static const struct {
+		u_int		rate;		/* tx/rx 802.11 rate */
+		u_int16_t	timeOn;		/* LED on time (ms) */
+		u_int16_t	timeOff;	/* LED off time (ms) */
+	} blinkrates[] = {
+		{ 108,  40,  10 },
+		{  96,  44,  11 },
+		{  72,  50,  13 },
+		{  48,  57,  14 },
+		{  36,  67,  16 },
+		{  24,  80,  20 },
+		{  22, 100,  25 },
+		{  18, 133,  34 },
+		{  12, 160,  40 },
+		{  10, 200,  50 },
+		{   6, 240,  58 },
+		{   4, 267,  66 },
+		{   2, 400, 100 },
+		{   0, 500, 130 },
+	};
+	const HAL_RATE_TABLE *rt;
+	int i, j;
+
+	memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+	rt = sc->sc_rates[mode];
+	KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
+	for (i = 0; i < rt->rateCount; i++)
+		sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
+	memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
+	for (i = 0; i < 32; i++) {
+		u_int8_t ix = rt->rateCodeToIndex[i];
+		if (ix == 0xff) {
+			sc->sc_hwmap[i].ledon = (500 * hz) / 1000;
+			sc->sc_hwmap[i].ledoff = (130 * hz) / 1000;
+			continue;
+		}
+		sc->sc_hwmap[i].ieeerate =
+			rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
+		sc->sc_hwmap[i].txflags = IEEE80211_RADIOTAP_F_DATAPAD;
+		if (rt->info[ix].shortPreamble ||
+		    rt->info[ix].phy == IEEE80211_T_OFDM)
+			sc->sc_hwmap[i].txflags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+		/* NB: receive frames include FCS */
+		sc->sc_hwmap[i].rxflags = sc->sc_hwmap[i].txflags |
+			IEEE80211_RADIOTAP_F_FCS;
+		/* setup blink rate table to avoid per-packet lookup */
+		for (j = 0; j < N(blinkrates)-1; j++)
+			if (blinkrates[j].rate == sc->sc_hwmap[i].ieeerate)
+				break;
+		/* NB: this uses the last entry if the rate isn't found */
+		/* XXX beware of overlow */
+		sc->sc_hwmap[i].ledon = (blinkrates[j].timeOn * hz) / 1000;
+		sc->sc_hwmap[i].ledoff = (blinkrates[j].timeOff * hz) / 1000;
+	}
+	sc->sc_currates = rt;
+	sc->sc_curmode = mode;
+	/*
+	 * All protection frames are transmited at 2Mb/s for
+	 * 11g, otherwise at 1Mb/s.
+	 * XXX select protection rate index from rate table.
+	 */
+	sc->sc_protrix = (mode == IEEE80211_MODE_11G ? 1 : 0);
+	/* NB: caller is responsible for reseting rate control state */
+#undef N
+}
+
+#ifdef AR_DEBUG
+static void
+ath_printrxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds;
+	int i;
+
+	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
+		printf("R%d (%p %p) %08x %08x %08x %08x %08x %08x %c\n",
+		    i, ds, (struct ath_desc *)bf->bf_daddr + i,
+		    ds->ds_link, ds->ds_data,
+		    ds->ds_ctl0, ds->ds_ctl1,
+		    ds->ds_hw[0], ds->ds_hw[1],
+		    !done ? ' ' : (ds->ds_rxstat.rs_status == 0) ? '*' : '!');
+	}
+}
+
+static void
+ath_printtxbuf(struct ath_buf *bf, int done)
+{
+	struct ath_desc *ds;
+	int i;
+
+	for (i = 0, ds = bf->bf_desc; i < bf->bf_nseg; i++, ds++) {
+		printf("T%d (%p %p) %08x %08x %08x %08x %08x %08x %08x %08x %c\n",
+		    i, ds, (struct ath_desc *)bf->bf_daddr + i,
+		    ds->ds_link, ds->ds_data,
+		    ds->ds_ctl0, ds->ds_ctl1,
+		    ds->ds_hw[0], ds->ds_hw[1], ds->ds_hw[2], ds->ds_hw[3],
+		    !done ? ' ' : (ds->ds_txstat.ts_status == 0) ? '*' : '!');
+	}
+}
+#endif /* AR_DEBUG */
+
+static void
+ath_watchdog(struct ifnet *ifp)
+{
+	struct ath_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+
+	ifp->if_timer = 0;
+	if ((ifp->if_flags & IFF_RUNNING) == 0 || sc->sc_invalid)
+		return;
+	if (sc->sc_tx_timer) {
+		if (--sc->sc_tx_timer == 0) {
+			if_printf(ifp, "device timeout\n");
+			ath_reset(ifp);
+			ifp->if_oerrors++;
+			sc->sc_stats.ast_watchdog++;
+		} else
+			ifp->if_timer = 1;
+	}
+	ieee80211_watchdog(ic);
+}
+
+/*
+ * Diagnostic interface to the HAL.  This is used by various
+ * tools to do things like retrieve register contents for
+ * debugging.  The mechanism is intentionally opaque so that
+ * it can change frequently w/o concern for compatiblity.
+ */
+static int
+ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad)
+{
+	struct ath_hal *ah = sc->sc_ah;
+	u_int id = ad->ad_id & ATH_DIAG_ID;
+	void *indata = NULL;
+	void *outdata = NULL;
+	u_int32_t insize = ad->ad_in_size;
+	u_int32_t outsize = ad->ad_out_size;
+	int error = 0;
+
+	if (ad->ad_id & ATH_DIAG_IN) {
+		/*
+		 * Copy in data.
+		 */
+		indata = malloc(insize, M_TEMP, M_WAITOK);
+		if (indata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+		error = copyin(ad->ad_in_data, indata, insize);
+		if (error)
+			goto bad;
+	}
+	if (ad->ad_id & ATH_DIAG_DYN) {
+		/*
+		 * Allocate a buffer for the results (otherwise the HAL
+		 * returns a pointer to a buffer where we can read the
+		 * results).  Note that we depend on the HAL leaving this
+		 * pointer for us to use below in reclaiming the buffer;
+		 * may want to be more defensive.
+		 */
+		outdata = malloc(outsize, M_TEMP, M_WAITOK);
+		if (outdata == NULL) {
+			error = ENOMEM;
+			goto bad;
+		}
+	}
+	if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) {
+		if (outsize < ad->ad_out_size)
+			ad->ad_out_size = outsize;
+		if (outdata != NULL)
+			error = copyout(outdata, ad->ad_out_data,
+					ad->ad_out_size);
+	} else {
+		error = EINVAL;
+	}
+bad:
+	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
+		free(indata, M_TEMP);
+	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
+		free(outdata, M_TEMP);
+	return error;
+}
+
+static int
+ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data,  struct ucred *cr)
+{
+#define	IS_RUNNING(ifp) \
+	((ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING))
+	struct ath_softc *sc = ifp->if_softc;
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifreq *ifr = (struct ifreq *)data;
+	int error = 0;
+	ATH_LOCK_INFO;
+
+	ATH_LOCK(sc);
+	switch (cmd) {
+	case SIOCSIFFLAGS:
+		if (IS_RUNNING(ifp)) {
+			/*
+			 * To avoid rescanning another access point,
+			 * do not call ath_init() here.  Instead,
+			 * only reflect promisc mode settings.
+			 */
+			ath_mode_init(sc);
+		} else if (ifp->if_flags & IFF_UP) {
+			/*
+			 * Beware of being called during attach/detach
+			 * to reset promiscuous mode.  In that case we
+			 * will still be marked UP but not RUNNING.
+			 * However trying to re-init the interface
+			 * is the wrong thing to do as we've already
+			 * torn down much of our state.  There's
+			 * probably a better way to deal with this.
+			 */
+			if (!sc->sc_invalid && ic->ic_bss != NULL)
+				ath_init(sc);	/* XXX lose error */
+		} else
+			ath_stop_locked(ifp);
+		break;
+	case SIOCADDMULTI:
+	case SIOCDELMULTI:
+		/*
+		 * The upper layer has already installed/removed
+		 * the multicast address(es), just recalculate the
+		 * multicast filter for the card.
+		 */
+		if (ifp->if_flags & IFF_RUNNING)
+			ath_mode_init(sc);
+		break;
+	case SIOCGATHSTATS:
+		/* NB: embed these numbers to get a consistent view */
+		sc->sc_stats.ast_tx_packets = ifp->if_opackets;
+		sc->sc_stats.ast_rx_packets = ifp->if_ipackets;
+		sc->sc_stats.ast_rx_rssi = ieee80211_getrssi(ic);
+		ATH_UNLOCK(sc);
+		/*
+		 * NB: Drop the softc lock in case of a page fault;
+		 * we'll accept any potential inconsisentcy in the
+		 * statistics.  The alternative is to copy the data
+		 * to a local structure.
+		 */
+		return copyout(&sc->sc_stats,
+				ifr->ifr_data, sizeof (sc->sc_stats));
+	case SIOCGATHDIAG:
+		error = ath_ioctl_diag(sc, (struct ath_diag *) ifr);
+		break;
+	default:
+		error = ieee80211_ioctl(ic, cmd, data, cr);
+		if (error == ENETRESET) {
+			if (IS_RUNNING(ifp) && 
+			    ic->ic_roaming != IEEE80211_ROAMING_MANUAL)
+				ath_init(sc);	/* XXX lose error */
+			error = 0;
+		}
+		if (error == ERESTART)
+			error = IS_RUNNING(ifp) ? ath_reset(ifp) : 0;
+		break;
+	}
+	ATH_UNLOCK(sc);
+	return error;
+#undef IS_RUNNING
+}
+
+static int
+ath_sysctl_slottime(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int slottime = ath_hal_getslottime(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &slottime, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0;
+}
+
+static int
+ath_sysctl_acktimeout(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int acktimeout = ath_hal_getacktimeout(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &acktimeout, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_setacktimeout(sc->sc_ah, acktimeout) ? EINVAL : 0;
+}
+
+static int
+ath_sysctl_ctstimeout(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int ctstimeout = ath_hal_getctstimeout(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &ctstimeout, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_setctstimeout(sc->sc_ah, ctstimeout) ? EINVAL : 0;
+}
+
+static int
+ath_sysctl_softled(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	int softled = sc->sc_softled;
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &softled, 0, req);
+	if (error || !req->newptr)
+		return error;
+	softled = (softled != 0);
+	if (softled != sc->sc_softled) {
+		if (softled) {
+			/* NB: handle any sc_ledpin change */
+			ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin);
+			ath_hal_gpioset(sc->sc_ah, sc->sc_ledpin,
+				!sc->sc_ledon);
+		}
+		sc->sc_softled = softled;
+	}
+	return 0;
+}
+
+static int
+ath_sysctl_rxantenna(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int defantenna = ath_hal_getdefantenna(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &defantenna, 0, req);
+	if (!error && req->newptr)
+		ath_hal_setdefantenna(sc->sc_ah, defantenna);
+	return error;
+}
+
+static int
+ath_sysctl_diversity(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int diversity = ath_hal_getdiversity(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &diversity, 0, req);
+	if (error || !req->newptr)
+		return error;
+	if (!ath_hal_setdiversity(sc->sc_ah, diversity))
+		return EINVAL;
+	sc->sc_diversity = diversity;
+	return 0;
+}
+
+static int
+ath_sysctl_diag(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int32_t diag;
+	int error;
+	(void) arg2;
+
+	if (!ath_hal_getdiag(sc->sc_ah, &diag))
+		return EINVAL;
+	error = sysctl_handle_int(oidp, &diag, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_setdiag(sc->sc_ah, diag) ? EINVAL : 0;
+}
+
+static int
+ath_sysctl_tpscale(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	struct ifnet *ifp = sc->sc_ifp;
+	u_int32_t scale;
+	int error;
+	(void) arg2;
+
+	ath_hal_gettpscale(sc->sc_ah, &scale);
+	error = sysctl_handle_int(oidp, &scale, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_settpscale(sc->sc_ah, scale) ? EINVAL : ath_reset(ifp);
+}
+
+static int
+ath_sysctl_tpc(SYSCTL_HANDLER_ARGS)
+{
+	struct ath_softc *sc = arg1;
+	u_int tpc = ath_hal_gettpc(sc->sc_ah);
+	int error;
+	(void) arg2;
+
+	error = sysctl_handle_int(oidp, &tpc, 0, req);
+	if (error || !req->newptr)
+		return error;
+	return !ath_hal_settpc(sc->sc_ah, tpc) ? EINVAL : 0;
+}
+
+static void
+ath_sysctlattach(struct ath_softc *sc)
+{
+	struct sysctl_ctx_list *ctx = &sc->sysctl_ctx;
+	struct sysctl_oid *tree = sc->sysctl_tree;
+	struct ath_hal *ah = sc->sc_ah;
+	crit_enter();
+
+	ath_hal_getcountrycode(sc->sc_ah, &sc->sc_countrycode);
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"countrycode", CTLFLAG_RD, &sc->sc_countrycode, 0,
+		"EEPROM country code");
+	ath_hal_getregdomain(sc->sc_ah, &sc->sc_regdomain);
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"regdomain", CTLFLAG_RD, &sc->sc_regdomain, 0,
+		"EEPROM regdomain code");
+	sc->sc_debug = ath_debug;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"debug", CTLFLAG_RW, &sc->sc_debug, 0,
+		"control debugging printfs");
+
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"slottime", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_slottime, "I", "802.11 slot time (us)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"acktimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_acktimeout, "I", "802.11 ACK timeout (us)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ctstimeout", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_ctstimeout, "I", "802.11 CTS timeout (us)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"softled", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_softled, "I", "enable/disable software LED support");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledpin", CTLFLAG_RW, &sc->sc_ledpin, 0,
+		"GPIO pin connected to LED");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledon", CTLFLAG_RW, &sc->sc_ledon, 0,
+		"setting to turn LED on");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"ledidle", CTLFLAG_RW, &sc->sc_ledidle, 0,
+		"idle time for inactivity LED (ticks)");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"txantenna", CTLFLAG_RW, &sc->sc_txantenna, 0,
+		"tx antenna (0=auto)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"rxantenna", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_rxantenna, "I", "default/rx antenna");
+	if (ath_hal_hasdiversity(ah))
+		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+			"diversity", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+			ath_sysctl_diversity, "I", "antenna diversity");
+	sc->sc_txintrperiod = ATH_TXINTR_PERIOD;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"txintrperiod", CTLFLAG_RW, &sc->sc_txintrperiod, 0,
+		"tx descriptor batching");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"diag", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_diag, "I", "h/w diagnostic control");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+		"tpscale", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+		ath_sysctl_tpscale, "I", "tx power scaling");
+	if (ath_hal_hastpc(ah))
+		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+			"tpc", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+			ath_sysctl_tpc, "I", "enable/disable per-packet TPC");
+	crit_exit();
+}
+
+static void
+ath_bpfattach(struct ath_softc *sc)
+{
+	struct ifnet *ifp = sc->sc_ifp;
+
+	bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
+	    sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
+	/*
+	 * Initialize constant fields.
+	 * XXX make header lengths a multiple of 32-bits so subsequent
+	 *     headers are properly aligned; this is a kludge to keep
+	 *     certain applications happy.
+	 *
+	 * NB: the channel is setup each time we transition to the
+	 *     RUN state to avoid filling it in for each frame.
+	 */
+	sc->sc_tx_th_len = 64;
+	sc->sc_tx_th.wt_ihdr.it_len = htole16(sc->sc_tx_th_len);
+	sc->sc_tx_th.wt_ihdr.it_present = htole32(ATH_TX_RADIOTAP_PRESENT);
+
+	sc->sc_rx_th_len = 64;
+	sc->sc_rx_th.wr_ihdr.it_len = htole16(sc->sc_rx_th_len);
+	sc->sc_rx_th.wr_ihdr.it_present = htole32(ATH_RX_RADIOTAP_PRESENT);
+}
+
+/*
+ * Announce various information on device/driver attach.
+ */
+static void
+ath_announce(struct ath_softc *sc)
+{
+#define	HAL_MODE_DUALBAND	(HAL_MODE_11A|HAL_MODE_11B)
+	struct ifnet *ifp = sc->sc_ifp;
+	struct ath_hal *ah = sc->sc_ah;
+	u_int modes, cc;
+
+	if_printf(ifp, "mac %d.%d phy %d.%d",
+		ah->ah_macVersion, ah->ah_macRev,
+		ah->ah_phyRev >> 4, ah->ah_phyRev & 0xf);
+	/*
+	 * Print radio revision(s).  We check the wireless modes
+	 * to avoid falsely printing revs for inoperable parts.
+	 * Dual-band radio revs are returned in the 5Ghz rev number.
+	 */
+	ath_hal_getcountrycode(ah, &cc);
+	modes = ath_hal_getwirelessmodes(ah, cc);
+	if ((modes & HAL_MODE_DUALBAND) == HAL_MODE_DUALBAND) {
+		if (ah->ah_analog5GhzRev && ah->ah_analog2GhzRev)
+			printf(" 5ghz radio %d.%d 2ghz radio %d.%d",
+				ah->ah_analog5GhzRev >> 4,
+				ah->ah_analog5GhzRev & 0xf,
+				ah->ah_analog2GhzRev >> 4,
+				ah->ah_analog2GhzRev & 0xf);
+		else
+			printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+				ah->ah_analog5GhzRev & 0xf);
+	} else
+		printf(" radio %d.%d", ah->ah_analog5GhzRev >> 4,
+			ah->ah_analog5GhzRev & 0xf);
+	printf("\n");
+	if (bootverbose) {
+		int i;
+		for (i = 0; i <= WME_AC_VO; i++) {
+			struct ath_txq *txq = sc->sc_ac2q[i];
+			if_printf(ifp, "Use hw queue %u for %s traffic\n",
+				txq->axq_qnum, ieee80211_wme_acnames[i]);
+		}
+		if_printf(ifp, "Use hw queue %u for CAB traffic\n",
+			sc->sc_cabq->axq_qnum);
+		if_printf(ifp, "Use hw queue %u for beacons\n", sc->sc_bhalq);
+	}
+#undef HAL_MODE_DUALBAND
+}
diff -N -u -r src.preview/sys/dev/netif/ath/if_ath_pci.c src/sys/dev/netif/ath/if_ath_pci.c
--- src.preview/sys/dev/netif/ath/if_ath_pci.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/if_ath_pci.c	2005-09-26 10:53:20.000000000 -0400
@@ -0,0 +1,307 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * DragonFly Port - Copyright (c) 2005 Andrew Atrens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * PCI/Cardbus front-end for the Atheros Wireless LAN controller driver.
+ */
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/errno.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+ 
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/if_llc.h>
+#include <net/if_arp.h>
+
+#include <netproto/802_11/ieee80211.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#include <netproto/802_11/ieee80211_crypto.h>
+#include <netproto/802_11/ieee80211_node.h>
+#include <netproto/802_11/ieee80211_proto.h>
+#include <netproto/802_11/ieee80211_var.h>
+
+#ifdef INET
+#include <netinet/in.h> 
+#include <netinet/if_ether.h>
+#endif
+
+#include "if_athvar.h"
+#include "../../../contrib/dev/ath/ah.h"
+
+#include <bus/pci/pcivar.h>
+#include <bus/pci/pcireg.h>
+
+/*
+ * PCI glue.
+ */
+
+struct ath_pci_softc {
+	struct ath_softc	sc_sc;
+	struct resource		*sc_sr;		/* memory resource */
+	struct resource		*sc_irq;	/* irq resource */
+	void			*sc_ih;		/* interrupt handler */
+};
+
+#define	BS_BAR	0x10
+#define	PCIR_RETRY_TIMEOUT	0x41
+
+static int
+ath_pci_probe(device_t dev)
+{
+	const char* devname;
+
+	devname = ath_hal_probe(pci_get_vendor(dev), pci_get_device(dev));
+	if (devname) {
+		device_set_desc(dev, devname);
+		return 0;
+	}
+	return ENXIO;
+}
+
+static u_int32_t
+ath_pci_setup(device_t dev)
+{
+	u_int32_t cmd;
+
+	/*
+	 * Enable memory mapping and bus mastering.
+	 */
+	cmd = pci_read_config(dev, PCIR_COMMAND, 4);
+	cmd |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN;
+	pci_write_config(dev, PCIR_COMMAND, cmd, 4);
+	cmd = pci_read_config(dev, PCIR_COMMAND, 4);
+	if ((cmd & PCIM_CMD_MEMEN) == 0) {
+		device_printf(dev, "failed to enable memory mapping\n");
+		return 0;
+	}
+	if ((cmd & PCIM_CMD_BUSMASTEREN) == 0) {
+		device_printf(dev, "failed to enable bus mastering\n");
+		return 0;
+	}
+
+	/*
+	 * Disable retry timeout to keep PCI Tx retries from
+	 * interfering with C3 CPU state.
+	 */
+	pci_write_config(dev, PCIR_RETRY_TIMEOUT, 0, 1);
+
+	return 1;
+}
+
+static int
+ath_pci_attach(device_t dev)
+{
+	struct ath_pci_softc *psc = device_get_softc(dev);
+	struct ath_softc *sc = &psc->sc_sc;
+	int error = ENXIO;
+	int rid;
+
+	sc->sc_dev = dev;
+
+	if (!ath_pci_setup(dev))
+
+	/* 
+	 * Setup memory-mapping of PCI registers.
+	 */
+	rid = BS_BAR;
+	psc->sc_sr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+					    RF_ACTIVE);
+	if (psc->sc_sr == NULL) {
+		device_printf(dev, "cannot map register space\n");
+		goto bad;
+	}
+	sc->sc_st = rman_get_bustag(psc->sc_sr);
+	sc->sc_sh = rman_get_bushandle(psc->sc_sr);
+	/*
+	 * Mark device invalid so any interrupts (shared or otherwise)
+	 * that arrive before the HAL is setup are discarded.
+	 */
+	sc->sc_invalid = 1;
+
+	/*
+	 * Arrange interrupt line.
+	 */
+	rid = 0;
+	psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+					     RF_SHAREABLE|RF_ACTIVE);
+	if (psc->sc_irq == NULL) {
+		device_printf(dev, "could not map interrupt\n");
+		goto bad1;
+	}
+	if (bus_setup_intr(dev, psc->sc_irq,
+			   INTR_TYPE_NET | INTR_MPSAFE,
+			   ath_intr, sc, &psc->sc_ih, NULL)) {
+		device_printf(dev, "could not establish interrupt\n");
+		goto bad2;
+	}
+
+	/*
+	 * Setup DMA descriptor area.
+	 */
+	if (bus_dma_tag_create(NULL,			/* parent */
+			       1, 0,			/* alignment, bounds */
+			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+			       BUS_SPACE_MAXADDR,	/* highaddr */
+			       NULL, NULL,		/* filter, filterarg */
+			       0x3ffff,			/* maxsize XXX */
+			       ATH_MAX_SCATTER,		/* nsegments */
+			       BUS_SPACE_MAXADDR,	/* maxsegsize */
+			       BUS_DMA_ALLOCNOW,	/* flags */
+			       &sc->sc_dmat)) {
+		device_printf(dev, "cannot allocate DMA tag\n");
+		goto bad3;
+	}
+
+	ATH_LOCK_INIT(sc);
+
+	error = ath_attach(pci_get_device(dev), sc);
+	if (error == 0)
+		return error;
+
+	ATH_LOCK_DESTROY(sc);
+	bus_dma_tag_destroy(sc->sc_dmat);
+bad3:
+	bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
+bad2:
+	bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
+bad1:
+	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr);
+bad:
+	return (error);
+}
+
+static int
+ath_pci_detach(device_t dev)
+{
+	struct ath_pci_softc *psc = device_get_softc(dev);
+	struct ath_softc *sc = &psc->sc_sc;
+
+	/* check if device was removed */
+	sc->sc_invalid = !bus_child_present(dev);
+
+	ath_detach(sc);
+
+	bus_generic_detach(dev);
+	bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
+	bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
+
+	bus_dma_tag_destroy(sc->sc_dmat);
+	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr);
+
+	ATH_LOCK_DESTROY(sc);
+
+	return (0);
+}
+
+static int
+ath_pci_shutdown(device_t dev)
+{
+	struct ath_pci_softc *psc = device_get_softc(dev);
+
+	ath_shutdown(&psc->sc_sc);
+	return (0);
+}
+
+static int
+ath_pci_suspend(device_t dev)
+{
+	struct ath_pci_softc *psc = device_get_softc(dev);
+
+	ath_suspend(&psc->sc_sc);
+
+	return (0);
+}
+
+static int
+ath_pci_resume(device_t dev)
+{
+	struct ath_pci_softc *psc = device_get_softc(dev);
+
+	if (!ath_pci_setup(dev))
+		return ENXIO;
+
+	ath_resume(&psc->sc_sc);
+
+	return (0);
+}
+
+static device_method_t ath_pci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		ath_pci_probe),
+	DEVMETHOD(device_attach,	ath_pci_attach),
+	DEVMETHOD(device_detach,	ath_pci_detach),
+	DEVMETHOD(device_shutdown,	ath_pci_shutdown),
+	DEVMETHOD(device_suspend,	ath_pci_suspend),
+	DEVMETHOD(device_resume,	ath_pci_resume),
+
+	{ 0,0 }
+};
+static driver_t ath_pci_driver = {
+	"ath",
+	ath_pci_methods,
+	sizeof (struct ath_pci_softc),
+	0,
+	0,
+	0
+};
+static	devclass_t ath_devclass;
+DRIVER_MODULE(if_ath, pci, ath_pci_driver, ath_devclass, 0, 0);
+DRIVER_MODULE(if_ath, cardbus, ath_pci_driver, ath_devclass, 0, 0);
+MODULE_VERSION(if_ath, 1);
+MODULE_DEPEND(if_ath, ath_hal, 1, 1, 1);	/* Atheros HAL */
+MODULE_DEPEND(if_ath, wlan, 1, 1, 1);		/* 802.11 media layer */
+MODULE_DEPEND(if_ath, ath_rate, 1, 1, 1);	/* rate control algorithm */
diff -N -u -r src.preview/sys/dev/netif/ath/if_athioctl.h src/sys/dev/netif/ath/if_athioctl.h
--- src.preview/sys/dev/netif/ath/if_athioctl.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/if_athioctl.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: src/sys/dev/ath/if_athioctl.h,v 1.7 2004/04/02 23:57:10 sam Exp $
+ */
+
+/*
+ * Ioctl-related defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHIOCTL_H
+#define _DEV_ATH_ATHIOCTL_H
+
+struct ath_stats {
+	u_int32_t	ast_watchdog;	/* device reset by watchdog */
+	u_int32_t	ast_hardware;	/* fatal hardware error interrupts */
+	u_int32_t	ast_bmiss;	/* beacon miss interrupts */
+	u_int32_t	ast_bstuck;	/* beacon stuck interrupts */
+	u_int32_t	ast_rxorn;	/* rx overrun interrupts */
+	u_int32_t	ast_rxeol;	/* rx eol interrupts */
+	u_int32_t	ast_txurn;	/* tx underrun interrupts */
+	u_int32_t	ast_mib;	/* mib interrupts */
+	u_int32_t	ast_intrcoal;	/* interrupts coalesced */
+	u_int32_t	ast_tx_packets;	/* packet sent on the interface */
+	u_int32_t	ast_tx_mgmt;	/* management frames transmitted */
+	u_int32_t	ast_tx_discard;	/* frames discarded prior to assoc */
+	u_int32_t	ast_tx_qstop;	/* output stopped 'cuz no buffer */
+	u_int32_t	ast_tx_encap;	/* tx encapsulation failed */
+	u_int32_t	ast_tx_nonode;	/* tx failed 'cuz no node */
+	u_int32_t	ast_tx_nombuf;	/* tx failed 'cuz no mbuf */
+	u_int32_t	ast_tx_nomcl;	/* tx failed 'cuz no cluster */
+	u_int32_t	ast_tx_linear;	/* tx linearized to cluster */
+	u_int32_t	ast_tx_nodata;	/* tx discarded empty frame */
+	u_int32_t	ast_tx_busdma;	/* tx failed for dma resrcs */
+	u_int32_t	ast_tx_xretries;/* tx failed 'cuz too many retries */
+	u_int32_t	ast_tx_fifoerr;	/* tx failed 'cuz FIFO underrun */
+	u_int32_t	ast_tx_filtered;/* tx failed 'cuz xmit filtered */
+	u_int32_t	ast_tx_shortretry;/* tx on-chip retries (short) */
+	u_int32_t	ast_tx_longretry;/* tx on-chip retries (long) */
+	u_int32_t	ast_tx_badrate;	/* tx failed 'cuz bogus xmit rate */
+	u_int32_t	ast_tx_noack;	/* tx frames with no ack marked */
+	u_int32_t	ast_tx_rts;	/* tx frames with rts enabled */
+	u_int32_t	ast_tx_cts;	/* tx frames with cts enabled */
+	u_int32_t	ast_tx_shortpre;/* tx frames with short preamble */
+	u_int32_t	ast_tx_altrate;	/* tx frames with alternate rate */
+	u_int32_t	ast_tx_protect;	/* tx frames with protection */
+	u_int32_t	ast_tx_ctsburst;/* tx frames with cts and bursting */
+	u_int32_t	ast_tx_ctsext;	/* tx frames with cts extension */
+	u_int32_t	ast_rx_nombuf;	/* rx setup failed 'cuz no mbuf */
+	u_int32_t	ast_rx_busdma;	/* rx setup failed for dma resrcs */
+	u_int32_t	ast_rx_orn;	/* rx failed 'cuz of desc overrun */
+	u_int32_t	ast_rx_crcerr;	/* rx failed 'cuz of bad CRC */
+	u_int32_t	ast_rx_fifoerr;	/* rx failed 'cuz of FIFO overrun */
+	u_int32_t	ast_rx_badcrypt;/* rx failed 'cuz decryption */
+	u_int32_t	ast_rx_badmic;	/* rx failed 'cuz MIC failure */
+	u_int32_t	ast_rx_phyerr;	/* rx failed 'cuz of PHY err */
+	u_int32_t	ast_rx_phy[32];	/* rx PHY error per-code counts */
+	u_int32_t	ast_rx_tooshort;/* rx discarded 'cuz frame too short */
+	u_int32_t	ast_rx_toobig;	/* rx discarded 'cuz frame too large */
+	u_int32_t	ast_rx_packets;	/* packet recv on the interface */
+	u_int32_t	ast_rx_mgt;	/* management frames received */
+	u_int32_t	ast_rx_ctl;	/* rx discarded 'cuz ctl frame */
+	int8_t		ast_tx_rssi;	/* tx rssi of last ack */
+	int8_t		ast_rx_rssi;	/* rx rssi from histogram */
+	u_int32_t	ast_be_xmit;	/* beacons transmitted */
+	u_int32_t	ast_be_nombuf;	/* beacon setup failed 'cuz no mbuf */
+	u_int32_t	ast_per_cal;	/* periodic calibration calls */
+	u_int32_t	ast_per_calfail;/* periodic calibration failed */
+	u_int32_t	ast_per_rfgain;	/* periodic calibration rfgain reset */
+	u_int32_t	ast_rate_calls;	/* rate control checks */
+	u_int32_t	ast_rate_raise;	/* rate control raised xmit rate */
+	u_int32_t	ast_rate_drop;	/* rate control dropped xmit rate */
+	u_int32_t	ast_ant_defswitch;/* rx/default antenna switches */
+	u_int32_t	ast_ant_txswitch;/* tx antenna switches */
+	u_int32_t	ast_ant_rx[8];	/* rx frames with antenna */
+	u_int32_t	ast_ant_tx[8];	/* tx frames with antenna */
+};
+
+#define	SIOCGATHSTATS	_IOWR('i', 137, struct ifreq)
+
+struct ath_diag {
+	char	ad_name[IFNAMSIZ];	/* if name, e.g. "ath0" */
+	u_int16_t ad_id;
+#define	ATH_DIAG_DYN	0x8000		/* allocate buffer in caller */
+#define	ATH_DIAG_IN	0x4000		/* copy in parameters */
+#define	ATH_DIAG_OUT	0x0000		/* copy out results (always) */
+#define	ATH_DIAG_ID	0x0fff
+	u_int16_t ad_in_size;
+	caddr_t	ad_in_data;
+	caddr_t	ad_out_data;
+	u_int	ad_out_size;
+
+};
+#define	SIOCGATHDIAG	_IOWR('i', 138, struct ath_diag)
+
+/*
+ * Radio capture format.
+ */
+#define ATH_RX_RADIOTAP_PRESENT (		\
+	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
+	(1 << IEEE80211_RADIOTAP_RATE)		| \
+	(1 << IEEE80211_RADIOTAP_CHANNEL)	| \
+	(1 << IEEE80211_RADIOTAP_ANTENNA)	| \
+	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL)	| \
+	0)
+
+struct ath_rx_radiotap_header {
+	struct ieee80211_radiotap_header wr_ihdr;
+	u_int8_t	wr_flags;		/* XXX for padding */
+	u_int8_t	wr_rate;
+	u_int16_t	wr_chan_freq;
+	u_int16_t	wr_chan_flags;
+	u_int8_t	wr_antenna;
+	u_int8_t	wr_antsignal;
+};
+
+#define ATH_TX_RADIOTAP_PRESENT (		\
+	(1 << IEEE80211_RADIOTAP_FLAGS)		| \
+	(1 << IEEE80211_RADIOTAP_RATE)		| \
+	(1 << IEEE80211_RADIOTAP_CHANNEL)	| \
+	(1 << IEEE80211_RADIOTAP_DBM_TX_POWER)	| \
+	(1 << IEEE80211_RADIOTAP_ANTENNA)	| \
+	0)
+
+struct ath_tx_radiotap_header {
+	struct ieee80211_radiotap_header wt_ihdr;
+	u_int8_t	wt_flags;		/* XXX for padding */
+	u_int8_t	wt_rate;
+	u_int16_t	wt_chan_freq;
+	u_int16_t	wt_chan_flags;
+	u_int8_t	wt_txpower;
+	u_int8_t	wt_antenna;
+};
+
+#endif /* _DEV_ATH_ATHIOCTL_H */
diff -N -u -r src.preview/sys/dev/netif/ath/if_athrate.h src/sys/dev/netif/ath/if_athrate.h
--- src.preview/sys/dev/netif/ath/if_athrate.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/if_athrate.h	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004 Video54 Technologies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/dev/ath/if_athrate.h,v 1.2 2004/12/31 22:41:45 sam Exp $
+ */
+#ifndef _ATH_RATECTRL_H_
+#define _ATH_RATECTRL_H_
+
+/*
+ * Interface definitions for transmit rate control modules for the
+ * Atheros driver.
+ *
+ * A rate control module is responsible for choosing the transmit rate
+ * for each data frame.  Management+control frames are always sent at
+ * a fixed rate.
+ *
+ * Only one module may be present at a time; the driver references
+ * rate control interfaces by symbol name.  If multiple modules are
+ * to be supported we'll need to switch to a registration-based scheme
+ * as is currently done, for example, for authentication modules.
+ *
+ * An instance of the rate control module is attached to each device
+ * at attach time and detached when the device is destroyed.  The module
+ * may associate data with each device and each node (station).  Both
+ * sets of storage are opaque except for the size of the per-node storage
+ * which must be provided when the module is attached.
+ *
+ * The rate control module is notified for each state transition and
+ * station association/reassociation.  Otherwise it is queried for a
+ * rate for each outgoing frame and provided status from each transmitted
+ * frame.  Any ancillary processing is the responsibility of the module
+ * (e.g. if periodic processing is required then the module should setup
+ * it's own timer).
+ *
+ * In addition to the transmit rate for each frame the module must also
+ * indicate the number of attempts to make at the specified rate.  If this
+ * number is != ATH_TXMAXTRY then an additional callback is made to setup
+ * additional transmit state.  The rate control code is assumed to write
+ * this additional data directly to the transmit descriptor.
+ */
+struct ath_softc;
+struct ath_node;
+struct ath_desc;
+
+struct ath_ratectrl {
+	size_t	arc_space;	/* space required for per-node state */
+};
+/*
+ * Attach/detach a rate control module.
+ */
+struct ath_ratectrl *ath_rate_attach(struct ath_softc *);
+void	ath_rate_detach(struct ath_ratectrl *);
+
+/*
+ * State storage handling.
+ */
+/*
+ * Initialize per-node state already allocated for the specified
+ * node; this space can be assumed initialized to zero.
+ */
+void	ath_rate_node_init(struct ath_softc *, struct ath_node *);
+/*
+ * Cleanup any per-node state prior to the node being reclaimed.
+ */
+void	ath_rate_node_cleanup(struct ath_softc *, struct ath_node *);
+/*
+ * Update rate control state on station associate/reassociate 
+ * (when operating as an ap or for nodes discovered when operating
+ * in ibss mode).
+ */
+void	ath_rate_newassoc(struct ath_softc *, struct ath_node *,
+		int isNewAssociation);
+/*
+ * Update/reset rate control state for 802.11 state transitions.
+ * Important mostly as the analog to ath_rate_newassoc when operating
+ * in station mode.
+ */
+void	ath_rate_newstate(struct ath_softc *, enum ieee80211_state);
+
+/*
+ * Transmit handling.
+ */
+/*
+ * Return the transmit info for a data packet.  If multi-rate state
+ * is to be setup then try0 should contain a value other than ATH_TXMATRY
+ * and ath_rate_setupxtxdesc will be called after deciding if the frame
+ * can be transmitted with multi-rate retry.
+ */
+void	ath_rate_findrate(struct ath_softc *, struct ath_node *,
+		int shortPreamble, size_t frameLen,
+		u_int8_t *rix, int *try0, u_int8_t *txrate);
+/*
+ * Setup any extended (multi-rate) descriptor state for a data packet.
+ * The rate index returned by ath_rate_findrate is passed back in.
+ */
+void	ath_rate_setupxtxdesc(struct ath_softc *, struct ath_node *,
+		struct ath_desc *, int shortPreamble, u_int8_t rix);
+/*
+ * Update rate control state for a packet associated with the
+ * supplied transmit descriptor.  The routine is invoked both
+ * for packets that were successfully sent and for those that
+ * failed (consult the descriptor for details).
+ */
+void
+ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an,
+	const struct ath_desc *ds, const struct ath_desc *ds0);
+
+#endif /* _ATH_RATECTRL_H_ */
diff -N -u -r src.preview/sys/dev/netif/ath/if_athvar.h src/sys/dev/netif/ath/if_athvar.h
--- src.preview/sys/dev/netif/ath/if_athvar.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/dev/netif/ath/if_athvar.h	2005-09-23 11:57:00.000000000 -0400
@@ -0,0 +1,535 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * DragonFly Port - Copyright (c) 2005 Andrew Atrens
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+/*
+ * Defintions for the Atheros Wireless LAN controller driver.
+ */
+#ifndef _DEV_ATH_ATHVAR_H
+#define _DEV_ATH_ATHVAR_H
+
+#include <sys/taskqueue.h>
+#include <sys/sysctl.h>
+
+#include "../../../contrib/dev/ath/ah.h"
+#include <netproto/802_11/ieee80211_radiotap.h>
+#include "if_athioctl.h"
+#include "if_athrate.h"
+
+#define	ATH_TIMEOUT		1000
+
+#define	ATH_RXBUF	40		/* number of RX buffers */
+#define	ATH_TXBUF	100		/* number of TX buffers */
+#define	ATH_TXDESC	10		/* number of descriptors per buffer */
+#define	ATH_TXMAXTRY	11		/* max number of transmit attempts */
+#define	ATH_TXINTR_PERIOD 5		/* max number of batched tx descriptors */
+
+#define	ATH_BEACON_AIFS_DEFAULT	 0	/* default aifs for ap beacon q */
+#define	ATH_BEACON_CWMIN_DEFAULT 0	/* default cwmin for ap beacon q */
+#define	ATH_BEACON_CWMAX_DEFAULT 0	/* default cwmax for ap beacon q */
+
+/*
+ * The key cache is used for h/w cipher state and also for
+ * tracking station state such as the current tx antenna.
+ * We also setup a mapping table between key cache slot indices
+ * and station state to short-circuit node lookups on rx.
+ * Different parts have different size key caches.  We handle
+ * up to ATH_KEYMAX entries (could dynamically allocate state).
+ */
+#define	ATH_KEYMAX	128		/* max key cache size we handle */
+#define	ATH_KEYBYTES	(ATH_KEYMAX/NBBY)	/* storage space in bytes */
+
+/* driver-specific node state */
+struct ath_node {
+	struct ieee80211_node an_node;	/* base class */
+	u_int8_t	an_tx_mgtrate;	/* h/w rate for management/ctl frames */
+	u_int8_t	an_tx_mgtratesp;/* short preamble h/w rate for " " */
+	u_int32_t	an_avgrssi;	/* average rssi over all rx frames */
+	HAL_NODE_STATS	an_halstats;	/* rssi statistics used by hal */
+	/* variable-length rate control state follows */
+};
+#define	ATH_NODE(ni)	((struct ath_node *)(ni))
+#define	ATH_NODE_CONST(ni)	((const struct ath_node *)(ni))
+
+#define ATH_RSSI_LPF_LEN	10
+#define ATH_RSSI_DUMMY_MARKER	0x127
+#define ATH_EP_MUL(x, mul)	((x) * (mul))
+#define ATH_RSSI_IN(x)		(ATH_EP_MUL((x), HAL_RSSI_EP_MULTIPLIER))
+#define ATH_LPF_RSSI(x, y, len) \
+    ((x != ATH_RSSI_DUMMY_MARKER) ? (((x) * ((len) - 1) + (y)) / (len)) : (y))
+#define ATH_RSSI_LPF(x, y) do {						\
+    if ((y) >= -20)							\
+    	x = ATH_LPF_RSSI((x), ATH_RSSI_IN((y)), ATH_RSSI_LPF_LEN);	\
+} while (0)
+
+struct ath_buf {
+	STAILQ_ENTRY(ath_buf)	bf_list;
+	int			bf_nseg;
+	int			bf_flags;	/* tx descriptor flags */
+	struct ath_desc		*bf_desc;	/* virtual addr of desc */
+	bus_addr_t		bf_daddr;	/* physical addr of desc */
+	bus_dmamap_t		bf_dmamap;	/* DMA map for mbuf chain */
+	struct mbuf		*bf_m;		/* mbuf for buf */
+	struct ieee80211_node	*bf_node;	/* pointer to the node */
+	bus_size_t		bf_mapsize;
+#define	ATH_MAX_SCATTER		ATH_TXDESC	/* max(tx,rx,beacon) desc's */
+	bus_dma_segment_t	bf_segs[ATH_MAX_SCATTER];
+};
+typedef STAILQ_HEAD(, ath_buf) ath_bufhead;
+
+/*
+ * DMA state for tx/rx descriptors.
+ */
+struct ath_descdma {
+	const char*		dd_name;
+	struct ath_desc		*dd_desc;	/* descriptors */
+	bus_addr_t		dd_desc_paddr;	/* physical addr of dd_desc */
+	bus_addr_t		dd_desc_len;	/* size of dd_desc */
+	bus_dma_segment_t	dd_dseg;
+	bus_dma_tag_t		dd_dmat;	/* bus DMA tag */
+	bus_dmamap_t		dd_dmamap;	/* DMA map for descriptors */
+	struct ath_buf		*dd_bufptr;	/* associated buffers */
+};
+
+/*
+ * Data transmit queue state.  One of these exists for each
+ * hardware transmit queue.  Packets sent to us from above
+ * are assigned to queues based on their priority.  Not all
+ * devices support a complete set of hardware transmit queues.
+ * For those devices the array sc_ac2q will map multiple
+ * priorities to fewer hardware queues (typically all to one
+ * hardware queue).
+ */
+struct ath_txq {
+	u_int			axq_qnum;	/* hardware q number */
+	u_int			axq_depth;	/* queue depth (stat only) */
+	u_int			axq_intrcnt;	/* interrupt count */
+	u_int32_t		*axq_link;	/* link ptr in last TX desc */
+	STAILQ_HEAD(, ath_buf)	axq_q;		/* transmit queue */
+	struct lwkt_token	axq_lock;	/* lock on q and link */
+	/*
+	 * State for patching up CTS when bursting.
+	 */
+	struct	ath_buf		*axq_linkbuf;	/* va of last buffer */
+	struct	ath_desc	*axq_lastdsWithCTS;
+						/* first desc of last descriptor
+						 * that contains CTS 
+						 */
+	struct	ath_desc	*axq_gatingds;	/* final desc of the gating desc
+						 * that determines whether
+						 * lastdsWithCTS has been DMA'ed
+						 * or not
+						 */
+};
+
+#define	ATH_TXQ_LOCK_INIT(_sc, _tq) lwkt_token_init(&(_tq)->axq_lock)
+
+#define ATH_TXQ_LOCK_DESTROY(_tq)       lwkt_token_uninit(&(_tq)->axq_lock)
+#define ATH_TXQ_LOCK(_tq)               lwkt_gettoken(&tokinfo, &(_tq)->axq_lock)
+#define	ATH_TXQ_UNLOCK(_txq)		lwkt_reltoken(&tokinfo)
+#define	ATH_TXQ_LOCK_ASSERT(_sc)	(void)_sc
+
+#define ATH_TXQ_INSERT_TAIL(_tq, _elm, _field) do { \
+	STAILQ_INSERT_TAIL(&(_tq)->axq_q, (_elm), _field); \
+	(_tq)->axq_depth++; \
+} while (0)
+#define ATH_TXQ_REMOVE_HEAD(_tq, _field) do { \
+	STAILQ_REMOVE_HEAD(&(_tq)->axq_q, _field); \
+	(_tq)->axq_depth--; \
+} while (0)
+
+struct ath_softc {
+        struct arpcom           sc_arp;         /* interface common */
+	struct ifnet		*sc_ifp;
+	struct ath_stats	sc_stats;	/* interface statistics */
+	struct ieee80211com	sc_ic;		/* IEEE 802.11 common */
+	int			sc_regdomain;
+	int			sc_countrycode;
+	int			sc_debug;
+	void			(*sc_recv_mgmt)(struct ieee80211com *,
+					struct mbuf *,
+					struct ieee80211_node *,
+					int, int, u_int32_t);
+	int			(*sc_newstate)(struct ieee80211com *,
+					enum ieee80211_state, int);
+	void 			(*sc_node_free)(struct ieee80211_node *);
+	device_t		sc_dev;
+	bus_space_tag_t		sc_st;		/* bus space tag */
+	bus_space_handle_t	sc_sh;		/* bus space handle */
+	bus_dma_tag_t		sc_dmat;	/* bus DMA tag */
+	struct lwkt_token	sc_mtx;		/* master lock (recursive) */
+	struct ath_hal		*sc_ah;		/* Atheros HAL */
+	struct ath_ratectrl	*sc_rc;		/* tx rate control support */
+	void			(*sc_setdefantenna)(struct ath_softc *, u_int);
+	unsigned int		sc_invalid : 1,	/* disable hardware accesses */
+				sc_mrretry : 1,	/* multi-rate retry support */
+				sc_softled : 1,	/* enable LED gpio status */
+				sc_splitmic: 1,	/* split TKIP MIC keys */
+				sc_needmib : 1,	/* enable MIB stats intr */
+				sc_diversity : 1,/* enable rx diversity */
+				sc_hasveol : 1,	/* tx VEOL support */
+				sc_ledstate: 1,	/* LED on/off state */
+				sc_blinking: 1,	/* LED blink operation active */
+				sc_mcastkey: 1,	/* mcast key cache search */
+				sc_hasclrkey:1;	/* CLR key supported */
+						/* rate tables */
+	const HAL_RATE_TABLE	*sc_rates[IEEE80211_MODE_MAX];
+	const HAL_RATE_TABLE	*sc_currates;	/* current rate table */
+	enum ieee80211_phymode	sc_curmode;	/* current phy mode */
+	u_int16_t		sc_curtxpow;	/* current tx power limit */
+	HAL_CHANNEL		sc_curchan;	/* current h/w channel */
+	u_int8_t		sc_rixmap[256];	/* IEEE to h/w rate table ix */
+	struct {
+		u_int8_t	ieeerate;	/* IEEE rate */
+		u_int8_t	rxflags;	/* radiotap rx flags */
+		u_int8_t	txflags;	/* radiotap tx flags */
+		u_int16_t	ledon;		/* softled on time */
+		u_int16_t	ledoff;		/* softled off time */
+	} sc_hwmap[32];				/* h/w rate ix mappings */
+	u_int8_t		sc_protrix;	/* protection rate index */
+	u_int			sc_txantenna;	/* tx antenna (fixed or auto) */
+	HAL_INT			sc_imask;	/* interrupt mask copy */
+	u_int			sc_keymax;	/* size of key cache */
+	u_int8_t		sc_keymap[ATH_KEYBYTES];/* key use bit map */
+
+	u_int			sc_ledpin;	/* GPIO pin for driving LED */
+	u_int			sc_ledon;	/* pin setting for LED on */
+	u_int			sc_ledidle;	/* idle polling interval */
+	int			sc_ledevent;	/* time of last LED event */
+	u_int8_t		sc_rxrate;	/* current rx rate for LED */
+	u_int8_t		sc_txrate;	/* current tx rate for LED */
+	u_int16_t		sc_ledoff;	/* off time for current blink */
+	struct callout		sc_ledtimer;	/* led off timer */
+
+	struct bpf_if		*sc_drvbpf;
+	union {
+		struct ath_tx_radiotap_header th;
+		u_int8_t	pad[64];
+	} u_tx_rt;
+	int			sc_tx_th_len;
+	union {
+		struct ath_rx_radiotap_header th;
+		u_int8_t	pad[64];
+	} u_rx_rt;
+	int			sc_rx_th_len;
+
+	struct task		sc_fataltask;	/* fatal int processing */
+
+	struct ath_descdma	sc_rxdma;	/* RX descriptos */
+	ath_bufhead		sc_rxbuf;	/* receive buffer */
+	u_int32_t		*sc_rxlink;	/* link ptr in last RX desc */
+	struct task		sc_rxtask;	/* rx int processing */
+	struct task		sc_rxorntask;	/* rxorn int processing */
+	u_int8_t		sc_defant;	/* current default antenna */
+	u_int8_t		sc_rxotherant;	/* rx's on non-default antenna*/
+
+	struct ath_descdma	sc_txdma;	/* TX descriptors */
+	ath_bufhead		sc_txbuf;	/* transmit buffer */
+	struct lwkt_token	sc_txbuflock;	/* txbuf lock */
+	int			sc_tx_timer;	/* transmit timeout */
+	u_int			sc_txqsetup;	/* h/w queues setup */
+	u_int			sc_txintrperiod;/* tx interrupt batching */
+	struct ath_txq		sc_txq[HAL_NUM_TX_QUEUES];
+	struct ath_txq		*sc_ac2q[5];	/* WME AC -> h/w q map */ 
+	struct task		sc_txtask;	/* tx int processing */
+
+	struct ath_descdma	sc_bdma;	/* beacon descriptors */
+	ath_bufhead		sc_bbuf;	/* beacon buffers */
+	u_int			sc_bhalq;	/* HAL q for outgoing beacons */
+	u_int			sc_bmisscount;	/* missed beacon transmits */
+	u_int32_t		sc_ant_tx[8];	/* recent tx frames/antenna */
+	struct ath_txq		*sc_cabq;	/* tx q for cab frames */
+	struct ieee80211_beacon_offsets sc_boff;/* dynamic update state */
+	struct task		sc_bmisstask;	/* bmiss int processing */
+	struct task		sc_bstucktask;	/* stuck beacon processing */
+	enum {
+		OK,				/* no change needed */
+		UPDATE,				/* update pending */
+		COMMIT				/* beacon sent, commit change */
+	} sc_updateslot;			/* slot time update fsm */
+
+	struct callout		sc_cal_ch;	/* callout handle for cals */
+	struct callout		sc_scan_ch;	/* callout handle for scan */
+
+    struct sysctl_ctx_list  sysctl_ctx;
+    struct sysctl_oid   *sysctl_tree;
+ 	u_int32_t tx_power;
+    u_int32_t last_rx_rssi;
+
+};
+#define sc_if                   sc_arp.ac_if
+#define	sc_tx_th		u_tx_rt.th
+#define	sc_rx_th		u_rx_rt.th
+
+#define ATH_LOCK_INFO           struct lwkt_tokref tokinfo;
+#define	ATH_LOCK_INIT(_sc)	lwkt_token_init(&(_sc)->sc_mtx)
+#define	ATH_LOCK_DESTROY(_sc)	lwkt_token_uninit(&(_sc)->sc_mtx)
+#define	ATH_LOCK(_sc)		lwkt_gettoken(&tokinfo, &(_sc)->sc_mtx)
+#define	ATH_UNLOCK(_sc)		lwkt_reltoken(&tokinfo)
+#define	ATH_LOCK_ASSERT(_sc)	(void)_sc
+
+#define	ATH_TXQ_SETUP(sc, i)	((sc)->sc_txqsetup & (1<<i))
+
+
+#define ATH_TXBUF_LOCK_INFO     struct lwkt_tokref txbuf_tokinfo;
+#define	ATH_TXBUF_LOCK_INIT(_sc) lwkt_token_init(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_LOCK_DESTROY(_sc)	lwkt_token_uninit(&(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_LOCK(_sc)		lwkt_gettoken(&txbuf_tokinfo, &(_sc)->sc_txbuflock)
+#define	ATH_TXBUF_UNLOCK(_sc)		lwkt_reltoken(&txbuf_tokinfo)
+#define	ATH_TXBUF_LOCK_ASSERT(_sc)      (void)_sc
+
+
+#define NET_LOCK_GIANT()		(void)0
+#define NET_UNLOCK_GIANT()		(void)0
+
+int	ath_attach(u_int16_t, struct ath_softc *);
+int	ath_detach(struct ath_softc *);
+void	ath_resume(struct ath_softc *);
+void	ath_suspend(struct ath_softc *);
+void	ath_shutdown(struct ath_softc *);
+void	ath_intr(void *);
+
+/*
+ * HAL definitions to comply with local coding convention.
+ */
+#define	ath_hal_detach(_ah) \
+	((*(_ah)->ah_detach)((_ah)))
+#define	ath_hal_reset(_ah, _opmode, _chan, _outdoor, _pstatus) \
+	((*(_ah)->ah_reset)((_ah), (_opmode), (_chan), (_outdoor), (_pstatus)))
+#define	ath_hal_getratetable(_ah, _mode) \
+	((*(_ah)->ah_getRateTable)((_ah), (_mode)))
+#define	ath_hal_getmac(_ah, _mac) \
+	((*(_ah)->ah_getMacAddress)((_ah), (_mac)))
+#define	ath_hal_setmac(_ah, _mac) \
+	((*(_ah)->ah_setMacAddress)((_ah), (_mac)))
+#define	ath_hal_intrset(_ah, _mask) \
+	((*(_ah)->ah_setInterrupts)((_ah), (_mask)))
+#define	ath_hal_intrget(_ah) \
+	((*(_ah)->ah_getInterrupts)((_ah)))
+#define	ath_hal_intrpend(_ah) \
+	((*(_ah)->ah_isInterruptPending)((_ah)))
+#define	ath_hal_getisr(_ah, _pmask) \
+	((*(_ah)->ah_getPendingInterrupts)((_ah), (_pmask)))
+#define	ath_hal_updatetxtriglevel(_ah, _inc) \
+	((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc)))
+#define	ath_hal_setpower(_ah, _mode, _sleepduration) \
+	((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE, (_sleepduration)))
+#define	ath_hal_keycachesize(_ah) \
+	((*(_ah)->ah_getKeyCacheSize)((_ah)))
+#define	ath_hal_keyreset(_ah, _ix) \
+	((*(_ah)->ah_resetKeyCacheEntry)((_ah), (_ix)))
+#define	ath_hal_keyset(_ah, _ix, _pk, _mac) \
+	((*(_ah)->ah_setKeyCacheEntry)((_ah), (_ix), (_pk), (_mac), AH_FALSE))
+#define	ath_hal_keyisvalid(_ah, _ix) \
+	(((*(_ah)->ah_isKeyCacheEntryValid)((_ah), (_ix))))
+#define	ath_hal_keysetmac(_ah, _ix, _mac) \
+	((*(_ah)->ah_setKeyCacheEntryMac)((_ah), (_ix), (_mac)))
+#define	ath_hal_getrxfilter(_ah) \
+	((*(_ah)->ah_getRxFilter)((_ah)))
+#define	ath_hal_setrxfilter(_ah, _filter) \
+	((*(_ah)->ah_setRxFilter)((_ah), (_filter)))
+#define	ath_hal_setmcastfilter(_ah, _mfilt0, _mfilt1) \
+	((*(_ah)->ah_setMulticastFilter)((_ah), (_mfilt0), (_mfilt1)))
+#define	ath_hal_waitforbeacon(_ah, _bf) \
+	((*(_ah)->ah_waitForBeaconDone)((_ah), (_bf)->bf_daddr))
+#define	ath_hal_putrxbuf(_ah, _bufaddr) \
+	((*(_ah)->ah_setRxDP)((_ah), (_bufaddr)))
+#define	ath_hal_gettsf32(_ah) \
+	((*(_ah)->ah_getTsf32)((_ah)))
+#define	ath_hal_gettsf64(_ah) \
+	((*(_ah)->ah_getTsf64)((_ah)))
+#define	ath_hal_resettsf(_ah) \
+	((*(_ah)->ah_resetTsf)((_ah)))
+#define	ath_hal_rxena(_ah) \
+	((*(_ah)->ah_enableReceive)((_ah)))
+#define	ath_hal_puttxbuf(_ah, _q, _bufaddr) \
+	((*(_ah)->ah_setTxDP)((_ah), (_q), (_bufaddr)))
+#define	ath_hal_gettxbuf(_ah, _q) \
+	((*(_ah)->ah_getTxDP)((_ah), (_q)))
+#define	ath_hal_numtxpending(_ah, _q) \
+	((*(_ah)->ah_numTxPending)((_ah), (_q)))
+#define	ath_hal_getrxbuf(_ah) \
+	((*(_ah)->ah_getRxDP)((_ah)))
+#define	ath_hal_txstart(_ah, _q) \
+	((*(_ah)->ah_startTxDma)((_ah), (_q)))
+#define	ath_hal_setchannel(_ah, _chan) \
+	((*(_ah)->ah_setChannel)((_ah), (_chan)))
+#define	ath_hal_calibrate(_ah, _chan) \
+	((*(_ah)->ah_perCalibration)((_ah), (_chan)))
+#define	ath_hal_setledstate(_ah, _state) \
+	((*(_ah)->ah_setLedState)((_ah), (_state)))
+#define	ath_hal_beaconinit(_ah, _nextb, _bperiod) \
+	((*(_ah)->ah_beaconInit)((_ah), (_nextb), (_bperiod)))
+#define	ath_hal_beaconreset(_ah) \
+	((*(_ah)->ah_resetStationBeaconTimers)((_ah)))
+#define	ath_hal_beacontimers(_ah, _bs) \
+	((*(_ah)->ah_setStationBeaconTimers)((_ah), (_bs)))
+#define	ath_hal_setassocid(_ah, _bss, _associd) \
+	((*(_ah)->ah_writeAssocid)((_ah), (_bss), (_associd)))
+#define	ath_hal_phydisable(_ah) \
+	((*(_ah)->ah_phyDisable)((_ah)))
+#define	ath_hal_setopmode(_ah) \
+	((*(_ah)->ah_setPCUConfig)((_ah)))
+#define	ath_hal_stoptxdma(_ah, _qnum) \
+	((*(_ah)->ah_stopTxDma)((_ah), (_qnum)))
+#define	ath_hal_stoppcurecv(_ah) \
+	((*(_ah)->ah_stopPcuReceive)((_ah)))
+#define	ath_hal_startpcurecv(_ah) \
+	((*(_ah)->ah_startPcuReceive)((_ah)))
+#define	ath_hal_stopdmarecv(_ah) \
+	((*(_ah)->ah_stopDmaReceive)((_ah)))
+#define	ath_hal_getdiagstate(_ah, _id, _indata, _insize, _outdata, _outsize) \
+	((*(_ah)->ah_getDiagState)((_ah), (_id), \
+		(_indata), (_insize), (_outdata), (_outsize)))
+#define	ath_hal_setuptxqueue(_ah, _type, _irq) \
+	((*(_ah)->ah_setupTxQueue)((_ah), (_type), (_irq)))
+#define	ath_hal_resettxqueue(_ah, _q) \
+	((*(_ah)->ah_resetTxQueue)((_ah), (_q)))
+#define	ath_hal_releasetxqueue(_ah, _q) \
+	((*(_ah)->ah_releaseTxQueue)((_ah), (_q)))
+#define	ath_hal_gettxqueueprops(_ah, _q, _qi) \
+	((*(_ah)->ah_getTxQueueProps)((_ah), (_q), (_qi)))
+#define	ath_hal_settxqueueprops(_ah, _q, _qi) \
+	((*(_ah)->ah_setTxQueueProps)((_ah), (_q), (_qi)))
+#define	ath_hal_getrfgain(_ah) \
+	((*(_ah)->ah_getRfGain)((_ah)))
+#define	ath_hal_getdefantenna(_ah) \
+	((*(_ah)->ah_getDefAntenna)((_ah)))
+#define	ath_hal_setdefantenna(_ah, _ant) \
+	((*(_ah)->ah_setDefAntenna)((_ah), (_ant)))
+#define	ath_hal_rxmonitor(_ah, _arg) \
+	((*(_ah)->ah_rxMonitor)((_ah), (_arg)))
+#define	ath_hal_mibevent(_ah, _stats) \
+	((*(_ah)->ah_procMibEvent)((_ah), (_stats)))
+#define	ath_hal_setslottime(_ah, _us) \
+	((*(_ah)->ah_setSlotTime)((_ah), (_us)))
+#define	ath_hal_getslottime(_ah) \
+	((*(_ah)->ah_getSlotTime)((_ah)))
+#define	ath_hal_setacktimeout(_ah, _us) \
+	((*(_ah)->ah_setAckTimeout)((_ah), (_us)))
+#define	ath_hal_getacktimeout(_ah) \
+	((*(_ah)->ah_getAckTimeout)((_ah)))
+#define	ath_hal_setctstimeout(_ah, _us) \
+	((*(_ah)->ah_setCTSTimeout)((_ah), (_us)))
+#define	ath_hal_getctstimeout(_ah) \
+	((*(_ah)->ah_getCTSTimeout)((_ah)))
+#define	ath_hal_getcapability(_ah, _cap, _param, _result) \
+	((*(_ah)->ah_getCapability)((_ah), (_cap), (_param), (_result)))
+#define	ath_hal_setcapability(_ah, _cap, _param, _v, _status) \
+	((*(_ah)->ah_setCapability)((_ah), (_cap), (_param), (_v), (_status)))
+#define	ath_hal_ciphersupported(_ah, _cipher) \
+	(ath_hal_getcapability(_ah, HAL_CAP_CIPHER, _cipher, NULL) == HAL_OK)
+#define	ath_hal_getregdomain(_ah, _prd) \
+	ath_hal_getcapability(_ah, HAL_CAP_REG_DMN, 0, (_prd))
+#define	ath_hal_getcountrycode(_ah, _pcc) \
+	(*(_pcc) = (_ah)->ah_countryCode)
+#define	ath_hal_tkipsplit(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
+#define	ath_hal_hwphycounters(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
+#define	ath_hal_hasdiversity(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 0, NULL) == HAL_OK)
+#define	ath_hal_getdiversity(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_DIVERSITY, 1, NULL) == HAL_OK)
+#define	ath_hal_setdiversity(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_DIVERSITY, 1, _v, NULL)
+#define	ath_hal_getdiag(_ah, _pv) \
+	(ath_hal_getcapability(_ah, HAL_CAP_DIAG, 0, _pv) == HAL_OK)
+#define	ath_hal_setdiag(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_DIAG, 0, _v, NULL)
+#define	ath_hal_getnumtxqueues(_ah, _pv) \
+	(ath_hal_getcapability(_ah, HAL_CAP_NUM_TXQUEUES, 0, _pv) == HAL_OK)
+#define	ath_hal_hasveol(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_VEOL, 0, NULL) == HAL_OK)
+#define	ath_hal_hastxpowlimit(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 0, NULL) == HAL_OK)
+#define	ath_hal_settxpowlimit(_ah, _pow) \
+	((*(_ah)->ah_setTxPowerLimit)((_ah), (_pow)))
+#define	ath_hal_gettxpowlimit(_ah, _ppow) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 1, _ppow) == HAL_OK)
+#define	ath_hal_getmaxtxpow(_ah, _ppow) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 2, _ppow) == HAL_OK)
+#define	ath_hal_gettpscale(_ah, _scale) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TXPOW, 3, _scale) == HAL_OK)
+#define	ath_hal_settpscale(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_TXPOW, 3, _v, NULL)
+#define	ath_hal_hastpc(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TPC, 0, NULL) == HAL_OK)
+#define	ath_hal_gettpc(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_TPC, 1, NULL) == HAL_OK)
+#define	ath_hal_settpc(_ah, _v) \
+	ath_hal_setcapability(_ah, HAL_CAP_TPC, 1, _v, NULL)
+#define	ath_hal_hasbursting(_ah) \
+	(ath_hal_getcapability(_ah, HAL_CAP_BURST, 0, NULL) == HAL_OK)
+#ifdef notyet
+#define ath_hal_hasmcastkeysearch(_ah) \
+        (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 0, NULL) == HAL_OK)
+#define ath_hal_getmcastkeysearch(_ah) \
+        (ath_hal_getcapability(_ah, HAL_CAP_MCAST_KEYSRCH, 1, NULL) == HAL_OK)
+#else
+#define ath_hal_getmcastkeysearch(_ah)  0
+#endif
+
+#define	ath_hal_setuprxdesc(_ah, _ds, _size, _intreq) \
+	((*(_ah)->ah_setupRxDesc)((_ah), (_ds), (_size), (_intreq)))
+#define	ath_hal_rxprocdesc(_ah, _ds, _dspa, _dsnext) \
+	((*(_ah)->ah_procRxDesc)((_ah), (_ds), (_dspa), (_dsnext)))
+#define	ath_hal_setuptxdesc(_ah, _ds, _plen, _hlen, _atype, _txpow, \
+		_txr0, _txtr0, _keyix, _ant, _flags, \
+		_rtsrate, _rtsdura) \
+	((*(_ah)->ah_setupTxDesc)((_ah), (_ds), (_plen), (_hlen), (_atype), \
+		(_txpow), (_txr0), (_txtr0), (_keyix), (_ant), \
+		(_flags), (_rtsrate), (_rtsdura)))
+#define	ath_hal_setupxtxdesc(_ah, _ds, \
+		_txr1, _txtr1, _txr2, _txtr2, _txr3, _txtr3) \
+	((*(_ah)->ah_setupXTxDesc)((_ah), (_ds), \
+		(_txr1), (_txtr1), (_txr2), (_txtr2), (_txr3), (_txtr3)))
+#define	ath_hal_filltxdesc(_ah, _ds, _l, _first, _last, _ds0) \
+	((*(_ah)->ah_fillTxDesc)((_ah), (_ds), (_l), (_first), (_last), (_ds0)))
+#define	ath_hal_txprocdesc(_ah, _ds) \
+	((*(_ah)->ah_procTxDesc)((_ah), (_ds)))
+#define	ath_hal_updateCTSForBursting(_ah, _ds, _prevds, _prevdsWithCTS, \
+		_gatingds,  _txOpLimit, _ctsDuration) \
+	((*(_ah)->ah_updateCTSForBursting)((_ah), (_ds), (_prevds), \
+		(_prevdsWithCTS), (_gatingds), (_txOpLimit), (_ctsDuration)))
+
+#define ath_hal_gpioCfgOutput(_ah, _gpio) \
+        ((*(_ah)->ah_gpioCfgOutput)((_ah), (_gpio)))
+#define ath_hal_gpioset(_ah, _gpio, _b) \
+        ((*(_ah)->ah_gpioSet)((_ah), (_gpio), (_b)))
+
+#endif /* _DEV_ATH_ATHVAR_H */
diff -N -u -r src.preview/sys/dev/netif/ndis/if_ndis.c src/sys/dev/netif/ndis/if_ndis.c
--- src.preview/sys/dev/netif/ndis/if_ndis.c	2005-08-10 23:22:57.000000000 -0400
+++ src/sys/dev/netif/ndis/if_ndis.c	2005-09-23 11:24:22.000000000 -0400
@@ -659,8 +659,8 @@
 		if (r == 0)
 			ic->ic_caps |= IEEE80211_C_PMGT;
 		bcopy(eaddr, &ic->ic_myaddr, sizeof(eaddr));
-		ieee80211_ifattach(ifp);
-		ieee80211_media_init(ifp, ieee80211_media_change,
+		ieee80211_ifattach(ic);
+		ieee80211_media_init(ic, ieee80211_media_change,
 		    ndis_media_status);
 		ic->ic_ibss_chan = IEEE80211_CHAN_ANYC;
 		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
@@ -701,11 +701,11 @@
 ndis_detach(dev)
 	device_t		dev;
 {
-	struct ndis_softc	*sc;
-	struct ifnet		*ifp;
+	struct ndis_softc	*sc = device_get_softc(dev);
+	struct ifnet		*ifp = &sc->arpcom.ac_if;
+	struct ieee80211com     *ic = &sc->sc_ic;
 	NDIS_LOCK_INFO;
 
-	sc = device_get_softc(dev);
 	NDIS_LOCK(sc);
 	ifp = &sc->arpcom.ac_if;
 	ifp->if_flags &= ~IFF_UP;
@@ -714,7 +714,7 @@
 		NDIS_UNLOCK(sc);
 		ndis_stop(sc);
 		if (sc->ndis_80211)
-			ieee80211_ifdetach(ifp);
+			ieee80211_ifdetach(ic);
 		else
 			ether_ifdetach(ifp);
 	} else
@@ -1655,7 +1655,7 @@
         case IEEE80211_MODE_11G:
                 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11G);
                 break;
-        case IEEE80211_MODE_TURBO:
+        case IEEE80211_MODE_TURBO_A:
                 imr->ifm_active |= IFM_MAKEMODE(IFM_IEEE80211_11A)
                                 |  IFM_IEEE80211_TURBO;
                 break;
diff -N -u -r src.preview/sys/dev/netif/ndis/if_ndisvar.h src/sys/dev/netif/ndis/if_ndisvar.h
--- src.preview/sys/dev/netif/ndis/if_ndisvar.h	2004-09-14 19:57:00.000000000 -0400
+++ src/sys/dev/netif/ndis/if_ndisvar.h	2005-09-23 11:24:22.000000000 -0400
@@ -68,10 +68,9 @@
 #define NDIS_INC(x)		\
 	(x)->ndis_txidx = ((x)->ndis_txidx + 1) % (x)->ndis_maxpkts 
 
-#define arpcom ic.ic_ac
-
 struct ndis_softc {
 	struct ieee80211com	ic;		/* interface info */
+	struct arpcom		arpcom;		/* interface info */
 #ifdef notdef
 	struct ieee80211com	arpcom;		/* interface info */
 #endif
diff -N -u -r src.preview/sys/dev/netif/wi/Makefile src/sys/dev/netif/wi/Makefile
--- src.preview/sys/dev/netif/wi/Makefile	2004-09-06 09:52:24.000000000 -0400
+++ src/sys/dev/netif/wi/Makefile	2005-09-23 11:24:22.000000000 -0400
@@ -5,6 +5,7 @@
 SRCS=	if_wi.c if_wi_pccard.c if_wi_pci.c \
 	opt_wi.h \
 	card_if.h device_if.h bus_if.h pci_if.h
+CFLAGS+= -g
 
 opt_wi.h:
 	echo '#define WI_SYMBOL_FIRMWARE 1' > ${.OBJDIR}/opt_wi.h
diff -N -u -r src.preview/sys/dev/netif/wi/if_wi.c src/sys/dev/netif/wi/if_wi.c
--- src.preview/sys/dev/netif/wi/if_wi.c	2005-07-03 12:47:20.000000000 -0400
+++ src/sys/dev/netif/wi/if_wi.c	2005-09-23 11:36:54.000000000 -0400
@@ -98,7 +98,6 @@
 #include <netproto/802_11/ieee80211_var.h>
 #include <netproto/802_11/ieee80211_ioctl.h>
 #include <netproto/802_11/ieee80211_radiotap.h>
-#include <netproto/802_11/if_wavelan_ieee.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
@@ -108,6 +107,7 @@
 
 #include <net/bpf.h>
 
+#include <netproto/802_11/if_wavelan_ieee.h>
 #include <dev/netif/wi/if_wireg.h>
 #include <dev/netif/wi/if_wivar.h>
 
@@ -236,7 +236,7 @@
 {
 	struct wi_softc	*sc = device_get_softc(dev);
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int i, nrates, buflen;
 	u_int16_t val;
 	u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE];
@@ -249,6 +249,7 @@
 	ifp->if_softc = sc;
 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
 
+	sc->sc_firmware_type = WI_NOTYPE;
 	sc->wi_cmd_count = 500;
 	/* Reset the NIC. */
 	error = wi_reset(sc);
@@ -292,10 +293,14 @@
 #endif
 	ifp->if_capenable = ifp->if_capabilities;
 
+	ic->ic_ifp = ifp;
 	ic->ic_phytype = IEEE80211_T_DS;
 	ic->ic_opmode = IEEE80211_M_STA;
-	ic->ic_caps = IEEE80211_C_PMGT | IEEE80211_C_AHDEMO;
 	ic->ic_state = IEEE80211_S_INIT;
+	ic->ic_caps = IEEE80211_C_PMGT
+		    | IEEE80211_C_WEP		/* everyone supports WEP */
+		    ;
+	ic->ic_max_aid = WI_MAX_AID;
 
 	/*
 	 * Query the card for available channels and setup the
@@ -308,11 +313,11 @@
 
 	val <<= 1;			/* shift for base 1 indices */
 	for (i = 1; i < 16; i++) {
-		if (isset((u_int8_t*)&val, i)) {
-			ic->ic_channels[i].ic_freq =
-				ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
-			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B;
-		}
+		if (!isset((u_int8_t*)&val, i))
+			continue;
+		ic->ic_channels[i].ic_freq =
+			ieee80211_ieee2mhz(i, IEEE80211_CHAN_B);
+		ic->ic_channels[i].ic_flags = IEEE80211_CHAN_B;
 	}
 
 	/*
@@ -454,15 +459,14 @@
 	/*
 	 * Call MI attach routine.
 	 */
-	ieee80211_ifattach(ifp);
+	ieee80211_ifattach(ic);
 	/* override state transition method */
 	sc->sc_newstate = ic->ic_newstate;
 	ic->ic_newstate = wi_newstate;
-	ieee80211_media_init(ifp, wi_media_change, wi_media_status);
+	ieee80211_media_init(ic, wi_media_change, wi_media_status);
 
 	bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO,
-		sizeof(struct ieee80211_frame) + sizeof(sc->sc_tx_th),
-		&sc->sc_drvbpf);
+		sizeof(struct ieee80211_frame) + 64, &sc->sc_drvbpf);
 	/*
 	 * Initialize constant fields.
 	 * XXX make header lengths a multiple of 32-bits so subsequent
@@ -510,6 +514,8 @@
 
 	wi_stop(ifp, 0);
 
+ 	bpfdetach(ifp);
+
 	ieee80211_ifdetach(ifp);
 	wi_free(dev);
 	WI_UNLOCK(sc);
@@ -573,13 +579,15 @@
 	u_int16_t status;
 	WI_LOCK_DECL();
 
+	WI_LOCK(sc);
+
 	if (sc->wi_gone || !sc->sc_enabled || (ifp->if_flags & IFF_UP) == 0) {
 		CSR_WRITE_2(sc, WI_INT_EN, 0);
 		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
+		WI_UNLOCK(sc);
 		return;
 	}
 
-	WI_LOCK(sc);
 
 	/* Disable interrupts. */
 	CSR_WRITE_2(sc, WI_INT_EN, 0);
@@ -752,9 +760,10 @@
 	ifp->if_flags |= IFF_RUNNING;
 	ifp->if_flags &= ~IFF_OACTIVE;
 	if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
+ 	    ic->ic_opmode == IEEE80211_M_IBSS ||
 	    ic->ic_opmode == IEEE80211_M_MONITOR ||
 	    ic->ic_opmode == IEEE80211_M_HOSTAP)
-		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ 		ieee80211_create_ibss(ic, ic->ic_ibss_chan);
 
 	/* Enable interrupts if not polling */
 #ifdef DEVICE_POLLING
@@ -839,6 +848,7 @@
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ieee80211_node *ni;
 	struct ieee80211_frame *wh;
+ 	struct ether_header *eh;
 	struct mbuf *m0;
 	struct wi_frame frmhdr;
 	int cur, fid, off, error;
@@ -884,14 +894,28 @@
 		} else {
 			if (ic->ic_state != IEEE80211_S_RUN)
 				break;
-			m0 = ifq_poll(&ifp->if_snd);
+ 			IF_POLL(&ifp->if_snd, m0);
 			if (m0 == NULL)
 				break;
 			if (sc->sc_txd[cur].d_len != 0) {
+ 				IF_PREPEND(&ifp->if_snd, m0);
 				ifp->if_flags |= IFF_OACTIVE;
 				break;
 			}
-			m0 = ifq_dequeue(&ifp->if_snd);
+ 			IF_DEQUEUE(&ifp->if_snd, m0);
+ 			if (m0->m_len < sizeof(struct ether_header) &&
+ 			    (m0 = m_pullup(m0, sizeof(struct ether_header))) == NULL) {
+ 				ifp->if_oerrors++;
+ 				continue;
+ 			}
+ 			eh = mtod(m0, struct ether_header *);
+ 			ni = ieee80211_find_txnode(ic, eh->ether_dhost);
+ 			if (ni == NULL) {
+ 				m_freem(m0);
+ 				continue;
+ 			}
+
+
 			ifp->if_opackets++;
 			m_copydata(m0, 0, ETHER_HDR_LEN, 
 			    (caddr_t)&frmhdr.wi_ehdr);
@@ -899,6 +923,7 @@
 
 			m0 = ieee80211_encap(ifp, m0, &ni);
 			if (m0 == NULL) {
+ 				ieee80211_free_node(ni);
 				ifp->if_oerrors++;
 				continue;
 			}
@@ -914,10 +939,13 @@
 		frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX);
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
 		    (wh->i_fc[1] & IEEE80211_FC1_WEP)) {
-			if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) {
-				ifp->if_oerrors++;
-				if (ni && ni != ic->ic_bss)
-					ieee80211_free_node(ic, ni);
+ 			struct ieee80211_key *k;
+ 
+ 			k = ieee80211_crypto_encap(ic, ni, m0);
+ 			if (k == NULL) {
+ 				if (ni != NULL)
+ 					ieee80211_free_node(ni);
+ 				m_freem(m0);
 				continue;
 			}
 			frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT);
@@ -941,8 +969,8 @@
 		error = wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0
 		     || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0;
 		m_freem(m0);
-		if (ni && ni != ic->ic_bss)
-			ieee80211_free_node(ic, ni);
+		if (ni != NULL)
+			ieee80211_free_node(ni);
 		if (error) {
 			ifp->if_oerrors++;
 			continue;
@@ -999,6 +1027,9 @@
 	/* Calibrate timer. */
 	wi_write_val(sc, WI_RID_TICK_TIME, 8);
 
+	if (bootverbose)
+		ieee80211_announce(ic);
+
 	return (0);
 #undef WI_INIT_TRIES
 }
@@ -1045,7 +1076,7 @@
 	}
 
 	/* TODO: rate control */
-	ieee80211_watchdog(ifp);
+	ieee80211_watchdog(&sc->sc_ic);
 }
 
 static int
@@ -1141,7 +1172,7 @@
 					ireq->i_len);
 			break;
 		default:
-			error = ieee80211_ioctl(ifp, cmd, data, cr);
+			error = ieee80211_ioctl(ic, cmd, data, cr);
 			break;
 		}
 		break;
@@ -1171,7 +1202,7 @@
 			sc->sc_nodelen = ireq->i_len;
 			break;
 		default:
-			error = ieee80211_ioctl(ifp, cmd, data, cr);
+			error = ieee80211_ioctl(ic, cmd, data, cr);
 			break;
 		}
 		break;
@@ -1180,7 +1211,7 @@
 			wi_init(sc);
 		break;
 	default:
-		error = ieee80211_ioctl(ifp, cmd, data, cr);
+		error = ieee80211_ioctl(ic, cmd, data, cr);
 		break;
 	}
 	if (error == ENETRESET) {
@@ -1229,23 +1260,22 @@
 	    (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0)
 		imr->ifm_status |= IFM_ACTIVE;
 	len = sizeof(val);
-	if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) != 0)
-		rate = 0;
-	else {
+	if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) == 0 &&
+	    len == sizeof(val)) {
+		val = le16toh(val);
 		rate = val * 2;
 		if (sc->sc_firmware_type == WI_LUCENT) {
-			if (rate == 4 * 2)
+			if (rate == 10)
 				rate = 11;	/* 5.5Mbps */
-			else if (rate == 5 * 2)
-				rate = 22;	/* 11Mbps */
 		} else {
 			if (rate == 4*2)
 				rate = 11;	/* 5.5Mbps */
 			else if (rate == 8*2)
 				rate = 22;	/* 11Mbps */
 		}
-	}
+	} else
+		rate = 0;
 	imr->ifm_active |= ieee80211_rate2media(ic, rate, IEEE80211_MODE_11B);
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
@@ -1270,7 +1300,7 @@
 {
 	struct ieee80211com *ic = &sc->sc_ic;
 	struct ieee80211_node *ni = ic->ic_bss;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 
 	if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid))
 		return;
@@ -1283,17 +1313,25 @@
 	 * change-of-BSSID indications.
 	 */
 	if ((ifp->if_flags & IFF_PROMISC) != 0 &&
-	    sc->sc_false_syns >= WI_MAX_FALSE_SYNS)
+	    !ppsratecheck(&sc->sc_last_syn, &sc->sc_false_syns,
+	                 WI_MAX_FALSE_SYNS))
 		return;
 
-	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	sc->sc_false_syns = MAX(0, sc->sc_false_syns - 1);
+	/*
+	 * XXX hack; we should create a new node with the new bssid
+	 * and replace the existing ic_bss with it but since we don't
+	 * process management frames to collect state we cheat by
+	 * reusing the existing node as we know wi_newstate will be
+	 * called and it will overwrite the node state.
+	 */
+	ieee80211_sta_join(ic, ieee80211_ref_node(ni));
 }
 
 static void
 wi_rx_monitor(struct wi_softc *sc, int fid)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame *rx_frame;
 	struct mbuf *m;
 	int datlen, hdrlen;
@@ -1376,7 +1414,7 @@
 wi_rx_intr(struct wi_softc *sc)
 {
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame frmhdr;
 	struct mbuf *m;
 	struct ieee80211_frame *wh;
@@ -1458,6 +1496,17 @@
 
 	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX);
 
+	wh = mtod(m, struct ieee80211_frame *);
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		/*
+		 * WEP is decrypted by hardware and the IV
+		 * is stripped.  Clear WEP bit so we don't
+		 * try to process it in ieee80211_input.
+		 * XXX fix for TKIP, et. al.
+		 */
+		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+	}
+
 	if (sc->sc_drvbpf) {
 		/* XXX replace divide by table */
 		sc->sc_rx_th.wr_rate = frmhdr.wi_rx_rate / 5;
@@ -1469,14 +1518,6 @@
 		bpf_ptap(sc->sc_drvbpf, m, &sc->sc_rx_th, sc->sc_rx_th_len);
 	}
 
-	wh = mtod(m, struct ieee80211_frame *);
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
-		/*
-		 * WEP is decrypted by hardware. Clear WEP bit
-		 * header for ieee80211_input().
-		 */
-		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
-	}
 
 	/* synchronize driver's BSSID with firmware's BSSID */
 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
@@ -1486,36 +1527,25 @@
 	/*
 	 * Locate the node for sender, track state, and
 	 * then pass this node (referenced) up to the 802.11
-	 * layer for its use.  We are required to pass
-	 * something so we fallback to ic_bss when this frame
-	 * is from an unknown sender.
+	 * layer for its use.
 	 */
-	if (ic->ic_opmode != IEEE80211_M_STA) {
-		ni = ieee80211_find_node(ic, wh->i_addr2);
-		if (ni == NULL)
-			ni = ieee80211_ref_node(ic->ic_bss);
-	} else
-		ni = ieee80211_ref_node(ic->ic_bss);
+	ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *) wh);
 	/*
 	 * Send frame up for processing.
 	 */
-	ieee80211_input(ifp, m, ni, rssi, rstamp);
+	ieee80211_input(ic, m, ni, rssi, rstamp);
 	/*
 	 * The frame may have caused the node to be marked for
 	 * reclamation (e.g. in response to a DEAUTH message)
 	 * so use free_node here instead of unref_node.
 	 */
-	if (ni == ic->ic_bss)
-		ieee80211_unref_node(&ni);
-	else
-		ieee80211_free_node(ic, ni);
+	ieee80211_free_node(ni);
 }
 
 static void
 wi_tx_ex_intr(struct wi_softc *sc)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	struct wi_frame frmhdr;
 	int fid;
 
@@ -1559,8 +1589,7 @@
 static void
 wi_tx_intr(struct wi_softc *sc)
 {
-	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int fid, cur;
 
 	if (sc->wi_gone)
@@ -1596,7 +1625,7 @@
 wi_info_intr(struct wi_softc *sc)
 {
 	struct ieee80211com *ic = &sc->sc_ic;
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int i, fid, len, off;
 	u_int16_t ltbuf[2];
 	u_int16_t stat;
@@ -1676,7 +1705,7 @@
 static int
 wi_write_multi(struct wi_softc *sc)
 {
-	struct ifnet *ifp = &sc->sc_ic.ic_if;
+	struct ifnet *ifp = &sc->sc_if;
 	int n;
 	struct ifmultiaddr *ifma;
 	struct wi_mcast mlist;
@@ -1827,7 +1856,7 @@
 	case WI_RID_TX_CRYPT_KEY:
 	case WI_RID_DEFLT_CRYPT_KEYS:
 	case WI_RID_TX_RATE:
-		return ieee80211_cfgget(ifp, cmd, data, cr);
+		return ieee80211_cfgget(ic, cmd, data, cr);
 
 	case WI_RID_MICROWAVE_OVEN:
 		if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) {
@@ -1881,7 +1910,7 @@
 
 	case WI_RID_READ_APS:
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		if (sc->sc_scan_timer > 0) {
 			error = EINPROGRESS;
 			break;
@@ -1918,11 +1947,11 @@
 		break;
 
 	case WI_RID_READ_CACHE:
-		return ieee80211_cfgget(ifp, cmd, data, cr);
+		return ieee80211_cfgget(ic, cmd, data, cr);
 
 	case WI_RID_SCAN_RES:		/* compatibility interface */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		if (sc->sc_scan_timer > 0) {
 			error = EINPROGRESS;
 			break;
@@ -2004,7 +2033,7 @@
 			    sc->sc_nodelen);
 			break;
 		default:
-			return ieee80211_cfgget(ifp, cmd, data, cr);
+			return ieee80211_cfgget(ic, cmd, data, cr);
 		}
 		break;
 	}
@@ -2024,6 +2053,7 @@
 	struct mbuf *m;
 	int i, len, error, mif, val;
 	struct ieee80211_rateset *rs;
+	WI_LOCK_DECL();
 
 	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
 	if (error)
@@ -2039,20 +2069,23 @@
 			error = ENOSPC;
 			break;
 		}
-		if (sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (sc->sc_enabled)
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    len);
-			if (error)
-				break;
+		if (error == 0) {
+			sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
+			memcpy(sc->sc_nodename, &wreq.wi_val[1],
+				sc->sc_nodelen);
 		}
-		sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2;
-		memcpy(sc->sc_nodename, &wreq.wi_val[1], sc->sc_nodelen);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MICROWAVE_OVEN:
 	case WI_RID_ROAMING_MODE:
 	case WI_RID_SYSTEM_SCALE:
 	case WI_RID_FRAG_THRESH:
+		/* XXX unlocked reads */
 		if (wreq.wi_type == WI_RID_MICROWAVE_OVEN &&
 		    (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0)
 			break;
@@ -2069,11 +2102,14 @@
 	case WI_RID_RTS_THRESH:
 	case WI_RID_CNFAUTHMODE:
 	case WI_RID_MAX_DATALEN:
+		WI_LOCK(sc);
 		if (sc->sc_enabled) {
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    sizeof(u_int16_t));
-			if (error)
+			if (error != 0) {
+				WI_UNLOCK(sc);
 				break;
+			}
 		}
 		switch (wreq.wi_type) {
 		case WI_RID_FRAG_THRESH:
@@ -2098,9 +2134,11 @@
 			sc->sc_max_datalen = le16toh(wreq.wi_val[0]);
 			break;
 		}
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_TX_RATE:
+		WI_LOCK(sc);
 		switch (le16toh(wreq.wi_val[0])) {
 		case 3:
 			ic->ic_fixed_rate = -1;
@@ -2112,46 +2150,56 @@
 				    / 2 == le16toh(wreq.wi_val[0]))
 					break;
 			}
-			if (i == rs->rs_nrates)
+			if (i == rs->rs_nrates) {
+				WI_UNLOCK(sc);
 				return EINVAL;
+			}
 			ic->ic_fixed_rate = i;
 		}
 		if (sc->sc_enabled)
 			error = wi_write_txrate(sc);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_SCAN_APS:
+		WI_LOCK(sc);
 		if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
 			error = wi_scan_ap(sc, 0x3fff, 0x000f);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_SCAN_REQ:		/* compatibility interface */
+		WI_LOCK(sc);
 		if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP)
 			error = wi_scan_ap(sc, wreq.wi_val[0], wreq.wi_val[1]);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MGMT_XMIT:
-		if (!sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (!sc->sc_enabled)
 			error = ENETDOWN;
-			break;
-		}
-		if (ic->ic_mgtq.ifq_len > 5) {
+		else if (ic->ic_mgtq.ifq_len > 5)
 			error = EAGAIN;
-			break;
-		}
-		/* XXX wi_len looks in u_int8_t, not in u_int16_t */
-		m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0, ifp, NULL);
-		if (m == NULL) {
-			error = ENOMEM;
-			break;
+		else {
+			/* NB: m_devget uses M_DONTWAIT so can hold the lock */
+			/* XXX wi_len looks in u_int8_t, not in u_int16_t */
+			m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0,
+				ifp, NULL);
+			if (m != NULL) {
+				IF_ENQUEUE(&ic->ic_mgtq, m);
+			} else
+				error = ENOMEM;
 		}
-		IF_ENQUEUE(&ic->ic_mgtq, m);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_MIF:
 		mif = wreq.wi_val[0];
 		val = wreq.wi_val[1];
+		WI_LOCK(sc);
 		error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0);
+		WI_UNLOCK(sc);
 		break;
 
 	case WI_RID_PROCFRAME:		/* ignore for compatibility */
@@ -2163,20 +2211,30 @@
 			error = ENOSPC;
 			break;
 		}
+		WI_LOCK(sc);
 		memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
 		ic->ic_des_esslen = le16toh(wreq.wi_val[0]) * 2;
 		memcpy(ic->ic_des_essid, &wreq.wi_val[1], ic->ic_des_esslen);
-		error = ENETRESET;
+		if (sc->sc_enabled)
+			wi_init(sc);	/* XXX no error return */
+		WI_UNLOCK(sc);
 		break;
 
 	default:
-		if (sc->sc_enabled) {
+		WI_LOCK(sc);
+		if (sc->sc_enabled)
 			error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val,
 			    len);
-			if (error)
-				break;
+		if (error == 0) {
+			/* XXX ieee80211_cfgset does a copyin */
+			error = ieee80211_cfgset(ic, cmd, data);
+			if (error == ENETRESET) {
+				if (sc->sc_enabled)
+					wi_init(sc);
+				error = 0;
+			}
 		}
-		error = ieee80211_cfgset(ifp, cmd, data);
+		WI_UNLOCK(sc);
 		break;
 	}
 	return error;
@@ -2248,16 +2306,18 @@
 
 	switch (sc->sc_firmware_type) {
 	case WI_LUCENT:
-		val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0;
+		val = (ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0;
 		error = wi_write_val(sc, WI_RID_ENCRYPTION, val);
 		if (error)
 			break;
-		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey);
+		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+			break;
+		error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_def_txkey);
 		if (error)
 			break;
 		memset(wkey, 0, sizeof(wkey));
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-			keylen = ic->ic_nw_keys[i].wk_len;
+			keylen = ic->ic_nw_keys[i].wk_keylen;
 			wkey[i].wi_keylen = htole16(keylen);
 			memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key,
 			    keylen);
@@ -2268,7 +2328,7 @@
 
 	case WI_INTERSIL:
 	case WI_SYMBOL:
-		if (ic->ic_flags & IEEE80211_F_WEPON) {
+		if (ic->ic_flags & IEEE80211_F_PRIVACY) {
 			/*
 			 * ONLY HWB3163 EVAL-CARD Firmware version
 			 * less than 0.8 variant2
@@ -2300,17 +2360,24 @@
 		error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val);
 		if (error)
 			break;
+		if ((val & PRIVACY_INVOKED) == 0)
+			break;
 		error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY,
-		    ic->ic_wep_txkey);
+		    ic->ic_def_txkey);
 		if (error)
 			break;
+		if (val & HOST_DECRYPT)
+			break;
 		/*
 		 * It seems that the firmware accept 104bit key only if
 		 * all the keys have 104bit length.  We get the length of
 		 * the transmit key and use it for all other keys.
 		 * Perhaps we should use software WEP for such situation.
 		 */
-		keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len;
+		if (ic->ic_def_txkey != IEEE80211_KEYIX_NONE)
+			keylen = ic->ic_nw_keys[ic->ic_def_txkey].wk_keylen;
+		else	/* XXX should not hapen */
+			keylen = IEEE80211_WEP_KEYLEN;
 		if (keylen > IEEE80211_WEP_KEYLEN)
 			keylen = 13;	/* 104bit keys */
 		else
@@ -2537,12 +2604,13 @@
 	for (i = 0; i < WI_TIMEOUT; i++) {
 		if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC)
 			break;
-		if (i == WI_TIMEOUT) {
-			if_printf(&sc->sc_ic.ic_if, "timeout in alloc\n");
-			return ETIMEDOUT;
-		}
 		DELAY(1);
 	}
+ 	if (i == WI_TIMEOUT) {
+ 		device_printf(sc->sc_dev, "timeout in alloc\n");
+ 		return ETIMEDOUT;
+ 	}
+
 	*idp = CSR_READ_2(sc, WI_ALLOC_FID);
 	CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC);
 	return 0;
@@ -2601,7 +2669,7 @@
 static int
 wi_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct wi_softc *sc = ifp->if_softc;
 	struct ieee80211_node *ni = ic->ic_bss;
 	int buflen;
@@ -2613,40 +2681,53 @@
 		ieee80211_state_name[ic->ic_state],
 		ieee80211_state_name[nstate]));
 
+	/*
+	 * Internal to the driver the INIT and RUN states are used
+	 * so bypass the net80211 state machine for other states.
+	 * Beware however that this requires use to net80211 state
+	 * management that otherwise would be handled for us.
+	 */
 	switch (nstate) {
 	case IEEE80211_S_INIT:
-		ic->ic_flags &= ~IEEE80211_F_SIBSS;
 		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
 		return (*sc->sc_newstate)(ic, nstate, arg);
 
+	case IEEE80211_S_SCAN:
+	case IEEE80211_S_AUTH:
+	case IEEE80211_S_ASSOC:
+		ic->ic_state = nstate;	/* NB: skip normal ieee80211 handling */
+		break;
+
 	case IEEE80211_S_RUN:
+		ni = ic->ic_bss;
 		sc->sc_flags &= ~WI_FLAGS_OUTRANGE;
 		buflen = IEEE80211_ADDR_LEN;
+		IEEE80211_ADDR_COPY(old_bssid, ni->ni_bssid);
 		wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen);
 		IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid);
 		buflen = sizeof(val);
 		wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen);
 		/* XXX validate channel */
 		ni->ni_chan = &ic->ic_channels[le16toh(val)];
+		ic->ic_ibss_chan = ni->ni_chan;
 		sc->sc_tx_th.wt_chan_freq = sc->sc_rx_th.wr_chan_freq =
 			htole16(ni->ni_chan->ic_freq);
 		sc->sc_tx_th.wt_chan_flags = sc->sc_rx_th.wr_chan_flags =
 			htole16(ni->ni_chan->ic_flags);
 
-		if (IEEE80211_ADDR_EQ(old_bssid, ni->ni_bssid))
-			sc->sc_false_syns++;
-		else
-			sc->sc_false_syns = 0;
-
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-			ni->ni_esslen = ic->ic_des_esslen;
-			memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
-			ni->ni_rates = ic->ic_sup_rates[IEEE80211_MODE_11B];
-			ni->ni_intval = ic->ic_lintval;
-			ni->ni_capinfo = IEEE80211_CAPINFO_ESS;
-			if (ic->ic_flags & IEEE80211_F_WEPON)
-				ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
-		} else {
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
+			/*
+			 * XXX hack; unceremoniously clear 
+			 * IEEE80211_F_DROPUNENC when operating with
+			 * wep enabled so we don't drop unencoded frames
+			 * at the 802.11 layer.  This is necessary because
+			 * we must strip the WEP bit from the 802.11 header
+			 * before passing frames to ieee80211_input because
+			 * the card has already stripped the WEP crypto 
+			 * header from the packet.
+			 */
+			if (ic->ic_flags & IEEE80211_F_PRIVACY)
+				ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
 			/* XXX check return value */
 			buflen = sizeof(ssid);
 			wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen);
@@ -2655,15 +2736,8 @@
 				ni->ni_esslen = IEEE80211_NWID_LEN;	/*XXX*/
 			memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen);
 		}
-		break;
-
-	case IEEE80211_S_SCAN:
-	case IEEE80211_S_AUTH:
-	case IEEE80211_S_ASSOC:
-		break;
+		return (*sc->sc_newstate)(ic, nstate, arg);
 	}
-
-	ic->ic_state = nstate;		/* NB: skip normal ieee80211 handling */
 	return 0;
 }
 
@@ -2680,8 +2754,8 @@
 		(void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0);
 		break;
 	case WI_INTERSIL:
-		val[0] = chanmask;	/* channel */
-		val[1] = txrate;	/* tx rate */
+		val[0] = htole16(chanmask);	/* channel */
+		val[1] = htole16(txrate);	/* tx rate */
 		error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val));
 		break;
 	case WI_SYMBOL:
@@ -2695,7 +2769,7 @@
 	}
 	if (error == 0) {
 		sc->sc_scan_timer = WI_SCAN_WAIT;
-		sc->sc_ic.ic_if.if_timer = 1;
+		sc->sc_if.if_timer = 1;
 		DPRINTF((&sc->sc_ic.ic_if, "wi_scan_ap: start scanning, "
 			"chamask 0x%x txrate 0x%x\n", chanmask, txrate));
 	}
diff -N -u -r src.preview/sys/dev/netif/wi/if_wi_pccard.c src/sys/dev/netif/wi/if_wi_pccard.c
--- src.preview/sys/dev/netif/wi/if_wi_pccard.c	2005-06-30 13:11:28.000000000 -0400
+++ src/sys/dev/netif/wi/if_wi_pccard.c	2005-09-23 11:24:22.000000000 -0400
@@ -137,8 +137,11 @@
 	PCMCIA_CARD(GEMTEK, WLAN, 0),
 	PCMCIA_CARD(HWN, AIRWAY80211, 0), 
 	PCMCIA_CARD(INTEL, PRO_WLAN_2011, 0),
-	PCMCIA_CARD(INTERSIL, MA401RA, 0),
-	PCMCIA_CARD(INTERSIL, DWL650, 0),
+#if 0
+	PCMCIA_CARD(INTERSIL, ISL37100P, 0),
+	PCMCIA_CARD(INTERSIL, ISL37110P, 0),
+	PCMCIA_CARD(INTERSIL, ISL37300P, 0),
+#endif
 	PCMCIA_CARD(INTERSIL2, PRISM2, 0),
 	PCMCIA_CARD(IODATA2, WCF12, 0),
 	PCMCIA_CARD(IODATA2, WNB11PCM, 0),
@@ -147,7 +150,9 @@
 	PCMCIA_CARD(MICROSOFT, MN_520, 0),
 	PCMCIA_CARD(NOKIA, C020_WLAN, 0),
 	PCMCIA_CARD(NOKIA, C110_WLAN, 0),
-	PCMCIA_CARD(PLANEX_2, GWNS11H, 0),
+#if 0
+	PCMCIA_CARD(PLANEX, GWNS11H, 0),
+#endif
 	PCMCIA_CARD(PROXIM, HARMONY, 0),
 	PCMCIA_CARD(PROXIM, RANGELANDS_8430, 0),
 	PCMCIA_CARD(SAMSUNG, SWL_2000N, 0),
@@ -164,7 +169,7 @@
 	device_t	dev;
 {
 	const struct pccard_product *pp;
-	u_int32_t fcn;
+	u_int32_t fcn = PCCARD_FUNCTION_UNSPEC;
 
 	/* Make sure we're a network driver */
 	fcn = pccard_get_function(dev);
@@ -195,12 +200,12 @@
 	if (error)
 		return (error);
 
-	wi_free(dev);
-
 	/* Make sure interrupts are disabled. */
 	CSR_WRITE_2(sc, WI_INT_EN, 0);
 	CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
 
+	wi_free(dev);
+
 	return (0);
 }
 
@@ -233,6 +238,7 @@
 		    spectrum24t_primsym, sizeof(spectrum24t_primsym),
 		    spectrum24t_secsym, sizeof(spectrum24t_secsym))) {
 			device_printf(dev, "couldn't load firmware\n");
+			return (ENXIO);
 		}
 #else
 		device_printf(dev, 
diff -N -u -r src.preview/sys/dev/netif/wi/if_wi_pci.c src/sys/dev/netif/wi/if_wi_pci.c
--- src.preview/sys/dev/netif/wi/if_wi_pci.c	2005-06-30 13:11:28.000000000 -0400
+++ src/sys/dev/netif/wi/if_wi_pci.c	2005-09-23 11:24:22.000000000 -0400
@@ -238,10 +238,9 @@
 static int
 wi_pci_suspend(device_t dev)
 {
-	struct wi_softc		*sc;
-	struct ifnet *ifp;
-	sc = device_get_softc(dev);
-	ifp = &sc->sc_if;
+	struct wi_softc	*sc = device_get_softc(dev);
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
 
 	wi_stop(ifp, 1);
 	
@@ -251,10 +250,9 @@
 static int
 wi_pci_resume(device_t dev)
 {
-	struct wi_softc *sc;
-	struct ifnet *ifp;
-	sc = device_get_softc(dev);
-	ifp = &sc->sc_if;
+	struct wi_softc	*sc = device_get_softc(dev);
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ifnet *ifp = ic->ic_ifp;
 
 	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE)
 		return (0);
diff -N -u -r src.preview/sys/dev/netif/wi/if_wivar.h src/sys/dev/netif/wi/if_wivar.h
--- src.preview/sys/dev/netif/wi/if_wivar.h	2005-06-30 11:57:27.000000000 -0400
+++ src/sys/dev/netif/wi/if_wivar.h	2005-09-23 11:24:22.000000000 -0400
@@ -60,7 +60,10 @@
 #define WI_RID_ROAMING_MODE	0xFC2D
 #define WI_RID_CUR_TX_RATE	0xFD44 /* current TX rate */
 
+#define	WI_MAX_AID		256	/* max stations for ap operation */
+
 struct wi_softc	{
+	struct arpcom		sc_arp;
 	struct ieee80211com	sc_ic;
 	int			(*sc_newstate)(struct ieee80211com *,
 					enum ieee80211_state, int);
@@ -156,6 +159,7 @@
 		u_int16_t               wi_confbits_param0;
 	} wi_debug;
 
+	struct timeval		sc_last_syn;
 	int			sc_false_syns;
 
 	u_int16_t		sc_txbuf[IEEE80211_MAX_LEN/2];
@@ -171,7 +175,7 @@
 	} u_rx_rt;
 	int			sc_rx_th_len;
 };
-#define	sc_if			sc_ic.ic_if
+#define	sc_if			sc_arp.ac_if
 #define	sc_tx_th		u_tx_rt.th
 #define	sc_rx_th		u_rx_rt.th
 
diff -N -u -r src.preview/sys/dev/usbmisc/umass/umass.c src/sys/dev/usbmisc/umass/umass.c
--- src.preview/sys/dev/usbmisc/umass/umass.c	2004-12-18 01:22:48.000000000 -0500
+++ src/sys/dev/usbmisc/umass/umass.c	2005-09-23 11:24:22.000000000 -0400
@@ -407,6 +407,10 @@
 	  UMASS_PROTO_ATAPI | UMASS_PROTO_CBI_I,
 	  FORCE_SHORT_INQUIRY
 	},
+	{ USB_VENDOR_IRIVER, USB_PRODUCT_IRIVER_IFP_1XX, RID_WILDCARD,
+	  UMASS_PROTO_SCSI | UMASS_PROTO_BBB,
+	  IGNORE_RESIDUE | NO_GETMAXLUN | RS_NO_CLEAR_UA
+	},
 	{ VID_EOT, PID_EOT, RID_EOT, 0, 0 }
 };
 
diff -N -u -r src.preview/sys/i386/conf/AB-MOBILE-FAST_IPSEC src/sys/i386/conf/AB-MOBILE-FAST_IPSEC
--- src.preview/sys/i386/conf/AB-MOBILE-FAST_IPSEC	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/i386/conf/AB-MOBILE-FAST_IPSEC	2005-09-23 11:59:42.000000000 -0400
@@ -0,0 +1,194 @@
+#
+# GENERIC -- Generic kernel configuration file for DragonFly/i386
+#
+# Check the LINT configuration file in sys/i386/conf, for an
+# exhaustive list of options.
+#
+# $DragonFly: src/sys/i386/conf/GENERIC,v 1.20 2004/09/26 01:52:48 dillon Exp $
+
+machine		i386
+cpu		I686_CPU
+ident		AB-MOBILE
+maxusers	0
+
+makeoptions	DEBUG=-gstabs+		#Build kernel with gdb(1) debug symbols
+
+options		INCLUDE_CONFIG_FILE
+
+options 	INET			#InterNETworking
+options 	INET6			#IPv6 communications protocols
+options 	FAST_IPSEC		#new IPsec
+
+#options 	IPSEC			#IP security
+#options 	IPSEC_ESP		#IP security (crypto; define w/ IPSEC)
+#options 	IPSEC_DEBUG		#debug for IP security
+
+options 	FFS			#Berkeley Fast Filesystem
+options 	FFS_ROOT		#FFS usable as root device [keep this!]
+options 	SOFTUPDATES		#Enable FFS soft updates support
+options 	UFS_DIRHASH		#Improve performance on big directories
+options 	MFS			#Memory Filesystem
+options 	MD_ROOT			#MD is a potential root device
+options 	NFS			#Network Filesystem
+options 	NFS_ROOT		#NFS usable as root device, NFS required
+options 	MSDOSFS			#MSDOS Filesystem
+options 	CD9660			#ISO 9660 Filesystem
+options 	CD9660_ROOT		#CD-ROM usable as root, CD9660 required
+options 	PROCFS			#Process filesystem
+options 	COMPAT_43		#Compatible with BSD 4.3 [KEEP THIS!]
+options 	COMPAT_LINUX		#Compatible with BSD 4.3 [KEEP THIS!]
+options         COMPAT_DF12             #Compatible with DragonFly 1.2 and earlier
+options 	DEBUG_LINUX		#Compatible with BSD 4.3 [KEEP THIS!]
+options 	UCONSOLE		#Allow users to grab the console
+options 	USERCONFIG		#boot -c editor
+options 	VISUAL_USERCONFIG	#visual boot -c editor
+options 	KTRACE			#ktrace(1) support
+options 	SYSVSHM			#SYSV-style shared memory
+options 	SYSVMSG			#SYSV-style message queues
+options 	SYSVSEM			#SYSV-style semaphores
+options 	P1003_1B		#Posix P1003_1B real-time extensions
+options 	_KPOSIX_PRIORITY_SCHEDULING
+options 	ICMP_BANDLIM		#Rate limit bad replies
+options 	KBD_INSTALL_CDEV	# install a CDEV entry in /dev
+
+# To make an SMP kernel, the next two are needed
+# options 	SMP			# Symmetric MultiProcessor Kernel
+# options 	APIC_IO			# Symmetric (APIC) I/O
+
+# Debugging for Development
+options 	DDB
+options 	DDB_TRACE
+options 	INVARIANTS
+options 	INVARIANT_SUPPORT
+
+device		isa
+device		eisa
+device		pci
+
+# Floppy drives
+#device		fdc0	at isa? port IO_FD1 irq 6 drq 2
+#device		fd0	at fdc0 drive 0
+#device		fd1	at fdc0 drive 1
+#
+# If you have a Toshiba Libretto with its Y-E Data PCMCIA floppy,
+# don't use the above line for fdc0 but the following one:
+#device		fdc0
+
+# ATA and ATAPI devices
+device		ata0	at isa? port IO_WD1 irq 14
+device		ata1	at isa? port IO_WD2 irq 15
+device		ata
+device		atadisk			# ATA disk drives
+device		atapicd			# ATAPI CDROM drives
+device		atapifd			# ATAPI floppy drives
+device		atapist			# ATAPI tape drives
+device		atapicam		# Emulate ATAPI devices as SCSI via CAM
+options 	ATA_STATIC_ID		#Static device numbering
+
+# SCSI peripherals
+device		scbus		# SCSI bus (required)
+device		da		# Direct Access (disks)
+device		sa		# Sequential Access (tape etc)
+device		cd		# CD
+device		pass		# Passthrough device (direct SCSI access)
+
+# atkbdc0 controls both the keyboard and the PS/2 mouse
+device		atkbdc0	at isa? port IO_KBD
+device		atkbd0	at atkbdc? irq 1 flags 0x1
+device		psm0	at atkbdc? irq 12
+
+device		vga0	at isa?
+
+# splash screen/screen saver
+pseudo-device	splash
+
+# syscons is the default console driver, resembling an SCO console
+device		sc0	at isa? flags 0x100
+
+# Enable this and PCVT_FREEBSD for pcvt vt220 compatible console driver
+#device		vt0	at isa?
+#options 	XSERVER			# support for X server on a vt console
+#options 	FAT_CURSOR		# start with block cursor
+# If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines
+#options 	PCVT_SCANSET=2		# IBM keyboards are non-std
+
+#device		agp		# support several AGP chipsets
+
+# Floating point support - do not disable.
+device		npx0	at nexus? port IO_NPX irq 13
+
+# Power management support (see LINT for more options)
+device		apm0	at nexus? disable flags 0x20 # Advanced Power Management
+
+# PCCARD (PCMCIA) support
+device		pccard
+device		cardbus
+device		cbb
+
+
+# PCI Ethernet NICs that use the common MII bus controller code.
+# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
+device		miibus		# MII bus support
+device		bfe		# Broadcom BCM440x 10/100 Ethernet
+
+# Generic 802.11 stack, used by wi
+device		wlan
+options         WLCACHE         # enables the signal-strength cache
+options         WLDEBUG         # enables verbose debugging output
+
+# Pseudo devices - the number indicates how many units to allocate.
+pseudo-device	loop		# Network loopback
+pseudo-device	ether		# Ethernet support
+pseudo-device	sl	1	# Kernel SLIP
+pseudo-device	ppp	2	# Kernel PPP
+pseudo-device	tun		# Packet tunnel.
+pseudo-device	pty		# Pseudo-ttys (telnet etc)
+pseudo-device	md		# Memory "disks"
+pseudo-device	gif		# IPv6 and IPv4 tunneling
+pseudo-device	faith	1	# IPv6-to-IPv4 relaying (translation)
+
+# The `bpf' pseudo-device enables the Berkeley Packet Filter.
+# Be aware of the administrative consequences of enabling this!
+pseudo-device	bpf		#Berkeley packet filter
+
+pseudo-device	crypto		# core crypto support, used by wlan
+pseudo-device	cryptodev	# /dev/crypto for access to h/w
+
+# USB support
+# USB support
+#device		ehci		# UHCI PCI->USB interface
+device		uhci		# UHCI PCI->USB interface
+#device		ohci		# OHCI PCI->USB interface
+device		usb		# USB Bus (required)
+device		uplcom		# Generic
+device		ugen		# Generic
+device		ukbd		# Keyboard
+device		umass		# Disks/Mass storage - Requires scbus and da
+device		ums		# Mouse
+options		USB_DEBUG
+
+# FireWire support
+device		firewire	# FireWire bus code
+device		sbp		# SCSI over FireWire (Requires scbus and da)
+device		fwe		# Ethernet over FireWire (non-standard!)
+device          dcons                   # dumb console driver
+device          dcons_crom              # FireWire attachment
+options         ALT_BREAK_TO_DEBUGGER
+
+device          pcm
+device          snd
+
+device          pf
+device          pfsync
+device          pflog
+
+options         ALTQ            #alternate queueing
+options         ALTQ_CBQ        #class based queueing
+options         ALTQ_RED        #random early detection
+options         ALTQ_RIO        #triple red for diffserv (needs RED)
+options         ALTQ_HFSC       #hierarchical fair service curve
+options         ALTQ_PRIQ       #priority queue
+#options        ALTQ_NOPCC      #don't use processor cycle counter
+options         ALTQ_DEBUG      #for debugging
+
+
diff -N -u -r src.preview/sys/i386/conf/WRAP-AUTO src/sys/i386/conf/WRAP-AUTO
--- src.preview/sys/i386/conf/WRAP-AUTO	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/i386/conf/WRAP-AUTO	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,129 @@
+#
+# GENERIC -- Generic kernel configuration file for DragonFly/i386
+#
+# Check the LINT configuration file in sys/i386/conf, for an
+# exhaustive list of options.
+#
+# $DragonFly: src/sys/i386/conf/GENERIC,v 1.20 2004/09/26 01:52:48 dillon Exp $
+
+machine		i386
+cpu		I586_CPU
+ident		WRAP-AUTO
+maxusers	0
+
+makeoptions	DEBUG=-g		#Build kernel with gdb(1) debug symbols
+
+options		INCLUDE_CONFIG_FILE
+
+options 	INET			#InterNETworking
+options 	INET6			#IPv6 communications protocols
+options 	FAST_IPSEC		#new IPsec
+
+options         NO_F00F_HACK
+
+options 	FFS			#Berkeley Fast Filesystem
+options 	FFS_ROOT		#FFS usable as root device [keep this!]
+
+options 	SOFTUPDATES		#Enable FFS soft updates support
+options 	UFS_DIRHASH		#Improve performance on big directories
+
+options 	MFS			#Memory Filesystem
+options 	MD_ROOT			#MD is a potential root device
+options 	NFS			#Network Filesystem
+options 	NFS_ROOT		#NFS usable as root device, NFS required
+options 	MSDOSFS			#MSDOS Filesystem
+options 	CD9660			#ISO 9660 Filesystem
+options 	CD9660_ROOT		#CD-ROM usable as root, CD9660 required
+options 	PROCFS			#Process filesystem
+options 	COMPAT_43		#Compatible with BSD 4.3 [KEEP THIS!]
+options 	COMPAT_LINUX		#Compatible with BSD 4.3 [KEEP THIS!]
+options 	DEBUG_LINUX		#Compatible with BSD 4.3 [KEEP THIS!]
+options 	KTRACE			#ktrace(1) support
+options 	SYSVSHM			#SYSV-style shared memory
+options 	SYSVMSG			#SYSV-style message queues
+options 	SYSVSEM			#SYSV-style semaphores
+options 	P1003_1B		#Posix P1003_1B real-time extensions
+options 	_KPOSIX_PRIORITY_SCHEDULING
+options 	ICMP_BANDLIM		#Rate limit bad replies
+
+options		NO_SWAPPING
+
+# Debugging for Development
+options 	DDB
+options 	DDB_TRACE
+options         DDB_UNATTENDED
+options 	INVARIANTS
+options 	INVARIANT_SUPPORT
+
+device		isa
+device		pci
+
+# ATA and ATAPI devices
+device		ata0	at isa? port IO_WD1 irq 14
+device		ata1	at isa? port IO_WD2 irq 15
+device		ata
+device		atadisk			# ATA disk drives
+device		atapicd			# ATAPI CDROM drives
+device		atapifd			# ATAPI floppy drives
+device		atapist			# ATAPI tape drives
+device		atapicam		# Emulate ATAPI devices as SCSI via CAM
+options 	ATA_STATIC_ID		#Static device numbering
+
+# SCSI peripherals
+device		scbus		# SCSI bus (required)
+device		da		# Direct Access (disks)
+device		sa		# Sequential Access (tape etc)
+device		cd		# CD
+device		pass		# Passthrough device (direct SCSI access)
+
+device         sio0    at isa? port IO_COM1 flags 0x30 irq 4
+
+# Floating point support - do not disable.
+device		npx0	at nexus? port IO_NPX irq 13
+
+# Power management support (see LINT for more options)
+device		apm0	at nexus? disable flags 0x20 # Advanced Power Management
+
+# PCI Ethernet NICs that use the common MII bus controller code.
+# NOTE: Be sure to keep the 'device miibus' line in order to use these NICs!
+device		miibus		# MII bus support
+device		sis
+
+# Generic 802.11 stack, used by wi
+device		wlan
+options         WLCACHE         # enables the signal-strength cache
+options         WLDEBUG         # enables verbose debugging output
+
+# Pseudo devices - the number indicates how many units to allocate.
+pseudo-device	loop		# Network loopback
+pseudo-device	ether		# Ethernet support
+pseudo-device	sl	1	# Kernel SLIP
+pseudo-device	ppp	1	# Kernel PPP
+pseudo-device	tun		# Packet tunnel.
+pseudo-device	pty		# Pseudo-ttys (telnet etc)
+pseudo-device	md		# Memory "disks"
+pseudo-device	gif		# IPv6 and IPv4 tunneling
+pseudo-device	faith	1	# IPv6-to-IPv4 relaying (translation)
+
+# The `bpf' pseudo-device enables the Berkeley Packet Filter.
+# Be aware of the administrative consequences of enabling this!
+pseudo-device	bpf		#Berkeley packet filter
+
+pseudo-device	crypto		# core crypto support, used by wlan
+pseudo-device	cryptodev	# /dev/crypto for access to h/w
+
+device          pf
+device          pfsync
+device          pflog
+
+options         ALTQ            #alternate queueing
+options         ALTQ_CBQ        #class based queueing
+options         ALTQ_RED        #random early detection
+options         ALTQ_RIO        #triple red for diffserv (needs RED)
+options         ALTQ_HFSC       #hierarchical fair service curve
+options         ALTQ_PRIQ       #priority queue
+#options        ALTQ_NOPCC      #don't use processor cycle counter
+options         ALTQ_DEBUG      #for debugging
+
+options         BROKEN_KEYBOARD_RESET
+
diff -N -u -r src.preview/sys/i386/i386/identcpu.c src/sys/i386/i386/identcpu.c
--- src.preview/sys/i386/i386/identcpu.c	2004-04-29 15:43:35.000000000 -0400
+++ src/sys/i386/i386/identcpu.c	2005-09-23 11:24:22.000000000 -0400
@@ -151,7 +151,8 @@
 	    (strcmp(cpu_vendor, "GenuineIntel") == 0 ||
 	    strcmp(cpu_vendor, "AuthenticAMD") == 0 ||
 	    strcmp(cpu_vendor, "GenuineTMx86") == 0 ||
-	    strcmp(cpu_vendor, "TransmetaCPU") == 0)) {
+	    strcmp(cpu_vendor, "TransmetaCPU") == 0 ||
+	    strcmp(cpu_vendor, "Geode by NSC") == 0)) {
 		do_cpuid(0x80000000, regs);
 		if (regs[0] >= 0x80000000) {
 			cpu_exthigh = regs[0];
@@ -532,6 +533,17 @@
 		}
 	} else if (strcmp(cpu_vendor, "IBM") == 0) {
 		strcpy(cpu_model, "Blue Lightning CPU");
+	} else if  (strcmp(cpu_vendor, "Geode by NSC") == 0) {
+		switch (cpu_id & 0xfff) {
+			case 0x540:
+				strcpy(cpu_model, "Geode SC1100");
+				cpu = CPU_GEODE1100;
+				tsc_is_broken = 1;
+				break;
+			default:
+				strcpy(cpu_model, "Geode/NSC unknown");
+			break;
+		}
 	}
 
 	/*
@@ -592,6 +604,7 @@
 	    strcmp(cpu_vendor, "AuthenticAMD") == 0 ||
 	    strcmp(cpu_vendor, "RiseRiseRise") == 0 ||
 	    strcmp(cpu_vendor, "CentaurHauls") == 0 ||
+	    strcmp(cpu_vendor, "Geode by NSC") == 0 ||
 		((strcmp(cpu_vendor, "CyrixInstead") == 0) &&
 		 ((cpu_id & 0xf00) > 0x500))) {
 		printf("  Stepping = %u", cpu_id & 0xf);
diff -N -u -r src.preview/sys/i386/i386/vm_machdep.c src/sys/i386/i386/vm_machdep.c
--- src.preview/sys/i386/i386/vm_machdep.c	2005-06-16 17:12:44.000000000 -0400
+++ src/sys/i386/i386/vm_machdep.c	2005-09-23 11:24:22.000000000 -0400
@@ -60,6 +60,7 @@
 
 #include <machine/clock.h>
 #include <machine/cpu.h>
+#include <machine/cputypes.h>
 #include <machine/md_var.h>
 #ifdef SMP
 #include <machine/smp.h>
@@ -461,6 +462,18 @@
 static void
 cpu_reset_real()
 {
+
+	if ( cpu == CPU_GEODE1100 ) {
+		int i = 50;
+		while(i--) {
+			outl(0xcf8, 0x80009044ul);
+			outb(0xcfc, 0xf);
+			DELAY(100000);	/* wait 0.1 sec */
+		}
+		printf("GEODE reset failed, attempting CPU shutdown\n");
+		DELAY(1000000);	/* wait 1 sec for printf to complete */
+	}
+
 	/*
 	 * Attempt to do a CPU reset via the keyboard controller,
 	 * do not turn of the GateA20, as any machine that fails
diff -N -u -r src.preview/sys/i386/include/cputypes.h src/sys/i386/include/cputypes.h
--- src.preview/sys/i386/include/cputypes.h	2003-06-17 00:28:35.000000000 -0400
+++ src/sys/i386/include/cputypes.h	2005-09-23 11:24:22.000000000 -0400
@@ -60,6 +60,7 @@
 #define	CPU_PII		14	/* Intel Pentium II */
 #define	CPU_PIII	15	/* Intel Pentium III */
 #define	CPU_P4		16	/* Intel Pentium 4 */
+#define	CPU_GEODE1100	17	/* NS Geode SC1100 */
 
 #ifndef LOCORE
 struct cpu_nameclass {
diff -N -u -r src.preview/sys/net/altq/altq_subr.c src/sys/net/altq/altq_subr.c
--- src.preview/sys/net/altq/altq_subr.c	2005-06-03 14:20:36.000000000 -0400
+++ src/sys/net/altq/altq_subr.c	2005-09-23 11:24:22.000000000 -0400
@@ -151,9 +151,10 @@
 		return 0;
 
 	crit_enter();
+
 	ifq_purge(ifq);
 	KKASSERT(ifq->ifq_len == 0);
-	ifq->altq_flags |= ALTQF_ENABLED;
 	if (ifq->altq_clfier != NULL)
 		ifq->altq_flags |= ALTQF_CLASSIFY;
 	crit_exit();
diff -N -u -r src.preview/sys/net/altq/if_altq.h src/sys/net/altq/if_altq.h
--- src.preview/sys/net/altq/if_altq.h	2005-02-11 17:25:57.000000000 -0500
+++ src/sys/net/altq/if_altq.h	2005-09-23 11:24:22.000000000 -0400
@@ -41,6 +41,7 @@
 	int	ifq_len;
 	int	ifq_maxlen;
 	int	ifq_drops;
+	struct  lwkt_token ifq_mtx;
 
 	/* alternate queueing related fields */
 	int	altq_type;		/* discipline type */
diff -N -u -r src.preview/sys/net/ethernet.h src/sys/net/ethernet.h
--- src.preview/sys/net/ethernet.h	2005-02-11 17:25:57.000000000 -0500
+++ src/sys/net/ethernet.h	2005-09-23 11:24:22.000000000 -0400
@@ -326,6 +326,7 @@
 #define	ETHERTYPE_PPPOEDISC	0x8863	/* PPP Over Ethernet Discovery Stage */
 #define	ETHERTYPE_PPPOE		0x8864	/* PPP Over Ethernet Session Stage */
 #define	ETHERTYPE_LANPROBE	0x8888	/* HP LanProbe test? */
+#define	ETHERTYPE_PAE		0x888e	/* EAPOL PAE/802.1x */
 #define	ETHERTYPE_LOOPBACK	0x9000	/* Loopback: used to test interfaces */
 #define	ETHERTYPE_LBACK		ETHERTYPE_LOOPBACK	/* DEC MOP loopback */
 #define	ETHERTYPE_XNSSM		0x9001	/* 3Com (Formerly Bridge Communications), XNS Systems Management */
@@ -348,6 +349,7 @@
 
 #define	ETHERMTU	(ETHER_MAX_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
 #define	ETHERMIN	(ETHER_MIN_LEN-ETHER_HDR_LEN-ETHER_CRC_LEN)
+#define	ETHERMTU_JUMBO	(ETHER_MAX_LEN_JUMBO - ETHER_HDR_LEN - ETHER_CRC_LEN)
 
 #ifdef _KERNEL
 
diff -N -u -r src.preview/sys/net/if.h src/sys/net/if.h
--- src.preview/sys/net/if.h	2005-05-25 10:59:05.000000000 -0400
+++ src/sys/net/if.h	2005-09-23 11:24:22.000000000 -0400
@@ -142,6 +142,9 @@
 #define	IFF_MULTICAST	0x8000		/* supports multicast */
 #define	IFF_POLLING	0x10000		/* Interface is in polling mode. */
 #define	IFF_PPROMISC	0x20000		/* user-requested promisc mode */
+#define IFF_MONITOR     0x40000         /* user-requested monitor mode */
+#define IFF_STATICARP   0x80000         /* static ARP */
+#define IFF_NEEDSGIANT  0x100000        /* hold Giant over if_start calls */
 
 /* flags set internally only: */
 #define	IFF_CANTCHANGE \
diff -N -u -r src.preview/sys/net/if_media.h src/sys/net/if_media.h
--- src.preview/sys/net/if_media.h	2005-02-14 11:21:34.000000000 -0500
+++ src/sys/net/if_media.h	2005-09-23 11:24:22.000000000 -0400
@@ -205,6 +205,24 @@
 #define	IFM_IEEE80211_FH	0x00040000	/* 2Ghz, GFSK mode */
 
 /*
+ * ATM
+ */
+#define IFM_ATM 		0x000000a0
+#define IFM_ATM_UNKNOWN		3
+#define IFM_ATM_UTP_25		4
+#define IFM_ATM_TAXI_100	5
+#define IFM_ATM_TAXI_140	6
+#define IFM_ATM_MM_155		7
+#define IFM_ATM_SM_155		8
+#define IFM_ATM_UTP_155		9
+#define IFM_ATM_MM_622		10
+#define IFM_ATM_SM_622		11
+#define IFM_ATM_VIRTUAL		12
+#define IFM_ATM_SDH		0x00000100      /* SDH instead of SONET */
+#define IFM_ATM_NOSCRAMB	0x00000200      /* no scrambling */
+#define IFM_ATM_UNASSIGNED	0x00000400      /* unassigned cells */
+
+/*
  * Shared media sub-types
  */
 #define	IFM_AUTO	0		/* Autoselect best media */
@@ -429,6 +447,56 @@
 	{ 0, NULL },							\
 }
 
+#define IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS {                       \
+        { IFM_AUTO, "autoselect" },                                     \
+        { IFM_IEEE80211_11A, "11a" },                                   \
+        { IFM_IEEE80211_11B, "11b" },                                   \
+        { IFM_IEEE80211_11G, "11g" },                                   \
+        { IFM_IEEE80211_FH, "fh" },                                     \
+        { 0, NULL },                                                    \
+}
+
+#define IFM_SUBTYPE_IEEE80211_MODE_ALIASES {                            \
+        { IFM_AUTO, "auto" },                                           \
+        { 0, NULL },                                                    \
+}
+
+# define IFM_SUBTYPE_ATM_DESCRIPTIONS {                                 \
+        { IFM_ATM_UNKNOWN,      "Unknown" },                            \
+        { IFM_ATM_UTP_25,       "UTP/25.6MBit" },                       \
+        { IFM_ATM_TAXI_100,     "Taxi/100MBit" },                       \
+        { IFM_ATM_TAXI_140,     "Taxi/140MBit" },                       \
+        { IFM_ATM_MM_155,       "Multi-mode/155MBit" },                 \
+        { IFM_ATM_SM_155,       "Single-mode/155MBit" },                \
+        { IFM_ATM_UTP_155,      "UTP/155MBit" },                        \
+        { IFM_ATM_MM_622,       "Multi-mode/622MBit" },                 \
+        { IFM_ATM_SM_622,       "Single-mode/622MBit" },                \
+        { IFM_ATM_VIRTUAL,      "Virtual" },                            \
+        { 0, NULL },                                                    \
+}
+
+# define IFM_SUBTYPE_ATM_ALIASES {                                      \
+        { IFM_ATM_UNKNOWN,      "UNKNOWN" },                            \
+        { IFM_ATM_UTP_25,       "UTP-25" },                             \
+        { IFM_ATM_TAXI_100,     "TAXI-100" },                           \
+        { IFM_ATM_TAXI_140,     "TAXI-140" },                           \
+        { IFM_ATM_MM_155,       "MM-155" },                             \
+        { IFM_ATM_SM_155,       "SM-155" },                             \
+        { IFM_ATM_UTP_155,      "UTP-155" },                            \
+        { IFM_ATM_MM_622,       "MM-622" },                             \
+        { IFM_ATM_SM_622,       "SM-622" },                             \
+        { IFM_ATM_VIRTUAL,      "VIRTUAL" },                            \
+        { 0, NULL },                                                    \
+}
+
+#define IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS {                           \
+         { IFM_ATM_SDH, "SDH" },                                         \
+         { IFM_ATM_NOSCRAMB, "Noscramb" },                               \
+         { IFM_ATM_UNASSIGNED, "Unassigned" },                           \
+         { 0, NULL },                                                    \
+}
+
+
 #define	IFM_SUBTYPE_SHARED_DESCRIPTIONS {				\
 	{ IFM_AUTO,	"autoselect" },					\
 	{ IFM_MANUAL,	"manual" },					\
diff -N -u -r src.preview/sys/net/if_var.h src/sys/net/if_var.h
--- src.preview/sys/net/if_var.h	2005-06-15 15:29:30.000000000 -0400
+++ src/sys/net/if_var.h	2005-09-23 11:24:22.000000000 -0400
@@ -77,6 +77,8 @@
 struct	ucred;
 
 #include <sys/queue.h>		/* get TAILQ macros */
+#include <machine/thread.h>
+#include <sys/thread.h>
 
 #include <net/altq/if_altq.h>
 
@@ -103,6 +105,7 @@
 	int	ifq_len;
 	int	ifq_maxlen;
 	int	ifq_drops;
+	struct  lwkt_token ifq_mtx;
 };
 
 #ifdef DEVICE_POLLING
diff -N -u -r src.preview/sys/net/ifq_var.h src/sys/net/ifq_var.h
--- src.preview/sys/net/ifq_var.h	2005-06-15 15:29:30.000000000 -0400
+++ src/sys/net/ifq_var.h	2005-09-23 11:24:22.000000000 -0400
@@ -49,13 +49,13 @@
 }
 #else
 static __inline int
-ifq_is_enabled(struct ifaltq *_ifq)
+ifq_is_enabled(struct ifaltq *_ifq __unused)
 {
 	return(0);
 }
 
 static __inline int
-ifq_is_attached(struct ifaltq *_ifq)
+ifq_is_attached(struct ifaltq *_ifq __unused)
 {
 	return(0);
 }
diff -N -u -r src.preview/sys/net/route.h src/sys/net/route.h
--- src.preview/sys/net/route.h	2005-05-01 00:05:35.000000000 -0400
+++ src/sys/net/route.h	2005-09-27 15:26:17.000000000 -0400
@@ -262,6 +262,7 @@
 #define	RTM_NEWMADDR	0xf	/* mcast group membership being added to if */
 #define	RTM_DELMADDR	0x10	/* mcast group membership being deleted */
 #define	RTM_IFANNOUNCE	0x11	/* iface arrival/departure */
+#define	RTM_IEEE80211   0x12    /* IEEE80211 wireless event */
 
 /*
  * Bitmask values for rtm_inits and rmx_locks.
@@ -308,6 +309,11 @@
 	struct ifnet	*rti_ifp;
 };
 
+#define SA_SIZE(sa)						\
+	(  (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ?	\
+	sizeof(long)	:					\
+	1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+
 #ifdef _KERNEL
 
 #define	rti_dst		rti_info[RTAX_DST]
@@ -327,6 +333,7 @@
 void	 route_init (void);
 void	 rt_dstmsg(int type, struct sockaddr *dst, int error);
 int	 rt_getifa (struct rt_addrinfo *);
+void     rt_ieee80211msg(struct ifnet *, int, void *, size_t);
 void	 rt_ifannouncemsg (struct ifnet *, int);
 void	 rt_ifmsg (struct ifnet *);
 int	 rt_llroute (struct sockaddr *dst, struct rtentry *rt0,
diff -N -u -r src.preview/sys/net/rtsock.c src/sys/net/rtsock.c
--- src.preview/sys/net/rtsock.c	2005-07-15 13:54:47.000000000 -0400
+++ src/sys/net/rtsock.c	2005-09-23 11:24:22.000000000 -0400
@@ -694,6 +694,7 @@
 	case RTM_IFINFO:
 		return sizeof(struct if_msghdr);
 	case RTM_IFANNOUNCE:
+        case RTM_IEEE80211:
 		return sizeof(struct if_announcemsghdr);
 	default:
 		return sizeof(struct rt_msghdr);
@@ -1007,6 +1008,59 @@
 
 /*
  * This is called to generate routing socket messages indicating
+ * IEEE80211 wireless events.
+ * XXX we piggyback on the RTM_IFANNOUNCE msg format in a clumsy way.
+ */
+void
+rt_ieee80211msg(struct ifnet *ifp, int what, void *data, size_t data_len)
+{
+       struct mbuf *m;
+       struct rt_addrinfo info;
+
+       struct rt_addrinfo addrinfo;
+       struct if_announcemsghdr *ifan;
+
+        if (route_cb.any_count == 0)
+                return;
+
+        bzero(&addrinfo, sizeof addrinfo);
+        m = rt_msg_mbuf(RTM_IEEE80211, &addrinfo);
+        if (m == NULL)
+                return;
+
+        ifan = mtod(m, struct if_announcemsghdr *);
+        ifan->ifan_index = ifp->if_index;
+        strlcpy(ifan->ifan_name, ifp->if_xname, sizeof ifan->ifan_name);
+        ifan->ifan_what = what;
+
+	/*
+	 * Append the ieee80211 data.  Try to stick it in the
+	 * mbuf containing the ifannounce msg; otherwise allocate
+	 * a new mbuf and append.
+	 *
+	 * NB: we assume m is a single mbuf.
+	 */
+	if (data_len > M_TRAILINGSPACE(m)) {
+		struct mbuf *n = m_get(M_NOWAIT, MT_DATA);
+		if (n == NULL) {
+			m_freem(m);
+			return;
+		}
+		bcopy(data, mtod(n, void *), data_len);
+		n->m_len = data_len;
+		m->m_next = n;
+	} else if (data_len > 0) {
+		bcopy(data, mtod(m, u_int8_t *) + m->m_len, data_len);
+		m->m_len += data_len;
+	}
+	if (m->m_flags & M_PKTHDR)
+		m->m_pkthdr.len += data_len;
+	mtod(m, struct if_announcemsghdr *)->ifan_msglen += data_len;
+	rts_input(m, 0);
+}
+
+/*
+ * This is called to generate routing socket messages indicating
  * network interface arrival and departure.
  */
 void
diff -N -u -r src.preview/sys/netproto/802_11/_ieee80211.h src/sys/netproto/802_11/_ieee80211.h
--- src.preview/sys/netproto/802_11/_ieee80211.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/_ieee80211.h	2005-09-23 11:44:24.000000000 -0400
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211__IEEE80211_H_
+#define _NET80211__IEEE80211_H_
+
+enum ieee80211_phytype {
+	IEEE80211_T_DS,			/* direct sequence spread spectrum */
+	IEEE80211_T_FH,			/* frequency hopping */
+	IEEE80211_T_OFDM,		/* frequency division multiplexing */
+	IEEE80211_T_TURBO,		/* high rate OFDM, aka turbo mode */
+};
+#define	IEEE80211_T_CCK	IEEE80211_T_DS	/* more common nomenclature */
+
+/* XXX not really a mode; there are really multiple PHY's */
+enum ieee80211_phymode {
+	IEEE80211_MODE_AUTO	= 0,	/* autoselect */
+	IEEE80211_MODE_11A	= 1,	/* 5GHz, OFDM */
+	IEEE80211_MODE_11B	= 2,	/* 2GHz, CCK */
+	IEEE80211_MODE_11G	= 3,	/* 2GHz, OFDM */
+	IEEE80211_MODE_FH	= 4,	/* 2GHz, GFSK */
+	IEEE80211_MODE_TURBO_A	= 5,	/* 5GHz, OFDM, 2x clock */
+	IEEE80211_MODE_TURBO_G	= 6,	/* 2GHz, OFDM, 2x clock */
+};
+#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_TURBO_G+1)
+
+enum ieee80211_opmode {
+	IEEE80211_M_STA		= 1,	/* infrastructure station */
+	IEEE80211_M_IBSS 	= 0,	/* IBSS (adhoc) station */
+	IEEE80211_M_AHDEMO	= 3,	/* Old lucent compatible adhoc demo */
+	IEEE80211_M_HOSTAP	= 6,	/* Software Access Point */
+	IEEE80211_M_MONITOR	= 8	/* Monitor mode */
+};
+
+/*
+ * 802.11g protection mode.
+ */
+enum ieee80211_protmode {
+	IEEE80211_PROT_NONE	= 0,	/* no protection */
+	IEEE80211_PROT_CTSONLY	= 1,	/* CTS to self */
+	IEEE80211_PROT_RTSCTS	= 2,	/* RTS-CTS */
+};
+
+/*
+ * Authentication mode.
+ */
+enum ieee80211_authmode {
+	IEEE80211_AUTH_NONE	= 0,
+	IEEE80211_AUTH_OPEN	= 1,		/* open */
+	IEEE80211_AUTH_SHARED	= 2,		/* shared-key */
+	IEEE80211_AUTH_8021X	= 3,		/* 802.1x */
+	IEEE80211_AUTH_AUTO	= 4,		/* auto-select/accept */
+	/* NB: these are used only for ioctls */
+	IEEE80211_AUTH_WPA	= 5,		/* WPA/RSN w/ 802.1x/PSK */
+};
+
+/*
+ * Roaming mode is effectively who controls the operation
+ * of the 802.11 state machine when operating as a station.
+ * State transitions are controlled either by the driver
+ * (typically when management frames are processed by the
+ * hardware/firmware), the host (auto/normal operation of
+ * the 802.11 layer), or explicitly through ioctl requests
+ * when applications like wpa_supplicant want control.
+ */
+enum ieee80211_roamingmode {
+	IEEE80211_ROAMING_DEVICE= 0,	/* driver/hardware control */
+	IEEE80211_ROAMING_AUTO	= 1,	/* 802.11 layer control */
+	IEEE80211_ROAMING_MANUAL= 2,	/* application control */
+};
+
+/*
+ * Channels are specified by frequency and attributes.
+ */
+struct ieee80211_channel {
+	u_int16_t	ic_freq;	/* setting in Mhz */
+	u_int16_t	ic_flags;	/* see below */
+};
+
+#define	IEEE80211_CHAN_MAX	255
+#define	IEEE80211_CHAN_BYTES	32	/* howmany(IEEE80211_CHAN_MAX, NBBY) */
+#define	IEEE80211_CHAN_ANY	0xffff	/* token for ``any channel'' */
+#define	IEEE80211_CHAN_ANYC \
+	((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
+
+/* bits 0-3 are for private use by drivers */
+/* channel attributes */
+#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
+#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
+#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+
+/*
+ * Useful combinations of channel characteristics.
+ */
+#define	IEEE80211_CHAN_FHSS \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+#define	IEEE80211_CHAN_A \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define	IEEE80211_CHAN_B \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define	IEEE80211_CHAN_PUREG \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+#define	IEEE80211_CHAN_G \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+#define	IEEE80211_CHAN_T \
+	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define	IEEE80211_CHAN_108G \
+	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+
+#define	IEEE80211_IS_CHAN_FHSS(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+#define	IEEE80211_IS_CHAN_A(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+#define	IEEE80211_IS_CHAN_B(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+#define	IEEE80211_IS_CHAN_PUREG(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+#define	IEEE80211_IS_CHAN_G(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+#define	IEEE80211_IS_CHAN_ANYG(_c) \
+	(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
+#define	IEEE80211_IS_CHAN_T(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+#define	IEEE80211_IS_CHAN_108G(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
+
+#define	IEEE80211_IS_CHAN_2GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
+#define	IEEE80211_IS_CHAN_5GHZ(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define	IEEE80211_IS_CHAN_OFDM(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
+#define	IEEE80211_IS_CHAN_CCK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
+#define	IEEE80211_IS_CHAN_GFSK(_c) \
+	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
+
+/* ni_chan encoding for FH phy */
+#define	IEEE80211_FH_CHANMOD	80
+#define	IEEE80211_FH_CHAN(set,pat)	(((set)-1)*IEEE80211_FH_CHANMOD+(pat))
+#define	IEEE80211_FH_CHANSET(chan)	((chan)/IEEE80211_FH_CHANMOD+1)
+#define	IEEE80211_FH_CHANPAT(chan)	((chan)%IEEE80211_FH_CHANMOD)
+
+/*
+ * 802.11 rate set.
+ */
+#define	IEEE80211_RATE_SIZE	8		/* 802.11 standard */
+#define	IEEE80211_RATE_MAXSIZE	15		/* max rates we'll handle */
+
+struct ieee80211_rateset {
+	u_int8_t		rs_nrates;
+	u_int8_t		rs_rates[IEEE80211_RATE_MAXSIZE];
+};
+
+#endif /* _NET80211__IEEE80211_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211.c src/sys/netproto/802_11/ieee80211.c
--- src.preview/sys/netproto/802_11/ieee80211.c	2005-01-25 19:37:40.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211.c	2005-09-23 11:44:24.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,79 +28,114 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211.c,v 1.13 2004/05/30 17:57:45 phk Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211.c,v 1.3 2005/01/26 00:37:40 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 generic handler
  */
 
-#include "opt_inet.h"
-
 #include <sys/param.h>
 #include <sys/systm.h> 
-#include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/module.h>
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
- 
+#include <sys/thread.h>
+#include <sys/thread2.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
-#include <net/if_llc.h>
-#include <net/route.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-#ifdef IEEE80211_DEBUG
-int	ieee80211_debug = 0;
-SYSCTL_INT(_debug, OID_AUTO, ieee80211, CTLFLAG_RW, &ieee80211_debug,
-	    0, "IEEE 802.11 media debugging printfs");
-#endif
-
-static void ieee80211_set11gbasicrates(struct ieee80211_rateset *,
-		enum ieee80211_phymode);
-
-static const char *ieee80211_phymode_name[] = {
+const char *ieee80211_phymode_name[] = {
 	"auto",		/* IEEE80211_MODE_AUTO */
 	"11a",		/* IEEE80211_MODE_11A */
 	"11b",		/* IEEE80211_MODE_11B */
 	"11g",		/* IEEE80211_MODE_11G */
 	"FH",		/* IEEE80211_MODE_FH */
-	"turbo",	/* IEEE80211_MODE_TURBO */
+	"turboA",	/* IEEE80211_MODE_TURBO_A */
+	"turboG",	/* IEEE80211_MODE_TURBO_G */
 };
 
+/* list of all instances */
+SLIST_HEAD(ieee80211_list, ieee80211com);
+static struct ieee80211_list ieee80211_list =
+	SLIST_HEAD_INITIALIZER(ieee80211_list);
+static u_int8_t ieee80211_vapmap[32];		/* enough for 256 */
+static struct lwkt_token ieee80211_vap_mtx;
+static int vap_initted = 0;
+#if 0
+MTX_SYSINIT(ieee80211, &ieee80211_vap_mtx, "net80211 instances", MTX_DEF);
+#endif
+
+static void
+ieee80211_add_vap(struct ieee80211com *ic)
+{
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	int i;
+	u_int8_t b;
+        struct lwkt_tokref tokinfo;
+
+	lwkt_gettoken(&tokinfo, &ieee80211_vap_mtx);
+	ic->ic_vap = 0;
+	for (i = 0; i < N(ieee80211_vapmap) && ieee80211_vapmap[i] == 0xff; i++)
+		ic->ic_vap += NBBY;
+	if (i == N(ieee80211_vapmap))
+		panic("vap table full");
+	for (b = ieee80211_vapmap[i]; b & 1; b >>= 1)
+		ic->ic_vap++;
+	setbit(ieee80211_vapmap, ic->ic_vap);
+	SLIST_INSERT_HEAD(&ieee80211_list, ic, ic_next);
+	lwkt_reltoken(&tokinfo);
+#undef N
+}
+
+static void
+ieee80211_remove_vap(struct ieee80211com *ic)
+{
+        struct lwkt_tokref tokinfo;
+	lwkt_gettoken(&tokinfo, &ieee80211_vap_mtx);
+	SLIST_REMOVE(&ieee80211_list, ic, ieee80211com, ic_next);
+	KASSERT(ic->ic_vap < sizeof(ieee80211_vapmap)*NBBY,
+		("invalid vap id %d", ic->ic_vap));
+	KASSERT(isset(ieee80211_vapmap, ic->ic_vap),
+		("vap id %d not allocated", ic->ic_vap));
+	clrbit(ieee80211_vapmap, ic->ic_vap);
+	lwkt_reltoken(&tokinfo);
+}
+
+/*
+ * Default reset method for use with the ioctl support.  This
+ * method is invoked after any state change in the 802.11
+ * layer that should be propagated to the hardware but not
+ * require re-initialization of the 802.11 state machine (e.g
+ * rescanning for an ap).  We always return ENETRESET which
+ * should cause the driver to re-initialize the device. Drivers
+ * can override this method to implement more optimized support.
+ */
+static int
+ieee80211_default_reset(struct ifnet *ifp)
+{
+	return ENETRESET;
+}
+
 void
-ieee80211_ifattach(struct ifnet *ifp)
+ieee80211_ifattach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_channel *c;
 	int i;
 
 	ether_ifattach(ifp, ic->ic_myaddr);
 	bpfattach_dlt(ifp, DLT_IEEE802_11,
 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
-	ieee80211_crypto_attach(ifp);
+
+	ieee80211_crypto_attach(ic);
 
 	/*
 	 * Fill in 802.11 available channel set, mark
@@ -135,33 +170,75 @@
 			if (IEEE80211_IS_CHAN_FHSS(c))
 				ic->ic_modecaps |= 1<<IEEE80211_MODE_FH;
 			if (IEEE80211_IS_CHAN_T(c))
-				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO;
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_A;
+			if (IEEE80211_IS_CHAN_108G(c))
+				ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO_G;
+			if (ic->ic_curchan == NULL) {
+				/* arbitrarily pick the first channel */
+				ic->ic_curchan = &ic->ic_channels[i];
+			}
 		}
 	}
 	/* validate ic->ic_curmode */
 	if ((ic->ic_modecaps & (1<<ic->ic_curmode)) == 0)
 		ic->ic_curmode = IEEE80211_MODE_AUTO;
 	ic->ic_des_chan = IEEE80211_CHAN_ANYC;	/* any channel is ok */
-
+#if 0
+	/*
+	 * Enable WME by default if we're capable.
+	 */
+	if (ic->ic_caps & IEEE80211_C_WME)
+		ic->ic_flags |= IEEE80211_F_WME;
+#endif
 	(void) ieee80211_setmode(ic, ic->ic_curmode);
 
+	if (ic->ic_bintval == 0)
+		ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
+	ic->ic_bmisstimeout = 7*ic->ic_bintval;	/* default 7 beacons */
+	ic->ic_dtim_period = IEEE80211_DTIM_DEFAULT;
+	IEEE80211_BEACON_LOCK_INIT(ic, "beacon");
+
 	if (ic->ic_lintval == 0)
-		ic->ic_lintval = 100;		/* default sleep */
-	ic->ic_bmisstimeout = 7*ic->ic_lintval;	/* default 7 beacons */
+		ic->ic_lintval = ic->ic_bintval;
+	ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX;
 
-	ieee80211_node_attach(ifp);
-	ieee80211_proto_attach(ifp);
+	ieee80211_node_attach(ic);
+	ieee80211_proto_attach(ic);
+
+	crit_enter();
+	if (!vap_initted) {
+		vap_initted = 1;
+		lwkt_token_init(&ieee80211_vap_mtx);
+	}
+	crit_exit();
+	ieee80211_add_vap(ic);
+
+	ieee80211_sysctl_attach(ic);		/* NB: requires ic_vap */
+
+	/*
+	 * Install a default reset method for the ioctl support.
+	 * The driver is expected to fill this in before calling us.
+	 */
+	if (ic->ic_reset == NULL)
+		ic->ic_reset = ieee80211_default_reset;
 }
 
 void
-ieee80211_ifdetach(struct ifnet *ifp)
+ieee80211_ifdetach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
+
+	ieee80211_remove_vap(ic);
 
-	ieee80211_proto_detach(ifp);
-	ieee80211_crypto_detach(ifp);
-	ieee80211_node_detach(ifp);
+	ieee80211_sysctl_detach(ic);
+	ieee80211_proto_detach(ic);
+	ieee80211_crypto_detach(ic);
+	ieee80211_node_detach(ic);
 	ifmedia_removeall(&ic->ic_media);
+
+	IEEE80211_BEACON_LOCK_DESTROY(ic);
+
+	bpfdetach(ifp);
 	ether_ifdetach(ifp);
 }
 
@@ -202,11 +279,11 @@
 	else if (c == IEEE80211_CHAN_ANYC)
 		return IEEE80211_CHAN_ANY;
 	else if (c != NULL) {
-		if_printf(&ic->ic_if, "invalid channel freq %u flags %x\n",
+		if_printf(ic->ic_ifp, "invalid channel freq %u flags %x\n",
 			c->ic_freq, c->ic_flags);
 		return 0;		/* XXX */
 	} else {
-		if_printf(&ic->ic_if, "invalid channel (NULL)\n");
+		if_printf(ic->ic_ifp, "invalid channel (NULL)\n");
 		return 0;		/* XXX */
 	}
 }
@@ -243,13 +320,13 @@
  * ieee80211_attach and before most anything else.
  */
 void
-ieee80211_media_init(struct ifnet *ifp,
+ieee80211_media_init(struct ieee80211com *ic,
 	ifm_change_cb_t media_change, ifm_stat_cb_t media_stat)
 {
 #define	ADD(_ic, _s, _o) \
 	ifmedia_add(&(_ic)->ic_media, \
 		IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL)
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ifmediareq imr;
 	int i, j, mode, rate, maxrate, mword, mopt, r;
 	struct ieee80211_rateset *rs;
@@ -259,7 +336,7 @@
 	 * Do late attach work that must wait for any subclass
 	 * (i.e. driver) work such as overriding methods.
 	 */
-	ieee80211_node_lateattach(ifp);
+	ieee80211_node_lateattach(ic);
 
 	/*
 	 * Fill in media characteristics.
@@ -275,6 +352,7 @@
 			IFM_IEEE80211_11G,
 			IFM_IEEE80211_FH,
 			IFM_IEEE80211_11A | IFM_IEEE80211_TURBO,
+			IFM_IEEE80211_11G | IFM_IEEE80211_TURBO,
 		};
 		if ((ic->ic_modecaps & (1<<mode)) == 0)
 			continue;
@@ -290,16 +368,12 @@
 			ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_MONITOR);
 		if (mode == IEEE80211_MODE_AUTO)
 			continue;
-		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
 		rs = &ic->ic_sup_rates[mode];
 		for (i = 0; i < rs->rs_nrates; i++) {
 			rate = rs->rs_rates[i];
 			mword = ieee80211_rate2media(ic, rate, mode);
 			if (mword == 0)
 				continue;
-			printf("%s%d%sMbps", (i != 0 ? " " : ""),
-			    (rate & IEEE80211_RATE_VAL) / 2,
-			    ((rate & 0x1) != 0 ? ".5" : ""));
 			ADD(ic, mword, mopt);
 			if (ic->ic_caps & IEEE80211_C_IBSS)
 				ADD(ic, mword, mopt | IFM_IEEE80211_ADHOC);
@@ -325,7 +399,6 @@
 			if (rate > maxrate)
 				maxrate = rate;
 		}
-		printf("\n");
 	}
 	for (i = 0; i < allrates.rs_nrates; i++) {
 		mword = ieee80211_rate2media(ic, allrates.rs_rates[i],
@@ -351,6 +424,31 @@
 #undef ADD
 }
 
+void
+ieee80211_announce(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	int i, mode, rate, mword;
+	struct ieee80211_rateset *rs;
+
+	for (mode = IEEE80211_MODE_11A; mode < IEEE80211_MODE_MAX; mode++) {
+		if ((ic->ic_modecaps & (1<<mode)) == 0)
+			continue;
+		if_printf(ifp, "%s rates: ", ieee80211_phymode_name[mode]);
+		rs = &ic->ic_sup_rates[mode];
+		for (i = 0; i < rs->rs_nrates; i++) {
+			rate = rs->rs_rates[i];
+			mword = ieee80211_rate2media(ic, rate, mode);
+			if (mword == 0)
+				continue;
+			printf("%s%d%sMbps", (i != 0 ? " " : ""),
+			    (rate & IEEE80211_RATE_VAL) / 2,
+			    ((rate & 0x1) != 0 ? ".5" : ""));
+		}
+		printf("\n");
+	}
+}
+
 static int
 findrate(struct ieee80211com *ic, enum ieee80211_phymode mode, int rate)
 {
@@ -365,17 +463,50 @@
 }
 
 /*
+ * Find an instance by it's mac address.
+ */
+struct ieee80211com *
+ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (IEEE80211_ADDR_EQ(mac, ic->ic_myaddr))
+			return ic;
+	return NULL;
+}
+
+static struct ieee80211com *
+ieee80211_find_instance(struct ifnet *ifp)
+{
+	struct ieee80211com *ic;
+
+	/* XXX lock */
+	/* XXX not right for multiple instances but works for now */
+	SLIST_FOREACH(ic, &ieee80211_list, ic_next)
+		if (ic->ic_ifp == ifp)
+			return ic;
+	return NULL;
+}
+
+/*
  * Handle a media change request.
  */
 int
 ieee80211_media_change(struct ifnet *ifp)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ieee80211com *ic;
 	struct ifmedia_entry *ime;
 	enum ieee80211_opmode newopmode;
 	enum ieee80211_phymode newphymode;
 	int i, j, newrate, error = 0;
 
+	ic = ieee80211_find_instance(ifp);
+	if (!ic) {
+		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
+		return EINVAL;
+	}
 	ime = ic->ic_media.ifm_cur;
 	/*
 	 * First, identify the phy mode.
@@ -400,13 +531,16 @@
 		return EINVAL;
 	}
 	/*
-	 * Turbo mode is an ``option''.  Eventually it
-	 * needs to be applied to 11g too.
+	 * Turbo mode is an ``option''.
+	 * XXX does not apply to AUTO
 	 */
 	if (ime->ifm_media & IFM_IEEE80211_TURBO) {
-		if (newphymode != IEEE80211_MODE_11A)
+		if (newphymode == IEEE80211_MODE_11A)
+			newphymode = IEEE80211_MODE_TURBO_A;
+		else if (newphymode == IEEE80211_MODE_11G)
+			newphymode = IEEE80211_MODE_TURBO_G;
+		else
 			return EINVAL;
-		newphymode = IEEE80211_MODE_TURBO;
 	}
 	/*
 	 * Validate requested mode is available.
@@ -513,14 +647,15 @@
 			break;
 		case IEEE80211_M_IBSS:
 			ic->ic_flags |= IEEE80211_F_IBSSON;
-#ifdef notdef
-			if (ic->ic_curmode == IEEE80211_MODE_11G)
-				ieee80211_set11gbasicrates(
-					&ic->ic_suprates[newphymode],
-					IEEE80211_MODE_11B);
-#endif
 			break;
 		}
+		/*
+		 * Yech, slot time may change depending on the
+		 * operating mode so reset it to be sure everything
+		 * is setup appropriately.
+		 */
+		ieee80211_reset_erp(ic);
+		ieee80211_wme_initparams(ic);	/* after opmode change */
 		error = ENETRESET;
 	}
 #ifdef notdef
@@ -533,26 +668,39 @@
 void
 ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct ieee80211_node *ni = NULL;
-	int old_status = imr->ifm_status;
+	struct ieee80211com *ic;
+	struct ieee80211_rateset *rs;
 
+	ic = ieee80211_find_instance(ifp);
+	if (!ic) {
+		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
+		return;
+	}
 	imr->ifm_status = IFM_AVALID;
 	imr->ifm_active = IFM_IEEE80211;
-/* XXX
-	if (ic->ic_state == IEEE80211_S_RUN) {
+	if (ic->ic_state == IEEE80211_S_RUN)
 		imr->ifm_status |= IFM_ACTIVE;
-		ifp->if_link_state = LINK_STATE_UP;
+	/*
+	 * Calculate a current rate if possible.
+	 */
+	if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
+		/*
+		 * A fixed rate is set, report that.
+		 */
+		rs = &ic->ic_sup_rates[ic->ic_curmode];
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_fixed_rate], ic->ic_curmode);
+	} else if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * In station mode report the current transmit rate.
+		 */
+		rs = &ic->ic_bss->ni_rates;
+		imr->ifm_active |= ieee80211_rate2media(ic,
+			rs->rs_rates[ic->ic_bss->ni_txrate], ic->ic_curmode);
 	} else
-		ifp->if_link_state = LINK_STATE_DOWN;
-*/
-	imr->ifm_active |= IFM_AUTO;
+		imr->ifm_active |= IFM_AUTO;
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
-		ni = ic->ic_bss;
-		/* calculate rate subtype */
-		imr->ifm_active |= ieee80211_rate2media(ic,
-			ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode);
 		break;
 	case IEEE80211_M_IBSS:
 		imr->ifm_active |= IFM_IEEE80211_ADHOC;
@@ -580,58 +728,41 @@
 	case IEEE80211_MODE_FH:
 		imr->ifm_active |= IFM_IEEE80211_FH;
 		break;
-	case IEEE80211_MODE_TURBO:
+	case IEEE80211_MODE_TURBO_A:
 		imr->ifm_active |= IFM_IEEE80211_11A
 				|  IFM_IEEE80211_TURBO;
 		break;
+	case IEEE80211_MODE_TURBO_G:
+		imr->ifm_active |= IFM_IEEE80211_11G
+				|  IFM_IEEE80211_TURBO;
+		break;
 	}
-
-	/* Notify that the link state has changed. */
-	if (imr->ifm_status != old_status)
-		rt_ifmsg(ifp);
 }
 
 void
-ieee80211_watchdog(struct ifnet *ifp)
-{
-	struct ieee80211com *ic = (void *)ifp;
-
-	if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
-		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
-	if (ic->ic_inact_timer && --ic->ic_inact_timer == 0)
-		ieee80211_timeout_nodes(ic);
-
-	if (ic->ic_mgt_timer != 0 || ic->ic_inact_timer != 0)
-		ifp->if_timer = 1;
-}
-
-/*
- * Mark the basic rates for the 11g rate table based on the
- * operating mode.  For real 11g we mark all the 11b rates
- * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
- * 11b rates.  There's also a pseudo 11a-mode used to mark only
- * the basic OFDM rates.
- */
-static void
-ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+ieee80211_watchdog(struct ieee80211com *ic)
 {
-	static const struct ieee80211_rateset basic[] = {
-	    { 3, { 12, 24, 48 } },		/* IEEE80211_MODE_11A */
-	    { 4, { 2, 4, 11, 22 } },		/* IEEE80211_MODE_11B */
-	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },/* IEEE80211_MODE_11G */
-	    { 0 },				/* IEEE80211_MODE_FH */
-	    { 0 },				/* IEEE80211_MODE_TURBO	*/
-	};
-	int i, j;
+	struct ieee80211_node_table *nt;
+	int need_inact_timer = 0;
 
-	for (i = 0; i < rs->rs_nrates; i++) {
-		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
-		for (j = 0; j < basic[mode].rs_nrates; j++)
-			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
-				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
-				break;
-			}
+	if (ic->ic_state != IEEE80211_S_INIT) {
+		if (ic->ic_mgt_timer && --ic->ic_mgt_timer == 0)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		nt = &ic->ic_scan;
+		if (nt->nt_inact_timer) {
+			if (--nt->nt_inact_timer == 0)
+				nt->nt_timeout(nt);
+			need_inact_timer += nt->nt_inact_timer;
+		}
+		nt = &ic->ic_sta;
+		if (nt->nt_inact_timer) {
+			if (--nt->nt_inact_timer == 0)
+				nt->nt_timeout(nt);
+			need_inact_timer += nt->nt_inact_timer;
+		}
 	}
+	if (ic->ic_mgt_timer != 0 || need_inact_timer)
+		ic->ic_ifp->if_timer = 1;
 }
 
 /*
@@ -650,7 +781,8 @@
 		IEEE80211_CHAN_B,	/* IEEE80211_MODE_11B */
 		IEEE80211_CHAN_PUREG,	/* IEEE80211_MODE_11G */
 		IEEE80211_CHAN_FHSS,	/* IEEE80211_MODE_FH */
-		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO	*/
+		IEEE80211_CHAN_T,	/* IEEE80211_MODE_TURBO_A */
+		IEEE80211_CHAN_108G,	/* IEEE80211_MODE_TURBO_G */
 	};
 	struct ieee80211_channel *c;
 	u_int modeflags;
@@ -658,8 +790,9 @@
 
 	/* validate new mode */
 	if ((ic->ic_modecaps & (1<<mode)) == 0) {
-		IEEE80211_DPRINTF(("%s: mode %u not supported (caps 0x%x)\n",
-			__func__, mode, ic->ic_modecaps));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: mode %u not supported (caps 0x%x)\n",
+			__func__, mode, ic->ic_modecaps);
 		return EINVAL;
 	}
 
@@ -681,8 +814,8 @@
 		}
 	}
 	if (i > IEEE80211_CHAN_MAX) {
-		IEEE80211_DPRINTF(("%s: no channels found for mode %u\n",
-			__func__, mode));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: no channels found for mode %u\n", __func__, mode);
 		return EINVAL;
 	}
 
@@ -714,56 +847,82 @@
 				ic->ic_ibss_chan = &ic->ic_channels[i];
 				break;
 			}
+		KASSERT(ic->ic_ibss_chan != NULL &&
+		    isset(ic->ic_chan_active,
+			ieee80211_chan2ieee(ic, ic->ic_ibss_chan)),
+		    ("Bad IBSS channel %u",
+		     ieee80211_chan2ieee(ic, ic->ic_ibss_chan)));
 	}
+	/*
+	 * If the desired channel is set but no longer valid then reset it.
+	 */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+	    isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ic->ic_des_chan)))
+		ic->ic_des_chan = IEEE80211_CHAN_ANYC;
 
 	/*
-	 * Set/reset state flags that influence beacon contents, etc.
-	 *
-	 * XXX what if we have stations already associated???
-	 * XXX probably not right for autoselect?
+	 * Do mode-specific rate setup.
 	 */
-	if (ic->ic_caps & IEEE80211_C_SHPREAMBLE)
-		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
 	if (mode == IEEE80211_MODE_11G) {
-		if (ic->ic_caps & IEEE80211_C_SHSLOT)
-			ic->ic_flags |= IEEE80211_F_SHSLOT;
+		/*
+		 * Use a mixed 11b/11g rate set.
+		 */
 		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
 			IEEE80211_MODE_11G);
-	} else {
-		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+	} else if (mode == IEEE80211_MODE_11B) {
+		/*
+		 * Force pure 11b rate set.
+		 */
+		ieee80211_set11gbasicrates(&ic->ic_sup_rates[mode],
+			IEEE80211_MODE_11B);
 	}
+	/*
+	 * Setup an initial rate set according to the
+	 * current/default channel selected above.  This
+	 * will be changed when scanning but must exist
+	 * now so driver have a consistent state of ic_ibss_chan.
+	 */
+	if (ic->ic_bss)		/* NB: can be called before lateattach */
+		ic->ic_bss->ni_rates = ic->ic_sup_rates[mode];
 
 	ic->ic_curmode = mode;
+	ieee80211_reset_erp(ic);	/* reset ERP state */
+	ieee80211_wme_initparams(ic);	/* reset WME stat */
+
 	return 0;
 #undef N
 }
 
 /*
  * Return the phy mode for with the specified channel so the
- * caller can select a rate set.  This is problematic and the
- * work here assumes how things work elsewhere in this code.
+ * caller can select a rate set.  This is problematic for channels
+ * where multiple operating modes are possible (e.g. 11g+11b).
+ * In those cases we defer to the current operating mode when set.
  */
 enum ieee80211_phymode
 ieee80211_chan2mode(struct ieee80211com *ic, struct ieee80211_channel *chan)
 {
-	/*
-	 * NB: this assumes the channel would not be supplied to us
-	 *     unless it was already compatible with the current mode.
-	 */
-	if (ic->ic_curmode != IEEE80211_MODE_AUTO)
-		return ic->ic_curmode;
-	/*
-	 * In autoselect mode; deduce a mode based on the channel
-	 * characteristics.  We assume that turbo-only channels
-	 * are not considered when the channel set is constructed.
-	 */
-	if (IEEE80211_IS_CHAN_5GHZ(chan))
+	if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+		/*
+		 * This assumes all 11a turbo channels are also
+		 * usable withut turbo, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_TURBO_A)
+			return IEEE80211_MODE_TURBO_A;
 		return IEEE80211_MODE_11A;
-	else if (IEEE80211_IS_CHAN_FHSS(chan))
+	} else if (IEEE80211_IS_CHAN_FHSS(chan))
 		return IEEE80211_MODE_FH;
-	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN))
+	else if (chan->ic_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
+		/*
+		 * This assumes all 11g channels are also usable
+		 * for 11b, which is currently true.
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
+			return IEEE80211_MODE_TURBO_G;
+		if (ic->ic_curmode == IEEE80211_MODE_11B)
+			return IEEE80211_MODE_11B;
 		return IEEE80211_MODE_11G;
-	else
+	} else
 		return IEEE80211_MODE_11B;
 }
 
@@ -813,7 +972,7 @@
 	mask = rate & IEEE80211_RATE_VAL;
 	switch (mode) {
 	case IEEE80211_MODE_11A:
-	case IEEE80211_MODE_TURBO:
+	case IEEE80211_MODE_TURBO_A:
 		mask |= IFM_IEEE80211_11A;
 		break;
 	case IEEE80211_MODE_11B:
@@ -831,6 +990,7 @@
 		/* NB: hack, 11g matches both 11b+11a rates */
 		/* fall thru... */
 	case IEEE80211_MODE_11G:
+	case IEEE80211_MODE_TURBO_G:
 		mask |= IFM_IEEE80211_11G;
 		break;
 	}
@@ -870,33 +1030,3 @@
 		ieeerates[IFM_SUBTYPE(mword)] : 0;
 #undef N
 }
-
-/*
- * Module glue.
- *
- * NB: the module name is "wlan" for compatibility with NetBSD.
- */
-
-static int
-ieee80211_modevent(module_t mod, int type, void *unused)
-{
-	switch (type) {
-	case MOD_LOAD:
-		if (bootverbose)
-			printf("wlan: <802.11 Link Layer>\n");
-		return 0;
-	case MOD_UNLOAD:
-	case MOD_SHUTDOWN:
-		return 0;
-	}
-	return EINVAL;
-}
-
-static moduledata_t ieee80211_mod = {
-	"wlan",
-	ieee80211_modevent,
-	0
-};
-DECLARE_MODULE(wlan, ieee80211_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
-MODULE_VERSION(wlan, 1);
-MODULE_DEPEND(wlan, crypto, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211.h src/sys/netproto/802_11/ieee80211.h
--- src.preview/sys/netproto/802_11/ieee80211.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211.h	2005-09-23 11:45:04.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,10 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211.h,v 1.5 2004/04/05 17:47:40 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_H_
-#define	_NETPROTO_802_11_IEEE80211_H_
+#ifndef _NET80211_IEEE80211_H_
+#define _NET80211_IEEE80211_H_
 
  * 802.11 protocol definitions.
@@ -42,112 +40,71 @@
 
 #define	IEEE80211_ADDR_LEN	6		/* size of 802.11 address */
 /* is 802.11 address multicast/broadcast? */
-#define	IEEE80211_IS_MULTICAST(a)	ETHER_IS_MULTICAST(a)
+#define	IEEE80211_IS_MULTICAST(_a)	(*(_a) & 0x01)
 
 /* IEEE 802.11 PLCP header */
 struct ieee80211_plcp_hdr {
-	uint16_t	i_sfd;
-	uint8_t		i_signal;
-	uint8_t		i_service;
-	uint16_t	i_length;
-	uint16_t	i_crc;
-} __attribute__((__packed__));
+	u_int16_t	i_sfd;
+	u_int8_t	i_signal;
+	u_int8_t	i_service;
+	u_int16_t	i_length;
+	u_int16_t	i_crc;
+} __packed;
 
-#define	IEEE80211_PLCP_SFD      0xF3A0 
-#define	IEEE80211_PLCP_SERVICE  0x00
+#define IEEE80211_PLCP_SFD      0xF3A0 
+#define IEEE80211_PLCP_SERVICE  0x00
 
 /*
  * generic definitions for IEEE 802.11 frames
  */
 struct ieee80211_frame {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_qosframe {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_qos[2];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_qos[2];
 	/* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
 	/* see below */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_qoscntl {
-	uint8_t		i_qos[2];
+	u_int8_t	i_qos[2];
 };
 
 struct ieee80211_frame_addr4 {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
-} __attribute__((__packed__));
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+} __packed;
 
 
 struct ieee80211_qosframe_addr4 {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr3[IEEE80211_ADDR_LEN];
-	uint8_t		i_seq[2];
-	uint8_t		i_addr4[IEEE80211_ADDR_LEN];
-	uint8_t		i_qos[2];
-} __attribute__((__packed__));
-
-/*
- * Management Notification Frame
- */
-struct ieee80211_mnf {
-	uint8_t		mnf_category;
-	uint8_t		mnf_action;
-	uint8_t		mnf_dialog;
-	uint8_t		mnf_status;
-} __attribute__((__packed__));
-#define	MNF_SETUP_REQ	0
-#define	MNF_SETUP_RESP	1
-#define	MNF_TEARDOWN	2
-
-/*
- * WME/802.11e Tspec Element
- */
-struct ieee80211_wme_tspec {
-	uint8_t		ts_id;
-	uint8_t		ts_len;
-	uint8_t		ts_oui[3];
-	uint8_t		ts_oui_type;
-	uint8_t		ts_oui_subtype;
-	uint8_t		ts_version;
-	uint8_t		ts_tsinfo[3];
-	uint8_t		ts_nom_msdu[2];
-	uint8_t		ts_max_msdu[2];
-	uint8_t		ts_min_svc[4];
-	uint8_t		ts_max_svc[4];
-	uint8_t		ts_inactv_intv[4];
-	uint8_t		ts_susp_intv[4];
-	uint8_t		ts_start_svc[4];
-	uint8_t		ts_min_rate[4];
-	uint8_t		ts_mean_rate[4];
-	uint8_t		ts_max_burst[4];
-	uint8_t		ts_min_phy[4];
-	uint8_t		ts_peak_rate[4];
-	uint8_t		ts_delay[4];
-	uint8_t		ts_surplus[2];
-	uint8_t		ts_medium_time[2];
-} __attribute__((__packed__));
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr3[IEEE80211_ADDR_LEN];
+	u_int8_t	i_seq[2];
+	u_int8_t	i_addr4[IEEE80211_ADDR_LEN];
+	u_int8_t	i_qos[2];
+} __packed;
 
 #define	IEEE80211_FC0_VERSION_MASK		0x03
 #define	IEEE80211_FC0_VERSION_SHIFT		0
@@ -189,6 +146,7 @@
 #define	IEEE80211_FC0_SUBTYPE_CFPOLL		0x60
 #define	IEEE80211_FC0_SUBTYPE_CF_ACK_CF_ACK	0x70
 #define	IEEE80211_FC0_SUBTYPE_QOS		0x80
+#define	IEEE80211_FC0_SUBTYPE_QOS_NULL		0xc0
 
 #define	IEEE80211_FC1_DIR_MASK			0x03
 #define	IEEE80211_FC1_DIR_NODS			0x00	/* STA->STA */
@@ -212,58 +170,170 @@
 
 #define	IEEE80211_QOS_TXOP			0x00ff
 /* bit 8 is reserved */
-#define	IEEE80211_QOS_ACKPOLICY			0x0600
-#define	IEEE80211_QOS_ESOP			0x0800
-#define	IEEE80211_QOS_TID			0xf000
+#define	IEEE80211_QOS_ACKPOLICY			0x60
+#define	IEEE80211_QOS_ACKPOLICY_S		5
+#define	IEEE80211_QOS_ESOP			0x10
+#define	IEEE80211_QOS_ESOP_S			4
+#define	IEEE80211_QOS_TID			0x0f
+
+/* does frame have QoS sequence control data */
+#define	IEEE80211_QOS_HAS_SEQ(wh) \
+	(((wh)->i_fc[0] & \
+	  (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+	  (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_wme_info {
+	u_int8_t	wme_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wme_len;	/* length in bytes */
+	u_int8_t	wme_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wme_type;	/* OUI type */
+	u_int8_t	wme_subtype;	/* OUI subtype */
+	u_int8_t	wme_version;	/* spec revision */
+	u_int8_t	wme_info;	/* QoS info */
+} __packed;
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+	u_int8_t	ts_id;
+	u_int8_t	ts_len;
+	u_int8_t	ts_oui[3];
+	u_int8_t	ts_oui_type;
+	u_int8_t	ts_oui_subtype;
+	u_int8_t	ts_version;
+	u_int8_t	ts_tsinfo[3];
+	u_int8_t	ts_nom_msdu[2];
+	u_int8_t	ts_max_msdu[2];
+	u_int8_t	ts_min_svc[4];
+	u_int8_t	ts_max_svc[4];
+	u_int8_t	ts_inactv_intv[4];
+	u_int8_t	ts_susp_intv[4];
+	u_int8_t	ts_start_svc[4];
+	u_int8_t	ts_min_rate[4];
+	u_int8_t	ts_mean_rate[4];
+	u_int8_t	ts_max_burst[4];
+	u_int8_t	ts_min_phy[4];
+	u_int8_t	ts_peak_rate[4];
+	u_int8_t	ts_delay[4];
+	u_int8_t	ts_surplus[2];
+	u_int8_t	ts_medium_time[2];
+} __packed;
+
+/*
+ * WME AC parameter field
+ */
+struct ieee80211_wme_acparams {
+	u_int8_t	acp_aci_aifsn;
+	u_int8_t	acp_logcwminmax;
+	u_int16_t	acp_txop;
+} __packed;
+
+#define WME_NUM_AC		4	/* 4 AC categories */
+
+#define WME_PARAM_ACI		0x60	/* Mask for ACI field */
+#define WME_PARAM_ACI_S		5	/* Shift for ACI field */
+#define WME_PARAM_ACM		0x10	/* Mask for ACM bit */
+#define WME_PARAM_ACM_S		4	/* Shift for ACM bit */
+#define WME_PARAM_AIFSN		0x0f	/* Mask for aifsn field */
+#define WME_PARAM_AIFSN_S	0	/* Shift for aifsn field */
+#define WME_PARAM_LOGCWMIN	0x0f	/* Mask for CwMin field (in log) */
+#define WME_PARAM_LOGCWMIN_S	0	/* Shift for CwMin field */
+#define WME_PARAM_LOGCWMAX	0xf0	/* Mask for CwMax field (in log) */
+#define WME_PARAM_LOGCWMAX_S	4	/* Shift for CwMax field */
+
+#define WME_AC_TO_TID(_ac) (       \
+	((_ac) == WME_AC_VO) ? 6 : \
+	((_ac) == WME_AC_VI) ? 5 : \
+	((_ac) == WME_AC_BK) ? 1 : \
+	0)
+
+#define TID_TO_WME_AC(_tid) (      \
+	((_tid) < 1) ? WME_AC_BE : \
+	((_tid) < 3) ? WME_AC_BK : \
+	((_tid) < 6) ? WME_AC_VI : \
+	WME_AC_VO)
+
+/*
+ * WME Parameter Element
+ */
+struct ieee80211_wme_param {
+	u_int8_t	param_id;
+	u_int8_t	param_len;
+	u_int8_t	param_oui[3];
+	u_int8_t	param_oui_type;
+	u_int8_t	param_oui_sybtype;
+	u_int8_t	param_version;
+	u_int8_t	param_qosInfo;
+#define	WME_QOSINFO_COUNT	0x0f	/* Mask for param count field */
+	u_int8_t	param_reserved;
+	struct ieee80211_wme_acparams	params_acParams[WME_NUM_AC];
+} __packed;
+
+/*
+ * Management Notification Frame
+ */
+struct ieee80211_mnf {
+	u_int8_t	mnf_category;
+	u_int8_t	mnf_action;
+	u_int8_t	mnf_dialog;
+	u_int8_t	mnf_status;
+} __packed;
+#define	MNF_SETUP_REQ	0
+#define	MNF_SETUP_RESP	1
+#define	MNF_TEARDOWN	2
 
 /*
  * Control frames.
  */
 struct ieee80211_frame_min {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_addr1[IEEE80211_ADDR_LEN];
-	uint8_t		i_addr2[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_addr1[IEEE80211_ADDR_LEN];
+	u_int8_t	i_addr2[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_rts {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
-	uint8_t		i_ta[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_cts {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_ack {
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_pspoll {
-	uint8_t		i_fc[2];
-	uint8_t		i_aid[2];
-	uint8_t		i_bssid[IEEE80211_ADDR_LEN];
-	uint8_t		i_ta[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_aid[2];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	i_ta[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 struct ieee80211_frame_cfend {		/* NB: also CF-End+CF-Ack */
-	uint8_t		i_fc[2];
-	uint8_t		i_dur[2];	/* should be zero */
-	uint8_t		i_ra[IEEE80211_ADDR_LEN];
-	uint8_t		i_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	i_fc[2];
+	u_int8_t	i_dur[2];	/* should be zero */
+	u_int8_t	i_ra[IEEE80211_ADDR_LEN];
+	u_int8_t	i_bssid[IEEE80211_ADDR_LEN];
 	/* FCS */
-} __attribute__((__packed__));
+} __packed;
 
 /*
  * BEACON management packets
@@ -277,7 +347,7 @@
  *		octet information[length]
  */
 
-typedef uint8_t *ieee80211_mgt_beacon_t;
+typedef u_int8_t *ieee80211_mgt_beacon_t;
 
 #define	IEEE80211_BEACON_INTERVAL(beacon) \
 	((beacon)[8] | ((beacon)[9] << 8))
@@ -303,103 +373,80 @@
  * 802.11i/WPA information element (maximally sized).
  */
 struct ieee80211_ie_wpa {
-	uint8_t		wpa_oui[3];	/* 0x00, 0x50, 0xf2 */
-	uint8_t		wpa_type;	/* OUI type */
-	uint16_t	wpa_version;	/* spec revision */
-	uint32_t	wpa_mcipher[1];	/* multicast/group key cipher */
-	uint16_t	wpa_uciphercnt;	/* # pairwise key ciphers */
-	uint32_t	wpa_uciphers[8];/* ciphers */
-	uint16_t	wpa_authselcnt;	/* authentication selector cnt*/
-	uint32_t	wpa_authsels[8];/* selectors */
-} __attribute__((__packed__));
-
-/*
- * Management information elements
- */
-struct ieee80211_information {
-	char	ssid[IEEE80211_NWID_LEN+1];
-	struct rates {
-		uint8_t	*p;
-	} rates;
-	struct fh {
-		uint16_t	dwell;
-		uint8_t	set;
-		uint8_t	pattern;
-		uint8_t	index;
-	} fh;
-	struct ds {
-		uint8_t	channel;
-	} ds;
-	struct cf {
-		uint8_t	count;
-		uint8_t	period;
-		uint8_t	maxdur[2];
-		uint8_t	dur[2];
-	} cf;
-	struct tim {
-		uint8_t	count;
-		uint8_t	period;
-		uint8_t	bitctl;
-		/* uint8_t	pvt[251]; The driver needs to use this. */
-	} tim;
-	struct ibss {
-		uint16_t	atim;
-	} ibss;
-	struct challenge {
-		uint8_t	*p;
-		uint8_t	len;
-	} challenge;
-	struct erp {
-		uint8_t	flags;
-	} erp;
-	struct country {
-		uint8_t	cc[3];		/* ISO CC+(I)ndoor/(O)utdoor */
-		struct {
-			uint8_t	schan;		/* starting channel */
-			uint8_t	nchan;		/* number channels */
-			uint8_t	maxtxpwr;	
-		} band[4];			/* up to 4 sub bands */
-	} country;
-	struct ath {
-		uint8_t	flags;
-	} ath;
-	struct ieee80211_ie_wpa	wpa;
-};
+	u_int8_t	wpa_id;		/* IEEE80211_ELEMID_VENDOR */
+	u_int8_t	wpa_len;	/* length in bytes */
+	u_int8_t	wpa_oui[3];	/* 0x00, 0x50, 0xf2 */
+	u_int8_t	wpa_type;	/* OUI type */
+	u_int16_t	wpa_version;	/* spec revision */
+	u_int32_t	wpa_mcipher[1];	/* multicast/group key cipher */
+	u_int16_t	wpa_uciphercnt;	/* # pairwise key ciphers */
+	u_int32_t	wpa_uciphers[8];/* ciphers */
+	u_int16_t	wpa_authselcnt;	/* authentication selector cnt*/
+	u_int32_t	wpa_authsels[8];/* selectors */
+	u_int16_t	wpa_caps;	/* 802.11i capabilities */
+	u_int16_t	wpa_pmkidcnt;	/* 802.11i pmkid count */
+	u_int16_t	wpa_pmkids[8];	/* 802.11i pmkids */
+} __packed;
+
+/*
+ * Management information element payloads.
+ */
 
 enum {
-	IEEE80211_ELEMID_SSID			= 0,
-	IEEE80211_ELEMID_RATES			= 1,
-	IEEE80211_ELEMID_FHPARMS		= 2,
-	IEEE80211_ELEMID_DSPARMS		= 3,
-	IEEE80211_ELEMID_CFPARMS		= 4,
-	IEEE80211_ELEMID_TIM			= 5,
-	IEEE80211_ELEMID_IBSSPARMS		= 6,
-	IEEE80211_ELEMID_COUNTRY		= 7,
-	IEEE80211_ELEMID_CHALLENGE		= 16,
+	IEEE80211_ELEMID_SSID		= 0,
+	IEEE80211_ELEMID_RATES		= 1,
+	IEEE80211_ELEMID_FHPARMS	= 2,
+	IEEE80211_ELEMID_DSPARMS	= 3,
+	IEEE80211_ELEMID_CFPARMS	= 4,
+	IEEE80211_ELEMID_TIM		= 5,
+	IEEE80211_ELEMID_IBSSPARMS	= 6,
+	IEEE80211_ELEMID_COUNTRY	= 7,
+	IEEE80211_ELEMID_CHALLENGE	= 16,
 	/* 17-31 reserved for challenge text extension */
-	IEEE80211_ELEMID_ERP			= 42,
-	IEEE80211_ELEMID_XRATES			= 50,
-	IEEE80211_ELEMID_TPC			= 150,
-	IEEE80211_ELEMID_CCKM			= 156,
-	IEEE80211_ELEMID_VENDOR			= 221,	/* vendor private */
+	IEEE80211_ELEMID_ERP		= 42,
+	IEEE80211_ELEMID_RSN		= 48,
+	IEEE80211_ELEMID_XRATES		= 50,
+	IEEE80211_ELEMID_TPC		= 150,
+	IEEE80211_ELEMID_CCKM		= 156,
+	IEEE80211_ELEMID_VENDOR		= 221,	/* vendor private */
 };
 
-#define	IEEE80211_CHALLENGE_LEN			128
+struct ieee80211_tim_ie {
+	u_int8_t	tim_ie;			/* IEEE80211_ELEMID_TIM */
+	u_int8_t	tim_len;
+	u_int8_t	tim_count;		/* DTIM count */
+	u_int8_t	tim_period;		/* DTIM period */
+	u_int8_t	tim_bitctl;		/* bitmap control */
+	u_int8_t	tim_bitmap[1];		/* variable-length bitmap */
+} __packed;
+
+struct ieee80211_country_ie {
+	u_int8_t	ie;			/* IEEE80211_ELEMID_COUNTRY */
+	u_int8_t	len;
+	u_int8_t	cc[3];			/* ISO CC+(I)ndoor/(O)utdoor */
+	struct {
+		u_int8_t schan;			/* starting channel */
+		u_int8_t nchan;			/* number channels */
+		u_int8_t maxtxpwr;		/* tx power cap */
+	} band[4] __packed;			/* up to 4 sub bands */
+} __packed;
+
+#define IEEE80211_CHALLENGE_LEN		128
 
-#define	IEEE80211_RATE_BASIC			0x80
-#define	IEEE80211_RATE_VAL			0x7f
+#define	IEEE80211_RATE_BASIC		0x80
+#define	IEEE80211_RATE_VAL		0x7f
 
 /* EPR information element flags */
-#define	IEEE80211_ERP_NON_ERP_PRESENT		0x01
-#define	IEEE80211_ERP_USE_PROTECTION		0x02
-#define	IEEE80211_ERP_BARKER_MODE		0x04
+#define	IEEE80211_ERP_NON_ERP_PRESENT	0x01
+#define	IEEE80211_ERP_USE_PROTECTION	0x02
+#define	IEEE80211_ERP_LONG_PREAMBLE	0x04
 
 /* Atheros private advanced capabilities info */
-#define	ATHEROS_CAP_TURBO_PRIME			0x01
-#define	ATHEROS_CAP_COMPRESSION			0x02
-#define	ATHEROS_CAP_FAST_FRAME			0x04
+#define	ATHEROS_CAP_TURBO_PRIME		0x01
+#define	ATHEROS_CAP_COMPRESSION		0x02
+#define	ATHEROS_CAP_FAST_FRAME		0x04
 /* bits 3-6 reserved */
-#define	ATHEROS_CAP_BOOST			0x80
+#define	ATHEROS_CAP_BOOST		0x80
 
 #define	ATH_OUI			0x7f0300		/* Atheros OUI */
 #define	ATH_OUI_TYPE		0x01
@@ -407,12 +454,11 @@
 
 #define	WPA_OUI			0xf25000
 #define	WPA_OUI_TYPE		0x01
-#define	WPA_OUI_VERSION		1		/* current supported version */
+#define	WPA_VERSION		1		/* current supported version */
 
 #define	WPA_CSE_NULL		0x00
 #define	WPA_CSE_WEP40		0x01
 #define	WPA_CSE_TKIP		0x02
-#define	WPA_CSE_WRAP		0x03		/* WPA2/802.11i */
 #define	WPA_CSE_CCMP		0x04
 #define	WPA_CSE_WEP104		0x05
 
@@ -420,6 +466,34 @@
 #define	WPA_ASE_8021X_UNSPEC	0x01
 #define	WPA_ASE_8021X_PSK	0x02
 
+#define	RSN_OUI			0xac0f00
+#define	RSN_VERSION		1		/* current supported version */
+
+#define	RSN_CSE_NULL		0x00
+#define	RSN_CSE_WEP40		0x01
+#define	RSN_CSE_TKIP		0x02
+#define	RSN_CSE_WRAP		0x03
+#define	RSN_CSE_CCMP		0x04
+#define	RSN_CSE_WEP104		0x05
+
+#define	RSN_ASE_NONE		0x00
+#define	RSN_ASE_8021X_UNSPEC	0x01
+#define	RSN_ASE_8021X_PSK	0x02
+
+#define	RSN_CAP_PREAUTH		0x01
+
+#define	WME_OUI			0xf25000
+#define	WME_OUI_TYPE		0x02
+#define	WME_INFO_OUI_SUBTYPE	0x00
+#define	WME_PARAM_OUI_SUBTYPE	0x01
+#define	WME_VERSION		1
+
+/* WME stream classes */
+#define	WME_AC_BE	0		/* best effort */
+#define	WME_AC_BK	1		/* background */
+#define	WME_AC_VI	2		/* video */
+#define	WME_AC_VO	3		/* voice */
+
 /*
  * AUTH management packets
  *
@@ -431,7 +505,7 @@
  *	octet chal.text[253]
  */
 
-typedef uint8_t *ieee80211_mgt_auth_t;
+typedef u_int8_t *ieee80211_mgt_auth_t;
 
 #define	IEEE80211_AUTH_ALGORITHM(auth) \
 	((auth)[0] | ((auth)[1] << 8))
@@ -440,9 +514,9 @@
 #define	IEEE80211_AUTH_STATUS(auth) \
 	((auth)[4] | ((auth)[5] << 8))
 
-#define	IEEE80211_AUTH_ALG_OPEN			0x0000
-#define	IEEE80211_AUTH_ALG_SHARED		0x0001
-#define	IEEE80211_AUTH_ALG_LEAP			0x0080
+#define	IEEE80211_AUTH_ALG_OPEN		0x0000
+#define	IEEE80211_AUTH_ALG_SHARED	0x0001
+#define	IEEE80211_AUTH_ALG_LEAP		0x0080
 
 enum {
 	IEEE80211_AUTH_OPEN_REQUEST		= 1,
@@ -498,13 +572,24 @@
 	IEEE80211_STATUS_DSSSOFDM_REQUIRED	= 26,
 };
 
-#define	IEEE80211_WEP_KEYLEN			5	/* 40bit */
-#define	IEEE80211_WEP_IVLEN			3	/* 24bit */
-#define	IEEE80211_WEP_KIDLEN			1	/* 1 octet */
-#define	IEEE80211_WEP_CRCLEN			4	/* CRC-32 */
-#define	IEEE80211_WEP_NKID			4	/* number of key ids */
+#define	IEEE80211_WEP_KEYLEN		5	/* 40bit */
+#define	IEEE80211_WEP_IVLEN		3	/* 24bit */
+#define	IEEE80211_WEP_KIDLEN		1	/* 1 octet */
+#define	IEEE80211_WEP_CRCLEN		4	/* CRC-32 */
+#define	IEEE80211_WEP_NKID		4	/* number of key ids */
+
+/*
+ * 802.11i defines an extended IV for use with non-WEP ciphers.
+ * When the EXTIV bit is set in the key id byte an additional
+ * 4 bytes immediately follow the IV for TKIP.  For CCMP the
+ * EXTIV bit is likewise set but the 8 bytes represent the
+ * CCMP header rather than IV+extended-IV.
+ */
+#define	IEEE80211_WEP_EXTIV		0x20
+#define	IEEE80211_WEP_EXTIVLEN		4	/* extended IV length */
+#define	IEEE80211_WEP_MICLEN		8	/* trailing MIC */
 
-#define	IEEE80211_CRC_LEN			4
+#define	IEEE80211_CRC_LEN		4
 
 /*
  * Maximum acceptable MTU is:
@@ -513,26 +598,42 @@
  * Min is arbitrarily chosen > IEEE80211_MIN_LEN.  The default
  * mtu is Ethernet-compatible; it's set by ether_ifattach.
  */
-#define	IEEE80211_MTU_MAX			2290
-#define	IEEE80211_MTU_MIN			32
+#define	IEEE80211_MTU_MAX		2290
+#define	IEEE80211_MTU_MIN		32
 
-#define	IEEE80211_MAX_LEN			(2300 + IEEE80211_CRC_LEN + \
+#define	IEEE80211_MAX_LEN		(2300 + IEEE80211_CRC_LEN + \
     (IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN))
+#define	IEEE80211_ACK_LEN \
+	(sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN)
 #define	IEEE80211_MIN_LEN \
 	(sizeof(struct ieee80211_frame_min) + IEEE80211_CRC_LEN)
 
+/*
+ * The 802.11 spec says at most 2007 stations may be
+ * associated at once.  For most AP's this is way more
+ * than is feasible so we use a default of 128.  This
+ * number may be overridden by the driver and/or by
+ * user configuration.
+ */
+#define	IEEE80211_AID_MAX		2007
+#define	IEEE80211_AID_DEF		128
+
+#define	IEEE80211_AID(b)	((b) &~ 0xc000)
+
 /* 
  * RTS frame length parameters.  The default is specified in
- * the 802.11 spec.  The max may be wrong for jumbo frames.
+ * the 802.11 spec as 512; we treat it as implementation-dependent
+ * so it's defined in ieee80211_var.h.  The max may be wrong
+ * for jumbo frames.
  */
-#define	IEEE80211_RTS_DEFAULT			512
-#define	IEEE80211_RTS_MIN			1
-#define	IEEE80211_RTS_MAX			IEEE80211_MAX_LEN
+#define	IEEE80211_RTS_MIN		1
+#define	IEEE80211_RTS_MAX		2346
 
-enum {
-	IEEE80211_AUTH_NONE	= 0,
-	IEEE80211_AUTH_OPEN	= 1,
-	IEEE80211_AUTH_SHARED	= 2,
-};
+/* 
+ * TX fragmentation parameters.  As above for RTS, we treat
+ * default as implementation-dependent so define it elsewhere.
+ */
+#define	IEEE80211_FRAG_MIN		256
+#define	IEEE80211_FRAG_MAX		2346
 
-#endif /* _NETPROTO_802_11_IEEE80211_H_ */
+#endif /* _NET80211_IEEE80211_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_acl.c src/sys/netproto/802_11/ieee80211_acl.c
--- src.preview/sys/netproto/802_11/ieee80211_acl.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_acl.c	2005-09-23 11:46:00.000000000 -0400
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11 MAC ACL support.
+ *
+ * When this module is loaded the sender address of each received
+ * frame is passed to the iac_check method and the module indicates
+ * if the frame should be accepted or rejected.  If the policy is
+ * set to ACL_POLICY_OPEN then all frames are accepted w/o checking
+ * the address.  Otherwise, the address is looked up in the database
+ * and if found the frame is either accepted (ACL_POLICY_ALLOW)
+ * or rejected (ACL_POLICY_DENT).
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/module.h>
+#include <sys/queue.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+enum {
+	ACL_POLICY_OPEN		= 0,	/* open, don't check ACL's */
+	ACL_POLICY_ALLOW	= 1,	/* allow traffic from MAC */
+	ACL_POLICY_DENY		= 2,	/* deny traffic from MAC */
+};
+
+#define	ACL_HASHSIZE	32
+
+struct acl {
+	TAILQ_ENTRY(acl)	acl_list;
+	LIST_ENTRY(acl)		acl_hash;
+	u_int8_t		acl_macaddr[IEEE80211_ADDR_LEN];
+};
+struct aclstate {
+	acl_lock_t		as_lock;
+	int			as_policy;
+	int			as_nacls;
+	TAILQ_HEAD(, acl)	as_list;	/* list of all ACL's */
+	LIST_HEAD(, acl)	as_hash[ACL_HASHSIZE];
+	struct ieee80211com	*as_ic;
+};
+
+/* simple hash is enough for variation of macaddr */
+#define	ACL_HASH(addr)	\
+	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % ACL_HASHSIZE)
+
+MALLOC_DEFINE(M_80211_ACL, "acl", "802.11 station acl");
+
+static	int acl_free_all(struct ieee80211com *);
+
+static int
+acl_attach(struct ieee80211com *ic)
+{
+	struct aclstate *as;
+
+	MALLOC(as, struct aclstate *, sizeof(struct aclstate),
+		M_80211_ACL, M_NOWAIT | M_ZERO);
+	if (as == NULL)
+		return 0;
+	ACL_LOCK_INIT(as, "acl");
+	TAILQ_INIT(&as->as_list);
+	as->as_policy = ACL_POLICY_OPEN;
+	as->as_ic = ic;
+	ic->ic_as = as;
+	return 1;
+}
+
+static void
+acl_detach(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+
+	acl_free_all(ic);
+	ic->ic_as = NULL;
+	ACL_LOCK_DESTROY(as);
+	FREE(as, M_DEVBUF);
+}
+
+static __inline struct acl *
+_find_acl(struct aclstate *as, const u_int8_t *macaddr)
+{
+	struct acl *acl;
+	int hash;
+
+	hash = ACL_HASH(macaddr);
+	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, macaddr))
+			return acl;
+	}
+	return NULL;
+}
+
+static void
+_acl_free(struct aclstate *as, struct acl *acl)
+{
+	ACL_LOCK_ASSERT(as);
+
+	TAILQ_REMOVE(&as->as_list, acl, acl_list);
+	LIST_REMOVE(acl, acl_hash);
+	FREE(acl, M_80211_ACL);
+	as->as_nacls--;
+}
+
+static int
+acl_check(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+
+	switch (as->as_policy) {
+	case ACL_POLICY_OPEN:
+		return 1;
+	case ACL_POLICY_ALLOW:
+		return _find_acl(as, mac) != NULL;
+	case ACL_POLICY_DENY:
+		return _find_acl(as, mac) == NULL;
+	}
+	return 0;		/* should not happen */
+}
+
+static int
+acl_add(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl, *new;
+	int hash;
+	IEEE80211_LOCK_INFO;
+
+	MALLOC(new, struct acl *, sizeof(struct acl), M_80211_ACL, M_NOWAIT | M_ZERO);
+	if (new == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+			"ACL: add %6D failed, no memory\n", mac, ":");
+		/* XXX statistic */
+		return ENOMEM;
+	}
+
+	ACL_LOCK(as);
+	hash = ACL_HASH(mac);
+	LIST_FOREACH(acl, &as->as_hash[hash], acl_hash) {
+		if (IEEE80211_ADDR_EQ(acl->acl_macaddr, mac)) {
+			ACL_UNLOCK(as);
+			FREE(new, M_80211_ACL);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+				"ACL: add %6D failed, already present\n",
+				(mac), ":");
+			return EEXIST;
+		}
+	}
+	IEEE80211_ADDR_COPY(new->acl_macaddr, mac);
+	TAILQ_INSERT_TAIL(&as->as_list, new, acl_list);
+	LIST_INSERT_HEAD(&as->as_hash[hash], new, acl_hash);
+	as->as_nacls++;
+	ACL_UNLOCK(as);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		"ACL: add %6D\n", (mac), ":");
+	return 0;
+}
+
+static int
+acl_remove(struct ieee80211com *ic, const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl;
+	IEEE80211_LOCK_INFO;
+
+	ACL_LOCK(as);
+	acl = _find_acl(as, mac);
+	if (acl != NULL)
+		_acl_free(as, acl);
+	ACL_UNLOCK(as);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		"ACL: remove %6D%s\n", (mac), ":",
+		acl == NULL ? ", not present" : "");
+
+	return (acl == NULL ? ENOENT : 0);
+}
+
+static int
+acl_free_all(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL, "ACL: %s\n", "free all");
+
+	ACL_LOCK(as);
+	while ((acl = TAILQ_FIRST(&as->as_list)) != NULL)
+		_acl_free(as, acl);
+	ACL_UNLOCK(as);
+
+	return 0;
+}
+
+static int
+acl_setpolicy(struct ieee80211com *ic, int policy)
+{
+	struct aclstate *as = ic->ic_as;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ACL,
+		"ACL: set policy to %u\n", policy);
+
+	switch (policy) {
+	case IEEE80211_MACCMD_POLICY_OPEN:
+		as->as_policy = ACL_POLICY_OPEN;
+		break;
+	case IEEE80211_MACCMD_POLICY_ALLOW:
+		as->as_policy = ACL_POLICY_ALLOW;
+		break;
+	case IEEE80211_MACCMD_POLICY_DENY:
+		as->as_policy = ACL_POLICY_DENY;
+		break;
+	default:
+		return EINVAL;
+	}
+	return 0;
+}
+
+static int
+acl_getpolicy(struct ieee80211com *ic)
+{
+	struct aclstate *as = ic->ic_as;
+
+	return as->as_policy;
+}
+
+static int
+acl_setioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+
+	return EINVAL;
+}
+
+static int
+acl_getioctl(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct aclstate *as = ic->ic_as;
+	struct acl *acl;
+	struct ieee80211req_maclist *ap;
+	int error, space, i;
+	IEEE80211_LOCK_INFO;
+
+	switch (ireq->i_val) {
+	case IEEE80211_MACCMD_POLICY:
+		ireq->i_val = as->as_policy;
+		return 0;
+	case IEEE80211_MACCMD_LIST:
+		space = as->as_nacls * IEEE80211_ADDR_LEN;
+		if (ireq->i_len == 0) {
+			ireq->i_len = space;	/* return required space */
+			return 0;		/* NB: must not error */
+		}
+		MALLOC(ap, struct ieee80211req_maclist *, space,
+			M_TEMP, M_NOWAIT);
+		if (ap == NULL)
+			return ENOMEM;
+		i = 0;
+		ACL_LOCK(as);
+		TAILQ_FOREACH(acl, &as->as_list, acl_list) {
+			IEEE80211_ADDR_COPY(ap[i].ml_macaddr, acl->acl_macaddr);
+			i++;
+		}
+		ACL_UNLOCK(as);
+		if (ireq->i_len >= space) {
+			error = copyout(ap, ireq->i_data, space);
+			ireq->i_len = space;
+		} else
+			error = copyout(ap, ireq->i_data, ireq->i_len);
+		FREE(ap, M_TEMP);
+		return error;
+	}
+	return EINVAL;
+}
+
+static const struct ieee80211_aclator mac = {
+	.iac_name	= "mac",
+	.iac_attach	= acl_attach,
+	.iac_detach	= acl_detach,
+	.iac_check	= acl_check,
+	.iac_add	= acl_add,
+	.iac_remove	= acl_remove,
+	.iac_flush	= acl_free_all,
+	.iac_setpolicy	= acl_setpolicy,
+	.iac_getpolicy	= acl_getpolicy,
+	.iac_setioctl	= acl_setioctl,
+	.iac_getioctl	= acl_getioctl,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_acl_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		if (bootverbose)
+			printf("wlan: <802.11 MAC ACL support>\n");
+		ieee80211_aclator_register(&mac);
+		return 0;
+	case MOD_UNLOAD:
+		ieee80211_aclator_unregister(&mac);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wlan_acl_mod = {
+	"wlan_acl",
+	wlan_acl_modevent,
+	0
+};
+DECLARE_MODULE(wlan_acl, wlan_acl_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_acl, 1);
+MODULE_DEPEND(wlan_acl, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto.c src/sys/netproto/802_11/ieee80211_crypto.c
--- src.preview/sys/netproto/802_11/ieee80211_crypto.c	2005-06-08 19:29:29.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_crypto.c	2005-09-23 11:43:12.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,295 +28,622 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.3 2003/10/17 23:15:30 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.c,v 1.2 2005/06/08 23:29:29 hsu Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
+/*
+ * IEEE 802.11 generic crypto support.
+ */
 #include <sys/param.h>
-#include <sys/systm.h> 
 #include <sys/mbuf.h>   
-#include <sys/malloc.h>
-#include <sys/kernel.h>
+
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
- 
+#include <sys/thread.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/ethernet.h>		/* XXX ETHER_HDR_LEN */
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
+/*
+ * Table of registered cipher modules.
+ */
+static	const struct ieee80211_cipher *ciphers[IEEE80211_CIPHER_MAX];
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-#include <crypto/rc4/rc4.h>
-#define	arc4_ctxlen()			sizeof (struct rc4_state)
-#define	arc4_setkey(_c,_k,_l)		rc4_init(_c,_k,_l)
-#define	arc4_encrypt(_c,_d,_s,_l)	rc4_crypt(_c,_s,_d,_l)
+static	int _ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
 
-static	void ieee80211_crc_init(void);
-static	uint32_t ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len);
+/*
+ * Default "null" key management routines.
+ */
+static int
+null_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k,
+	ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+	if (!(&ic->ic_nw_keys[0] <= k &&
+	     k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) {
+		/*
+		 * Not in the global key table, the driver should handle this
+		 * by allocating a slot in the h/w key table/cache.  In
+		 * lieu of that return key slot 0 for any unicast key
+		 * request.  We disallow the request if this is a group key.
+		 * This default policy does the right thing for legacy hardware
+		 * with a 4 key table.  It also handles devices that pass
+		 * packets through untouched when marked with the WEP bit
+		 * and key index 0.
+		 */
+		if (k->wk_flags & IEEE80211_KEY_GROUP)
+			return 0;
+		*keyix = 0;	/* NB: use key index 0 for ucast key */
+	} else {
+		*keyix = k - ic->ic_nw_keys;
+	}
+	*rxkeyix = IEEE80211_KEYIX_NONE;	/* XXX maybe *keyix? */
+	return 1;
+}
+static int
+null_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k)
+{
+	return 1;
+}
+static 	int
+null_key_set(struct ieee80211com *ic, const struct ieee80211_key *k,
+	     const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return 1;
+}
+static void null_key_update(struct ieee80211com *ic) {}
 
+/*
+ * Write-arounds for common operations.
+ */
+static __inline void
+cipher_detach(struct ieee80211_key *key)
+{
+	key->wk_cipher->ic_detach(key);
+}
+
+static __inline void *
+cipher_attach(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	return key->wk_cipher->ic_attach(ic, key);
+}
+
+/* 
+ * Wrappers for driver key management methods.
+ */
+static __inline int
+dev_key_alloc(struct ieee80211com *ic,
+	const struct ieee80211_key *key,
+	ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+	return ic->ic_crypto.cs_key_alloc(ic, key, keyix, rxkeyix);
+}
+
+static __inline int
+dev_key_delete(struct ieee80211com *ic,
+	const struct ieee80211_key *key)
+{
+	return ic->ic_crypto.cs_key_delete(ic, key);
+}
+
+static __inline int
+dev_key_set(struct ieee80211com *ic, const struct ieee80211_key *key,
+	const u_int8_t mac[IEEE80211_ADDR_LEN])
+{
+	return ic->ic_crypto.cs_key_set(ic, key, mac);
+}
+
+/*
+ * Setup crypto support.
+ */
 void
-ieee80211_crypto_attach(struct ifnet *ifp)
+ieee80211_crypto_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ieee80211_crypto_state *cs = &ic->ic_crypto;
+	int i;
 
+	/* NB: we assume everything is pre-zero'd */
+	cs->cs_def_txkey = IEEE80211_KEYIX_NONE;
+	cs->cs_max_keyix = IEEE80211_WEP_NKID;
+	ciphers[IEEE80211_CIPHER_NONE] = &ieee80211_cipher_none;
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		ieee80211_crypto_resetkey(ic, &cs->cs_nw_keys[i],
+			IEEE80211_KEYIX_NONE);
 	/*
-	 * Setup crypto support.
+	 * Initialize the driver key support routines to noop entries.
+	 * This is useful especially for the cipher test modules.
 	 */
-	ieee80211_crc_init();
-	ic->ic_iv = arc4random();
+	cs->cs_key_alloc = null_key_alloc;
+	cs->cs_key_set = null_key_set;
+	cs->cs_key_delete = null_key_delete;
+	cs->cs_key_update_begin = null_key_update;
+	cs->cs_key_update_end = null_key_update;
 }
 
+/*
+ * Teardown crypto support.
+ */
 void
-ieee80211_crypto_detach(struct ifnet *ifp)
+ieee80211_crypto_detach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	ieee80211_crypto_delglobalkeys(ic);
+}
 
-	if (ic->ic_wep_ctx != NULL) {
-		free(ic->ic_wep_ctx, M_DEVBUF);
-		ic->ic_wep_ctx = NULL;
+/*
+ * Register a crypto cipher module.
+ */
+void
+ieee80211_crypto_register(const struct ieee80211_cipher *cip)
+{
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
+	}
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
 	}
+	ciphers[cip->ic_cipher] = cip;
 }
 
-struct mbuf *
-ieee80211_wep_crypt(struct ifnet *ifp, struct mbuf *m0, int txflag)
+/*
+ * Unregister a crypto cipher module.
+ */
+void
+ieee80211_crypto_unregister(const struct ieee80211_cipher *cip)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct mbuf *m, *n, *n0;
-	struct ieee80211_frame *wh;
-	int i, left, len, moff, noff, kid;
-	uint32_t iv, crc;
-	uint8_t *ivp;
-	void *ctx;
-	uint8_t keybuf[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
-	uint8_t crcbuf[IEEE80211_WEP_CRCLEN];
-
-	n0 = NULL;
-	if ((ctx = ic->ic_wep_ctx) == NULL) {
-		ctx = malloc(arc4_ctxlen(), M_DEVBUF, M_NOWAIT | M_ZERO); /* XXX */
-		if (ctx == NULL) {
-			ic->ic_stats.is_crypto_nomem++;
-			goto fail;
-		}
-		ic->ic_wep_ctx = ctx;
+	if (cip->ic_cipher >= IEEE80211_CIPHER_MAX) {
+		printf("%s: cipher %s has an invalid cipher index %u\n",
+			__func__, cip->ic_name, cip->ic_cipher);
+		return;
 	}
-	m = m0;
-	left = m->m_pkthdr.len;
-	MGETHDR(n, MB_DONTWAIT, m->m_type);
-	n0 = n;
-	if (n == NULL) {
-		if (txflag)
-			ic->ic_stats.is_tx_nombuf++;
-		else
-			ic->ic_stats.is_rx_nombuf++;
-		goto fail;
-	}
-	M_MOVE_PKTHDR(n, m);
-	len = IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN;
-	if (txflag) {
-		n->m_pkthdr.len += len;
-	} else {
-		n->m_pkthdr.len -= len;
-		left -= len;
+	if (ciphers[cip->ic_cipher] != NULL && ciphers[cip->ic_cipher] != cip) {
+		printf("%s: cipher %s registered with a different template\n",
+			__func__, cip->ic_name);
+		return;
+	}
+	/* NB: don't complain about not being registered */
+	/* XXX disallow if references */
+	ciphers[cip->ic_cipher] = NULL;
+}
+
+int
+ieee80211_crypto_available(u_int cipher)
+{
+	return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL;
+}
+
+/* XXX well-known names! */
+static const char *cipher_modnames[] = {
+	"wlan_wep",	/* IEEE80211_CIPHER_WEP */
+	"wlan_tkip",	/* IEEE80211_CIPHER_TKIP */
+	"wlan_aes_ocb",	/* IEEE80211_CIPHER_AES_OCB */
+	"wlan_ccmp",	/* IEEE80211_CIPHER_AES_CCM */
+	"wlan_ckip",	/* IEEE80211_CIPHER_CKIP */
+};
+
+/*
+ * Establish a relationship between the specified key and cipher
+ * and, if necessary, allocate a hardware index from the driver.
+ * Note that when a fixed key index is required it must be specified
+ * and we blindly assign it w/o consulting the driver (XXX).
+ *
+ * This must be the first call applied to a key; all the other key
+ * routines assume wk_cipher is setup.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
+ */
+int
+ieee80211_crypto_newkey(struct ieee80211com *ic,
+	int cipher, int flags, struct ieee80211_key *key)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	const struct ieee80211_cipher *cip;
+	ieee80211_keyix keyix, rxkeyix;
+	void *keyctx;
+	int oflags;
+
+	/*
+	 * Validate cipher and set reference to cipher routines.
+	 */
+	if (cipher >= IEEE80211_CIPHER_MAX) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			"%s: invalid cipher %u\n", __func__, cipher);
+		ic->ic_stats.is_crypto_badcipher++;
+		return 0;
 	}
-	n->m_len = MHLEN;
-	if (n->m_pkthdr.len >= MINCLSIZE) {
-		MCLGET(n, MB_DONTWAIT);
-		if (n->m_flags & M_EXT)
-			n->m_len = n->m_ext.ext_size;
-	}
-	len = sizeof(struct ieee80211_frame);
-	memcpy(mtod(n, caddr_t), mtod(m, caddr_t), len);
-	wh = mtod(n, struct ieee80211_frame *);
-	left -= len;
-	moff = len;
-	noff = len;
-	if (txflag) {
-		kid = ic->ic_wep_txkey;
-		wh->i_fc[1] |= IEEE80211_FC1_WEP;
-                iv = ic->ic_iv;
+	if (cip == NULL) {
 		/*
-		 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
-		 * (B, 255, N) with 3 <= B < 8
+		 * Auto-load cipher module if we have a well-known name
+		 * for it.  It might be better to use string names rather
+		 * than numbers and craft a module name based on the cipher
+		 * name; e.g. wlan_cipher_<cipher-name>.
 		 */
-		if (iv >= 0x03ff00 &&
-		    (iv & 0xf8ff00) == 0x00ff00)
-			iv += 0x000100;
-		ic->ic_iv = iv + 1;
-		/* put iv in little endian to prepare 802.11i */
-		ivp = mtod(n, uint8_t *) + noff;
-		for (i = 0; i < IEEE80211_WEP_IVLEN; i++) {
-			ivp[i] = iv & 0xff;
-			iv >>= 8;
+		if (cipher < N(cipher_modnames)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unregistered cipher %u, load module %s\n",
+				__func__, cipher, cipher_modnames[cipher]);
+			ieee80211_load_module(cipher_modnames[cipher]);
+			/*
+			 * If cipher module loaded it should immediately
+			 * call ieee80211_crypto_register which will fill
+			 * in the entry in the ciphers array.
+			 */
+			cip = ciphers[cipher];
 		}
-		ivp[IEEE80211_WEP_IVLEN] = kid << 6;	/* pad and keyid */
-		noff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
-	} else {
-		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
-		ivp = mtod(m, uint8_t *) + moff;
-		kid = ivp[IEEE80211_WEP_IVLEN] >> 6;
-		moff += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
-	}
-	memcpy(keybuf, ivp, IEEE80211_WEP_IVLEN);
-	memcpy(keybuf + IEEE80211_WEP_IVLEN, ic->ic_nw_keys[kid].wk_key,
-	    ic->ic_nw_keys[kid].wk_len);
-	arc4_setkey(ctx, keybuf,
-	    IEEE80211_WEP_IVLEN + ic->ic_nw_keys[kid].wk_len);
-
-	/* encrypt with calculating CRC */
-	crc = ~0;
-	while (left > 0) {
-		len = m->m_len - moff;
-		if (len == 0) {
-			m = m->m_next;
-			moff = 0;
-			continue;
+		if (cip == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unable to load cipher %u, module %s\n",
+				__func__, cipher,
+				cipher < N(cipher_modnames) ?
+					cipher_modnames[cipher] : "<unknown>");
+			ic->ic_stats.is_crypto_nocipher++;
+			return 0;
 		}
-		if (len > n->m_len - noff) {
-			len = n->m_len - noff;
-			if (len == 0) {
-				MGET(n->m_next, MB_DONTWAIT, n->m_type);
-				if (n->m_next == NULL) {
-					if (txflag)
-						ic->ic_stats.is_tx_nombuf++;
-					else
-						ic->ic_stats.is_rx_nombuf++;
-					goto fail;
-				}
-				n = n->m_next;
-				n->m_len = MLEN;
-				if (left >= MINCLSIZE) {
-					MCLGET(n, MB_DONTWAIT);
-					if (n->m_flags & M_EXT)
-						n->m_len = n->m_ext.ext_size;
-				}
-				noff = 0;
-				continue;
-			}
+	}
+
+	oflags = key->wk_flags;
+	flags &= IEEE80211_KEY_COMMON;
+	/*
+	 * If the hardware does not support the cipher then
+	 * fallback to a host-based implementation.
+	 */
+	if ((ic->ic_caps & (1<<cipher)) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no h/w support for cipher %s, falling back to s/w\n",
+		    __func__, cip->ic_name);
+		flags |= IEEE80211_KEY_SWCRYPT;
+	}
+	/*
+	 * Hardware TKIP with software MIC is an important
+	 * combination; we handle it by flagging each key,
+	 * the cipher modules honor it.
+	 */
+	if (cipher == IEEE80211_CIPHER_TKIP &&
+	    (ic->ic_caps & IEEE80211_C_TKIPMIC) == 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no h/w support for TKIP MIC, falling back to s/w\n",
+		    __func__);
+		flags |= IEEE80211_KEY_SWMIC;
+	}
+
+	/*
+	 * Bind cipher to key instance.  Note we do this
+	 * after checking the device capabilities so the
+	 * cipher module can optimize space usage based on
+	 * whether or not it needs to do the cipher work.
+	 */
+	if (key->wk_cipher != cip || key->wk_flags != flags) {
+again:
+		/*
+		 * Fillin the flags so cipher modules can see s/w
+		 * crypto requirements and potentially allocate
+		 * different state and/or attach different method
+		 * pointers.
+		 *
+		 * XXX this is not right when s/w crypto fallback
+		 *     fails and we try to restore previous state.
+		 */
+		key->wk_flags = flags;
+		keyctx = cip->ic_attach(ic, key);
+		if (keyctx == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				"%s: unable to attach cipher %s\n",
+				__func__, cip->ic_name);
+			key->wk_flags = oflags;	/* restore old flags */
+			ic->ic_stats.is_crypto_attachfail++;
+			return 0;
 		}
-		if (len > left)
-			len = left;
-		arc4_encrypt(ctx, mtod(n, caddr_t) + noff,
-		    mtod(m, caddr_t) + moff, len);
-		if (txflag)
-			crc = ieee80211_crc_update(crc,
-			    mtod(m, uint8_t *) + moff, len);
-		else
-			crc = ieee80211_crc_update(crc,
-			    mtod(n, uint8_t *) + noff, len);
-		left -= len;
-		moff += len;
-		noff += len;
-	}
-	crc = ~crc;
-	if (txflag) {
-		*(uint32_t *)crcbuf = htole32(crc);
-		if (n->m_len >= noff + sizeof(crcbuf))
-			n->m_len = noff + sizeof(crcbuf);
-		else {
-			n->m_len = noff;
-			MGET(n->m_next, MB_DONTWAIT, n->m_type);
-			if (n->m_next == NULL) {
-				ic->ic_stats.is_tx_nombuf++;
-				goto fail;
+		cipher_detach(key);
+		key->wk_cipher = cip;		/* XXX refcnt? */
+		key->wk_private = keyctx;
+	}
+	/*
+	 * Commit to requested usage so driver can see the flags.
+	 */
+	key->wk_flags = flags;
+
+	/*
+	 * Ask the driver for a key index if we don't have one.
+	 * Note that entries in the global key table always have
+	 * an index; this means it's safe to call this routine
+	 * for these entries just to setup the reference to the
+	 * cipher template.  Note also that when using software
+	 * crypto we also call the driver to give us a key index.
+	 */
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		if (!dev_key_alloc(ic, key, &keyix, &rxkeyix)) {
+			/*
+			 * Driver has no room; fallback to doing crypto
+			 * in the host.  We change the flags and start the
+			 * procedure over.  If we get back here then there's
+			 * no hope and we bail.  Note that this can leave
+			 * the key in a inconsistent state if the caller
+			 * continues to use it.
+			 */
+			if ((key->wk_flags & IEEE80211_KEY_SWCRYPT) == 0) {
+				ic->ic_stats.is_crypto_swfallback++;
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+				    "%s: no h/w resources for cipher %s, "
+				    "falling back to s/w\n", __func__,
+				    cip->ic_name);
+				oflags = key->wk_flags;
+				flags |= IEEE80211_KEY_SWCRYPT;
+				if (cipher == IEEE80211_CIPHER_TKIP)
+					flags |= IEEE80211_KEY_SWMIC;
+				goto again;
 			}
-			n = n->m_next;
-			n->m_len = sizeof(crcbuf);
-			noff = 0;
+			ic->ic_stats.is_crypto_keyfail++;
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "%s: unable to setup cipher %s\n",
+			    __func__, cip->ic_name);
+			return 0;
 		}
-		arc4_encrypt(ctx, mtod(n, caddr_t) + noff, crcbuf,
-		    sizeof(crcbuf));
-	} else {
-		n->m_len = noff;
-		for (noff = 0; noff < sizeof(crcbuf); noff += len) {
-			len = sizeof(crcbuf) - noff;
-			if (len > m->m_len - moff)
-				len = m->m_len - moff;
-			if (len > 0)
-				arc4_encrypt(ctx, crcbuf + noff,
-				    mtod(m, caddr_t) + moff, len);
-			m = m->m_next;
-			moff = 0;
-		}
-		if (crc != le32toh(*(uint32_t *)crcbuf)) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				if_printf(ifp, "decrypt CRC error\n");
-				if (ieee80211_debug > 1)
-					ieee80211_dump_pkt(n0->m_data,
-					    n0->m_len, -1, -1);
-			}
-#endif
-			ic->ic_stats.is_rx_decryptcrc++;
-			goto fail;
+		key->wk_keyix = keyix;
+		key->wk_rxkeyix = rxkeyix;
+	}
+	return 1;
+#undef N
+}
+
+/*
+ * Remove the key (no locking, for internal use).
+ */
+static int
+_ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	ieee80211_keyix keyix;
+
+	KASSERT(key->wk_cipher != NULL, ("No cipher!"));
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+	    "%s: %s keyix %u flags 0x%x rsc %ju tsc %ju len %u\n",
+	    __func__, key->wk_cipher->ic_name,
+	    key->wk_keyix, key->wk_flags,
+	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+
+	keyix = key->wk_keyix;
+	if (keyix != IEEE80211_KEYIX_NONE) {
+		/*
+		 * Remove hardware entry.
+		 */
+		/* XXX key cache */
+		if (!dev_key_delete(ic, key)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "%s: driver did not delete key index %u\n",
+			    __func__, keyix);
+			ic->ic_stats.is_crypto_delkey++;
+			/* XXX recovery? */
 		}
 	}
-	m_freem(m0);
-	return n0;
+	cipher_detach(key);
+	memset(key, 0, sizeof(*key));
+	ieee80211_crypto_resetkey(ic, key, IEEE80211_KEYIX_NONE);
+	return 1;
+}
+
+/*
+ * Remove the specified key.
+ */
+int
+ieee80211_crypto_delkey(struct ieee80211com *ic, struct ieee80211_key *key)
+{
+	int status;
+
+	ieee80211_key_update_begin(ic);
+	status = _ieee80211_crypto_delkey(ic, key);
+	ieee80211_key_update_end(ic);
+	return status;
+}
+
+/*
+ * Clear the global key table.
+ */
+void
+ieee80211_crypto_delglobalkeys(struct ieee80211com *ic)
+{
+	int i;
 
-  fail:
-	m_freem(m0);
-	m_freem(n0);
-	return NULL;
+	ieee80211_key_update_begin(ic);
+	for (i = 0; i < IEEE80211_WEP_NKID; i++)
+		(void) _ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[i]);
+	ieee80211_key_update_end(ic);
 }
 
 /*
- * CRC 32 -- routine from RFC 2083
+ * Set the contents of the specified key.
+ *
+ * Locking must be handled by the caller using:
+ *	ieee80211_key_update_begin(ic);
+ *	ieee80211_key_update_end(ic);
  */
+int
+ieee80211_crypto_setkey(struct ieee80211com *ic, struct ieee80211_key *key,
+		const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+	const struct ieee80211_cipher *cip = key->wk_cipher;
+
+	KASSERT(cip != NULL, ("No cipher!"));
 
-/* Table of CRCs of all 8-bit messages */
-static uint32_t ieee80211_crc_table[256];
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+	    "%s: %s keyix %u flags 0x%x mac %6D rsc %ju tsc %ju len %u\n",
+	    __func__, cip->ic_name, key->wk_keyix,
+	    key->wk_flags, (macaddr), ":",
+	    key->wk_keyrsc, key->wk_keytsc, key->wk_keylen);
+
+	/*
+	 * Give cipher a chance to validate key contents.
+	 * XXX should happen before modifying state.
+	 */
+	if (!cip->ic_setkey(key)) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: cipher %s rejected key index %u len %u flags 0x%x\n",
+		    __func__, cip->ic_name, key->wk_keyix,
+		    key->wk_keylen, key->wk_flags);
+		ic->ic_stats.is_crypto_setkey_cipher++;
+		return 0;
+	}
+	if (key->wk_keyix == IEEE80211_KEYIX_NONE) {
+		/* XXX nothing allocated, should not happen */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "%s: no key index; should not happen!\n", __func__);
+		ic->ic_stats.is_crypto_setkey_nokey++;
+		return 0;
+	}
+	return dev_key_set(ic, key, macaddr);
+}
 
-/* Make the table for a fast CRC. */
-static void
-ieee80211_crc_init(void)
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+struct ieee80211_key *
+ieee80211_crypto_encap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m)
 {
-	uint32_t c;
-	int n, k;
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	const struct ieee80211_cipher *cip;
+	u_int8_t keyid;
 
-	for (n = 0; n < 256; n++) {
-		c = (uint32_t)n;
-		for (k = 0; k < 8; k++) {
-			if (c & 1)
-				c = 0xedb88320UL ^ (c >> 1);
-			else
-				c = c >> 1;
+	/*
+	 * Multicast traffic always uses the multicast key.
+	 * Otherwise if a unicast key is set we use that and
+	 * it is always key index 0.  When no unicast key is
+	 * set we fall back to the default transmit key.
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "[%6D] no default transmit key (%s) deftxkey %u\n",
+			    (wh->i_addr1), ":", __func__,
+			    ic->ic_def_txkey);
+			ic->ic_stats.is_tx_nodefkey++;
+			return NULL;
 		}
-		ieee80211_crc_table[n] = c;
+		keyid = ic->ic_def_txkey;
+		k = &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		keyid = 0;
+		k = &ni->ni_ucastkey;
 	}
+	cip = k->wk_cipher;
+	return (cip->ic_encap(k, m, keyid<<6) ? k : NULL);
 }
 
 /*
- * Update a running CRC with the bytes buf[0..len-1]--the CRC
- * should be initialized to all 1's, and the transmitted value
- * is the 1's complement of the final running CRC
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame that has the WEP/Privacy bit set.
  */
+struct ieee80211_key *
+ieee80211_crypto_decap(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m, int hdrlen)
+{
+#define	IEEE80211_WEP_HDRLEN	(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN)
+#define	IEEE80211_WEP_MINLEN \
+	(sizeof(struct ieee80211_frame) + \
+	IEEE80211_WEP_HDRLEN + IEEE80211_WEP_CRCLEN)
+	struct ieee80211_key *k;
+	struct ieee80211_frame *wh;
+	const struct ieee80211_cipher *cip;
+	const u_int8_t *ivp;
+	u_int8_t keyid;
+
+	/* NB: this minimum size data frame could be bigger */
+	if (m->m_pkthdr.len < IEEE80211_WEP_MINLEN) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: WEP data frame too short, len %u\n",
+			__func__, m->m_pkthdr.len);
+		ic->ic_stats.is_rx_tooshort++;	/* XXX need unique stat? */
+		return NULL;
+	}
+
+	/*
+	 * Locate the key. If unicast and there is no unicast
+	 * key then we fall back to the key id in the header.
+	 * This assumes unicast keys are only configured when
+	 * the key id in the header is meaningless (typically 0).
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	ivp = mtod(m, const u_int8_t *) + hdrlen;	/* XXX contig */
+	keyid = ivp[IEEE80211_WEP_IVLEN];
+	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
+	    ni->ni_ucastkey.wk_cipher == &ieee80211_cipher_none)
+		k = &ic->ic_nw_keys[keyid >> 6];
+	else
+		k = &ni->ni_ucastkey;
+
+	/*
+	 * Insure crypto header is contiguous for all decap work.
+	 */
+	cip = k->wk_cipher;
+	if (m->m_len < hdrlen + cip->ic_header &&
+	    (m = m_pullup(m, hdrlen + cip->ic_header)) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		    "[%6D] unable to pullup %s header\n",
+		    (wh->i_addr2), ":", cip->ic_name);
+		ic->ic_stats.is_rx_wepfail++;	/* XXX */
+		return 0;
+	}
+
+	return (cip->ic_decap(k, m, hdrlen) ? k : NULL);
+#undef IEEE80211_WEP_MINLEN
+#undef IEEE80211_WEP_HDRLEN
+}
 
-static uint32_t
-ieee80211_crc_update(uint32_t crc, uint8_t *buf, int len)
+/*
+ * Append the specified data to the indicated mbuf chain,
+ * Extend the mbuf chain if the new data does not fit in
+ * existing space.
+ *
+ * Return 1 if able to complete the job; otherwise 0.
+ */
+int
+m_append(struct mbuf *m0, int len, c_caddr_t cp)
 {
-	uint8_t *endbuf;
+	struct mbuf *m, *n;
+	int remainder, space;
 
-	for (endbuf = buf + len; buf < endbuf; buf++)
-		crc = ieee80211_crc_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
-	return crc;
+	for (m = m0; m->m_next != NULL; m = m->m_next)
+		;
+	remainder = len;
+	space = M_TRAILINGSPACE(m);
+	if (space > 0) {
+		/*
+		 * Copy into available space.
+		 */
+		if (space > remainder)
+			space = remainder;
+		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
+		m->m_len += space;
+		cp += space, remainder -= space;
+	}
+	while (remainder > 0) {
+		/*
+		 * Allocate a new mbuf; could check space
+		 * and allocate a cluster instead.
+		 */
+		n = m_get(MB_DONTWAIT, m->m_type);
+		if (n == NULL)
+			break;
+		n->m_len = min(MLEN, remainder);
+		bcopy(cp, mtod(n, caddr_t), n->m_len);
+		cp += n->m_len, remainder -= n->m_len;
+		m->m_next = n;
+		m = n;
+	}
+	if (m0->m_flags & M_PKTHDR)
+		m0->m_pkthdr.len += len - remainder;
+	return (remainder == 0);
 }
+
+
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto.h src/sys/netproto/802_11/ieee80211_crypto.h
--- src.preview/sys/netproto/802_11/ieee80211_crypto.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_crypto.h	2005-09-23 11:46:12.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,24 +29,209 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.2 2003/06/27 05:13:52 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_CRYPTO_H_
-#define	_NETPROTO_802_11_IEEE80211_CRYPTO_H_
+#ifndef _NET80211_IEEE80211_CRYPTO_H_
+#define _NET80211_IEEE80211_CRYPTO_H_
 
 /*
  * 802.11 protocol crypto-related definitions.
  */
 #define	IEEE80211_KEYBUF_SIZE	16
+#define	IEEE80211_MICBUF_SIZE	(8+8)	/* space for both tx+rx keys */
 
+/*
+ * Old WEP-style key.  Deprecated.
+ */
 struct ieee80211_wepkey {
-	int			wk_len;
-	uint8_t			wk_key[IEEE80211_KEYBUF_SIZE];
+	u_int		wk_len;		/* key length in bytes */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE];
+};
+
+struct ieee80211_cipher;
+
+/*
+ * Crypto key state.  There is sufficient room for all supported
+ * ciphers (see below).  The underlying ciphers are handled
+ * separately through loadable cipher modules that register with
+ * the generic crypto support.  A key has a reference to an instance
+ * of the cipher; any per-key state is hung off wk_private by the
+ * cipher when it is attached.  Ciphers are automatically called
+ * to detach and cleanup any such state when the key is deleted.
+ *
+ * The generic crypto support handles encap/decap of cipher-related
+ * frame contents for both hardware- and software-based implementations.
+ * A key requiring software crypto support is automatically flagged and
+ * the cipher is expected to honor this and do the necessary work.
+ * Ciphers such as TKIP may also support mixed hardware/software
+ * encrypt/decrypt and MIC processing.
+ */
+typedef u_int16_t ieee80211_keyix;	/* h/w key index */
+
+struct ieee80211_key {
+	u_int8_t	wk_keylen;	/* key length in bytes */
+	u_int8_t	wk_pad;
+	u_int16_t	wk_flags;
+#define	IEEE80211_KEY_XMIT	0x01	/* key used for xmit */
+#define	IEEE80211_KEY_RECV	0x02	/* key used for recv */
+#define	IEEE80211_KEY_GROUP	0x04	/* key used for WPA group operation */
+#define	IEEE80211_KEY_SWCRYPT	0x10	/* host-based encrypt/decrypt */
+#define	IEEE80211_KEY_SWMIC	0x20	/* host-based enmic/demic */
+	ieee80211_keyix	wk_keyix;	/* h/w key index */
+	ieee80211_keyix	wk_rxkeyix;	/* optional h/w rx key index */
+	u_int8_t	wk_key[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+#define	wk_txmic	wk_key+IEEE80211_KEYBUF_SIZE+0	/* XXX can't () right */
+#define	wk_rxmic	wk_key+IEEE80211_KEYBUF_SIZE+8	/* XXX can't () right */
+	u_int64_t	wk_keyrsc;	/* key receive sequence counter */
+	u_int64_t	wk_keytsc;	/* key transmit sequence counter */
+	const struct ieee80211_cipher *wk_cipher;
+	void		*wk_private;	/* private cipher state */
+};
+#define	IEEE80211_KEY_COMMON 		/* common flags passed in by apps */\
+	(IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP)
+
+/*
+ * NB: these values are ordered carefully; there are lots of
+ * of implications in any reordering.  In particular beware
+ * that 4 is not used to avoid conflicting with IEEE80211_F_PRIVACY.
+ */
+#define	IEEE80211_CIPHER_WEP		0
+#define	IEEE80211_CIPHER_TKIP		1
+#define	IEEE80211_CIPHER_AES_OCB	2
+#define	IEEE80211_CIPHER_AES_CCM	3
+#define	IEEE80211_CIPHER_CKIP		5
+#define	IEEE80211_CIPHER_NONE		6	/* pseudo value */
+
+#define	IEEE80211_CIPHER_MAX		(IEEE80211_CIPHER_NONE+1)
+
+#define	IEEE80211_KEYIX_NONE	((ieee80211_keyix) -1)
+
+#if defined(__KERNEL__) || defined(_KERNEL)
+
+struct ieee80211com;
+struct ieee80211_node;
+struct mbuf;
+
+/*
+ * Crypto state kept in each ieee80211com.  Some of this
+ * can/should be shared when virtual AP's are supported.
+ *
+ * XXX save reference to ieee80211com to properly encapsulate state.
+ * XXX split out crypto capabilities from ic_caps
+ */
+struct ieee80211_crypto_state {
+	struct ieee80211_key	cs_nw_keys[IEEE80211_WEP_NKID];
+	ieee80211_keyix		cs_def_txkey;	/* default/group tx key index */
+	u_int16_t		cs_max_keyix;	/* max h/w key index */
+
+	int			(*cs_key_alloc)(struct ieee80211com *,
+					const struct ieee80211_key *,
+					ieee80211_keyix *, ieee80211_keyix *);
+	int			(*cs_key_delete)(struct ieee80211com *, 
+					const struct ieee80211_key *);
+	int			(*cs_key_set)(struct ieee80211com *,
+					const struct ieee80211_key *,
+					const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	void			(*cs_key_update_begin)(struct ieee80211com *);
+	void			(*cs_key_update_end)(struct ieee80211com *);
+};
+
+void	ieee80211_crypto_attach(struct ieee80211com *);
+void	ieee80211_crypto_detach(struct ieee80211com *);
+int	ieee80211_crypto_newkey(struct ieee80211com *,
+		int cipher, int flags, struct ieee80211_key *);
+int	ieee80211_crypto_delkey(struct ieee80211com *,
+		struct ieee80211_key *);
+int	ieee80211_crypto_setkey(struct ieee80211com *,
+		struct ieee80211_key *, const u_int8_t macaddr[IEEE80211_ADDR_LEN]);
+void	ieee80211_crypto_delglobalkeys(struct ieee80211com *);
+
+/*
+ * Template for a supported cipher.  Ciphers register with the
+ * crypto code and are typically loaded as separate modules
+ * (the null cipher is always present).
+ * XXX may need refcnts
+ */
+struct ieee80211_cipher {
+	const char *ic_name;		/* printable name */
+	u_int	ic_cipher;		/* IEEE80211_CIPHER_* */
+	u_int	ic_header;		/* size of privacy header (bytes) */
+	u_int	ic_trailer;		/* size of privacy trailer (bytes) */
+	u_int	ic_miclen;		/* size of mic trailer (bytes) */
+	void*	(*ic_attach)(struct ieee80211com *, struct ieee80211_key *);
+	void	(*ic_detach)(struct ieee80211_key *);
+	int	(*ic_setkey)(struct ieee80211_key *);
+	int	(*ic_encap)(struct ieee80211_key *, struct mbuf *,
+			u_int8_t keyid);
+	int	(*ic_decap)(struct ieee80211_key *, struct mbuf *, int);
+	int	(*ic_enmic)(struct ieee80211_key *, struct mbuf *, int);
+	int	(*ic_demic)(struct ieee80211_key *, struct mbuf *, int);
 };
+extern	const struct ieee80211_cipher ieee80211_cipher_none;
+
+void	ieee80211_crypto_register(const struct ieee80211_cipher *);
+void	ieee80211_crypto_unregister(const struct ieee80211_cipher *);
+int	ieee80211_crypto_available(u_int cipher);
+
+struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *,
+		struct ieee80211_node *, struct mbuf *);
+struct ieee80211_key *ieee80211_crypto_decap(struct ieee80211com *,
+		struct ieee80211_node *, struct mbuf *, int);
+
+/*
+ * Check and remove any MIC.
+ */
+static __inline int
+ieee80211_crypto_demic(struct ieee80211com *ic __unused, struct ieee80211_key *k,
+	struct mbuf *m, int force)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? cip->ic_demic(k, m, force) : 1);
+}
+
+/*
+ * Add any MIC.
+ */
+static __inline int
+ieee80211_crypto_enmic(struct ieee80211com *ic __unused,
+	struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	const struct ieee80211_cipher *cip = k->wk_cipher;
+	return (cip->ic_miclen > 0 ? cip->ic_enmic(k, m, force) : 1);
+}
+
+/* 
+ * Reset key state to an unused state.  The crypto
+ * key allocation mechanism insures other state (e.g.
+ * key data) is properly setup before a key is used.
+ */
+static __inline void
+ieee80211_crypto_resetkey(struct ieee80211com *ic,
+	struct ieee80211_key *k, ieee80211_keyix ix)
+{
+	k->wk_cipher = &ieee80211_cipher_none;;
+	k->wk_private = k->wk_cipher->ic_attach(ic, k);
+	k->wk_keyix = k->wk_rxkeyix = ix;
+	k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+}
+
+/*
+ * Crypt-related notification methods.
+ */
+void	ieee80211_notify_replay_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, const struct ieee80211_key *,
+		u_int64_t rsc);
+void	ieee80211_notify_michael_failure(struct ieee80211com *,
+		const struct ieee80211_frame *, u_int keyix);
+
+/*
+ * Append the specified data to the indicated mbuf chain,
+ * Extend the mbuf chain if the new data does not fit in
+ * existing space.
+ *
+ * Return 1 if able to complete the job; otherwise 0.
+ */
+int m_append(struct mbuf *m0, int len, c_caddr_t cp);
 
-void		ieee80211_crypto_attach(struct ifnet *);
-void		ieee80211_crypto_detach(struct ifnet *);
-struct mbuf	*ieee80211_wep_crypt(struct ifnet *, struct mbuf *, int);
-#endif /* _NETPROTO_802_11_IEEE80211_CRYPTO_H_ */
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+#endif /* _NET80211_IEEE80211_CRYPTO_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto_ccmp.c src/sys/netproto/802_11/ieee80211_crypto_ccmp.c
--- src.preview/sys/netproto/802_11/ieee80211_crypto_ccmp.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_crypto_ccmp.c	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,653 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11i AES-CCMP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+#include <crypto/rijndael/rijndael.h>
+
+#define AES_BLOCK_LEN 16
+
+struct ccmp_ctx {
+	struct ieee80211com *cc_ic;	/* for diagnostics */
+	rijndael_ctx	     cc_aes;
+};
+
+static	void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void ccmp_detach(struct ieee80211_key *);
+static	int ccmp_setkey(struct ieee80211_key *);
+static	int ccmp_encap(struct ieee80211_key *k, struct mbuf *, u_int8_t keyid);
+static	int ccmp_decap(struct ieee80211_key *, struct mbuf *, int);
+static	int ccmp_enmic(struct ieee80211_key *, struct mbuf *, int);
+static	int ccmp_demic(struct ieee80211_key *, struct mbuf *, int);
+
+static const struct ieee80211_cipher ccmp = {
+	.ic_name	= "AES-CCM",
+	.ic_cipher	= IEEE80211_CIPHER_AES_CCM,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+			  IEEE80211_WEP_EXTIVLEN,
+	.ic_trailer	= IEEE80211_WEP_MICLEN,
+	.ic_miclen	= 0,
+	.ic_attach	= ccmp_attach,
+	.ic_detach	= ccmp_detach,
+	.ic_setkey	= ccmp_setkey,
+	.ic_encap	= ccmp_encap,
+	.ic_decap	= ccmp_decap,
+	.ic_enmic	= ccmp_enmic,
+	.ic_demic	= ccmp_demic,
+};
+
+static	int ccmp_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+static	int ccmp_decrypt(struct ieee80211_key *, u_int64_t pn,
+		struct mbuf *, int hdrlen);
+
+static void *
+ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx;
+
+	MALLOC(ctx, struct ccmp_ctx *, sizeof(struct ccmp_ctx),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		return NULL;
+	}
+	ctx->cc_ic = ic;
+	return ctx;
+}
+
+static void
+ccmp_detach(struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+
+	FREE(ctx, M_DEVBUF);
+}
+
+static int
+ccmp_setkey(struct ieee80211_key *k)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+
+	if (k->wk_keylen != (128/NBBY)) {
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			"%s: Invalid key length %u, expecting %u\n",
+			__func__, k->wk_keylen, 128/NBBY);
+		return 0;
+	}
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT)
+		rijndael_set_key(&ctx->cc_aes, k->wk_key, k->wk_keylen*NBBY);
+	return 1;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+ccmp_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->cc_ic;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	M_PREPEND(m, ccmp.ic_header, M_NOWAIT);
+	if (m == NULL)
+		return 0;
+	ivp = mtod(m, u_int8_t *);
+	ovbcopy(ivp + ccmp.ic_header, ivp, hdrlen);
+	ivp += hdrlen;
+
+	k->wk_keytsc++;		/* XXX wrap at 48 bits */
+	ivp[0] = k->wk_keytsc >> 0;		/* PN0 */
+	ivp[1] = k->wk_keytsc >> 8;		/* PN1 */
+	ivp[2] = 0;				/* Reserved */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* PN2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* PN3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* PN4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* PN5 */
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !ccmp_encrypt(k, m, hdrlen))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+ccmp_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+
+	return 1;
+}
+
+static __inline uint64_t
+READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
+{
+	uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+	uint16_t iv16 = (b4 << 0) | (b5 << 8);
+	return (((uint64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame. The specified key should be correct but
+ * is also verified.
+ */
+static int
+ccmp_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+	struct ccmp_ctx *ctx = k->wk_private;
+	struct ieee80211_frame *wh;
+	uint8_t *ivp;
+	uint64_t pn;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	ivp = mtod(m, uint8_t *) + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			"[%6D] Missing ExtIV for AES-CCM cipher\n",
+			(wh->i_addr2), ":");
+		ctx->cc_ic->ic_stats.is_rx_ccmpformat++;
+		return 0;
+	}
+	pn = READ_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
+	if (pn <= k->wk_keyrsc) {
+		/*
+		 * Replay violation.
+		 */
+		ieee80211_notify_replay_failure(ctx->cc_ic, wh, k, pn);
+		ctx->cc_ic->ic_stats.is_rx_ccmpreplay++;
+		return 0;
+	}
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * If so we just strip the header; otherwise we need to
+	 * handle the decrypt in software.  Note that for the
+	 * latter we leave the header in place for use in the
+	 * decryption work.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !ccmp_decrypt(k, pn, m, hdrlen))
+		return 0;
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + ccmp.ic_header, hdrlen);
+	m_adj(m, ccmp.ic_header);
+	m_adj(m, -ccmp.ic_trailer);
+
+	/*
+	 * Ok to update rsc now.
+	 */
+	k->wk_keyrsc = pn;
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+ccmp_demic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	return 1;
+}
+
+static __inline void
+xor_block(uint8_t *b, const uint8_t *a, size_t len)
+{
+	int i;
+	for (i = 0; i < len; i++)
+		b[i] ^= a[i];
+}
+
+/*
+ * Host AP crypt: host-based CCMP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline at xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static void
+ccmp_init_blocks(rijndael_ctx *ctx, struct ieee80211_frame *wh,
+	u_int64_t pn, size_t dlen,
+	uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN],
+	uint8_t auth[AES_BLOCK_LEN], uint8_t s0[AES_BLOCK_LEN])
+{
+#define	IS_4ADDRESS(wh) \
+	((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+#define	IS_QOS_DATA(wh)	IEEE80211_QOS_HAS_SEQ(wh)
+
+	/* CCM Initial Block:
+	 * Flag (Include authentication header, M=3 (8-octet MIC),
+	 *       L=1 (2-octet Dlen))
+	 * Nonce: 0x00 | A2 | PN
+	 * Dlen */
+	b0[0] = 0x59;
+	/* NB: b0[1] set below */
+	IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
+	b0[8] = pn >> 40;
+	b0[9] = pn >> 32;
+	b0[10] = pn >> 24;
+	b0[11] = pn >> 16;
+	b0[12] = pn >> 8;
+	b0[13] = pn >> 0;
+	b0[14] = (dlen >> 8) & 0xff;
+	b0[15] = dlen & 0xff;
+
+	/* AAD:
+	 * FC with bits 4..6 and 11..13 masked to zero; 14 is always one
+	 * A1 | A2 | A3
+	 * SC with bits 4..15 (seq#) masked to zero
+	 * A4 (if present)
+	 * QC (if present)
+	 */
+	aad[0] = 0;	/* AAD length >> 8 */
+	/* NB: aad[1] set below */
+	aad[2] = wh->i_fc[0] & 0x8f;	/* XXX magic #s */
+	aad[3] = wh->i_fc[1] & 0xc7;	/* XXX magic #s */
+	/* NB: we know 3 addresses are contiguous */
+	memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
+	aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
+	aad[23] = 0; /* all bits masked */
+	/*
+	 * Construct variable-length portion of AAD based
+	 * on whether this is a 4-address frame/QOS frame.
+	 * We always zero-pad to 32 bytes before running it
+	 * through the cipher.
+	 *
+	 * We also fill in the priority bits of the CCM
+	 * initial block as we know whether or not we have
+	 * a QOS frame.
+	 */
+	if (IS_4ADDRESS(wh)) {
+		IEEE80211_ADDR_COPY(aad + 24,
+			((struct ieee80211_frame_addr4 *)wh)->i_addr4);
+		if (IS_QOS_DATA(wh)) {
+			struct ieee80211_qosframe_addr4 *qwh4 =
+				(struct ieee80211_qosframe_addr4 *) wh;
+			aad[30] = qwh4->i_qos[0] & 0x0f;/* just priority bits */
+			aad[31] = 0;
+			b0[1] = aad[30];
+			aad[1] = 22 + IEEE80211_ADDR_LEN + 2;
+		} else {
+			*(u_int16_t *)&aad[30] = 0;
+			b0[1] = 0;
+			aad[1] = 22 + IEEE80211_ADDR_LEN;
+		}
+	} else {
+		if (IS_QOS_DATA(wh)) {
+			struct ieee80211_qosframe *qwh =
+				(struct ieee80211_qosframe*) wh;
+			aad[24] = qwh->i_qos[0] & 0x0f;	/* just priority bits */
+			aad[25] = 0;
+			b0[1] = aad[24];
+			aad[1] = 22 + 2;
+		} else {
+			*(u_int16_t *)&aad[24] = 0;
+			b0[1] = 0;
+			aad[1] = 22;
+		}
+		*(u_int16_t *)&aad[26] = 0;
+		*(u_int32_t *)&aad[28] = 0;
+	}
+
+	/* Start with the first block and AAD */
+	rijndael_encrypt(ctx, b0, auth);
+	xor_block(auth, aad, AES_BLOCK_LEN);
+	rijndael_encrypt(ctx, auth, auth);
+	xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN);
+	rijndael_encrypt(ctx, auth, auth);
+	b0[0] &= 0x07;
+	b0[14] = b0[15] = 0;
+	rijndael_encrypt(ctx, b0, s0);
+#undef	IS_QOS_DATA
+#undef	IS_4ADDRESS
+}
+
+#define	CCMP_ENCRYPT(_i, _b, _b0, _pos, _e, _len) do {	\
+	/* Authentication */				\
+	xor_block(_b, _pos, _len);			\
+	rijndael_encrypt(&ctx->cc_aes, _b, _b);		\
+	/* Encryption, with counter */			\
+	_b0[14] = (_i >> 8) & 0xff;			\
+	_b0[15] = _i & 0xff;				\
+	rijndael_encrypt(&ctx->cc_aes, _b0, _e);	\
+	xor_block(_pos, _e, _len);			\
+} while (0)
+
+static int
+ccmp_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+	struct ccmp_ctx *ctx = key->wk_private;
+	struct ieee80211_frame *wh;
+	struct mbuf *m = m0;
+	int data_len, i, space;
+	uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN],
+		e[AES_BLOCK_LEN], s0[AES_BLOCK_LEN];
+	uint8_t *pos;
+
+	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header);
+	ccmp_init_blocks(&ctx->cc_aes, wh, key->wk_keytsc,
+		data_len, b0, aad, b, s0);
+
+	i = 1;
+	pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
+	/* NB: assumes header is entirely in first mbuf */
+	space = m->m_len - (hdrlen + ccmp.ic_header);
+	for (;;) {
+		if (space > data_len)
+			space = data_len;
+		/*
+		 * Do full blocks.
+		 */
+		while (space >= AES_BLOCK_LEN) {
+			CCMP_ENCRYPT(i, b, b0, pos, e, AES_BLOCK_LEN);
+			pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
+			data_len -= AES_BLOCK_LEN;
+			i++;
+		}
+		if (data_len <= 0)		/* no more data */
+			break;
+		m = m->m_next;
+		if (m == NULL) {		/* last buffer */
+			if (space != 0) {
+				/*
+				 * Short last block.
+				 */
+				CCMP_ENCRYPT(i, b, b0, pos, e, space);
+			}
+			break;
+		}
+		if (space != 0) {
+			uint8_t *pos_next;
+			int space_next;
+			int len, dl, sp;
+			struct mbuf *n;
+
+			/*
+			 * Block straddles one or more mbufs, gather data
+			 * into the block buffer b, apply the cipher, then
+			 * scatter the results back into the mbuf chain.
+			 * The buffer will automatically get space bytes
+			 * of data at offset 0 copied in+out by the
+			 * CCMP_ENCRYPT request so we must take care of
+			 * the remaining data.
+			 */
+			n = m;
+			dl = data_len;
+			sp = space;
+			for (;;) {
+				pos_next = mtod(n, uint8_t *);
+				len = min(dl, AES_BLOCK_LEN);
+				space_next = len > sp ? len - sp : 0;
+				if (n->m_len >= space_next) {
+					/*
+					 * This mbuf has enough data; just grab
+					 * what we need and stop.
+					 */
+					xor_block(b+sp, pos_next, space_next);
+					break;
+				}
+				/*
+				 * This mbuf's contents are insufficient,
+				 * take 'em all and prepare to advance to
+				 * the next mbuf.
+				 */
+				xor_block(b+sp, pos_next, n->m_len);
+				sp += n->m_len, dl -= n->m_len;
+				n = n->m_next;
+				if (n == NULL)
+					break;
+			}
+
+			CCMP_ENCRYPT(i, b, b0, pos, e, space);
+
+			/* NB: just like above, but scatter data to mbufs */
+			dl = data_len;
+			sp = space;
+			for (;;) {
+				pos_next = mtod(m, uint8_t *);
+				len = min(dl, AES_BLOCK_LEN);
+				space_next = len > sp ? len - sp : 0;
+				if (m->m_len >= space_next) {
+					xor_block(pos_next, e+sp, space_next);
+					break;
+				}
+				xor_block(pos_next, e+sp, m->m_len);
+				sp += m->m_len, dl -= m->m_len;
+				m = m->m_next;
+				if (m == NULL)
+					goto done;
+			}
+			/*
+			 * Do bookkeeping.  m now points to the last mbuf
+			 * we grabbed data from.  We know we consumed a
+			 * full block of data as otherwise we'd have hit
+			 * the end of the mbuf chain, so deduct from data_len.
+			 * Otherwise advance the block number (i) and setup
+			 * pos+space to reflect contents of the new mbuf.
+			 */
+			data_len -= AES_BLOCK_LEN;
+			i++;
+			pos = pos_next + space_next;
+			space = m->m_len - space_next;
+		} else {
+			/*
+			 * Setup for next buffer.
+			 */
+			pos = mtod(m, uint8_t *);
+			space = m->m_len;
+		}
+	}
+done:
+	/* tack on MIC */
+	xor_block(b, s0, ccmp.ic_trailer);
+	return m_append(m0, ccmp.ic_trailer, b);
+}
+#undef CCMP_ENCRYPT
+
+#define	CCMP_DECRYPT(_i, _b, _b0, _pos, _a, _len) do {	\
+	/* Decrypt, with counter */			\
+	_b0[14] = (_i >> 8) & 0xff;			\
+	_b0[15] = _i & 0xff;				\
+	rijndael_encrypt(&ctx->cc_aes, _b0, _b);	\
+	xor_block(_pos, _b, _len);			\
+	/* Authentication */				\
+	xor_block(_a, _pos, _len);			\
+	rijndael_encrypt(&ctx->cc_aes, _a, _a);		\
+} while (0)
+
+static int
+ccmp_decrypt(struct ieee80211_key *key, u_int64_t pn, struct mbuf *m, int hdrlen)
+{
+	struct ccmp_ctx *ctx = key->wk_private;
+	struct ieee80211_frame *wh;
+	uint8_t aad[2 * AES_BLOCK_LEN];
+	uint8_t b0[AES_BLOCK_LEN], b[AES_BLOCK_LEN], a[AES_BLOCK_LEN];
+	uint8_t mic[AES_BLOCK_LEN];
+	size_t data_len;
+	int i;
+	uint8_t *pos;
+	u_int space;
+
+	ctx->cc_ic->ic_stats.is_crypto_ccmp++;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	data_len = m->m_pkthdr.len - (hdrlen + ccmp.ic_header + ccmp.ic_trailer);
+	ccmp_init_blocks(&ctx->cc_aes, wh, pn, data_len, b0, aad, a, b);
+	m_copydata(m, m->m_pkthdr.len - ccmp.ic_trailer, ccmp.ic_trailer, mic);
+	xor_block(mic, b, ccmp.ic_trailer);
+
+	i = 1;
+	pos = mtod(m, uint8_t *) + hdrlen + ccmp.ic_header;
+	space = m->m_len - (hdrlen + ccmp.ic_header);
+	for (;;) {
+		if (space > data_len)
+			space = data_len;
+		while (space >= AES_BLOCK_LEN) {
+			CCMP_DECRYPT(i, b, b0, pos, a, AES_BLOCK_LEN);
+			pos += AES_BLOCK_LEN, space -= AES_BLOCK_LEN;
+			data_len -= AES_BLOCK_LEN;
+			i++;
+		}
+		if (data_len <= 0)		/* no more data */
+			break;
+		m = m->m_next;
+		if (m == NULL) {		/* last buffer */
+			if (space != 0)		/* short last block */
+				CCMP_DECRYPT(i, b, b0, pos, a, space);
+			break;
+		}
+		if (space != 0) {
+			uint8_t *pos_next;
+			u_int space_next;
+			u_int len;
+
+			/*
+			 * Block straddles buffers, split references.  We
+			 * do not handle splits that require >2 buffers
+			 * since rx'd frames are never badly fragmented
+			 * because drivers typically recv in clusters.
+			 */
+			pos_next = mtod(m, uint8_t *);
+			len = min(data_len, AES_BLOCK_LEN);
+			space_next = len > space ? len - space : 0;
+			KASSERT(m->m_len >= space_next,
+				("not enough data in following buffer, "
+				"m_len %u need %u\n", m->m_len, space_next));
+
+			xor_block(b+space, pos_next, space_next);
+			CCMP_DECRYPT(i, b, b0, pos, a, space);
+			xor_block(pos_next, b+space, space_next);
+			data_len -= len;
+			i++;
+
+			pos = pos_next + space_next;
+			space = m->m_len - space_next;
+		} else {
+			/*
+			 * Setup for next buffer.
+			 */
+			pos = mtod(m, uint8_t *);
+			space = m->m_len;
+		}
+	}
+	if (memcmp(mic, a, ccmp.ic_trailer) != 0) {
+		IEEE80211_DPRINTF(ctx->cc_ic, IEEE80211_MSG_CRYPTO,
+			"[%6D] AES-CCM decrypt failed; MIC mismatch\n",
+			(wh->i_addr2), ":");
+		ctx->cc_ic->ic_stats.is_rx_ccmpmic++;
+		return 0;
+	}
+	return 1;
+}
+#undef CCMP_DECRYPT
+
+/*
+ * Module glue.
+ */
+static int
+ccmp_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_crypto_register(&ccmp);
+		return 0;
+	case MOD_UNLOAD:
+		ieee80211_crypto_unregister(&ccmp);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t ccmp_mod = {
+	"wlan_ccmp",
+	ccmp_modevent,
+	0
+};
+DECLARE_MODULE(wlan_ccmp, ccmp_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_ccmp, 1);
+MODULE_DEPEND(wlan_ccmp, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto_none.c src/sys/netproto/802_11/ieee80211_crypto_none.c
--- src.preview/sys/netproto/802_11/ieee80211_crypto_none.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_crypto_none.c	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11 NULL crypto support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/module.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+static	void *none_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void none_detach(struct ieee80211_key *);
+static	int none_setkey(struct ieee80211_key *);
+static	int none_encap(struct ieee80211_key *, struct mbuf *, u_int8_t);
+static	int none_decap(struct ieee80211_key *, struct mbuf *, int);
+static	int none_enmic(struct ieee80211_key *, struct mbuf *, int);
+static	int none_demic(struct ieee80211_key *, struct mbuf *, int);
+
+const struct ieee80211_cipher ieee80211_cipher_none = {
+	.ic_name	= "NONE",
+	.ic_cipher	= IEEE80211_CIPHER_NONE,
+	.ic_header	= 0,
+	.ic_trailer	= 0,
+	.ic_miclen	= 0,
+	.ic_attach	= none_attach,
+	.ic_detach	= none_detach,
+	.ic_setkey	= none_setkey,
+	.ic_encap	= none_encap,
+	.ic_decap	= none_decap,
+	.ic_enmic	= none_enmic,
+	.ic_demic	= none_demic,
+};
+
+static void *
+none_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	return ic;		/* for diagnostics+stats */
+}
+
+static void
+none_detach(struct ieee80211_key *k)
+{
+	(void) k;
+}
+
+static int
+none_setkey(struct ieee80211_key *k)
+{
+	(void) k;
+	return 1;
+}
+
+static int
+none_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+	struct ieee80211com *ic = k->wk_private;
+#ifdef IEEE80211_DEBUG
+	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+#endif
+
+	/*
+	 * The specified key is not setup; this can
+	 * happen, at least, when changing keys.
+	 */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		"[%6D] key id %u is not set (encap)\n",
+		(wh->i_addr1), ":", keyid>>6);
+	ic->ic_stats.is_tx_badcipher++;
+	return 0;
+}
+
+static int
+none_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+	struct ieee80211com *ic = k->wk_private;
+#ifdef IEEE80211_DEBUG
+	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+	const u_int8_t *ivp = (const u_int8_t *)&wh[1];
+#endif
+
+	/*
+	 * The specified key is not setup; this can
+	 * happen, at least, when changing keys.
+	 */
+	/* XXX useful to know dst too */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		"[%6D] key id %u is not set (decap)\n",
+		(wh->i_addr2), ":", ivp[IEEE80211_WEP_IVLEN] >> 6);
+	ic->ic_stats.is_rx_badkeyid++;
+	return 0;
+}
+
+static int
+none_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	struct ieee80211com *ic = k->wk_private;
+
+	ic->ic_stats.is_tx_badcipher++;
+	return 0;
+}
+
+static int
+none_demic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	struct ieee80211com *ic = k->wk_private;
+
+	ic->ic_stats.is_rx_badkeyid++;
+	return 0;
+}
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto_tkip.c src/sys/netproto/802_11/ieee80211_crypto_tkip.c
--- src.preview/sys/netproto/802_11/ieee80211_crypto_tkip.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_crypto_tkip.c	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,994 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11i TKIP crypto support.
+ *
+ * Part of this module is derived from similar code in the Host
+ * AP driver. The code is used with the consent of the author and
+ * it's license is included below.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+static	void *tkip_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void tkip_detach(struct ieee80211_key *);
+static	int tkip_setkey(struct ieee80211_key *);
+static	int tkip_encap(struct ieee80211_key *, struct mbuf *m, u_int8_t keyid);
+static	int tkip_enmic(struct ieee80211_key *, struct mbuf *, int);
+static	int tkip_decap(struct ieee80211_key *, struct mbuf *, int);
+static	int tkip_demic(struct ieee80211_key *, struct mbuf *, int);
+
+static const struct ieee80211_cipher tkip  = {
+	.ic_name	= "TKIP",
+	.ic_cipher	= IEEE80211_CIPHER_TKIP,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
+			  IEEE80211_WEP_EXTIVLEN,
+	.ic_trailer	= IEEE80211_WEP_CRCLEN,
+	.ic_miclen	= IEEE80211_WEP_MICLEN,
+	.ic_attach	= tkip_attach,
+	.ic_detach	= tkip_detach,
+	.ic_setkey	= tkip_setkey,
+	.ic_encap	= tkip_encap,
+	.ic_decap	= tkip_decap,
+	.ic_enmic	= tkip_enmic,
+	.ic_demic	= tkip_demic,
+};
+
+typedef	uint8_t u8;
+typedef	uint16_t u16;
+typedef	uint32_t __u32;
+typedef	uint32_t u32;
+#define	memmove(dst, src, n)	ovbcopy(src, dst, n)
+
+struct tkip_ctx {
+	struct ieee80211com *tc_ic;	/* for diagnostics */
+
+	u16	tx_ttak[5];
+	int	tx_phase1_done;
+	u8	tx_rc4key[16];		/* XXX for test module; make locals? */
+
+	u16	rx_ttak[5];
+	int	rx_phase1_done;
+	u8	rx_rc4key[16];		/* XXX for test module; make locals? */
+	uint64_t rx_rsc;		/* held until MIC verified */
+};
+
+static	void michael_mic(struct tkip_ctx *, const u8 *key,
+		struct mbuf *m, u_int off, size_t data_len,
+		u8 mic[IEEE80211_WEP_MICLEN]);
+static	int tkip_encrypt(struct tkip_ctx *, struct ieee80211_key *,
+		struct mbuf *, int hdr_len);
+static	int tkip_decrypt(struct tkip_ctx *, struct ieee80211_key *,
+		struct mbuf *, int hdr_len);
+
+static void *
+tkip_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx;
+
+	MALLOC(ctx, struct tkip_ctx *, sizeof(struct tkip_ctx),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		return NULL;
+	}
+
+	ctx->tc_ic = ic;
+	return ctx;
+}
+
+static void
+tkip_detach(struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	FREE(ctx, M_DEVBUF);
+}
+
+static int
+tkip_setkey(struct ieee80211_key *k)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	if (k->wk_keylen != (128/NBBY)) {
+		(void) ctx;		/* XXX */
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+			"%s: Invalid key length %u, expecting %u\n",
+			__func__, k->wk_keylen, 128/NBBY);
+		return 0;
+	}
+	k->wk_keytsc = 1;		/* TSC starts at 1 */
+	return 1;
+}
+
+/*
+ * Add privacy headers and do any s/w encryption required.
+ */
+static int
+tkip_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+#ifdef IEEE80211_DEBUG
+		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+#endif
+
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			"[%6D] Discard frame due to countermeasures (%s)\n",
+			(wh->i_addr2), ":", __func__);
+		ic->ic_stats.is_crypto_tkipcm++;
+		return 0;
+	}
+	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+
+	/*
+	 * Copy down 802.11 header and add the IV, KeyID, and ExtIV.
+	 */
+	M_PREPEND(m, tkip.ic_header, M_NOWAIT);
+	if (m == NULL)
+		return 0;
+	ivp = mtod(m, u_int8_t *);
+	memmove(ivp, ivp + tkip.ic_header, hdrlen);
+	ivp += hdrlen;
+
+	ivp[0] = k->wk_keytsc >> 8;		/* TSC1 */
+	ivp[1] = (ivp[0] | 0x20) & 0x7f;	/* WEP seed */
+	ivp[2] = k->wk_keytsc >> 0;		/* TSC0 */
+	ivp[3] = keyid | IEEE80211_WEP_EXTIV;	/* KeyID | ExtID */
+	ivp[4] = k->wk_keytsc >> 16;		/* TSC2 */
+	ivp[5] = k->wk_keytsc >> 24;		/* TSC3 */
+	ivp[6] = k->wk_keytsc >> 32;		/* TSC4 */
+	ivp[7] = k->wk_keytsc >> 40;		/* TSC5 */
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		if (!tkip_encrypt(ctx, k, m, hdrlen))
+			return 0;
+		/* NB: tkip_encrypt handles wk_keytsc */
+	} else
+		k->wk_keytsc++;
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+tkip_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
+		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+		struct ieee80211com *ic = ctx->tc_ic;
+		int hdrlen;
+		uint8_t mic[IEEE80211_WEP_MICLEN];
+
+		ic->ic_stats.is_crypto_tkipenmic++;
+
+		hdrlen = ieee80211_hdrspace(ic, wh);
+
+		michael_mic(ctx, k->wk_txmic,
+			m, hdrlen, m->m_pkthdr.len - hdrlen, mic);
+		return m_append(m, tkip.ic_miclen, mic);
+	}
+	return 1;
+}
+
+static __inline uint64_t
+READ_6(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5)
+{
+	uint32_t iv32 = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
+	uint16_t iv16 = (b4 << 0) | (b5 << 8);
+	return (((uint64_t)iv16) << 32) | iv32;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame.  If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+tkip_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->tc_ic;
+	struct ieee80211_frame *wh;
+	uint8_t *ivp;
+
+	/*
+	 * Header should have extended IV and sequence number;
+	 * verify the former and validate the latter.
+	 */
+	wh = mtod(m, struct ieee80211_frame *);
+	ivp = mtod(m, uint8_t *) + hdrlen;
+	if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
+		/*
+		 * No extended IV; discard frame.
+		 */
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+			"[%6D] missing ExtIV for TKIP cipher\n",
+			(wh->i_addr2), ":");
+		ctx->tc_ic->ic_stats.is_rx_tkipformat++;
+		return 0;
+	}
+	/*
+	 * Handle TKIP counter measures requirement.
+	 */
+	if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			"[%6D] discard frame due to countermeasures (%s)\n",
+			(wh->i_addr2), ":", __func__);
+		ic->ic_stats.is_crypto_tkipcm++;
+		return 0;
+	}
+
+	ctx->rx_rsc = READ_6(ivp[2], ivp[0], ivp[4], ivp[5], ivp[6], ivp[7]);
+	if (ctx->rx_rsc <= k->wk_keyrsc) {
+		/*
+		 * Replay violation; notify upper layer.
+		 */
+		ieee80211_notify_replay_failure(ctx->tc_ic, wh, k, ctx->rx_rsc);
+		ctx->tc_ic->ic_stats.is_rx_tkipreplay++;
+		return 0;
+	}
+	/*
+	 * NB: We can't update the rsc in the key until MIC is verified.
+	 *
+	 * We assume we are not preempted between doing the check above
+	 * and updating wk_keyrsc when stripping the MIC in tkip_demic.
+	 * Otherwise we might process another packet and discard it as
+	 * a replay.
+	 */
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * If so we just strip the header; otherwise we need to
+	 * handle the decrypt in software.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !tkip_decrypt(ctx, k, m, hdrlen))
+		return 0;
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	memmove(mtod(m, uint8_t *) + tkip.ic_header, mtod(m, void *), hdrlen);
+	m_adj(m, tkip.ic_header);
+	m_adj(m, -tkip.ic_trailer);
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+tkip_demic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+	struct tkip_ctx *ctx = k->wk_private;
+
+	if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
+		struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+		struct ieee80211com *ic = ctx->tc_ic;
+		int hdrlen = ieee80211_hdrspace(ic, wh);
+		u8 mic[IEEE80211_WEP_MICLEN];
+		u8 mic0[IEEE80211_WEP_MICLEN];
+
+		ic->ic_stats.is_crypto_tkipdemic++;
+
+		michael_mic(ctx, k->wk_rxmic, 
+			m, hdrlen, m->m_pkthdr.len - (hdrlen + tkip.ic_miclen),
+			mic);
+		m_copydata(m, m->m_pkthdr.len - tkip.ic_miclen,
+			tkip.ic_miclen, mic0);
+		if (memcmp(mic, mic0, tkip.ic_miclen)) {
+			/* NB: 802.11 layer handles statistic and debug msg */
+			ieee80211_notify_michael_failure(ic, wh,
+				k->wk_rxkeyix != IEEE80211_KEYIX_NONE ?
+					k->wk_rxkeyix : k->wk_keyix);
+			return 0;
+		}
+	}
+	/*
+	 * Strip MIC from the tail.
+	 */
+	m_adj(m, -tkip.ic_miclen);
+
+	/*
+	 * Ok to update rsc now that MIC has been verified.
+	 */
+	k->wk_keyrsc = ctx->rx_rsc;
+
+	return 1;
+}
+
+/*
+ * Host AP crypt: host-based TKIP encryption implementation for Host AP driver
+ *
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline at xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. See README and COPYING for
+ * more details.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+static const __u32 crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+static __inline u16 RotR1(u16 val)
+{
+	return (val >> 1) | (val << 15);
+}
+
+static __inline u8 Lo8(u16 val)
+{
+	return val & 0xff;
+}
+
+static __inline u8 Hi8(u16 val)
+{
+	return val >> 8;
+}
+
+static __inline u16 Lo16(u32 val)
+{
+	return val & 0xffff;
+}
+
+static __inline u16 Hi16(u32 val)
+{
+	return val >> 16;
+}
+
+static __inline u16 Mk16(u8 hi, u8 lo)
+{
+	return lo | (((u16) hi) << 8);
+}
+
+static __inline u16 Mk16_le(const u16 *v)
+{
+	return le16toh(*v);
+}
+
+static const u16 Sbox[256] = {
+	0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+	0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+	0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+	0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+	0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+	0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+	0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+	0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+	0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+	0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+	0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+	0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+	0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+	0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+	0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+	0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+	0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+	0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+	0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+	0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+	0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+	0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+	0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+	0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+	0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+	0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+	0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+	0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+	0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+	0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+	0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+	0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+static __inline u16 _S_(u16 v)
+{
+	u16 t = Sbox[Hi8(v)];
+	return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+	int i, j;
+
+	/* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+	TTAK[0] = Lo16(IV32);
+	TTAK[1] = Hi16(IV32);
+	TTAK[2] = Mk16(TA[1], TA[0]);
+	TTAK[3] = Mk16(TA[3], TA[2]);
+	TTAK[4] = Mk16(TA[5], TA[4]);
+
+	for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+		j = 2 * (i & 1);
+		TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+		TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+		TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+		TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+		TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+	}
+}
+
+#ifndef _BYTE_ORDER
+#error "Don't know native byte order"
+#endif
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+			       u16 IV16)
+{
+	/* Make temporary area overlap WEP seed so that the final copy can be
+	 * avoided on little endian hosts. */
+	u16 *PPK = (u16 *) &WEPSeed[4];
+
+	/* Step 1 - make copy of TTAK and bring in TSC */
+	PPK[0] = TTAK[0];
+	PPK[1] = TTAK[1];
+	PPK[2] = TTAK[2];
+	PPK[3] = TTAK[3];
+	PPK[4] = TTAK[4];
+	PPK[5] = TTAK[4] + IV16;
+
+	/* Step 2 - 96-bit bijective mixing using S-box */
+	PPK[0] += _S_(PPK[5] ^ Mk16_le((const u16 *) &TK[0]));
+	PPK[1] += _S_(PPK[0] ^ Mk16_le((const u16 *) &TK[2]));
+	PPK[2] += _S_(PPK[1] ^ Mk16_le((const u16 *) &TK[4]));
+	PPK[3] += _S_(PPK[2] ^ Mk16_le((const u16 *) &TK[6]));
+	PPK[4] += _S_(PPK[3] ^ Mk16_le((const u16 *) &TK[8]));
+	PPK[5] += _S_(PPK[4] ^ Mk16_le((const u16 *) &TK[10]));
+
+	PPK[0] += RotR1(PPK[5] ^ Mk16_le((const u16 *) &TK[12]));
+	PPK[1] += RotR1(PPK[0] ^ Mk16_le((const u16 *) &TK[14]));
+	PPK[2] += RotR1(PPK[1]);
+	PPK[3] += RotR1(PPK[2]);
+	PPK[4] += RotR1(PPK[3]);
+	PPK[5] += RotR1(PPK[4]);
+
+	/* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+	 * WEPSeed[0..2] is transmitted as WEP IV */
+	WEPSeed[0] = Hi8(IV16);
+	WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+	WEPSeed[2] = Lo8(IV16);
+	WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((const u16 *) &TK[0])) >> 1);
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+	{
+		int i;
+		for (i = 0; i < 6; i++)
+			PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8);
+	}
+#endif
+}
+
+static void
+wep_encrypt(u8 *key, struct mbuf *m0, u_int off, size_t data_len,
+	uint8_t icv[IEEE80211_WEP_CRCLEN])
+{
+	u32 i, j, k, crc;
+	size_t buflen;
+	u8 S[256];
+	u8 *pos;
+	struct mbuf *m;
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Compute CRC32 over unencrypted data and apply RC4 to data */
+	crc = ~0;
+	i = j = 0;
+	m = m0;
+	pos = mtod(m, uint8_t *) + off;
+	buflen = m->m_len - off;
+	for (;;) {
+		if (buflen > data_len)
+			buflen = data_len;
+		data_len -= buflen;
+		for (k = 0; k < buflen; k++) {
+			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+			i = (i + 1) & 0xff;
+			j = (j + S[i]) & 0xff;
+			S_SWAP(i, j);
+			*pos++ ^= S[(S[i] + S[j]) & 0xff];
+		}
+		m = m->m_next;
+		if (m == NULL) {
+			KASSERT(data_len == 0,
+			    ("out of buffers with data_len %zu\n", data_len));
+			break;
+		}
+		pos = mtod(m, uint8_t *);
+		buflen = m->m_len;
+	}
+	crc = ~crc;
+
+	/* Append little-endian CRC32 and encrypt it to produce ICV */
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		icv[k] ^= S[(S[i] + S[j]) & 0xff];
+	}
+}
+
+static int
+wep_decrypt(u8 *key, struct mbuf *m, u_int off, size_t data_len)
+{
+	u32 i, j, k, crc;
+	u8 S[256];
+	u8 *pos, icv[4];
+	size_t buflen;
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + key[i & 0x0f]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	/* Apply RC4 to data and compute CRC32 over decrypted data */
+	crc = ~0;
+	i = j = 0;
+	pos = mtod(m, uint8_t *) + off;
+	buflen = m->m_len - off;
+	for (;;) {
+		if (buflen > data_len)
+			buflen = data_len;
+		data_len -= buflen;
+		for (k = 0; k < buflen; k++) {
+			i = (i + 1) & 0xff;
+			j = (j + S[i]) & 0xff;
+			S_SWAP(i, j);
+			*pos ^= S[(S[i] + S[j]) & 0xff];
+			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+			pos++;
+		}
+		m = m->m_next;
+		if (m == NULL) {
+			KASSERT(data_len == 0,
+			    ("out of buffers with data_len %zu\n", data_len));
+			break;
+		}
+		pos = mtod(m, uint8_t *);
+		buflen = m->m_len;
+	}
+	crc = ~crc;
+
+	/* Encrypt little-endian CRC32 and verify that it matches with the
+	 * received ICV */
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	for (k = 0; k < 4; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
+			/* ICV mismatch - drop frame */
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static __inline u32 rotl(u32 val, int bits)
+{
+	return (val << bits) | (val >> (32 - bits));
+}
+
+
+static __inline u32 rotr(u32 val, int bits)
+{
+	return (val >> bits) | (val << (32 - bits));
+}
+
+
+static __inline u32 xswap(u32 val)
+{
+	return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r)	\
+do {				\
+	r ^= rotl(l, 17);	\
+	l += r;			\
+	r ^= xswap(l);		\
+	l += r;			\
+	r ^= rotl(l, 3);	\
+	l += r;			\
+	r ^= rotr(l, 2);	\
+	l += r;			\
+} while (0)
+
+
+static __inline u32 get_le32_split(u8 b0, u8 b1, u8 b2, u8 b3)
+{
+	return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
+}
+
+static __inline u32 get_le32(const u8 *p)
+{
+	return get_le32_split(p[0], p[1], p[2], p[3]);
+}
+
+
+static __inline void put_le32(u8 *p, u32 v)
+{
+	p[0] = v;
+	p[1] = v >> 8;
+	p[2] = v >> 16;
+	p[3] = v >> 24;
+}
+
+/*
+ * Craft pseudo header used to calculate the MIC.
+ */
+static void
+michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16])
+{
+	const struct ieee80211_frame_addr4 *wh =
+		(const struct ieee80211_frame_addr4 *) wh0;
+
+	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
+	case IEEE80211_FC1_DIR_NODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_TODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr2);
+		break;
+	case IEEE80211_FC1_DIR_FROMDS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr1); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr3);
+		break;
+	case IEEE80211_FC1_DIR_DSTODS:
+		IEEE80211_ADDR_COPY(hdr, wh->i_addr3); /* DA */
+		IEEE80211_ADDR_COPY(hdr + IEEE80211_ADDR_LEN, wh->i_addr4);
+		break;
+	}
+
+	if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+		const struct ieee80211_qosframe *qwh =
+			(const struct ieee80211_qosframe *) wh;
+		hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID;
+	} else
+		hdr[12] = 0;
+	hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+static void
+michael_mic(struct tkip_ctx *ctx, const u8 *key,
+	struct mbuf *m, u_int off, size_t data_len,
+	u8 mic[IEEE80211_WEP_MICLEN])
+{
+	uint8_t hdr[16];
+	u32 l, r;
+	const uint8_t *data;
+	u_int space;
+
+	michael_mic_hdr(mtod(m, struct ieee80211_frame *), hdr);
+
+	l = get_le32(key);
+	r = get_le32(key + 4);
+
+	/* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+	l ^= get_le32(hdr);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[4]);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[8]);
+	michael_block(l, r);
+	l ^= get_le32(&hdr[12]);
+	michael_block(l, r);
+
+	/* first buffer has special handling */
+	data = mtod(m, const uint8_t *) + off;
+	space = m->m_len - off;
+	for (;;) {
+		if (space > data_len)
+			space = data_len;
+		/* collect 32-bit blocks from current buffer */
+		while (space >= sizeof(uint32_t)) {
+			l ^= get_le32(data);
+			michael_block(l, r);
+			data += sizeof(uint32_t), space -= sizeof(uint32_t);
+			data_len -= sizeof(uint32_t);
+		}
+		if (data_len < sizeof(uint32_t))
+			break;
+		m = m->m_next;
+		if (m == NULL) {
+			KASSERT(0, ("out of data, data_len %zu\n", data_len));
+			break;
+		}
+		if (space != 0) {
+			const uint8_t *data_next;
+			/*
+			 * Block straddles buffers, split references.
+			 */
+			data_next = mtod(m, const uint8_t *);
+			KASSERT(m->m_len >= sizeof(uint32_t) - space,
+				("not enough data in following buffer, "
+				"m_len %u need %zu\n", m->m_len,
+				sizeof(uint32_t) - space));
+			switch (space) {
+			case 1:
+				l ^= get_le32_split(data[0], data_next[0],
+					data_next[1], data_next[2]);
+				data = data_next + 3;
+				space = m->m_len - 3;
+				break;
+			case 2:
+				l ^= get_le32_split(data[0], data[1],
+					data_next[0], data_next[1]);
+				data = data_next + 2;
+				space = m->m_len - 2;
+				break;
+			case 3:
+				l ^= get_le32_split(data[0], data[1],
+					data[2], data_next[0]);
+				data = data_next + 1;
+				space = m->m_len - 1;
+				break;
+			}
+			michael_block(l, r);
+			data_len -= sizeof(uint32_t);
+		} else {
+			/*
+			 * Setup for next buffer.
+			 */
+			data = mtod(m, const uint8_t *);
+			space = m->m_len;
+		}
+	}
+	/* Last block and padding (0x5a, 4..7 x 0) */
+	switch (data_len) {
+	case 0:
+		l ^= get_le32_split(0x5a, 0, 0, 0);
+		break;
+	case 1:
+		l ^= get_le32_split(data[0], 0x5a, 0, 0);
+		break;
+	case 2:
+		l ^= get_le32_split(data[0], data[1], 0x5a, 0);
+		break;
+	case 3:
+		l ^= get_le32_split(data[0], data[1], data[2], 0x5a);
+		break;
+	}
+	michael_block(l, r);
+	/* l ^= 0; */
+	michael_block(l, r);
+
+	put_le32(mic, l);
+	put_le32(mic + 4, r);
+}
+
+static int
+tkip_encrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+	struct mbuf *m, int hdrlen)
+{
+	struct ieee80211_frame *wh;
+	uint8_t icv[IEEE80211_WEP_CRCLEN];
+
+	ctx->tc_ic->ic_stats.is_crypto_tkip++;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	if (!ctx->tx_phase1_done) {
+		tkip_mixing_phase1(ctx->tx_ttak, key->wk_key, wh->i_addr2,
+				   (u32)(key->wk_keytsc >> 16));
+		ctx->tx_phase1_done = 1;
+	}
+	tkip_mixing_phase2(ctx->tx_rc4key, key->wk_key, ctx->tx_ttak,
+		(u16) key->wk_keytsc);
+
+	wep_encrypt(ctx->tx_rc4key,
+		m, hdrlen + tkip.ic_header,
+		m->m_pkthdr.len - (hdrlen + tkip.ic_header),
+		icv);
+	(void) m_append(m, IEEE80211_WEP_CRCLEN, icv);	/* XXX check return */
+
+	key->wk_keytsc++;
+	if ((u16)(key->wk_keytsc) == 0)
+		ctx->tx_phase1_done = 0;
+	return 1;
+}
+
+static int
+tkip_decrypt(struct tkip_ctx *ctx, struct ieee80211_key *key,
+	struct mbuf *m, int hdrlen)
+{
+	struct ieee80211_frame *wh;
+	u32 iv32;
+	u16 iv16;
+
+	ctx->tc_ic->ic_stats.is_crypto_tkip++;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	/* NB: tkip_decap already verified header and left seq in rx_rsc */
+	iv16 = (u16) ctx->rx_rsc;
+	iv32 = (u32) (ctx->rx_rsc >> 16);
+
+	if (iv32 != (u32)(key->wk_keyrsc >> 16) || !ctx->rx_phase1_done) {
+		tkip_mixing_phase1(ctx->rx_ttak, key->wk_key,
+			wh->i_addr2, iv32);
+		ctx->rx_phase1_done = 1;
+	}
+	tkip_mixing_phase2(ctx->rx_rc4key, key->wk_key, ctx->rx_ttak, iv16);
+
+	/* NB: m is unstripped; deduct headers + ICV to get payload */
+	if (wep_decrypt(ctx->rx_rc4key,
+		m, hdrlen + tkip.ic_header,
+	        m->m_pkthdr.len - (hdrlen + tkip.ic_header + tkip.ic_trailer))) {
+		if (iv32 != (u32)(key->wk_keyrsc >> 16)) {
+			/* Previously cached Phase1 result was already lost, so
+			 * it needs to be recalculated for the next packet. */
+			ctx->rx_phase1_done = 0;
+		}
+		IEEE80211_DPRINTF(ctx->tc_ic, IEEE80211_MSG_CRYPTO,
+		    "[%6D] TKIP ICV mismatch on decrypt\n",
+		    (wh->i_addr2), ":");
+		ctx->tc_ic->ic_stats.is_rx_tkipicv++;
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Module glue.
+ */
+static int
+tkip_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_crypto_register(&tkip);
+		return 0;
+	case MOD_UNLOAD:
+		ieee80211_crypto_unregister(&tkip);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t tkip_mod = {
+	"wlan_tkip",
+	tkip_modevent,
+	0
+};
+DECLARE_MODULE(wlan_tkip, tkip_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_tkip, 1);
+MODULE_DEPEND(wlan_tkip, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_crypto_wep.c src/sys/netproto/802_11/ieee80211_crypto_wep.c
--- src.preview/sys/netproto/802_11/ieee80211_crypto_wep.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_crypto_wep.c	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,496 @@
+/*-
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11 WEP crypto support.
+ */
+#include <sys/param.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+static	void *wep_attach(struct ieee80211com *, struct ieee80211_key *);
+static	void wep_detach(struct ieee80211_key *);
+static	int wep_setkey(struct ieee80211_key *);
+static	int wep_encap(struct ieee80211_key *, struct mbuf *, u_int8_t keyid);
+static	int wep_decap(struct ieee80211_key *, struct mbuf *, int);
+static	int wep_enmic(struct ieee80211_key *, struct mbuf *, int);
+static	int wep_demic(struct ieee80211_key *, struct mbuf *, int);
+
+static const struct ieee80211_cipher wep = {
+	.ic_name	= "WEP",
+	.ic_cipher	= IEEE80211_CIPHER_WEP,
+	.ic_header	= IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN,
+	.ic_trailer	= IEEE80211_WEP_CRCLEN,
+	.ic_miclen	= 0,
+	.ic_attach	= wep_attach,
+	.ic_detach	= wep_detach,
+	.ic_setkey	= wep_setkey,
+	.ic_encap	= wep_encap,
+	.ic_decap	= wep_decap,
+	.ic_enmic	= wep_enmic,
+	.ic_demic	= wep_demic,
+};
+
+static	int wep_encrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+static	int wep_decrypt(struct ieee80211_key *, struct mbuf *, int hdrlen);
+
+struct wep_ctx {
+	struct ieee80211com *wc_ic;	/* for diagnostics */
+	u_int32_t	wc_iv;		/* initial vector for crypto */
+};
+
+static void *
+wep_attach(struct ieee80211com *ic, struct ieee80211_key *k)
+{
+	struct wep_ctx *ctx;
+
+	MALLOC(ctx, struct wep_ctx *, sizeof(struct wep_ctx),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		ic->ic_stats.is_crypto_nomem++;
+		return NULL;
+	}
+
+	ctx->wc_ic = ic;
+	get_random_bytes(&ctx->wc_iv, sizeof(ctx->wc_iv));
+	return ctx;
+}
+
+static void
+wep_detach(struct ieee80211_key *k)
+{
+	struct wep_ctx *ctx = k->wk_private;
+
+	FREE(ctx, M_DEVBUF);
+}
+
+static int
+wep_setkey(struct ieee80211_key *k)
+{
+	return k->wk_keylen >= 40/NBBY;
+}
+
+/*
+ * Add privacy headers appropriate for the specified key.
+ */
+static int
+wep_encap(struct ieee80211_key *k, struct mbuf *m, u_int8_t keyid)
+{
+	struct wep_ctx *ctx = k->wk_private;
+	struct ieee80211com *ic = ctx->wc_ic;
+	u_int32_t iv;
+	u_int8_t *ivp;
+	int hdrlen;
+
+	hdrlen = ieee80211_hdrspace(ic, mtod(m, void *));
+
+	/*
+	 * Copy down 802.11 header and add the IV + KeyID.
+	 */
+	M_PREPEND(m, wep.ic_header, M_NOWAIT);
+	if (m == NULL)
+		return 0;
+	ivp = mtod(m, u_int8_t *);
+	ovbcopy(ivp + wep.ic_header, ivp, hdrlen);
+	ivp += hdrlen;
+
+	/*
+	 * XXX
+	 * IV must not duplicate during the lifetime of the key.
+	 * But no mechanism to renew keys is defined in IEEE 802.11
+	 * for WEP.  And the IV may be duplicated at other stations
+	 * because the session key itself is shared.  So we use a
+	 * pseudo random IV for now, though it is not the right way.
+	 *
+	 * NB: Rather than use a strictly random IV we select a
+	 * random one to start and then increment the value for
+	 * each frame.  This is an explicit tradeoff between
+	 * overhead and security.  Given the basic insecurity of
+	 * WEP this seems worthwhile.
+	 */
+
+	/*
+	 * Skip 'bad' IVs from Fluhrer/Mantin/Shamir:
+	 * (B, 255, N) with 3 <= B < 16 and 0 <= N <= 255
+	 */
+	iv = ctx->wc_iv;
+	if ((iv & 0xff00) == 0xff00) {
+		int B = (iv & 0xff0000) >> 16;
+		if (3 <= B && B < 16)
+			iv += 0x0100;
+	}
+	ctx->wc_iv = iv + 1;
+
+	/*
+	 * NB: Preserve byte order of IV for packet
+	 *     sniffers; it doesn't matter otherwise.
+	 */
+#if _BYTE_ORDER == _BIG_ENDIAN
+	ivp[0] = iv >> 0;
+	ivp[1] = iv >> 8;
+	ivp[2] = iv >> 16;
+#else
+	ivp[2] = iv >> 0;
+	ivp[1] = iv >> 8;
+	ivp[0] = iv >> 16;
+#endif
+	ivp[3] = keyid;
+
+	/*
+	 * Finally, do software encrypt if neeed.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !wep_encrypt(k, m, hdrlen))
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Add MIC to the frame as needed.
+ */
+static int
+wep_enmic(struct ieee80211_key *k, struct mbuf *m, int force)
+{
+
+	return 1;
+}
+
+/*
+ * Validate and strip privacy headers (and trailer) for a
+ * received frame.  If necessary, decrypt the frame using
+ * the specified key.
+ */
+static int
+wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen)
+{
+	struct wep_ctx *ctx = k->wk_private;
+	struct ieee80211_frame *wh;
+
+	wh = mtod(m, struct ieee80211_frame *);
+
+	/*
+	 * Check if the device handled the decrypt in hardware.
+	 * handle the decrypt in software.
+	 */
+	if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
+	    !wep_decrypt(k, m, hdrlen)) {
+		IEEE80211_DPRINTF(ctx->wc_ic, IEEE80211_MSG_CRYPTO,
+		    "[%6D] WEP ICV mismatch on decrypt\n",
+		    (wh->i_addr2), ":");
+		ctx->wc_ic->ic_stats.is_rx_wepfail++;
+		return 0;
+	}
+
+	/*
+	 * Copy up 802.11 header and strip crypto bits.
+	 */
+	ovbcopy(mtod(m, void *), mtod(m, u_int8_t *) + wep.ic_header, hdrlen);
+	m_adj(m, wep.ic_header);
+	m_adj(m, -wep.ic_trailer);
+
+	return 1;
+}
+
+/*
+ * Verify and strip MIC from the frame.
+ */
+static int
+wep_demic(struct ieee80211_key *k, struct mbuf *skb, int force)
+{
+	return 1;
+}
+
+static const uint32_t crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+static int
+wep_encrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+	struct wep_ctx *ctx = key->wk_private;
+	struct mbuf *m = m0;
+	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	uint8_t icv[IEEE80211_WEP_CRCLEN];
+	uint32_t i, j, k, crc;
+	size_t buflen, data_len;
+	uint8_t S[256];
+	uint8_t *pos;
+	u_int off, keylen;
+
+	ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+	/* NB: this assumes the header was pulled up */
+	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	off = hdrlen + wep.ic_header;
+	data_len = m->m_pkthdr.len - off;
+
+	/* Compute CRC32 over unencrypted data and apply RC4 to data */
+	crc = ~0;
+	i = j = 0;
+	pos = mtod(m, uint8_t *) + off;
+	buflen = m->m_len - off;
+	for (;;) {
+		if (buflen > data_len)
+			buflen = data_len;
+		data_len -= buflen;
+		for (k = 0; k < buflen; k++) {
+			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+			i = (i + 1) & 0xff;
+			j = (j + S[i]) & 0xff;
+			S_SWAP(i, j);
+			*pos++ ^= S[(S[i] + S[j]) & 0xff];
+		}
+		if (m->m_next == NULL) {
+			if (data_len != 0) {		/* out of data */
+				IEEE80211_DPRINTF(ctx->wc_ic,
+				    IEEE80211_MSG_CRYPTO,
+				    "[%6D] out of data for WEP (data_len %zu)\n",
+				    (mtod(m0, struct ieee80211_frame *)->i_addr2), ":",
+				    data_len);
+				return 0;
+			}
+			break;
+		}
+		m = m->m_next;
+		pos = mtod(m, uint8_t *);
+		buflen = m->m_len;
+	}
+	crc = ~crc;
+
+	/* Append little-endian CRC32 and encrypt it to produce ICV */
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		icv[k] ^= S[(S[i] + S[j]) & 0xff];
+	}
+	return m_append(m0, IEEE80211_WEP_CRCLEN, icv);
+#undef S_SWAP
+}
+
+static int
+wep_decrypt(struct ieee80211_key *key, struct mbuf *m0, int hdrlen)
+{
+#define S_SWAP(a,b) do { uint8_t t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+	struct wep_ctx *ctx = key->wk_private;
+	struct mbuf *m = m0;
+	u_int8_t rc4key[IEEE80211_WEP_IVLEN + IEEE80211_KEYBUF_SIZE];
+	uint8_t icv[IEEE80211_WEP_CRCLEN];
+	uint32_t i, j, k, crc;
+	size_t buflen, data_len;
+	uint8_t S[256];
+	uint8_t *pos;
+	u_int off, keylen;
+
+	ctx->wc_ic->ic_stats.is_crypto_wep++;
+
+	/* NB: this assumes the header was pulled up */
+	memcpy(rc4key, mtod(m, u_int8_t *) + hdrlen, IEEE80211_WEP_IVLEN);
+	memcpy(rc4key + IEEE80211_WEP_IVLEN, key->wk_key, key->wk_keylen);
+
+	/* Setup RC4 state */
+	for (i = 0; i < 256; i++)
+		S[i] = i;
+	j = 0;
+	keylen = key->wk_keylen + IEEE80211_WEP_IVLEN;
+	for (i = 0; i < 256; i++) {
+		j = (j + S[i] + rc4key[i % keylen]) & 0xff;
+		S_SWAP(i, j);
+	}
+
+	off = hdrlen + wep.ic_header;
+	data_len = m->m_pkthdr.len - (off + wep.ic_trailer),
+
+	/* Compute CRC32 over unencrypted data and apply RC4 to data */
+	crc = ~0;
+	i = j = 0;
+	pos = mtod(m, uint8_t *) + off;
+	buflen = m->m_len - off;
+	for (;;) {
+		if (buflen > data_len)
+			buflen = data_len;
+		data_len -= buflen;
+		for (k = 0; k < buflen; k++) {
+			i = (i + 1) & 0xff;
+			j = (j + S[i]) & 0xff;
+			S_SWAP(i, j);
+			*pos ^= S[(S[i] + S[j]) & 0xff];
+			crc = crc32_table[(crc ^ *pos) & 0xff] ^ (crc >> 8);
+			pos++;
+		}
+		m = m->m_next;
+		if (m == NULL) {
+			if (data_len != 0) {		/* out of data */
+				IEEE80211_DPRINTF(ctx->wc_ic,
+				    IEEE80211_MSG_CRYPTO,
+				    "[%6D] out of data for WEP (data_len %zu)\n",
+				    (mtod(m0,
+					struct ieee80211_frame *)->i_addr2), ":",
+				    data_len);
+				return 0;
+			}
+			break;
+		}
+		pos = mtod(m, uint8_t *);
+		buflen = m->m_len;
+	}
+	crc = ~crc;
+
+	/* Encrypt little-endian CRC32 and verify that it matches with
+	 * received ICV */
+	icv[0] = crc;
+	icv[1] = crc >> 8;
+	icv[2] = crc >> 16;
+	icv[3] = crc >> 24;
+	for (k = 0; k < IEEE80211_WEP_CRCLEN; k++) {
+		i = (i + 1) & 0xff;
+		j = (j + S[i]) & 0xff;
+		S_SWAP(i, j);
+		/* XXX assumes ICV is contiguous in mbuf */
+		if ((icv[k] ^ S[(S[i] + S[j]) & 0xff]) != *pos++) {
+			/* ICV mismatch - drop frame */
+			return 0;
+		}
+	}
+	return 1;
+#undef S_SWAP
+}
+
+/*
+ * Module glue.
+ */
+static int
+wep_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_crypto_register(&wep);
+		return 0;
+	case MOD_UNLOAD:
+		ieee80211_crypto_unregister(&wep);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wep_mod = {
+	"wlan_wep",
+	wep_modevent,
+	0
+};
+DECLARE_MODULE(wlan_wep, wep_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_wep, 1);
+MODULE_DEPEND(wlan_wep, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_dragonfly.c src/sys/netproto/802_11/ieee80211_dragonfly.c
--- src.preview/sys/netproto/802_11/ieee80211_dragonfly.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_dragonfly.c	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,378 @@
+/*-
+ * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * IEEE 802.11 support (DragonFly-specific code)
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h> 
+#include <sys/linker.h>
+#include <sys/mbuf.h>   
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+#include <machine/atomic.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+SYSCTL_NODE(_net, OID_AUTO, wlan, CTLFLAG_RD, 0, "IEEE 80211 parameters");
+
+#ifdef IEEE80211_DEBUG
+int	ieee80211_debug = 0;
+#if 0
+SYSCTL_INT(_net_wlan, OID_AUTO, debug, CTLFLAG_RW, &ieee80211_debug,
+	    0, "debugging printfs");
+#endif
+#endif
+
+static int
+ieee80211_sysctl_inact(SYSCTL_HANDLER_ARGS)
+{
+	int inact = (*(int *)arg1) * IEEE80211_INACT_WAIT;
+	int error;
+
+	error = sysctl_handle_int(oidp, &inact, 0, req);
+	if (error || !req->newptr)
+		return error;
+	*(int *)arg1 = inact / IEEE80211_INACT_WAIT;
+	return 0;
+}
+
+static int
+ieee80211_sysctl_parent(SYSCTL_HANDLER_ARGS)
+{
+	struct ieee80211com *ic = arg1;
+	const char *name = ic->ic_ifp->if_xname;
+
+	return SYSCTL_OUT(req, name, strlen(name));
+}
+
+void
+ieee80211_sysctl_attach(struct ieee80211com *ic)
+{
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid *oid;
+	char num[14];			/* sufficient for 32 bits */
+
+	MALLOC(ctx, struct sysctl_ctx_list *, sizeof(struct sysctl_ctx_list),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ctx == NULL) {
+		if_printf(ic->ic_ifp, "%s: cannot allocate sysctl context!\n",
+			__func__);
+		return;
+	}
+	sysctl_ctx_init(ctx);
+
+	snprintf(num, sizeof(num), "%u", ic->ic_vap);
+        oid = SYSCTL_ADD_NODE(ctx,
+                              SYSCTL_STATIC_CHILDREN(_net),
+                              OID_AUTO,
+                              "wlan",
+                              CTLFLAG_RD,
+                              0, "");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"%parent", CTLFLAG_RD, ic, 0, ieee80211_sysctl_parent, "A",
+		"parent device");
+#ifdef IEEE80211_DEBUG
+	ic->ic_debug = ieee80211_debug;
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"debug", CTLFLAG_RW, &ic->ic_debug, 0,
+		"control debugging printfs");
+#endif
+	/* XXX inherit from tunables */
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"inact_run", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_run, 0,
+		ieee80211_sysctl_inact, "I",
+		"station inactivity timeout (sec)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"inact_probe", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_probe, 0,
+		ieee80211_sysctl_inact, "I",
+		"station inactivity probe timeout (sec)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"inact_auth", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_auth, 0,
+		ieee80211_sysctl_inact, "I",
+		"station authentication timeout (sec)");
+	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"inact_init", CTLTYPE_INT | CTLFLAG_RW, &ic->ic_inact_init, 0,
+		ieee80211_sysctl_inact, "I",
+		"station initial state timeout (sec)");
+	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+		"driver_caps", CTLFLAG_RW, &ic->ic_caps, 0,
+		"driver capabilities");
+	ic->ic_sysctl = ctx;
+}
+
+void
+ieee80211_sysctl_detach(struct ieee80211com *ic)
+{
+
+	if (ic->ic_sysctl != NULL) {
+		sysctl_ctx_free(ic->ic_sysctl);
+		ic->ic_sysctl = NULL;
+	}
+}
+
+static __inline int
+atomic_cmpset_int(volatile u_int *dst, u_int exp, u_int src)
+{
+	int res = exp;
+	__asm __volatile (
+		"       lock ;                  "
+		"       cmpxchgl %1,%2 ;        "
+		"       setz    %%al ;          "
+		"       movzbl  %%al,%0 ;       "
+		"1:                             "
+		"# atomic_cmpset_int"
+		: "+a" (res)                    /* 0 (result) */
+		: "r" (src),                    /* 1 */
+		"m" (*(dst))                  /* 2 */
+		: "memory");                             
+
+	return (res);
+}
+
+int
+ieee80211_node_dectestref(struct ieee80211_node *ni)
+{
+	/* XXX need equivalent of atomic_dec_and_test */
+	atomic_subtract_int(&ni->ni_refcnt, 1);
+	return atomic_cmpset_int(&ni->ni_refcnt, 0, 1);
+}
+
+/*
+ * Allocate and setup a management frame of the specified
+ * size.  We return the mbuf and a pointer to the start
+ * of the contiguous data area that's been reserved based
+ * on the packet length.  The data area is forced to 32-bit
+ * alignment and the buffer length to a multiple of 4 bytes.
+ * This is done mainly so beacon frames (that require this)
+ * can use this interface too.
+ */
+struct mbuf *
+ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
+{
+	struct mbuf *m;
+	u_int len;
+
+	/*
+	 * NB: we know the mbuf routines will align the data area
+	 *     so we don't need to do anything special.
+	 */
+	/* XXX 4-address frame? */
+	len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
+	KASSERT(len <= MCLBYTES, ("802.11 mgt frame too large: %u", len));
+	if (len < MINCLSIZE) {
+		m = m_gethdr(M_NOWAIT, MT_HEADER);
+		/*
+		 * Align the data in case additional headers are added.
+		 * This should only happen when a WEP header is added
+		 * which only happens for shared key authentication mgt
+		 * frames which all fit in MHLEN.
+		 */
+		if (m != NULL)
+			MH_ALIGN(m, len);
+	} else
+		m = m_getcl(M_NOWAIT, MT_HEADER, M_PKTHDR);
+	if (m != NULL) {
+		m->m_data += sizeof(struct ieee80211_frame);
+		*frm = m->m_data;
+	}
+	return m;
+}
+
+#include <sys/libkern.h>
+
+void
+get_random_bytes(void *p, size_t n)
+{
+	u_int8_t *dp = p;
+
+	while (n > 0) {
+		u_int32_t v = arc4random();
+		size_t nb = n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n;
+		bcopy(&v, dp, n > sizeof(u_int32_t) ? sizeof(u_int32_t) : n);
+		dp += sizeof(u_int32_t), n -= nb;
+	}
+}
+
+void
+ieee80211_notify_node_join(struct ieee80211com *ic __unused, struct ieee80211_node *ni __unused, int newassoc __unused)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_join_event iev;
+
+	memset(&iev, 0, sizeof(iev));
+	if (ni == ic->ic_bss) {
+		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_bssid);
+		rt_ieee80211msg(ifp, newassoc ?
+			RTM_IEEE80211_ASSOC : RTM_IEEE80211_REASSOC,
+			&iev, sizeof(iev));
+#if notyet
+		if_link_state_change(ifp, LINK_STATE_UP);
+#endif
+	} else {
+		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
+		rt_ieee80211msg(ifp, newassoc ?
+			RTM_IEEE80211_JOIN : RTM_IEEE80211_REJOIN,
+			&iev, sizeof(iev));
+	}
+}
+
+void
+ieee80211_notify_node_leave(struct ieee80211com *ic __unused, struct ieee80211_node *ni __unused)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_leave_event iev;
+
+	if (ni == ic->ic_bss) {
+		rt_ieee80211msg(ifp, RTM_IEEE80211_DISASSOC, NULL, 0);
+#if notyet
+		if_link_state_change(ifp, LINK_STATE_DOWN);
+#endif
+	} else {
+		/* fire off wireless event station leaving */
+		memset(&iev, 0, sizeof(iev));
+		IEEE80211_ADDR_COPY(iev.iev_addr, ni->ni_macaddr);
+		rt_ieee80211msg(ifp, RTM_IEEE80211_LEAVE, &iev, sizeof(iev));
+	}
+}
+
+void
+ieee80211_notify_scan_done(struct ieee80211com *ic)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		"%s: notify scan done\n", ic->ic_ifp->if_xname);
+
+	/* dispatch wireless event indicating scan completed */
+	rt_ieee80211msg(ifp, RTM_IEEE80211_SCAN, NULL, 0);
+}
+
+void
+ieee80211_notify_replay_failure(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh, const struct ieee80211_key *k,
+	u_int64_t rsc)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		"[%6D] %s replay detected <rsc %ju, csc %ju, keyix %u>\n",
+		(wh->i_addr2), ":", k->wk_cipher->ic_name,
+		(intmax_t) rsc, (intmax_t) k->wk_keyrsc, k->wk_keyix);
+
+	if (ifp != NULL) {		/* NB: for cipher test modules */
+		struct ieee80211_replay_event iev;
+
+		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
+		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
+		iev.iev_cipher = k->wk_cipher->ic_cipher;
+		if (k->wk_rxkeyix != IEEE80211_KEYIX_NONE)
+			iev.iev_keyix = k->wk_rxkeyix;
+		else
+			iev.iev_keyix = k->wk_keyix;
+		iev.iev_keyrsc = k->wk_keyrsc;
+		iev.iev_rsc = rsc;
+		rt_ieee80211msg(ifp, RTM_IEEE80211_REPLAY, &iev, sizeof(iev));
+	}
+}
+
+void
+ieee80211_notify_michael_failure(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh, u_int keyix)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+		"[%6D] michael MIC verification failed <keyix %u>\n",
+	       (wh->i_addr2), ":", keyix);
+	ic->ic_stats.is_rx_tkipmic++;
+
+	if (ifp != NULL) {		/* NB: for cipher test modules */
+		struct ieee80211_michael_event iev;
+
+		IEEE80211_ADDR_COPY(iev.iev_dst, wh->i_addr1);
+		IEEE80211_ADDR_COPY(iev.iev_src, wh->i_addr2);
+		iev.iev_cipher = IEEE80211_CIPHER_TKIP;
+		iev.iev_keyix = keyix;
+		rt_ieee80211msg(ifp, RTM_IEEE80211_MICHAEL, &iev, sizeof(iev));
+	}
+}
+
+void
+ieee80211_load_module(const char *modname __unused)
+{
+#ifdef notyet
+	struct thread *td = curthread;
+
+	if (suser(td) == 0 && securelevel_gt(td->td_ucred, 0) == 0) {
+		mtx_lock(&Giant);
+		(void) linker_load_module(modname, NULL, NULL, NULL, NULL);
+		mtx_unlock(&Giant);
+	}
+#else
+	printf("%s: load the %s module by hand for now.\n", __func__, modname);
+#endif
+}
+
+/*
+ * Module glue.
+ *
+ * NB: the module name is "wlan" for compatibility with NetBSD.
+ */
+static int
+wlan_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		if (bootverbose)
+			printf("wlan: <802.11 Link Layer>\n");
+		return 0;
+	case MOD_UNLOAD:
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wlan_mod = {
+	"wlan",
+	wlan_modevent,
+	0
+};
+DECLARE_MODULE(wlan, wlan_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan, 1);
+MODULE_DEPEND(wlan, ether, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_dragonfly.h src/sys/netproto/802_11/ieee80211_dragonfly.h
--- src.preview/sys/netproto/802_11/ieee80211_dragonfly.h	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_dragonfly.h	2005-09-23 11:46:13.000000000 -0400
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 2003-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_FREEBSD_H_
+#define _NET80211_IEEE80211_FREEBSD_H_
+
+#include <machine/thread.h>
+#include <sys/thread.h>
+
+/*
+ * Beacon locking definitions.
+ */
+
+#define IEEE80211_LOCK_INFO struct lwkt_tokref tokinfo;
+#define IEEE80211_SCANLOCK_INFO struct lwkt_tokref scan_tokinfo;
+
+typedef struct lwkt_token ieee80211_beacon_lock_t;
+#define IEEE80211_BEACON_LOCK_INIT(_ic, _name)	lwkt_token_init(&(_ic)->ic_beaconlock)
+#define IEEE80211_BEACON_LOCK_DESTROY(_ic)	lwkt_token_uninit(&(_ic)->ic_beaconlock)
+#define IEEE80211_BEACON_LOCK(_ic)		lwkt_gettoken(&tokinfo, &((_ic)->ic_beaconlock))
+#define IEEE80211_BEACON_UNLOCK(_ic)		lwkt_reltoken(&tokinfo)
+#define	IEEE80211_BEACON_LOCK_ASSERT(_ic)	(void)0
+
+/*
+ * Node locking definitions.
+ */
+typedef struct lwkt_token ieee80211_node_lock_t;
+#define IEEE80211_NODE_LOCK_INIT(_nt, _name)	lwkt_token_init(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK_DESTROY(_nt)	lwkt_token_uninit(&(_nt)->nt_nodelock)
+#define IEEE80211_NODE_LOCK(_nt)		lwkt_gettoken(&tokinfo, &((_nt)->nt_nodelock))
+#define IEEE80211_NODE_UNLOCK(_nt)		lwkt_reltoken(&tokinfo)
+#define	IEEE80211_NODE_LOCK_ASSERT(_nt)		(void)0
+
+/*
+ * Node table scangen locking definitions.
+ */
+typedef struct lwkt_token ieee80211_scan_lock_t;
+#define IEEE80211_SCAN_LOCK_INIT(_nt, _name)	lwkt_token_init(&(_nt)->nt_scanlock)
+#define IEEE80211_SCAN_LOCK_DESTROY(_nt) 	lwkt_token_uninit(&(_nt)->nt_scanlock)
+#define IEEE80211_SCAN_LOCK(_nt)		lwkt_gettoken(&scan_tokinfo,&((_nt)->nt_scanlock))
+#define IEEE80211_SCAN_UNLOCK(_nt)		lwkt_reltoken(&scan_tokinfo)
+#define	IEEE80211_SCAN_LOCK_ASSERT(_nt) 	(void)0
+
+/*
+ * Per-node power-save queue definitions. 
+ */
+#define	IEEE80211_NODE_SAVEQ_INIT(_ni, _name) do {		\
+	lwkt_token_init(&(_ni)->ni_savedq.ifq_mtx);\
+	(_ni)->ni_savedq.ifq_maxlen = IEEE80211_PS_MAX_QUEUE;	\
+} while (0)
+#define	IEEE80211_NODE_SAVEQ_DESTROY(_ni) \
+	lwkt_token_uninit(&(_ni)->ni_savedq.ifq_mtx)
+#define	IEEE80211_NODE_SAVEQ_QLEN(_ni) \
+	IF_QLEN(&(_ni)->ni_savedq)
+#define	IEEE80211_NODE_SAVEQ_LOCK(_ni) do {	\
+	(void)0; \
+} while (0)
+#define	IEEE80211_NODE_SAVEQ_UNLOCK(_ni) do {	\
+	(void)0; \
+} while (0)
+#define	IEEE80211_NODE_SAVEQ_DEQUEUE(_ni, _m, _qlen) do {	\
+	IEEE80211_NODE_SAVEQ_LOCK(_ni);				\
+	IF_DEQUEUE(&(_ni)->ni_savedq, _m);			\
+	(_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni);		\
+	IEEE80211_NODE_SAVEQ_UNLOCK(_ni);			\
+} while (0)
+#define	IEEE80211_NODE_SAVEQ_DRAIN(_ni, _qlen) do {		\
+	IEEE80211_NODE_SAVEQ_LOCK(_ni);				\
+	(_qlen) = IEEE80211_NODE_SAVEQ_QLEN(_ni);		\
+	IF_DRAIN(&(_ni)->ni_savedq);				\
+	IEEE80211_NODE_SAVEQ_UNLOCK(_ni);			\
+} while (0)
+/* XXX could be optimized */
+#define	_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(_ni, _m) do {	\
+	IF_DEQUEUE(&(_ni)->ni_savedq, m);			\
+} while (0)
+#define	_IEEE80211_NODE_SAVEQ_ENQUEUE(_ni, _m, _qlen, _age) do {\
+	(_m)->m_nextpkt = NULL;					\
+	if ((_ni)->ni_savedq.ifq_tail != NULL) { 		\
+		_age -= M_AGE_GET((_ni)->ni_savedq.ifq_tail);	\
+		(_ni)->ni_savedq.ifq_tail->m_nextpkt = (_m);	\
+	} else { 						\
+		(_ni)->ni_savedq.ifq_head = (_m); 		\
+	}							\
+	M_AGE_SET(_m, _age);					\
+	(_ni)->ni_savedq.ifq_tail = (_m); 			\
+	(_qlen) = ++(_ni)->ni_savedq.ifq_len; 			\
+} while (0)
+
+/*
+ * 802.1x MAC ACL database locking definitions.
+ */
+typedef struct lwkt_token acl_lock_t;
+#define	ACL_LOCK_INIT(_as, _name) \
+	lwkt_token_init(&(_as)->as_lock)
+#define	ACL_LOCK_DESTROY(_as)		lwkt_token_uninit(&(_as)->as_lock)
+#define	ACL_LOCK(_as)			lwkt_gettoken(&tokinfo, &(_as)->as_lock)
+#define	ACL_UNLOCK(_as)			lwkt_reltoken(&tokinfo)
+#define	ACL_LOCK_ASSERT(_as)            (void)0
+
+/*
+ * Node reference counting definitions.
+ *
+ * ieee80211_node_initref	initialize the reference count to 1
+ * ieee80211_node_incref	add a reference
+ * ieee80211_node_decref	remove a reference
+ * ieee80211_node_dectestref	remove a reference and return 1 if this
+ *				is the last reference, otherwise 0
+ * ieee80211_node_refcnt	reference count for printing (only)
+ */
+#include <machine/atomic.h>
+
+#define ieee80211_node_initref(_ni) \
+	do { ((_ni)->ni_refcnt = 1); } while (0)
+#define ieee80211_node_incref(_ni) \
+	atomic_add_int(&(_ni)->ni_refcnt, 1)
+#define	ieee80211_node_decref(_ni) \
+	atomic_subtract_int(&(_ni)->ni_refcnt, 1)
+struct ieee80211_node;
+int	ieee80211_node_dectestref(struct ieee80211_node *ni);
+#define	ieee80211_node_refcnt(_ni)	(_ni)->ni_refcnt
+
+struct mbuf *ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
+#define	M_LINK0		M_PROTO1		/* WEP requested */
+#define	M_PWR_SAV	M_PROTO4		/* bypass PS handling */
+#define	M_MORE_DATA	M_PROTO5		/* more data frames to follow */
+/*
+ * Encode WME access control bits in the PROTO flags.
+ * This is safe since it's passed directly in to the
+ * driver and there's no chance someone else will clobber
+ * them on us.
+ */
+#define	M_WME_AC_MASK	(M_PROTO2|M_PROTO3)
+/* XXX 5 is wrong if M_PROTO* are redefined */
+#define	M_WME_AC_SHIFT	5
+
+#define	M_WME_SETAC(m, ac) \
+	((m)->m_flags = ((m)->m_flags &~ M_WME_AC_MASK) | \
+		((ac) << M_WME_AC_SHIFT))
+#define	M_WME_GETAC(m)	(((m)->m_flags >> M_WME_AC_SHIFT) & 0x3)
+
+/*
+ * Mbufs on the power save queue are tagged with an age and
+ * timed out.  We reuse the hardware checksum field in the
+ * mbuf packet header to store this data.
+ */
+#define	M_AGE_SET(m,v)		(m->m_pkthdr.csum_data = v)
+#define	M_AGE_GET(m)		(m->m_pkthdr.csum_data)
+#define	M_AGE_SUB(m,adj)	(m->m_pkthdr.csum_data -= adj)
+
+void	get_random_bytes(void *, size_t);
+
+struct ieee80211com;
+
+void	ieee80211_sysctl_attach(struct ieee80211com *);
+void	ieee80211_sysctl_detach(struct ieee80211com *);
+
+void	ieee80211_load_module(const char *);
+
+/* XXX this stuff belongs elsewhere */
+/*
+ * Message formats for messages from the net80211 layer to user
+ * applications via the routing socket.  These messages are appended
+ * to an if_announcemsghdr structure.
+ */
+struct ieee80211_join_event {
+	uint8_t		iev_addr[6];
+};
+
+struct ieee80211_leave_event {
+	uint8_t		iev_addr[6];
+};
+
+struct ieee80211_replay_event {
+	uint8_t		iev_src[6];	/* src MAC */
+	uint8_t		iev_dst[6];	/* dst MAC */
+	uint8_t		iev_cipher;	/* cipher type */
+	uint8_t		iev_keyix;	/* key id/index */
+	uint64_t	iev_keyrsc;	/* RSC from key */
+	uint64_t	iev_rsc;	/* RSC from frame */
+};
+
+struct ieee80211_michael_event {
+	uint8_t		iev_src[6];	/* src MAC */
+	uint8_t		iev_dst[6];	/* dst MAC */
+	uint8_t		iev_cipher;	/* cipher type */
+	uint8_t		iev_keyix;	/* key id/index */
+};
+
+#define	RTM_IEEE80211_ASSOC	100	/* station associate (bss mode) */
+#define	RTM_IEEE80211_REASSOC	101	/* station re-associate (bss mode) */
+#define	RTM_IEEE80211_DISASSOC	102	/* station disassociate (bss mode) */
+#define	RTM_IEEE80211_JOIN	103	/* station join (ap mode) */
+#define	RTM_IEEE80211_LEAVE	104	/* station leave (ap mode) */
+#define	RTM_IEEE80211_SCAN	105	/* scan complete, results available */
+#define	RTM_IEEE80211_REPLAY	106	/* sequence counter replay detected */
+#define	RTM_IEEE80211_MICHAEL	107	/* Michael MIC failure detected */
+#define	RTM_IEEE80211_REJOIN	108	/* station re-associate (ap mode) */
+
+#endif /* _NET80211_IEEE80211_FREEBSD_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_input.c src/sys/netproto/802_11/ieee80211_input.c
--- src.preview/sys/netproto/802_11/ieee80211_input.c	2005-01-25 19:37:40.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_input.c	2005-09-26 10:50:44.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,43 +28,97 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_input.c,v 1.21 2004/06/13 17:29:09 mlaier Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_input.c,v 1.2 2005/01/26 00:37:40 joerg Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
 #include <sys/param.h>
-#include <sys/systm.h> 
+#include <sys/systm.h>
 #include <sys/mbuf.h>   
 #include <sys/malloc.h>
+#include <sys/endian.h>
 #include <sys/kernel.h>
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/thread.h>
 
-#include <machine/atomic.h>
- 
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
 #include <net/if_llc.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/vlan/if_vlan_var.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
+#ifdef IEEE80211_DEBUG
+#include <machine/stdarg.h>
+
+/*
+ * Decide if a received management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		return (ic->ic_flags & IEEE80211_F_SCAN);
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
+
+/*
+ * Emit a debug message about discarding a frame or information
+ * element.  One format is for extracting the mac address from
+ * the frame header; the other is for when a header is not
+ * available or otherwise appropriate.
+ */
+#define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...) do {		\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_frame(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+} while (0)
+#define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...) do {	\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_ie(_ic, _wh, _type, _fmt, __VA_ARGS__);\
+} while (0)
+#define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...) do {	\
+	if ((_ic)->ic_debug & (_m))					\
+		ieee80211_discard_mac(_ic, _mac, _type, _fmt, __VA_ARGS__);\
+} while (0)
+
+static const u_int8_t *ieee80211_getbssid(struct ieee80211com *,
+	const struct ieee80211_frame *);
+static void ieee80211_discard_frame(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_ie(struct ieee80211com *,
+	const struct ieee80211_frame *, const char *type, const char *fmt, ...);
+static void ieee80211_discard_mac(struct ieee80211com *,
+	const u_int8_t mac[IEEE80211_ADDR_LEN], const char *type,
+	const char *fmt, ...);
+#else
+#define	IEEE80211_DISCARD(_ic, _m, _wh, _type, _fmt, ...)
+#define	IEEE80211_DISCARD_IE(_ic, _m, _wh, _type, _fmt, ...)
+#define	IEEE80211_DISCARD_MAC(_ic, _m, _mac, _type, _fmt, ...)
+#endif /* IEEE80211_DEBUG */
+
+static struct mbuf *ieee80211_defrag(struct ieee80211com *,
+	struct ieee80211_node *, struct mbuf *, int);
+static struct mbuf *ieee80211_decap(struct ieee80211com *, struct mbuf *, int);
+static void ieee80211_send_error(struct ieee80211com *, struct ieee80211_node *,
+		const u_int8_t *mac, int subtype, int arg);
+static void ieee80211_deliver_data(struct ieee80211com *,
+	struct ieee80211_node *, struct mbuf *);
+static void ieee80211_node_pwrsave(struct ieee80211_node *, int enable);
+static void ieee80211_recv_pspoll(struct ieee80211com *,
+	struct ieee80211_node *, struct mbuf *);
 
 /*
  * Process a received frame.  The node associated with the sender
@@ -76,22 +130,25 @@
  * mean ``better signal''.  The receive timestamp is currently not used
  * by the 802.11 layer.
  */
-void
-ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
-	int rssi, uint32_t rstamp)
+int
+ieee80211_input(struct ieee80211com *ic, struct mbuf *m,
+	struct ieee80211_node *ni, int rssi, u_int32_t rstamp)
 {
-	struct ieee80211com *ic = (void *)ifp;
+#define	SEQ_LEQ(a,b)	((int)((a)-(b)) <= 0)
+#define	HAS_SEQ(type)	((type & 0x4) == 0)
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
+	struct ieee80211_key *key;
 	struct ether_header *eh;
-	struct mbuf *m1;
-	int len;
-	uint8_t dir, type, subtype;
-	uint8_t *bssid;
-	uint16_t rxseq;
+	int hdrspace;
+	u_int8_t dir, type, subtype;
+	u_int8_t *bssid;
+	u_int16_t rxseq;
 
 	KASSERT(ni != NULL, ("null node"));
+	ni->ni_inact = ni->ni_inact_reload;
 
-#ifdef M_HASFCS
+#if notyet
 	/* trim CRC here so WEP can find its own CRC at the end of packet. */
 	if (m->m_flags & M_HASFCS) {
 		m_adj(m, -IEEE80211_CRC_LEN);
@@ -101,6 +158,7 @@
 	KASSERT(m->m_pkthdr.len >= sizeof(struct ieee80211_frame_min),
 		("frame length too short: %u", m->m_pkthdr.len));
 
+	type = -1;			/* undefined */
 	/*
 	 * In monitor mode, send everything directly to bpf.
 	 * XXX may want to include the CRC
@@ -108,38 +166,40 @@
 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
 		goto out;
 
+	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame_min)) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL,
+		    "too short (1): len %u", m->m_pkthdr.len);
+		ic->ic_stats.is_rx_tooshort++;
+		goto out;
+	}
+	/*
+	 * Bit of a cheat here, we use a pointer for a 3-address
+	 * frame format but don't reference fields past outside
+	 * ieee80211_frame_min w/o first validating the data is
+	 * present.
+	 */
 	wh = mtod(m, struct ieee80211_frame *);
+
 	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
 	    IEEE80211_FC0_VERSION_0) {
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "receive packet with wrong version: %x\n",
-			    wh->i_fc[0]);
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+		    ni->ni_macaddr, NULL, "wrong version %x", wh->i_fc[0]);
 		ic->ic_stats.is_rx_badversion++;
 		goto err;
 	}
 
 	dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
 	type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK;
-	/*
-	 * NB: We are not yet prepared to handle control frames,
-	 *     but permitting drivers to send them to us allows
-	 *     them to go through bpf tapping at the 802.11 layer.
-	 */
-	if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
-		/* XXX statistic */
-		IEEE80211_DPRINTF2(("%s: frame too short, len %u\n",
-			__func__, m->m_pkthdr.len));
-		ic->ic_stats.is_rx_tooshort++;
-		goto out;		/* XXX */
-	}
-	if (ic->ic_state != IEEE80211_S_SCAN) {
+	subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+	if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) {
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
-			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid)) {
+			bssid = wh->i_addr2;
+			if (!IEEE80211_ADDR_EQ(bssid, ni->ni_bssid)) {
 				/* not interested in */
-				IEEE80211_DPRINTF2(("%s: discard frame from "
-					"bss %6D\n", __func__,
-					wh->i_addr2, ":"));
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, NULL, "%s", "not to bss");
 				ic->ic_stats.is_rx_wrongbss++;
 				goto out;
 			}
@@ -147,46 +207,105 @@
 		case IEEE80211_M_IBSS:
 		case IEEE80211_M_AHDEMO:
 		case IEEE80211_M_HOSTAP:
-			if (dir == IEEE80211_FC1_DIR_NODS)
-				bssid = wh->i_addr3;
-			else
+			if (dir != IEEE80211_FC1_DIR_NODS)
 				bssid = wh->i_addr1;
+			else if (type == IEEE80211_FC0_TYPE_CTL)
+				bssid = wh->i_addr1;
+			else {
+				if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+					IEEE80211_DISCARD_MAC(ic,
+					    IEEE80211_MSG_ANY, ni->ni_macaddr,
+					    NULL, "too short (2): len %u",
+					    m->m_pkthdr.len);
+					ic->ic_stats.is_rx_tooshort++;
+					goto out;
+				}
+				bssid = wh->i_addr3;
+			}
+			if (type != IEEE80211_FC0_TYPE_DATA)
+				break;
+			/*
+			 * Data frame, validate the bssid.
+			 */
 			if (!IEEE80211_ADDR_EQ(bssid, ic->ic_bss->ni_bssid) &&
 			    !IEEE80211_ADDR_EQ(bssid, ifp->if_broadcastaddr)) {
 				/* not interested in */
-				IEEE80211_DPRINTF2(("%s: discard frame from "
-					"bss %6D\n", __func__,
-					bssid, ":"));
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, NULL, "%s", "not to bss");
 				ic->ic_stats.is_rx_wrongbss++;
 				goto out;
 			}
+			/*
+			 * For adhoc mode we cons up a node when it doesn't
+			 * exist. This should probably done after an ACL check.
+			 */
+			if (ni == ic->ic_bss &&
+			    ic->ic_opmode != IEEE80211_M_HOSTAP) {
+				/*
+				 * Fake up a node for this newly
+				 * discovered member of the IBSS.
+				 */
+				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
+						    wh->i_addr2);
+				if (ni == NULL) {
+					/* NB: stat kept for alloc failure */
+					goto err;
+				}
+			}
 			break;
-		case IEEE80211_M_MONITOR:
-			goto out;
 		default:
-			/* XXX catch bad values */
-			break;
+			goto out;
 		}
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
-		rxseq = ni->ni_rxseq;
-		ni->ni_rxseq =
-		    le16toh(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
-		/* TODO: fragment */
-		if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
-		    rxseq == ni->ni_rxseq) {
-			/* duplicate, silently discarded */
-			ic->ic_stats.is_rx_dup++; /* XXX per-station stat */
-			goto out;
+		if (HAS_SEQ(type)) {
+			u_int8_t tid;
+			if (IEEE80211_QOS_HAS_SEQ(wh)) {
+				tid = ((struct ieee80211_qosframe *)wh)->
+					i_qos[0] & IEEE80211_QOS_TID;
+				if (TID_TO_WME_AC(tid) >= WME_AC_VI)
+					ic->ic_wme.wme_hipri_traffic++;
+				tid++;
+			} else
+				tid = 0;
+			rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+			    SEQ_LEQ(rxseq, ni->ni_rxseqs[tid])) {
+				/* duplicate, discard */
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    bssid, "duplicate",
+				    "seqno <%u,%u> fragno <%u,%u> tid %u",
+				    rxseq >> IEEE80211_SEQ_SEQ_SHIFT,
+				    ni->ni_rxseqs[tid] >>
+					IEEE80211_SEQ_SEQ_SHIFT,
+				    rxseq & IEEE80211_SEQ_FRAG_MASK,
+				    ni->ni_rxseqs[tid] &
+					IEEE80211_SEQ_FRAG_MASK,
+				    tid);
+				ic->ic_stats.is_rx_dup++;
+				IEEE80211_NODE_STAT(ni, rx_dup);
+				goto out;
+			}
+			ni->ni_rxseqs[tid] = rxseq;
 		}
-		ni->ni_inact = 0;
 	}
 
 	switch (type) {
 	case IEEE80211_FC0_TYPE_DATA:
+		hdrspace = ieee80211_hdrspace(ic, wh);
+		if (m->m_len < hdrspace &&
+		    (m = m_pullup(m, hdrspace)) == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, NULL,
+			    "data too short: expecting %u", hdrspace);
+			ic->ic_stats.is_rx_tooshort++;
+			goto out;		/* XXX */
+		}
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			if (dir != IEEE80211_FC1_DIR_FROMDS) {
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
@@ -199,6 +318,8 @@
 				 * It should be silently discarded for
 				 * SIMPLEX interface.
 				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, NULL, "%s", "multicast echo");
 				ic->ic_stats.is_rx_mcastecho++;
 				goto out;
 			}
@@ -206,199 +327,458 @@
 		case IEEE80211_M_IBSS:
 		case IEEE80211_M_AHDEMO:
 			if (dir != IEEE80211_FC1_DIR_NODS) {
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
+			/* XXX no power-save support */
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (dir != IEEE80211_FC1_DIR_TODS) {
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unknown dir 0x%x", dir);
 				ic->ic_stats.is_rx_wrongdir++;
 				goto out;
 			}
 			/* check if source STA is associated */
 			if (ni == ic->ic_bss) {
-				IEEE80211_DPRINTF(("%s: data from unknown src "
-					"%6D\n", __func__,
-					wh->i_addr2, ":"));
-				/* NB: caller deals with reference */
-				ni = ieee80211_dup_bss(ic, wh->i_addr2);
-				if (ni != NULL) {
-					IEEE80211_SEND_MGMT(ic, ni,
-					    IEEE80211_FC0_SUBTYPE_DEAUTH,
-					    IEEE80211_REASON_NOT_AUTHED);
-					ieee80211_free_node(ic, ni);
-				}
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unknown src");
+				ieee80211_send_error(ic, ni, wh->i_addr2,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_NOT_AUTHED);
 				ic->ic_stats.is_rx_notassoc++;
 				goto err;
 			}
 			if (ni->ni_associd == 0) {
-				IEEE80211_DPRINTF(("ieee80211_input: "
-				    "data from unassoc src %6D\n",
-				    wh->i_addr2, ":"));
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "data", "%s", "unassoc src");
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DISASSOC,
 				    IEEE80211_REASON_NOT_ASSOCED);
-				ieee80211_unref_node(&ni);
 				ic->ic_stats.is_rx_notassoc++;
 				goto err;
 			}
+
+			/*
+			 * Check for power save state change.
+			 */
+			if (((wh->i_fc[1] & IEEE80211_FC1_PWR_MGT) ^
+			    (ni->ni_flags & IEEE80211_NODE_PWR_MGT)))
+				ieee80211_node_pwrsave(ni,
+					wh->i_fc[1] & IEEE80211_FC1_PWR_MGT);
 			break;
-		case IEEE80211_M_MONITOR:
-			break;
+		default:
+			/* XXX here to keep compiler happy */
+			goto out;
 		}
+
+		/*
+		 * Handle privacy requirements.  Note that we
+		 * must not be preempted from here until after
+		 * we (potentially) call ieee80211_crypto_demic;
+		 * otherwise we may violate assumptions in the
+		 * crypto cipher modules used to do delayed update
+		 * of replay sequence numbers.
+		 */
 		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
-			if (ic->ic_flags & IEEE80211_F_WEPON) {
-				m = ieee80211_wep_crypt(ifp, m, 0);
-				if (m == NULL) {
-					ic->ic_stats.is_rx_wepfail++;
-					goto err;
-				}
-				wh = mtod(m, struct ieee80211_frame *);
-			} else {
-				ic->ic_stats.is_rx_nowep++;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "WEP", "%s", "PRIVACY off");
+				ic->ic_stats.is_rx_noprivacy++;
+				IEEE80211_NODE_STAT(ni, rx_noprivacy);
+				goto out;
+			}
+			key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				IEEE80211_NODE_STAT(ni, rx_wepfail);
+			}
+			wh = mtod(m, struct ieee80211_frame *);
+			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+		} else {
+			key = NULL;
+		}
+
+		/*
+		 * Next up, any fragmentation.
+		 */
+		if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+			m = ieee80211_defrag(ic, ni, m, hdrspace);
+			if (m == NULL) {
+				/* Fragment dropped or frame not complete yet */
 				goto out;
 			}
 		}
+		wh = NULL;		/* no longer valid, catch any uses */
+
+		/*
+		 * Next strip any MSDU crypto bits.
+		 */
+		if (key != NULL && !ieee80211_crypto_demic(ic, key, m, 0)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+			    ni->ni_macaddr, "data", "%s", "demic error");
+			IEEE80211_NODE_STAT(ni, rx_demicfail);
+			goto out;
+		}
+
 		/* copy to listener after decrypt */
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 
-		m = ieee80211_decap(ifp, m);
+		/*
+		 * Finally, strip the 802.11 header.
+		 */
+		m = ieee80211_decap(ic, m, hdrspace);
 		if (m == NULL) {
+			/* don't count Null data frames as errors */
+			if (subtype == IEEE80211_FC0_SUBTYPE_NODATA)
+				goto out;
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+			    ni->ni_macaddr, "data", "%s", "decap error");
 			ic->ic_stats.is_rx_decap++;
+			IEEE80211_NODE_STAT(ni, rx_decap);
 			goto err;
 		}
-		ifp->if_ipackets++;
-
-		/* perform as a bridge within the AP */
-		m1 = NULL;
-		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-			eh = mtod(m, struct ether_header *);
-			if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
-				m1 = m_copypacket(m, MB_DONTWAIT);
-				if (m1 == NULL)
-					ifp->if_oerrors++;
-				else
-					m1->m_flags |= M_MCAST;
-			} else {
-				ni = ieee80211_find_node(ic, eh->ether_dhost);
-				if (ni != NULL) {
-					if (ni->ni_associd != 0) {
-						m1 = m;
-						m = NULL;
-					}
-					ieee80211_free_node(ic, ni);
-				}
+		eh = mtod(m, struct ether_header *);
+		if (!ieee80211_node_is_authorized(ni)) {
+			/*
+			 * Deny any non-PAE frames received prior to
+			 * authorization.  For open/shared-key
+			 * authentication the port is mark authorized
+			 * after authentication completes.  For 802.1x
+			 * the port is not marked authorized by the
+			 * authenticator until the handshake has completed.
+			 */
+			if (eh->ether_type != htons(ETHERTYPE_PAE)) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_INPUT,
+				    eh->ether_shost, "data",
+				    "unauthorized port: ether type 0x%x len %u",
+				    eh->ether_type, m->m_pkthdr.len);
+				ic->ic_stats.is_rx_unauth++;
+				IEEE80211_NODE_STAT(ni, rx_unauth);
+				goto err;
 			}
-			if (m1 != NULL) {
-				len = m1->m_pkthdr.len;
-				IF_ENQUEUE(&ifp->if_snd, m1);
-				if (m != NULL)
-					ifp->if_omcasts++;
-				ifp->if_obytes += len;
+		} else {
+			/*
+			 * When denying unencrypted frames, discard
+			 * any non-PAE frames received without encryption.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_DROPUNENC) &&
+			    key == NULL &&
+			    eh->ether_type != htons(ETHERTYPE_PAE)) {
+				/*
+				 * Drop unencrypted frames.
+				 */
+				ic->ic_stats.is_rx_unencrypted++;
+				IEEE80211_NODE_STAT(ni, rx_unencrypted);
+				goto out;
 			}
 		}
-		if (m != NULL)
-			(*ifp->if_input)(ifp, m);
-		return;
+		ifp->if_ipackets++;
+		IEEE80211_NODE_STAT(ni, rx_data);
+		IEEE80211_NODE_STAT_ADD(ni, rx_bytes, m->m_pkthdr.len);
+
+		ieee80211_deliver_data(ic, ni, m);
+		return IEEE80211_FC0_TYPE_DATA;
 
 	case IEEE80211_FC0_TYPE_MGT:
+		IEEE80211_NODE_STAT(ni, rx_mgmt);
 		if (dir != IEEE80211_FC1_DIR_NODS) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+			    wh, "data", "%s", "unknown dir 0x%x", dir);
 			ic->ic_stats.is_rx_wrongdir++;
 			goto err;
 		}
-		if (ic->ic_opmode == IEEE80211_M_AHDEMO) {
-			ic->ic_stats.is_rx_ahdemo_mgt++;
+		if (m->m_pkthdr.len < sizeof(struct ieee80211_frame)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_ANY,
+			    ni->ni_macaddr, "mgt", "too short: len %u",
+			    m->m_pkthdr.len);
+			ic->ic_stats.is_rx_tooshort++;
 			goto out;
 		}
-		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
-
-		/* drop frames without interest */
-		if (ic->ic_state == IEEE80211_S_SCAN) {
-			if (subtype != IEEE80211_FC0_SUBTYPE_BEACON &&
-			    subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
-				ic->ic_stats.is_rx_mgtdiscard++;
+#ifdef IEEE80211_DEBUG
+		if ((ieee80211_msg_debug(ic) && doprint(ic, subtype)) ||
+		    ieee80211_msg_dumppkts(ic)) {
+			if_printf(ic->ic_ifp, "received %s from %6D rssi %d\n",
+			    ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    (wh->i_addr2), ":", rssi);
+		}
+#endif
+		if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+			if (subtype != IEEE80211_FC0_SUBTYPE_AUTH) {
+				/*
+				 * Only shared key auth frames with a challenge
+				 * should be encrypted, discard all others.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, ieee80211_mgt_subtype_name[subtype >>
+					IEEE80211_FC0_SUBTYPE_SHIFT],
+				    "%s", "WEP set but not permitted");
+				ic->ic_stats.is_rx_mgtdiscard++; /* XXX */
 				goto out;
 			}
-		} else {
-			if (ic->ic_opmode != IEEE80211_M_IBSS &&
-			    subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
-				ic->ic_stats.is_rx_mgtdiscard++;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				/*
+				 * Discard encrypted frames when privacy is off.
+				 */
+				IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+				    wh, "mgt", "%s", "WEP set but PRIVACY off");
+				ic->ic_stats.is_rx_noprivacy++;
 				goto out;
 			}
-		}
-
-		if (ifp->if_flags & IFF_DEBUG) {
-			/* avoid to print too many frames */
-			int doprint = 0;
-
-			switch (subtype) {
-			case IEEE80211_FC0_SUBTYPE_BEACON:
-				if (ic->ic_state == IEEE80211_S_SCAN)
-					doprint = 1;
-				break;
-			case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
-				if (ic->ic_opmode == IEEE80211_M_IBSS)
-					doprint = 1;
-				break;
-			default:
-				doprint = 1;
-				break;
+			hdrspace = ieee80211_hdrspace(ic, wh);
+			key = ieee80211_crypto_decap(ic, ni, m, hdrspace);
+			if (key == NULL) {
+				/* NB: stats+msgs handled in crypto_decap */
+				goto out;
 			}
-#ifdef IEEE80211_DEBUG
-			doprint += ieee80211_debug;
-#endif
-			if (doprint)
-				if_printf(ifp, "received %s from %6D rssi %d\n",
-				    ieee80211_mgt_subtype_name[subtype
-				    >> IEEE80211_FC0_SUBTYPE_SHIFT],
-				    wh->i_addr2, ":", rssi);
+			wh = mtod(m, struct ieee80211_frame *);
+			wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
 		}
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 		(*ic->ic_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
 		m_freem(m);
-		return;
+		return type;
 
 	case IEEE80211_FC0_TYPE_CTL:
+		IEEE80211_NODE_STAT(ni, rx_ctrl);
 		ic->ic_stats.is_rx_ctl++;
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			switch (subtype) {
+			case IEEE80211_FC0_SUBTYPE_PS_POLL:
+				ieee80211_recv_pspoll(ic, ni, m);
+				break;
+			}
+		}
 		goto out;
 	default:
-		IEEE80211_DPRINTF(("%s: bad type %x\n", __func__, type));
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+		    wh, NULL, "bad frame type 0x%x", type);
 		/* should not come here */
 		break;
 	}
-  err:
+err:
 	ifp->if_ierrors++;
-  out:
+out:
+	if (m != NULL) {
+		if (ic->ic_rawbpf)
+			bpf_mtap(ic->ic_rawbpf, m);
+		m_freem(m);
+	}
+	return type;
+#undef SEQ_LEQ
+}
+
+/*
+ * This function reassemble fragments.
+ */
+static struct mbuf *
+ieee80211_defrag(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct mbuf *m, int hdrspace)
+{
+	struct ieee80211_frame *wh = mtod(m, struct ieee80211_frame *);
+	struct ieee80211_frame *lwh;
+	u_int16_t rxseq;
+	u_int8_t fragno;
+	u_int8_t more_frag = wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG;
+	struct mbuf *mfrag;
+	IEEE80211_LOCK_INFO;
+
+	KASSERT(!IEEE80211_IS_MULTICAST(wh->i_addr1), ("multicast fragm?"));
+
+	rxseq = le16toh(*(u_int16_t *)wh->i_seq);
+	fragno = rxseq & IEEE80211_SEQ_FRAG_MASK;
+
+	/* Quick way out, if there's nothing to defragment */
+	if (!more_frag && fragno == 0 && ni->ni_rxfrag[0] == NULL)
+		return m;
+
+	/*
+	 * Remove frag to insure it doesn't get reaped by timer.
+	 */
+	if (ni->ni_table == NULL) {
+		/*
+		 * Should never happen.  If the node is orphaned (not in
+		 * the table) then input packets should not reach here.
+		 * Otherwise, a concurrent request that yanks the table
+		 * should be blocked by other interlocking and/or by first
+		 * shutting the driver down.  Regardless, be defensive
+		 * here and just bail
+		 */
+		/* XXX need msg+stat */
+		m_freem(m);
+		return NULL;
+	}
+	IEEE80211_NODE_LOCK(ni->ni_table);
+	mfrag = ni->ni_rxfrag[0];
+	ni->ni_rxfrag[0] = NULL;
+	IEEE80211_NODE_UNLOCK(ni->ni_table);
+
+	/*
+	 * Validate new fragment is in order and
+	 * related to the previous ones.
+	 */
+	if (mfrag != NULL) {
+		u_int16_t last_rxseq;
+
+		lwh = mtod(mfrag, struct ieee80211_frame *);
+		last_rxseq = le16toh(*(u_int16_t *)lwh->i_seq);
+		/* NB: check seq # and frag together */
+		if (rxseq != last_rxseq+1 ||
+		    !IEEE80211_ADDR_EQ(wh->i_addr1, lwh->i_addr1) ||
+		    !IEEE80211_ADDR_EQ(wh->i_addr2, lwh->i_addr2)) {
+			/*
+			 * Unrelated fragment or no space for it,
+			 * clear current fragments.
+			 */
+			m_freem(mfrag);
+			mfrag = NULL;
+		}
+	}
+
+ 	if (mfrag == NULL) {
+		if (fragno != 0) {		/* !first fragment, discard */
+			IEEE80211_NODE_STAT(ni, rx_defrag);
+			m_freem(m);
+			return NULL;
+		}
+		mfrag = m;
+	} else {				/* concatenate */
+		m_adj(m, hdrspace);		/* strip header */
+		m_cat(mfrag, m);
+		/* NB: m_cat doesn't update the packet header */
+		mfrag->m_pkthdr.len += m->m_pkthdr.len;
+		/* track last seqnum and fragno */
+		lwh = mtod(mfrag, struct ieee80211_frame *);
+		*(u_int16_t *) lwh->i_seq = *(u_int16_t *) wh->i_seq;
+	}
+	if (more_frag) {			/* more to come, save */
+		ni->ni_rxfragstamp = ticks;
+		ni->ni_rxfrag[0] = mfrag;
+		mfrag = NULL;
+	}
+	return mfrag;
+}
+
+static __inline int
+ifa_handoff(struct ifaltq *_ifq, struct mbuf *_m, struct ifnet *_ifp,
+           int _adjust)
+{
+        int _need_if_start = 0;
+        crit_enter();
+
+        if (IF_QFULL(_ifq)) {
+                IF_DROP(_ifq);
+                crit_exit();
+                m_freem(_m);
+                return (0);
+        }
+        if (_ifp != NULL) {
+                _ifp->if_obytes += _m->m_pkthdr.len + _adjust;
+                if (_m->m_flags & M_MCAST)
+                        _ifp->if_omcasts++;
+                _need_if_start = !(_ifp->if_flags & IFF_OACTIVE);
+        }
+        IF_ENQUEUE(_ifq, _m);
+        if (_need_if_start)
+                (*_ifp->if_start)(_ifp);
+        crit_exit();
+        return (1);
+}
+
+
+static void
+ieee80211_deliver_data(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m)
+{
+	struct ether_header *eh = mtod(m, struct ether_header *);
+	struct ifnet *ifp = ic->ic_ifp;
+
+	/* perform as a bridge within the AP */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
+		struct mbuf *m1 = NULL;
+
+		if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
+			m1 = m_copypacket(m, MB_DONTWAIT);
+			if (m1 == NULL)
+				ifp->if_oerrors++;
+			else
+				m1->m_flags |= M_MCAST;
+		} else {
+			/*
+			 * Check if the destination is known; if so
+			 * and the port is authorized dispatch directly.
+			 */
+			struct ieee80211_node *sta =
+			    ieee80211_find_node(&ic->ic_sta, eh->ether_dhost);
+			if (sta != NULL) {
+				if (ieee80211_node_is_authorized(sta)) {
+					/*
+					 * Beware of sending to ourself; this
+					 * needs to happen via the normal
+					 * input path.
+					 */
+					if (sta != ic->ic_bss) {
+						m1 = m;
+						m = NULL;
+					}
+				} else {
+					ic->ic_stats.is_rx_unauth++;
+					IEEE80211_NODE_STAT(sta, rx_unauth);
+				}
+				ieee80211_free_node(sta);
+			}
+		}
+		if (m1 != NULL)
+			ifa_handoff(&ifp->if_snd, m1, ifp, 0);
+	}
+	if (m != NULL) {
+		if (ni->ni_vlan != 0) {
+			/* attach vlan tag */
+			/* XXX goto err? */
+			VLAN_INPUT_TAG(m, ni->ni_vlan);
+		}
+		(*ifp->if_input)(ifp, m);
+	}
+	return;
+/*  out: XX */
 	if (m != NULL) {
-		if (ic->ic_rawbpf != NULL)
+		if (ic->ic_rawbpf)
 			bpf_mtap(ic->ic_rawbpf, m);
 		m_freem(m);
 	}
 }
 
-struct mbuf *
-ieee80211_decap(struct ifnet *ifp, struct mbuf *m)
+static struct mbuf *
+ieee80211_decap(struct ieee80211com *ic, struct mbuf *m, int hdrlen)
 {
+	struct ieee80211_qosframe_addr4 wh;	/* Max size address frames */
 	struct ether_header *eh;
-	struct ieee80211_frame wh;
 	struct llc *llc;
 
-	if (m->m_len < sizeof(wh) + sizeof(*llc)) {
-		m = m_pullup(m, sizeof(wh) + sizeof(*llc));
-		if (m == NULL)
-			return NULL;
+	if (m->m_len < hdrlen + sizeof(*llc) &&
+	    (m = m_pullup(m, hdrlen + sizeof(*llc))) == NULL) {
+		/* XXX stat, msg */
+		return NULL;
 	}
-	memcpy(&wh, mtod(m, caddr_t), sizeof(wh));
-	llc = (struct llc *)(mtod(m, caddr_t) + sizeof(wh));
+	memcpy(&wh, mtod(m, caddr_t), hdrlen);
+	llc = (struct llc *)(mtod(m, caddr_t) + hdrlen);
 	if (llc->llc_dsap == LLC_SNAP_LSAP && llc->llc_ssap == LLC_SNAP_LSAP &&
 	    llc->llc_control == LLC_UI && llc->llc_snap.org_code[0] == 0 &&
 	    llc->llc_snap.org_code[1] == 0 && llc->llc_snap.org_code[2] == 0) {
-		m_adj(m, sizeof(wh) + sizeof(struct llc) - sizeof(*eh));
+		m_adj(m, hdrlen + sizeof(struct llc) - sizeof(*eh));
 		llc = NULL;
 	} else {
-		m_adj(m, sizeof(wh) - sizeof(*eh));
+		m_adj(m, hdrlen - sizeof(*eh));
 	}
 	eh = mtod(m, struct ether_header *);
 	switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) {
@@ -415,13 +795,12 @@
 		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr3);
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
-		/* not yet supported */
-		IEEE80211_DPRINTF(("%s: DS to DS\n", __func__));
-		m_freem(m);
-		return NULL;
+		IEEE80211_ADDR_COPY(eh->ether_dhost, wh.i_addr3);
+		IEEE80211_ADDR_COPY(eh->ether_shost, wh.i_addr4);
+		break;
 	}
 #ifdef ALIGNED_POINTER
-	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), uint32_t)) {
+	if (!ALIGNED_POINTER(mtod(m, caddr_t) + sizeof(*eh), u_int32_t)) {
 		struct mbuf *n, *n0, **np;
 		caddr_t newdata;
 		int off, pktlen;
@@ -481,133 +860,963 @@
 /*
  * Install received rate set information in the node's state block.
  */
-static int
-ieee80211_setup_rates(struct ieee80211com *ic, struct ieee80211_node *ni,
-	uint8_t *rates, uint8_t *xrates, int flags)
+int
+ieee80211_setup_rates(struct ieee80211_node *ni,
+	const u_int8_t *rates, const u_int8_t *xrates, int flags)
 {
+	struct ieee80211com *ic = ni->ni_ic;
 	struct ieee80211_rateset *rs = &ni->ni_rates;
 
 	memset(rs, 0, sizeof(*rs));
 	rs->rs_nrates = rates[1];
 	memcpy(rs->rs_rates, rates + 2, rs->rs_nrates);
 	if (xrates != NULL) {
-		uint8_t nxrates;
+		u_int8_t nxrates;
 		/*
 		 * Tack on 11g extended supported rate element.
 		 */
 		nxrates = xrates[1];
 		if (rs->rs_nrates + nxrates > IEEE80211_RATE_MAXSIZE) {
 			nxrates = IEEE80211_RATE_MAXSIZE - rs->rs_nrates;
-			IEEE80211_DPRINTF(("%s: extended rate set too large;"
-				" only using %u of %u rates\n",
-				__func__, nxrates, xrates[1]));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_XRATE,
+			     "[%6D] extended rate set too large;"
+			     " only using %u of %u rates\n",
+			     (ni->ni_macaddr), ":", nxrates, xrates[1]);
 			ic->ic_stats.is_rx_rstoobig++;
 		}
 		memcpy(rs->rs_rates + rs->rs_nrates, xrates+2, nxrates);
 		rs->rs_nrates += nxrates;
 	}
-	return ieee80211_fix_rate(ic, ni, flags);
+	return ieee80211_fix_rate(ni, flags);
 }
 
-/* Verify the existence and length of __elem or get out. */
-#define	IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
-	if ((__elem) == NULL) {						\
-		IEEE80211_DPRINTF(("%s: no " #__elem "in %s frame\n",	\
-			__func__, ieee80211_mgt_subtype_name[subtype >>	\
-				IEEE80211_FC0_SUBTYPE_SHIFT]));		\
-		ic->ic_stats.is_rx_elem_missing++;			\
-		return;							\
-	}								\
-	if ((__elem)[1] > (__maxlen)) {					\
-		IEEE80211_DPRINTF(("%s: bad " #__elem " len %d in %s "	\
-			"frame from %6D\n", __func__, (__elem)[1],	\
-			ieee80211_mgt_subtype_name[subtype >>		\
-				IEEE80211_FC0_SUBTYPE_SHIFT],		\
-			wh->i_addr2, ":"));				\
-		ic->ic_stats.is_rx_elem_toobig++;			\
-		return;							\
-	}								\
-} while (0)
-
-#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
-	if ((_len) < (_minlen)) {					\
-		IEEE80211_DPRINTF(("%s: %s frame too short from %6D\n",	\
-			__func__,					\
-			ieee80211_mgt_subtype_name[subtype >>		\
-				IEEE80211_FC0_SUBTYPE_SHIFT],		\
-			wh->i_addr2, ":"));				\
-		ic->ic_stats.is_rx_elem_toosmall++;			\
-		return;							\
-	}								\
-} while (0)
-
-void
-ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
-	struct ieee80211_node *ni,
-	int subtype, int rssi, uint32_t rstamp)
+static void
+ieee80211_auth_open(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    struct ieee80211_node *ni, int rssi, u_int32_t rstamp, u_int16_t seq,
+    u_int16_t status)
 {
-	struct ifnet *ifp = &ic->ic_if;
-	struct ieee80211_frame *wh;
-	uint8_t *frm, *efrm;
-	uint8_t *ssid, *rates, *xrates;
-	int reassoc, resp, newassoc, allocbs;
 
-	wh = mtod(m0, struct ieee80211_frame *);
-	frm = (uint8_t *)&wh[1];
-	efrm = mtod(m0, uint8_t *) + m0->m_len;
-	switch (subtype) {
-	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
-	case IEEE80211_FC0_SUBTYPE_BEACON: {
-		uint8_t *tstamp, *bintval, *capinfo, *country;
-		uint8_t chan, bchan, fhindex, erp;
-		uint16_t fhdwell;
-		int isprobe;
+	if (ni->ni_authmode == IEEE80211_AUTH_SHARED) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "open auth",
+		    "bad sta auth mode %u", ni->ni_authmode);
+		ic->ic_stats.is_rx_bad_auth++;	/* XXX */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			/* XXX hack to workaround calling convention */
+			ieee80211_send_error(ic, ni, wh->i_addr2, 
+			    IEEE80211_FC0_SUBTYPE_AUTH,
+			    (seq + 1) | (IEEE80211_STATUS_ALG<<16));
+		}
+		return;
+	}
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_MONITOR:
+		/* should not come here */
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "open auth",
+		    "bad operating mode %u", ic->ic_opmode);
+		break;
 
-		if (ic->ic_opmode != IEEE80211_M_IBSS &&
-		    ic->ic_state != IEEE80211_S_SCAN) {
-			/* XXX: may be useful for background scan */
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN ||
+		    seq != IEEE80211_AUTH_OPEN_REQUEST) {
+			ic->ic_stats.is_rx_bad_auth++;
 			return;
 		}
-		isprobe = (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP);
+		/* always accept open authentication requests */
+		if (ni == ic->ic_bss) {
+			ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
+			if (ni == NULL)
+				return;
+		} else if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+			(void) ieee80211_ref_node(ni);
+		/*
+		 * Mark the node as referenced to reflect that it's
+		 * reference count has been bumped to insure it remains
+		 * after the transaction completes.
+		 */
+		ni->ni_flags |= IEEE80211_NODE_AREF;
 
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+		    "[%6D] station authenticated (open)\n",
+		    (ni->ni_macaddr), ":");
 		/*
-		 * beacon/probe response frame format
-		 *	[8] time stamp
-		 *	[2] beacon interval
-		 *	[2] capability information
-		 *	[tlv] ssid
-		 *	[tlv] supported rates
-		 *	[tlv] country information
-		 *	[tlv] parameter set (FH/DS)
-		 *	[tlv] erp information
-		 *	[tlv] extended supported rates
+		 * When 802.1x is not in use mark the port
+		 * authorized at this point so traffic can flow.
 		 */
-		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
-		tstamp  = frm;	frm += 8;
-		bintval = frm;	frm += 2;
-		capinfo = frm;	frm += 2;
-		ssid = rates = xrates = country = NULL;
-		bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
-		chan = bchan;
-		fhdwell = 0;
-		fhindex = 0;
-		erp = 0;
-		while (frm < efrm) {
-			switch (*frm) {
+		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ni);
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH ||
+		    seq != IEEE80211_AUTH_OPEN_RESPONSE) {
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		if (status != 0) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			    "[%6D] open auth failed (reason %d)\n",
+			    (ni->ni_macaddr), ":", status);
+			/* XXX can this happen? */
+			if (ni != ic->ic_bss)
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_auth_fail++;
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+		} else
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		break;
+	}
+}
+
+/*
+ * Send a management frame error response to the specified
+ * station.  If ni is associated with the station then use
+ * it; otherwise allocate a temporary node suitable for
+ * transmitting the frame and then free the reference so
+ * it will go away as soon as the frame has been transmitted.
+ */
+static void
+ieee80211_send_error(struct ieee80211com *ic, struct ieee80211_node *ni,
+	const u_int8_t *mac, int subtype, int arg)
+{
+	int istmp;
+
+	if (ni == ic->ic_bss) {
+		ni = ieee80211_tmp_node(ic, mac);
+		if (ni == NULL) {
+			/* XXX msg */
+			return;
+		}
+		istmp = 1;
+	} else
+		istmp = 0;
+	IEEE80211_SEND_MGMT(ic, ni, subtype, arg);
+	if (istmp)
+		ieee80211_free_node(ni);
+}
+
+static int
+alloc_challenge(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (ni->ni_challenge == NULL)
+		MALLOC(ni->ni_challenge, u_int32_t*, IEEE80211_CHALLENGE_LEN,
+		    M_DEVBUF, M_NOWAIT);
+	if (ni->ni_challenge == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+		    "[%6D] shared key challenge alloc failed\n",
+		    (ni->ni_macaddr), ":");
+		/* XXX statistic */
+	}
+	return (ni->ni_challenge != NULL);
+}
+
+/* XXX TODO: add statistics */
+static void
+ieee80211_auth_shared(struct ieee80211com *ic, struct ieee80211_frame *wh,
+    u_int8_t *frm, u_int8_t *efrm, struct ieee80211_node *ni, int rssi,
+    u_int32_t rstamp, u_int16_t seq, u_int16_t status)
+{
+	u_int8_t *challenge;
+	int allocbs, estatus;
+
+	/*
+	 * NB: this can happen as we allow pre-shared key
+	 * authentication to be enabled w/o wep being turned
+	 * on so that configuration of these can be done
+	 * in any order.  It may be better to enforce the
+	 * ordering in which case this check would just be
+	 * for sanity/consistency.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "%s", " PRIVACY is disabled");
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+	/*
+	 * Pre-shared key authentication is evil; accept
+	 * it only if explicitly configured (it is supported
+	 * mainly for compatibility with clients like OS X).
+	 */
+	if (ni->ni_authmode != IEEE80211_AUTH_AUTO &&
+	    ni->ni_authmode != IEEE80211_AUTH_SHARED) {
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "bad sta auth mode %u", ni->ni_authmode);
+		ic->ic_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
+		estatus = IEEE80211_STATUS_ALG;
+		goto bad;
+	}
+
+	challenge = NULL;
+	if (frm + 1 < efrm) {
+		if ((frm[1] + 2) > (efrm - frm)) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "ie %d/%d too long",
+			    frm[0], (frm[1] + 2) - (efrm - frm));
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (*frm == IEEE80211_ELEMID_CHALLENGE)
+			challenge = frm;
+		frm += frm[1] + 2;
+	}
+	switch (seq) {
+	case IEEE80211_AUTH_SHARED_CHALLENGE:
+	case IEEE80211_AUTH_SHARED_RESPONSE:
+		if (challenge == NULL) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "%s", "no challenge");
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+		if (challenge[1] != IEEE80211_CHALLENGE_LEN) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad challenge len %d", challenge[1]);
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_CHALLENGE;
+			goto bad;
+		}
+	default:
+		break;
+	}
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_MONITOR:
+	case IEEE80211_M_AHDEMO:
+	case IEEE80211_M_IBSS:
+		IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+		    ni->ni_macaddr, "shared key auth",
+		    "bad operating mode %u", ic->ic_opmode);
+		return;
+	case IEEE80211_M_HOSTAP:
+		if (ic->ic_state != IEEE80211_S_RUN) {
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad state %u", ic->ic_state);
+			estatus = IEEE80211_STATUS_ALG;	/* XXX */
+			goto bad;
+		}
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_REQUEST:
+			if (ni == ic->ic_bss) {
+				ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
+				if (ni == NULL) {
+					/* NB: no way to return an error */
+					return;
+				}
+				allocbs = 1;
+			} else {
+				if ((ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+					(void) ieee80211_ref_node(ni);
+				allocbs = 0;
+			}
+			/*
+			 * Mark the node as referenced to reflect that it's
+			 * reference count has been bumped to insure it remains
+			 * after the transaction completes.
+			 */
+			ni->ni_flags |= IEEE80211_NODE_AREF;
+			ni->ni_rssi = rssi;
+			ni->ni_rstamp = rstamp;
+			if (!alloc_challenge(ic, ni)) {
+				/* NB: don't return error so they rexmit */
+				return;
+			}
+			get_random_bytes(ni->ni_challenge,
+				IEEE80211_CHALLENGE_LEN);
+			IEEE80211_DPRINTF(ic,
+				IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				"[%6D] shared key %sauth request\n",
+				(ni->ni_macaddr), ":",
+				allocbs ? "" : "re");
+			break;
+		case IEEE80211_AUTH_SHARED_RESPONSE:
+			if (ni == ic->ic_bss) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "unknown station");
+				/* NB: don't send a response */
+				return;
+			}
+			if (ni->ni_challenge == NULL) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "no challenge recorded");
+				ic->ic_stats.is_rx_bad_auth++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			if (memcmp(ni->ni_challenge, &challenge[2],
+			           challenge[1]) != 0) {
+				IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+				    ni->ni_macaddr, "shared key response",
+				    "%s", "challenge mismatch");
+				ic->ic_stats.is_rx_auth_fail++;
+				estatus = IEEE80211_STATUS_CHALLENGE;
+				goto bad;
+			}
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+			    "[%6D] station authenticated (shared key)\n",
+			    (ni->ni_macaddr), ":");
+ 			ieee80211_node_authorize(ni);
+			break;
+		default:
+			IEEE80211_DISCARD_MAC(ic, IEEE80211_MSG_AUTH,
+			    ni->ni_macaddr, "shared key auth",
+			    "bad seq %d", seq);
+			ic->ic_stats.is_rx_bad_auth++;
+			estatus = IEEE80211_STATUS_SEQUENCE;
+			goto bad;
+		}
+		IEEE80211_SEND_MGMT(ic, ni,
+			IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+		break;
+
+	case IEEE80211_M_STA:
+		if (ic->ic_state != IEEE80211_S_AUTH)
+			return;
+		switch (seq) {
+		case IEEE80211_AUTH_SHARED_PASS:
+			if (ni->ni_challenge != NULL) {
+				FREE(ni->ni_challenge, M_DEVBUF);
+				ni->ni_challenge = NULL;
+			}
+			if (status != 0) {
+				IEEE80211_DPRINTF(ic,
+				    IEEE80211_MSG_DEBUG | IEEE80211_MSG_AUTH,
+				    "[%6D] shared key auth failed (reason %d)\n",
+				    (ieee80211_getbssid(ic, wh)), ":",
+				    status);
+				/* XXX can this happen? */
+				if (ni != ic->ic_bss)
+					ni->ni_fails++;
+				ic->ic_stats.is_rx_auth_fail++;
+				return;
+			}
+			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
+			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+			break;
+		case IEEE80211_AUTH_SHARED_CHALLENGE:
+			if (!alloc_challenge(ic, ni))
+				return;
+			/* XXX could optimize by passing recvd challenge */
+			memcpy(ni->ni_challenge, &challenge[2], challenge[1]);
+			IEEE80211_SEND_MGMT(ic, ni,
+				IEEE80211_FC0_SUBTYPE_AUTH, seq + 1);
+			break;
+		default:
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_AUTH,
+			    wh, "shared key auth", "bad seq %d", seq);
+			ic->ic_stats.is_rx_bad_auth++;
+			return;
+		}
+		break;
+	}
+	return;
+bad:
+	/*
+	 * Send an error response; but only when operating as an AP.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		/* XXX hack to workaround calling convention */
+		ieee80211_send_error(ic, ni, wh->i_addr2,
+		    IEEE80211_FC0_SUBTYPE_AUTH,
+		    (seq + 1) | (estatus<<16));
+	} else if (ic->ic_opmode == IEEE80211_M_STA) {
+		/*
+		 * Kick the state machine.  This short-circuits
+		 * using the mgt frame timeout to trigger the
+		 * state transition.
+		 */
+		if (ic->ic_state == IEEE80211_S_AUTH)
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+	}
+}
+
+/* Verify the existence and length of __elem or get out. */
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {			\
+	if ((__elem) == NULL) {						\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "%s", "no " #__elem );				\
+		ic->ic_stats.is_rx_elem_missing++;			\
+		return;							\
+	}								\
+	if ((__elem)[1] > (__maxlen)) {					\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "bad " #__elem " len %d", (__elem)[1]);		\
+		ic->ic_stats.is_rx_elem_toobig++;			\
+		return;							\
+	}								\
+} while (0)
+
+#define	IEEE80211_VERIFY_LENGTH(_len, _minlen) do {			\
+	if ((_len) < (_minlen)) {					\
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ELEMID,		\
+		    wh, ieee80211_mgt_subtype_name[subtype >>		\
+			IEEE80211_FC0_SUBTYPE_SHIFT],			\
+		    "%s", "ie too short");				\
+		ic->ic_stats.is_rx_elem_toosmall++;			\
+		return;							\
+	}								\
+} while (0)
+
+#ifdef IEEE80211_DEBUG
+static void
+ieee80211_ssid_mismatch(struct ieee80211com *ic, const char *tag,
+	u_int8_t mac[IEEE80211_ADDR_LEN], u_int8_t *ssid)
+{
+	printf("[%6D] discard %s frame, ssid mismatch: ",
+		(mac), ":", tag);
+	ieee80211_print_essid(ssid + 2, ssid[1]);
+	printf("\n");
+}
+
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		if (ieee80211_msg_input(ic))				\
+			ieee80211_ssid_mismatch(ic, 			\
+			    ieee80211_mgt_subtype_name[subtype >>	\
+				IEEE80211_FC0_SUBTYPE_SHIFT],		\
+				wh->i_addr2, _ssid);			\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#else /* !IEEE80211_DEBUG */
+#define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
+	if ((_ssid)[1] != 0 &&						\
+	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+		ic->ic_stats.is_rx_ssidmismatch++;			\
+		return;							\
+	}								\
+} while (0)
+#endif /* !IEEE80211_DEBUG */
+
+/* unalligned little endian access */     
+#define LE_READ_2(p)					\
+	((u_int16_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8)))
+#define LE_READ_4(p)					\
+	((u_int32_t)					\
+	 ((((const u_int8_t *)(p))[0]      ) |		\
+	  (((const u_int8_t *)(p))[1] <<  8) |		\
+	  (((const u_int8_t *)(p))[2] << 16) |		\
+	  (((const u_int8_t *)(p))[3] << 24)))
+
+static int __inline
+iswpaoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmeoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI);
+}
+
+static int __inline
+iswmeparam(const u_int8_t *frm)
+{
+	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+		frm[6] == WME_PARAM_OUI_SUBTYPE;
+}
+
+static int __inline
+iswmeinfo(const u_int8_t *frm)
+{
+	return frm[1] > 5 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+		frm[6] == WME_INFO_OUI_SUBTYPE;
+}
+
+static int __inline
+isatherosoui(const u_int8_t *frm)
+{
+	return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+/*
+ * Convert a WPA cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+wpa_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case WPA_SEL(WPA_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case WPA_SEL(WPA_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case WPA_SEL(WPA_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert a WPA key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+wpa_keymgmt(u_int8_t *sel)
+{
+#define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case WPA_SEL(WPA_ASE_8021X_UNSPEC):
+		return WPA_ASE_8021X_UNSPEC;
+	case WPA_SEL(WPA_ASE_8021X_PSK):
+		return WPA_ASE_8021X_PSK;
+	case WPA_SEL(WPA_ASE_NONE):
+		return WPA_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Parse a WPA information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_wpa(struct ieee80211com *ic, u_int8_t *frm,
+	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: OUI, type,
+	 * version, mcast cipher, and 2 selector counts.
+	 * Other, variable-length data, must be checked separately.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_WPA1) == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "not WPA, flags 0x%x", ic->ic_flags);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (len < 14) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "too short, len %u", len);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 6, len -= 4;		/* NB: len is payload only */
+	/* NB: iswapoui already validated the OUI and type */
+	w = LE_READ_2(frm);
+	if (w != WPA_VERSION) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "bad version %u", w);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "mcast cipher mismatch; got %u, expected %u",
+		    w, rsn->rsn_mcastcipher);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "ucast cipher data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "%s", "ucast cipher set empty");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "key mgmt alg data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= wpa_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "%s", "no acceptable key mgmt alg");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & WPA_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = WPA_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	if (len > 2)		/* optional capabilities */
+		rsn->rsn_caps = LE_READ_2(frm);
+
+	return 0;
+}
+
+/*
+ * Convert an RSN cipher selector OUI to an internal
+ * cipher algorithm.  Where appropriate we also
+ * record any key length.
+ */
+static int
+rsn_cipher(u_int8_t *sel, u_int8_t *keylen)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_CSE_NULL):
+		return IEEE80211_CIPHER_NONE;
+	case RSN_SEL(RSN_CSE_WEP40):
+		if (keylen)
+			*keylen = 40 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_WEP104):
+		if (keylen)
+			*keylen = 104 / NBBY;
+		return IEEE80211_CIPHER_WEP;
+	case RSN_SEL(RSN_CSE_TKIP):
+		return IEEE80211_CIPHER_TKIP;
+	case RSN_SEL(RSN_CSE_CCMP):
+		return IEEE80211_CIPHER_AES_CCM;
+	case RSN_SEL(RSN_CSE_WRAP):
+		return IEEE80211_CIPHER_AES_OCB;
+	}
+	return 32;		/* NB: so 1<< is discarded */
+#undef WPA_SEL
+}
+
+/*
+ * Convert an RSN key management/authentication algorithm
+ * to an internal code.
+ */
+static int
+rsn_keymgmt(u_int8_t *sel)
+{
+#define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
+	u_int32_t w = LE_READ_4(sel);
+
+	switch (w) {
+	case RSN_SEL(RSN_ASE_8021X_UNSPEC):
+		return RSN_ASE_8021X_UNSPEC;
+	case RSN_SEL(RSN_ASE_8021X_PSK):
+		return RSN_ASE_8021X_PSK;
+	case RSN_SEL(RSN_ASE_NONE):
+		return RSN_ASE_NONE;
+	}
+	return 0;		/* NB: so is discarded */
+#undef RSN_SEL
+}
+
+/*
+ * Parse a WPA/RSN information element to collect parameters
+ * and validate the parameters against what has been
+ * configured for the system.
+ */
+static int
+ieee80211_parse_rsn(struct ieee80211com *ic, u_int8_t *frm,
+	struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh)
+{
+	u_int8_t len = frm[1];
+	u_int32_t w;
+	int n;
+
+	/*
+	 * Check the length once for fixed parts: 
+	 * Other, variable-length data, must be checked separately.
+	 */
+	if ((ic->ic_flags & IEEE80211_F_WPA2) == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "not RSN, flags 0x%x", ic->ic_flags);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (len < 10) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "too short, len %u", len);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2;
+	w = LE_READ_2(frm);
+	if (w != RSN_VERSION) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "bad version %u", w);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 2, len -= 2;
+
+	/* multicast/group cipher */
+	w = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+	if (w != rsn->rsn_mcastcipher) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "mcast cipher mismatch; got %u, expected %u",
+		    w, rsn->rsn_mcastcipher);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	frm += 4, len -= 4;
+
+	/* unicast ciphers */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4+2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "ucast cipher data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_ucastcipherset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "%s", "ucast cipher set empty");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & (1<<IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+
+	/* key management algorithms */
+	n = LE_READ_2(frm);
+	frm += 2, len -= 2;
+	if (len < n*4) {
+		IEEE80211_DISCARD_IE(ic, 
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "key mgmt alg data too short; len %u, n %u",
+		    len, n);
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	w = 0;
+	for (; n > 0; n--) {
+		w |= rsn_keymgmt(frm);
+		frm += 4, len -= 4;
+	}
+	w &= rsn->rsn_keymgmtset;
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "%s", "no acceptable key mgmt alg");
+		return IEEE80211_REASON_IE_INVALID;
+	}
+	if (w & RSN_ASE_8021X_UNSPEC)
+		rsn->rsn_keymgmt = RSN_ASE_8021X_UNSPEC;
+	else
+		rsn->rsn_keymgmt = RSN_ASE_8021X_PSK;
+
+	/* optional RSN capabilities */
+	if (len > 2)
+		rsn->rsn_caps = LE_READ_2(frm);
+	/* XXXPMKID */
+
+	return 0;
+}
+
+static int
+ieee80211_parse_wmeparams(struct ieee80211com *ic, u_int8_t *frm,
+	const struct ieee80211_frame *wh)
+{
+#define	MS(_v, _f)	(((_v) & _f) >> _f##_S)
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	u_int len = frm[1], qosinfo;
+	int i;
+
+	if (len < sizeof(struct ieee80211_wme_param)-2) {
+		IEEE80211_DISCARD_IE(ic,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+		    wh, "WME", "too short, len %u", len);
+		return -1;
+	}
+	qosinfo = frm[__offsetof(struct ieee80211_wme_param, param_qosInfo)];
+	qosinfo &= WME_QOSINFO_COUNT;
+	/* XXX do proper check for wraparound */
+	if (qosinfo == wme->wme_wmeChanParams.cap_info)
+		return 0;
+	frm += __offsetof(struct ieee80211_wme_param, params_acParams);
+	for (i = 0; i < WME_NUM_AC; i++) {
+		struct wmeParams *wmep =
+			&wme->wme_wmeChanParams.cap_wmeParams[i];
+		/* NB: ACI not used */
+		wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+		wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+		wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+		wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+		wmep->wmep_txopLimit = LE_READ_2(frm+2);
+		frm += 4;
+	}
+	wme->wme_wmeChanParams.cap_info = qosinfo;
+	return 1;
+#undef MS
+}
+
+void
+ieee80211_saveie(u_int8_t **iep, const u_int8_t *ie)
+{
+	u_int ielen = ie[1]+2;
+	/*
+	 * Record information element for later use.
+	 */
+	if (*iep == NULL || (*iep)[1] != ie[1]) {
+		if (*iep != NULL)
+			FREE(*iep, M_DEVBUF);
+		MALLOC(*iep, void*, ielen, M_DEVBUF, M_NOWAIT);
+	}
+	if (*iep != NULL)
+		memcpy(*iep, ie, ielen);
+	/* XXX note failure */
+}
+
+void
+ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
+	struct ieee80211_node *ni,
+	int subtype, int rssi, u_int32_t rstamp)
+{
+#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+#define	ISREASSOC(_st)	((_st) == IEEE80211_FC0_SUBTYPE_REASSOC_RESP)
+	struct ieee80211_frame *wh;
+	u_int8_t *frm, *efrm;
+	u_int8_t *ssid, *rates, *xrates, *wpa, *wme;
+	int reassoc, resp, allocbs;
+	u_int8_t rate;
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	frm = (u_int8_t *)&wh[1];
+	efrm = mtod(m0, u_int8_t *) + m0->m_len;
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+	case IEEE80211_FC0_SUBTYPE_BEACON: {
+		struct ieee80211_scanparams scan;
+
+		/*
+		 * We process beacon/probe response frames:
+		 *    o when scanning, or
+		 *    o station mode when associated (to collect state
+		 *      updates such as 802.11g slot time), or
+		 *    o adhoc mode (to discover neighbors)
+		 * Frames otherwise received are discarded.
+		 */ 
+		if (!((ic->ic_flags & IEEE80211_F_SCAN) ||
+		      (ic->ic_opmode == IEEE80211_M_STA && ni->ni_associd) ||
+		       ic->ic_opmode == IEEE80211_M_IBSS)) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
+		/*
+		 * beacon/probe response frame format
+		 *	[8] time stamp
+		 *	[2] beacon interval
+		 *	[2] capability information
+		 *	[tlv] ssid
+		 *	[tlv] supported rates
+		 *	[tlv] country information
+		 *	[tlv] parameter set (FH/DS)
+		 *	[tlv] erp information
+		 *	[tlv] extended supported rates
+		 *	[tlv] WME
+		 *	[tlv] WPA or RSN
+		 */
+		IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+		memset(&scan, 0, sizeof(scan));
+		scan.tstamp  = frm;				frm += 8;
+		scan.bintval = le16toh(*(u_int16_t *)frm);	frm += 2;
+		scan.capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		scan.bchan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+		scan.chan = scan.bchan;
+
+		while (frm < efrm) {
+			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
-				ssid = frm;
+				scan.ssid = frm;
 				break;
 			case IEEE80211_ELEMID_RATES:
-				rates = frm;
+				scan.rates = frm;
 				break;
 			case IEEE80211_ELEMID_COUNTRY:
-				country = frm;
+				scan.country = frm;
 				break;
 			case IEEE80211_ELEMID_FHPARMS:
 				if (ic->ic_phytype == IEEE80211_T_FH) {
-					fhdwell = (frm[3] << 8) | frm[2];
-					chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
-					fhindex = frm[6];
+					scan.fhdwell = LE_READ_2(&frm[2]);
+					scan.chan = IEEE80211_FH_CHAN(frm[4], frm[5]);
+					scan.fhindex = frm[6];
 				}
 				break;
 			case IEEE80211_ELEMID_DSPARMS:
@@ -616,48 +1825,64 @@
 				 * is problematic for multi-mode devices.
 				 */
 				if (ic->ic_phytype != IEEE80211_T_FH)
-					chan = frm[2];
+					scan.chan = frm[2];
 				break;
 			case IEEE80211_ELEMID_TIM:
+				/* XXX ATIM? */
+				scan.tim = frm;
+				scan.timoff = frm - mtod(m0, u_int8_t *);
 				break;
 			case IEEE80211_ELEMID_IBSSPARMS:
 				break;
 			case IEEE80211_ELEMID_XRATES:
-				xrates = frm;
+				scan.xrates = frm;
 				break;
 			case IEEE80211_ELEMID_ERP:
 				if (frm[1] != 1) {
-					IEEE80211_DPRINTF(("%s: invalid ERP "
-						"element; length %u, expecting "
-						"1\n", __func__, frm[1]));
+					IEEE80211_DISCARD_IE(ic,
+					    IEEE80211_MSG_ELEMID, wh, "ERP",
+					    "bad len %u", frm[1]);
 					ic->ic_stats.is_rx_elem_toobig++;
 					break;
 				}
-				erp = frm[2];
+				scan.erp = frm[2];
+				break;
+			case IEEE80211_ELEMID_RSN:
+				scan.wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm))
+					scan.wpa = frm;
+				else if (iswmeparam(frm) || iswmeinfo(frm))
+					scan.wme = frm;
+				/* XXX Atheros OUI support */
 				break;
 			default:
-				IEEE80211_DPRINTF2(("%s: element id %u/len %u "
-					"ignored\n", __func__, *frm, frm[1]));
+				IEEE80211_DISCARD_IE(ic, IEEE80211_MSG_ELEMID,
+				    wh, "unhandled",
+				    "id %u, len %u", *frm, frm[1]);
 				ic->ic_stats.is_rx_elem_unknown++;
 				break;
 			}
 			frm += frm[1] + 2;
 		}
-		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
-		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
+		IEEE80211_VERIFY_ELEMENT(scan.rates, IEEE80211_RATE_MAXSIZE);
+		IEEE80211_VERIFY_ELEMENT(scan.ssid, IEEE80211_NWID_LEN);
 		if (
 #if IEEE80211_CHAN_MAX < 255
-		    chan > IEEE80211_CHAN_MAX ||
+		    scan.chan > IEEE80211_CHAN_MAX ||
 #endif
-		    isclr(ic->ic_chan_active, chan)) {
-			IEEE80211_DPRINTF(("%s: ignore %s with invalid channel "
-				"%u\n", __func__,
-				isprobe ? "probe response" : "beacon",
-				chan));
+		    isclr(ic->ic_chan_active, scan.chan)) {
+			IEEE80211_DISCARD(ic,
+			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "invalid channel %u", scan.chan);
 			ic->ic_stats.is_rx_badchan++;
 			return;
 		}
-		if (chan != bchan && ic->ic_phytype != IEEE80211_T_FH) {
+		if (scan.chan != scan.bchan &&
+		    ic->ic_phytype != IEEE80211_T_FH) {
 			/*
 			 * Frame was received on a channel different from the
 			 * one indicated in the DS params element id;
@@ -668,108 +1893,131 @@
 			 *     the rssi value should be correct even for
 			 *     different hop pattern in FH.
 			 */
-			IEEE80211_DPRINTF(("%s: ignore %s on channel %u marked "
-				"for channel %u\n", __func__,
-				isprobe ? "probe response" : "beacon",
-				bchan, chan));
+			IEEE80211_DISCARD(ic,
+			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "for off-channel %u", scan.chan);
 			ic->ic_stats.is_rx_chanmismatch++;
 			return;
 		}
+		if (!(IEEE80211_BINTVAL_MIN <= scan.bintval &&
+		      scan.bintval <= IEEE80211_BINTVAL_MAX)) {
+			IEEE80211_DISCARD(ic,
+			    IEEE80211_MSG_ELEMID | IEEE80211_MSG_INPUT,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "bogus beacon interval", scan.bintval);
+			ic->ic_stats.is_rx_badbintval++;
+			return;
+		}
 
 		/*
-		 * Use mac and channel for lookup so we collect all
-		 * potential AP's when scanning.  Otherwise we may
-		 * see the same AP on multiple channels and will only
-		 * record the last one.  We could filter APs here based
-		 * on rssi, etc. but leave that to the end of the scan
-		 * so we can keep the selection criteria in one spot.
-		 * This may result in a bloat of the scanned AP list but
-		 * it shouldn't be too much.
+		 * Count frame now that we know it's to be processed.
 		 */
-		ni = ieee80211_lookup_node(ic, wh->i_addr2,
-				&ic->ic_channels[chan]);
-#ifdef IEEE80211_DEBUG
-		if (ieee80211_debug &&
-		    (ni == NULL || ic->ic_state == IEEE80211_S_SCAN)) {
-			printf("%s: %s%s on chan %u (bss chan %u) ",
-			    __func__, (ni == NULL ? "new " : ""),
-			    isprobe ? "probe response" : "beacon",
-			    chan, bchan);
-			ieee80211_print_essid(ssid + 2, ssid[1]);
-			printf(" from %6D\n", wh->i_addr2, ":");
-			printf("%s: caps 0x%x bintval %u erp 0x%x\n",
-				__func__, le16toh(*(uint16_t *)capinfo),
-				le16toh(*(uint16_t *)bintval), erp);
-			if (country)
-				printf("%s: country info %*D\n",
-					__func__, country[1], country+2, " ");
-		}
-#endif
-		if (ni == NULL) {
-			ni = ieee80211_alloc_node(ic, wh->i_addr2);
-			if (ni == NULL)
-				return;
-			ni->ni_esslen = ssid[1];
-			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
-			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
-			allocbs = 1;
-		} else if (ssid[1] != 0 && isprobe) {
-			/*
-			 * Update ESSID at probe response to adopt hidden AP by
-			 * Lucent/Cisco, which announces null ESSID in beacon.
-			 */
-			ni->ni_esslen = ssid[1];
-			memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
-			memcpy(ni->ni_essid, ssid + 2, ssid[1]);
-			allocbs = 0;
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON) {
+			ic->ic_stats.is_rx_beacon++;		/* XXX remove */
+			IEEE80211_NODE_STAT(ni, rx_beacons);
 		} else
-			allocbs = 0;
-		IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
-		ni->ni_rssi = rssi;
-		ni->ni_rstamp = rstamp;
-		memcpy(ni->ni_tstamp, tstamp, sizeof(ni->ni_tstamp));
-		ni->ni_intval = le16toh(*(uint16_t *)bintval);
-		ni->ni_capinfo = le16toh(*(uint16_t *)capinfo);
-		/* XXX validate channel # */
-		ni->ni_chan = &ic->ic_channels[chan];
-		ni->ni_fhdwell = fhdwell;
-		ni->ni_fhindex = fhindex;
-		ni->ni_erp = erp;
-		/* NB: must be after ni_chan is setup */
-		ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT);
-		/*
-		 * When scanning we record results (nodes) with a zero
-		 * refcnt.  Otherwise we want to hold the reference for
-		 * ibss neighbors so the nodes don't get released prematurely.
-		 * Anything else can be discarded (XXX and should be handled
-		 * above so we don't do so much work). 
-		 */
-		if (ic->ic_state == IEEE80211_S_SCAN)
-			ieee80211_unref_node(&ni);	/* NB: do not free */
-		else if (ic->ic_opmode == IEEE80211_M_IBSS &&
-		    allocbs && isprobe) {
-			/*
-			 * Fake an association so the driver can setup it's
-			 * private state.  The rate set has been setup above;
-			 * there is no handshake as in ap/station operation.
-			 */
-			if (ic->ic_newassoc)
-				(*ic->ic_newassoc)(ic, ni, 1);
-			/* NB: hold reference */
-		} else {
-			/* XXX optimize to avoid work done above */
-			ieee80211_free_node(ic, ni);
+			IEEE80211_NODE_STAT(ni, rx_proberesp);
+
+		/*
+		 * When operating in station mode, check for state updates.
+		 * Be careful to ignore beacons received while doing a
+		 * background scan.  We consider only 11g/WMM stuff right now.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_STA &&
+		    ni->ni_associd != 0 &&
+		    ((ic->ic_flags & IEEE80211_F_SCAN) == 0 ||
+		     IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))) {
+			/* record tsf of last beacon */
+			memcpy(ni->ni_tstamp.data, scan.tstamp,
+				sizeof(ni->ni_tstamp));
+			if (ni->ni_erp != scan.erp) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] erp change: was 0x%x, now 0x%x\n",
+				    (wh->i_addr2), ":",
+				    ni->ni_erp, scan.erp);
+				if (ic->ic_curmode == IEEE80211_MODE_11G &&
+				    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+					ic->ic_flags |= IEEE80211_F_USEPROT;
+				else
+					ic->ic_flags &= ~IEEE80211_F_USEPROT;
+				ni->ni_erp = scan.erp;
+				/* XXX statistic */
+			}
+			if ((ni->ni_capinfo ^ scan.capinfo) & IEEE80211_CAPINFO_SHORT_SLOTTIME) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] capabilities change: before 0x%x,"
+				     " now 0x%x\n",
+				     (wh->i_addr2), ":",
+				     ni->ni_capinfo, scan.capinfo);
+				/*
+				 * NB: we assume short preamble doesn't
+				 *     change dynamically
+				 */
+				ieee80211_set_shortslottime(ic,
+					ic->ic_curmode == IEEE80211_MODE_11A ||
+					(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+				ni->ni_capinfo = scan.capinfo;
+				/* XXX statistic */
+			}
+			if (scan.wme != NULL &&
+			    (ni->ni_flags & IEEE80211_NODE_QOS) &&
+			    ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0)
+				ieee80211_wme_updateparams(ic);
+			if (scan.tim != NULL) {
+				struct ieee80211_tim_ie *ie =
+				    (struct ieee80211_tim_ie *) scan.tim;
+
+				ni->ni_dtim_count = ie->tim_count;
+				ni->ni_dtim_period = ie->tim_period;
+			}
+			if (ic->ic_flags & IEEE80211_F_SCAN)
+				ieee80211_add_scan(ic, &scan, wh,
+					subtype, rssi, rstamp);
+			return;
+		}
+		/*
+		 * If scanning, just pass information to the scan module.
+		 */
+		if (ic->ic_flags & IEEE80211_F_SCAN) {
+			ieee80211_add_scan(ic, &scan, wh,
+				subtype, rssi, rstamp);
+			return;
+		}
+		if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
+			if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_macaddr)) {
+				/*
+				 * Create a new entry in the neighbor table.
+				 */
+				ni = ieee80211_add_neighbor(ic, wh, &scan);
+			} else {
+				/*
+				 * Record tsf for potential resync.
+				 */
+				memcpy(ni->ni_tstamp.data, scan.tstamp,
+					sizeof(ni->ni_tstamp));
+			}
+			if (ni != NULL) {
+				ni->ni_rssi = rssi;
+				ni->ni_rstamp = rstamp;
+			}
 		}
 		break;
 	}
 
-	case IEEE80211_FC0_SUBTYPE_PROBE_REQ: {
-		uint8_t rate;
-
-		if (ic->ic_opmode == IEEE80211_M_STA)
+	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+		if (ic->ic_opmode == IEEE80211_M_STA ||
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
-		if (ic->ic_state != IEEE80211_S_RUN)
+		}
+		if (IEEE80211_IS_MULTICAST(wh->i_addr2)) {
+			/* frame must be directed */
+			ic->ic_stats.is_rx_mgtdiscard++;	/* XXX stat */
 			return;
+		}
 
 		/*
 		 * prreq frame format
@@ -794,48 +2042,57 @@
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
-		if (ssid[1] != 0 &&
-		    (ssid[1] != ic->ic_bss->ni_esslen ||
-		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen) != 0)) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				printf("%s: ssid unmatch ", __func__);
-				ieee80211_print_essid(ssid + 2, ssid[1]);
-				printf(" from %6D\n", wh->i_addr2, ":");
-			}
-#endif
-			ic->ic_stats.is_rx_ssidmismatch++;
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+		if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_INPUT,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "no ssid with ssid suppression enabled");
+			ic->ic_stats.is_rx_ssidmismatch++; /*XXX*/
 			return;
 		}
 
 		if (ni == ic->ic_bss) {
-			ni = ieee80211_dup_bss(ic, wh->i_addr2);
+			if (ic->ic_opmode == IEEE80211_M_IBSS) {
+				/*
+				 * XXX Cannot tell if the sender is operating
+				 * in ibss mode.  But we need a new node to
+				 * send the response so blindly add them to the
+				 * neighbor table.
+				 */
+				ni = ieee80211_fakeup_adhoc_node(&ic->ic_sta,
+					wh->i_addr2);
+			} else
+				ni = ieee80211_tmp_node(ic, wh->i_addr2);
 			if (ni == NULL)
 				return;
-			IEEE80211_DPRINTF(("%s: new req from %6D\n",
-				__func__, wh->i_addr2, ":"));
 			allocbs = 1;
 		} else
 			allocbs = 0;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] recv probe req\n", (wh->i_addr2), ":");
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
-		rate = ieee80211_setup_rates(ic, ni, rates, xrates,
-				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
-				| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+		rate = ieee80211_setup_rates(ni, rates, xrates,
+			  IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE
+			| IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
 		if (rate & IEEE80211_RATE_BASIC) {
-			IEEE80211_DPRINTF(("%s: rate negotiation failed: %6D\n",
-				__func__, wh->i_addr2, ":"));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_XRATE,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "recv'd rate set invalid");
 		} else {
 			IEEE80211_SEND_MGMT(ic, ni,
 				IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
 		}
-		if (allocbs)
-			ieee80211_free_node(ic, ni);
+		if (allocbs && ic->ic_opmode != IEEE80211_M_IBSS) {
+			/* reclaim immediately */
+			ieee80211_free_node(ni);
+		}
 		break;
-	}
 
 	case IEEE80211_FC0_SUBTYPE_AUTH: {
-		uint16_t algo, seq, status;
+		u_int16_t algo, seq, status;
 		/*
 		 * auth frame format
 		 *	[2] algorithm
@@ -844,97 +2101,71 @@
 		 *	[tlv*] challenge
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
-		algo   = le16toh(*(uint16_t *)frm);
-		seq    = le16toh(*(uint16_t *)(frm + 2));
-		status = le16toh(*(uint16_t *)(frm + 4));
-		if (algo != IEEE80211_AUTH_ALG_OPEN) {
-			/* TODO: shared key auth */
-			IEEE80211_DPRINTF(("%s: unsupported auth %d from %6D\n",
-				__func__, algo, wh->i_addr2, ":"));
-			ic->ic_stats.is_rx_auth_unsupported++;
+		algo   = le16toh(*(u_int16_t *)frm);
+		seq    = le16toh(*(u_int16_t *)(frm + 2));
+		status = le16toh(*(u_int16_t *)(frm + 4));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+		    "[%6D] recv auth frame with algorithm %d seq %d\n",
+		    (wh->i_addr2), ":", algo, seq);
+		/*
+		 * Consult the ACL policy module if setup.
+		 */
+		if (ic->ic_acl != NULL &&
+		    !ic->ic_acl->iac_check(ic, wh->i_addr2)) {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ACL,
+			    wh, "auth", "%s", "disallowed by ACL");
+			ic->ic_stats.is_rx_acl++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_AUTH,
+				    (seq+1) | (IEEE80211_STATUS_UNSPECIFIED<<16));
+			}
 			return;
 		}
-		switch (ic->ic_opmode) {
-		case IEEE80211_M_IBSS:
-			if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			ieee80211_new_state(ic, IEEE80211_S_AUTH,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-			break;
-
-		case IEEE80211_M_AHDEMO:
-			/* should not come here */
-			break;
-
-		case IEEE80211_M_HOSTAP:
-			if (ic->ic_state != IEEE80211_S_RUN || seq != 1) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			if (ni == ic->ic_bss) {
-				ni = ieee80211_alloc_node(ic, wh->i_addr2);
-				if (ni == NULL)
-					return;
-				IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
-				ni->ni_rssi = rssi;
-				ni->ni_rstamp = rstamp;
-				ni->ni_chan = ic->ic_bss->ni_chan;
-				allocbs = 1;
-			} else
-				allocbs = 0;
-			IEEE80211_SEND_MGMT(ic, ni,
-				IEEE80211_FC0_SUBTYPE_AUTH, 2);
-			if (ifp->if_flags & IFF_DEBUG)
-				if_printf(ifp, "station %6D %s authenticated\n",
-				    ni->ni_macaddr, ":", 
-				    (allocbs ? "newly" : "already"));
-			break;
-
-		case IEEE80211_M_STA:
-			if (ic->ic_state != IEEE80211_S_AUTH || seq != 2) {
-				IEEE80211_DPRINTF(("%s: discard auth from %6D; "
-					"state %u, seq %u\n", __func__,
-					wh->i_addr2, ":",
-					ic->ic_state, seq));
-				ic->ic_stats.is_rx_bad_auth++;
-				break;
-			}
-			if (status != 0) {
-				if_printf(&ic->ic_if,
-				    "authentication failed (reason %d) for %6D\n",
-				    status,
-				    wh->i_addr3, ":");
-				if (ni != ic->ic_bss)
-					ni->ni_fails++;
-				ic->ic_stats.is_rx_auth_fail++;
-				return;
+		if (ic->ic_flags & IEEE80211_F_COUNTERM) {
+			IEEE80211_DISCARD(ic,
+			    IEEE80211_MSG_AUTH | IEEE80211_MSG_CRYPTO,
+			    wh, "auth", "%s", "TKIP countermeasures enabled");
+			ic->ic_stats.is_rx_auth_countermeasures++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					IEEE80211_REASON_MIC_FAILURE);
 			}
-			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
-			    wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
-			break;
-		case IEEE80211_M_MONITOR:
-			break;
+			return;
 		}
+		if (algo == IEEE80211_AUTH_ALG_SHARED)
+			ieee80211_auth_shared(ic, wh, frm + 6, efrm, ni, rssi,
+			    rstamp, seq, status);
+		else if (algo == IEEE80211_AUTH_ALG_OPEN)
+			ieee80211_auth_open(ic, wh, ni, rssi, rstamp, seq,
+			    status);
+		else {
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+			    wh, "auth", "unsupported alg %d", algo);
+			ic->ic_stats.is_rx_auth_unsupported++;
+			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+				/* XXX not right */
+				IEEE80211_SEND_MGMT(ic, ni,
+					IEEE80211_FC0_SUBTYPE_AUTH,
+					(seq+1) | (IEEE80211_STATUS_ALG<<16));
+			}
+			return;
+		} 
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: {
-		uint16_t capinfo, bintval;
+		u_int16_t capinfo, lintval;
+		struct ieee80211_rsnparms rsn;
+		u_int8_t reason;
 
 		if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
-		    (ic->ic_state != IEEE80211_S_RUN))
+		    ic->ic_state != IEEE80211_S_RUN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
+		}
 
 		if (subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
 			reassoc = 1;
@@ -951,19 +2182,22 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WPA or RSN
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, (reassoc ? 10 : 4));
 		if (!IEEE80211_ADDR_EQ(wh->i_addr3, ic->ic_bss->ni_bssid)) {
-			IEEE80211_DPRINTF(("%s: ignore other bss from %6D\n",
-				__func__, wh->i_addr2, ":"));
+			IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+			    wh, ieee80211_mgt_subtype_name[subtype >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+			    "%s", "wrong bssid");
 			ic->ic_stats.is_rx_assoc_bss++;
 			return;
 		}
-		capinfo = le16toh(*(uint16_t *)frm);	frm += 2;
-		bintval = le16toh(*(uint16_t *)frm);	frm += 2;
+		capinfo = le16toh(*(u_int16_t *)frm);	frm += 2;
+		lintval = le16toh(*(u_int16_t *)frm);	frm += 2;
 		if (reassoc)
 			frm += 6;	/* ignore current AP info */
-		ssid = rates = xrates = NULL;
+		ssid = rates = xrates = wpa = wme = NULL;
 		while (frm < efrm) {
 			switch (*frm) {
 			case IEEE80211_ELEMID_SSID:
@@ -975,97 +2209,169 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			/* XXX verify only one of RSN and WPA ie's? */
+			case IEEE80211_ELEMID_RSN:
+				wpa = frm;
+				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswpaoui(frm))
+					wpa = frm;
+				else if (iswmeinfo(frm))
+					wme = frm;
+				/* XXX Atheros OUI support */
+				break;
 			}
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN);
-		if (ssid[1] != ic->ic_bss->ni_esslen ||
-		    memcmp(ssid + 2, ic->ic_bss->ni_essid, ssid[1]) != 0) {
-#ifdef IEEE80211_DEBUG
-			if (ieee80211_debug) {
-				printf("%s: ssid unmatch ", __func__);
-				ieee80211_print_essid(ssid + 2, ssid[1]);
-				printf(" from %6D\n", wh->i_addr2, ":");
-			}
-#endif
-			ic->ic_stats.is_rx_ssidmismatch++;
-			return;
-		}
+		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid);
+
 		if (ni == ic->ic_bss) {
-			IEEE80211_DPRINTF(("%s: not authenticated for %6D\n",
-				__func__, wh->i_addr2, ":"));
-			ni = ieee80211_dup_bss(ic, wh->i_addr2);
-			if (ni != NULL) {
-				IEEE80211_SEND_MGMT(ic, ni,
-				    IEEE80211_FC0_SUBTYPE_DEAUTH,
-				    IEEE80211_REASON_ASSOC_NOT_AUTHED);
-				ieee80211_free_node(ic, ni);
-			}
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, sta not authenticated\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc");
+			ieee80211_send_error(ic, ni, wh->i_addr2,
+			    IEEE80211_FC0_SUBTYPE_DEAUTH,
+			    IEEE80211_REASON_ASSOC_NOT_AUTHED);
 			ic->ic_stats.is_rx_assoc_notauth++;
 			return;
 		}
-		/* XXX per-node cipher suite */
-		/* XXX some stations use the privacy bit for handling APs
-		       that suport both encrypted and unencrypted traffic */
-		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0 ||
-		    (capinfo & IEEE80211_CAPINFO_PRIVACY) !=
-		    ((ic->ic_flags & IEEE80211_F_WEPON) ?
-		     IEEE80211_CAPINFO_PRIVACY : 0)) {
-			IEEE80211_DPRINTF(("%s: capability mismatch %x for %6D\n",
-				__func__, capinfo, wh->i_addr2, ":"));
-			ni->ni_associd = 0;
+		/* assert right associstion security credentials */
+		if (wpa == NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+			    "[%6D] no WPA/RSN IE in association request\n",
+			    (wh->i_addr2), ":");
+			IEEE80211_SEND_MGMT(ic, ni,
+			    IEEE80211_FC0_SUBTYPE_DEAUTH,
+			    IEEE80211_REASON_RSN_REQUIRED);
+			ieee80211_node_leave(ic, ni);
+			/* XXX distinguish WPA/RSN? */
+			ic->ic_stats.is_rx_assoc_badwpaie++;
+			return;	
+		}
+		if (wpa != NULL) {
+			/*
+			 * Parse WPA information element.  Note that
+			 * we initialize the param block from the node
+			 * state so that information in the IE overrides
+			 * our defaults.  The resulting parameters are
+			 * installed below after the association is assured.
+			 */
+			rsn = ni->ni_rsn;
+			if (wpa[0] != IEEE80211_ELEMID_RSN)
+				reason = ieee80211_parse_wpa(ic, wpa, &rsn, wh);
+			else
+				reason = ieee80211_parse_rsn(ic, wpa, &rsn, wh);
+			if (reason != 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH, reason);
+				ieee80211_node_leave(ic, ni);
+				/* XXX distinguish WPA/RSN? */
+				ic->ic_stats.is_rx_assoc_badwpaie++;
+				return;
+			}
+			IEEE80211_DPRINTF(ic,
+			    IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
+			    "[%6D] %s ie: mc %u/%u uc %u/%u key %u caps 0x%x\n",
+			    (wh->i_addr2), ":",
+			    wpa[0] != IEEE80211_ELEMID_RSN ?  "WPA" : "RSN",
+			    rsn.rsn_mcastcipher, rsn.rsn_mcastkeylen,
+			    rsn.rsn_ucastcipher, rsn.rsn_ucastkeylen,
+			    rsn.rsn_keymgmt, rsn.rsn_caps);
+		}
+		/* discard challenge after association */
+		if (ni->ni_challenge != NULL) {
+			FREE(ni->ni_challenge, M_DEVBUF);
+			ni->ni_challenge = NULL;
+		}
+		/* NB: 802.11 spec says to ignore station's privacy bit */
+		if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, capability mismatch 0x%x\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc", capinfo);
 			IEEE80211_SEND_MGMT(ic, ni, resp,
 				IEEE80211_STATUS_CAPINFO);
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_rx_assoc_capmismatch++;
 			return;
 		}
-		ieee80211_setup_rates(ic, ni, rates, xrates,
+		rate = ieee80211_setup_rates(ni, rates, xrates,
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
-		if (ni->ni_rates.rs_nrates == 0) {
-			IEEE80211_DPRINTF(("%s: rate unmatch for %6D\n",
-				__func__, wh->i_addr2, ":"));
-			ni->ni_associd = 0;
+		/*
+		 * If constrained to 11g-only stations reject an
+		 * 11b-only station.  We cheat a bit here by looking
+		 * at the max negotiated xmit rate and assuming anyone
+		 * with a best rate <24Mb/s is an 11b station.
+		 */
+		if ((rate & IEEE80211_RATE_BASIC) ||
+		    ((ic->ic_flags & IEEE80211_F_PUREG) && rate < 48)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			    "[%6D] deny %s request, rate set mismatch\n",
+			    (wh->i_addr2), ":",
+			    reassoc ? "reassoc" : "assoc");
 			IEEE80211_SEND_MGMT(ic, ni, resp,
 				IEEE80211_STATUS_BASIC_RATE);
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_rx_assoc_norate++;
 			return;
 		}
 		ni->ni_rssi = rssi;
 		ni->ni_rstamp = rstamp;
-		ni->ni_intval = bintval;
+		ni->ni_intval = lintval;
 		ni->ni_capinfo = capinfo;
 		ni->ni_chan = ic->ic_bss->ni_chan;
 		ni->ni_fhdwell = ic->ic_bss->ni_fhdwell;
 		ni->ni_fhindex = ic->ic_bss->ni_fhindex;
-		if (ni->ni_associd == 0) {
-			/* XXX handle rollover at 2007 */
-			/* XXX guarantee uniqueness */
-			ni->ni_associd = 0xc000 | ic->ic_bss->ni_associd++;
-			newassoc = 1;
-		} else
-			newassoc = 0;
-		/* XXX for 11g must turn off short slot time if long
-	           slot time sta associates */
-		IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D %s associated\n",
-			    ni->ni_macaddr, ":",
-			    (newassoc ? "newly" : "already"));
-		/* give driver a chance to setup state like ni_txrate */
-		if (ic->ic_newassoc)
-			(*ic->ic_newassoc)(ic, ni, newassoc);
+		if (wpa != NULL) {
+			/*
+			 * Record WPA/RSN parameters for station, mark
+			 * node as using WPA and record information element
+			 * for applications that require it.
+			 */
+			ni->ni_rsn = rsn;
+			ieee80211_saveie(&ni->ni_wpa_ie, wpa);
+		} else if (ni->ni_wpa_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_wpa_ie, M_DEVBUF);
+			ni->ni_wpa_ie = NULL;
+		}
+		if (wme != NULL) {
+			/*
+			 * Record WME parameters for station, mark node
+			 * as capable of QoS and record information
+			 * element for applications that require it.
+			 */
+			ieee80211_saveie(&ni->ni_wme_ie, wme);
+			ni->ni_flags |= IEEE80211_NODE_QOS;
+		} else if (ni->ni_wme_ie != NULL) {
+			/*
+			 * Flush any state from a previous association.
+			 */
+			FREE(ni->ni_wme_ie, M_DEVBUF);
+			ni->ni_wme_ie = NULL;
+			ni->ni_flags &= ~IEEE80211_NODE_QOS;
+		}
+		ieee80211_node_join(ic, ni, resp);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
 	case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: {
-		uint16_t status;
+		u_int16_t capinfo, associd;
+		u_int16_t status;
 
 		if (ic->ic_opmode != IEEE80211_M_STA ||
-		    ic->ic_state != IEEE80211_S_ASSOC)
+		    ic->ic_state != IEEE80211_S_ASSOC) {
+			ic->ic_stats.is_rx_mgtdiscard++;
 			return;
+		}
 
 		/*
 		 * asresp frame format
@@ -1074,26 +2380,28 @@
 		 *	[2] association ID
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 6);
 		ni = ic->ic_bss;
-		ni->ni_capinfo = le16toh(*(uint16_t *)frm);
+		capinfo = le16toh(*(u_int16_t *)frm);
 		frm += 2;
-
-		status = le16toh(*(uint16_t *)frm);
+		status = le16toh(*(u_int16_t *)frm);
 		frm += 2;
 		if (status != 0) {
-			if_printf(ifp, "association failed (reason %d) for %6D\n",
-			    status, wh->i_addr3, ":");
-			if (ni != ic->ic_bss)
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] %sassoc failed (reason %d)\n",
+			    (wh->i_addr2), ":",
+			    ISREASSOC(subtype) ?  "re" : "", status);
+			if (ni != ic->ic_bss)	/* XXX never true? */
 				ni->ni_fails++;
-			ic->ic_stats.is_rx_auth_fail++;
+			ic->ic_stats.is_rx_auth_fail++;	/* XXX */
 			return;
 		}
-		ni->ni_associd = le16toh(*(uint16_t *)frm);
+		associd = le16toh(*(u_int16_t *)frm);
 		frm += 2;
 
-		rates = xrates = NULL;
+		rates = xrates = wpa = wme = NULL;
 		while (frm < efrm) {
 			switch (*frm) {
 			case IEEE80211_ELEMID_RATES:
@@ -1102,29 +2410,94 @@
 			case IEEE80211_ELEMID_XRATES:
 				xrates = frm;
 				break;
+			case IEEE80211_ELEMID_VENDOR:
+				if (iswmeoui(frm))
+					wme = frm;
+				/* XXX Atheros OUI support */
+				break;
 			}
 			frm += frm[1] + 2;
 		}
 
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE);
-		ieee80211_setup_rates(ic, ni, rates, xrates,
+		rate = ieee80211_setup_rates(ni, rates, xrates,
 				IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
 				IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
-		if (ni->ni_rates.rs_nrates != 0)
-			ieee80211_new_state(ic, IEEE80211_S_RUN,
-				wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK);
+		if (rate & IEEE80211_RATE_BASIC) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] %sassoc failed (rate set mismatch)\n",
+			    (wh->i_addr2), ":",
+			    ISREASSOC(subtype) ?  "re" : "");
+			if (ni != ic->ic_bss)	/* XXX never true? */
+				ni->ni_fails++;
+			ic->ic_stats.is_rx_assoc_norate++;
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, 0);
+			return;
+		}
+
+		ni->ni_capinfo = capinfo;
+		ni->ni_associd = associd;
+		if (wme != NULL &&
+		    ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
+			ni->ni_flags |= IEEE80211_NODE_QOS;
+			ieee80211_wme_updateparams(ic);
+		} else
+			ni->ni_flags &= ~IEEE80211_NODE_QOS;
+		/*
+		 * Configure state now that we are associated.
+		 *
+		 * XXX may need different/additional driver callbacks?
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_11A ||
+		    (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE)) {
+			ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+		} else {
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+		}
+		ieee80211_set_shortslottime(ic,
+			ic->ic_curmode == IEEE80211_MODE_11A ||
+			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
+		/*
+		 * Honor ERP protection.
+		 *
+		 * NB: ni_erp should zero for non-11g operation.
+		 * XXX check ic_curmode anyway?
+		 */
+		if (ic->ic_curmode == IEEE80211_MODE_11G &&
+		    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		else
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] %sassoc success: %s preamble, %s slot time%s%s\n",
+		    (wh->i_addr2), ":",
+		    ISREASSOC(subtype) ? "re" : "",
+		    ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+		    ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+		    ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : "",
+		    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
+		);
+		ieee80211_new_state(ic, IEEE80211_S_RUN, subtype);
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH: {
-		uint16_t reason;
+		u_int16_t reason;
+
+		if (ic->ic_state == IEEE80211_S_SCAN) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
 		/*
 		 * deauth frame format
 		 *	[2] reason
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(uint16_t *)frm);
+		reason = le16toh(*(u_int16_t *)frm);
 		ic->ic_stats.is_rx_deauth++;
+		IEEE80211_NODE_STAT(ni, rx_deauth);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			ieee80211_new_state(ic, IEEE80211_S_AUTH,
@@ -1132,29 +2505,37 @@
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss) {
-				if (ifp->if_flags & IFF_DEBUG)
-					if_printf(ifp, "station %6D deauthenticated"
-					    " by peer (reason %d)\n",
-					    ni->ni_macaddr, ":", reason);
-				/* node will be free'd on return */
-				ieee80211_unref_node(&ni);
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    "station %6D deauthenticated by peer "
+				    "(reason %d)\n",
+				    (ni->ni_macaddr), ":", reason);
+				ieee80211_node_leave(ic, ni);
 			}
 			break;
 		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
 			break;
 		}
 		break;
 	}
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC: {
-		uint16_t reason;
+		u_int16_t reason;
+
+		if (ic->ic_state != IEEE80211_S_RUN &&
+		    ic->ic_state != IEEE80211_S_ASSOC &&
+		    ic->ic_state != IEEE80211_S_AUTH) {
+			ic->ic_stats.is_rx_mgtdiscard++;
+			return;
+		}
 		/*
 		 * disassoc frame format
 		 *	[2] reason
 		 */
 		IEEE80211_VERIFY_LENGTH(efrm - frm, 2);
-		reason = le16toh(*(uint16_t *)frm);
+		reason = le16toh(*(u_int16_t *)frm);
 		ic->ic_stats.is_rx_disassoc++;
+		IEEE80211_NODE_STAT(ni, rx_disassoc);
 		switch (ic->ic_opmode) {
 		case IEEE80211_M_STA:
 			ieee80211_new_state(ic, IEEE80211_S_ASSOC,
@@ -1162,25 +2543,271 @@
 			break;
 		case IEEE80211_M_HOSTAP:
 			if (ni != ic->ic_bss) {
-				if (ifp->if_flags & IFF_DEBUG)
-					if_printf(ifp, "station %6D disassociated"
-					    " by peer (reason %d)\n",
-					    ni->ni_macaddr, ":", reason);
-				ni->ni_associd = 0;
-				/* XXX node reclaimed how? */
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "[%6D] sta disassociated by peer (reason %d)\n",
+				    (ni->ni_macaddr), ":", reason);
+				ieee80211_node_leave(ic, ni);
 			}
 			break;
 		default:
+			ic->ic_stats.is_rx_mgtdiscard++;
 			break;
 		}
 		break;
 	}
 	default:
-		IEEE80211_DPRINTF(("%s: mgmt frame with subtype 0x%x not "
-			"handled\n", __func__, subtype));
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_ANY,
+		     wh, "mgt", "subtype 0x%x not handled", subtype);
 		ic->ic_stats.is_rx_badsubtype++;
 		break;
 	}
+#undef ISREASSOC
+#undef ISPROBE
 }
 #undef IEEE80211_VERIFY_LENGTH
 #undef IEEE80211_VERIFY_ELEMENT
+
+/*
+ * Handle station power-save state change.
+ */
+static void
+ieee80211_node_pwrsave(struct ieee80211_node *ni, int enable)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct mbuf *m;
+
+	if (enable) {
+		if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) == 0)
+			ic->ic_ps_sta++;
+		ni->ni_flags |= IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] power save mode on, %u sta's in ps mode\n",
+		    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+		return;
+	}
+
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT)
+		ic->ic_ps_sta--;
+	ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%6D] power save mode off, %u sta's in ps mode\n",
+	    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+	/* XXX if no stations in ps mode, flush mc frames */
+
+	/*
+	 * Flush queued unicast frames.
+	 */
+	if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0) {
+		if (ic->ic_set_tim != NULL)
+			ic->ic_set_tim(ni, 0);		/* just in case */
+		return;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+	    "[%6D] flush ps queue, %u packets queued\n",
+	    (ni->ni_macaddr), ":", IEEE80211_NODE_SAVEQ_QLEN(ni));
+	for (;;) {
+		int qlen;
+
+		IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+		if (m == NULL)
+			break;
+		/* 
+		 * If this is the last packet, turn off the TIM bit.
+		 * If there are more packets, set the more packets bit
+		 * in the mbuf so ieee80211_encap will mark the 802.11
+		 * head to indicate more data frames will follow.
+		 */
+		if (qlen != 0)
+			m->m_flags |= M_MORE_DATA;
+		/* XXX need different driver interface */
+		/* XXX bypasses q max */
+		IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+	}
+	if (ic->ic_set_tim != NULL)
+		ic->ic_set_tim(ni, 0);
+}
+
+/*
+ * Process a received ps-poll frame.
+ */
+static void
+ieee80211_recv_pspoll(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct mbuf *m0)
+{
+	struct ieee80211_frame_min *wh;
+	struct mbuf *m;
+	u_int16_t aid;
+	int qlen;
+
+	wh = mtod(m0, struct ieee80211_frame_min *);
+	if (ni->ni_associd == 0) {
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+		    (struct ieee80211_frame *) wh, "ps-poll",
+		    "%s", "unassociated station");
+		ic->ic_stats.is_ps_unassoc++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
+		return;
+	}
+
+	aid = le16toh(*(u_int16_t *)wh->i_dur);
+	if (aid != ni->ni_associd) {
+		IEEE80211_DISCARD(ic, IEEE80211_MSG_POWER | IEEE80211_MSG_DEBUG,
+		    (struct ieee80211_frame *) wh, "ps-poll",
+		    "aid mismatch: sta aid 0x%x poll aid 0x%x",
+		    ni->ni_associd, aid);
+		ic->ic_stats.is_ps_badaid++;
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+			IEEE80211_REASON_NOT_ASSOCED);
+		return;
+	}
+
+	/* Okay, take the first queued packet and put it out... */
+	IEEE80211_NODE_SAVEQ_DEQUEUE(ni, m, qlen);
+	if (m == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, but queue empty\n",
+		    (wh->i_addr2), ":");
+		ieee80211_send_nulldata(ieee80211_ref_node(ni));
+		ic->ic_stats.is_ps_qempty++;	/* XXX node stat */
+		if (ic->ic_set_tim != NULL)
+			ic->ic_set_tim(ni, 0);	/* just in case */
+		return;
+	}
+	/* 
+	 * If there are more packets, set the more packets bit
+	 * in the packet dispatched to the station; otherwise
+	 * turn off the TIM bit.
+	 */
+	if (qlen != 0) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, send packet, %u still queued\n",
+		    (ni->ni_macaddr), ":", qlen);
+		m->m_flags |= M_MORE_DATA;
+	} else {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] recv ps-poll, send packet, queue empty\n",
+		    (ni->ni_macaddr), ":");
+		if (ic->ic_set_tim != NULL)
+			ic->ic_set_tim(ni, 0);
+	}
+	m->m_flags |= M_PWR_SAV;		/* bypass PS handling */
+	IF_ENQUEUE(&ic->ic_ifp->if_snd, m);
+}
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Debugging support.
+ */
+
+/*
+ * Return the bssid of a frame.
+ */
+static const u_int8_t *
+ieee80211_getbssid(struct ieee80211com *ic, const struct ieee80211_frame *wh)
+{
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		return wh->i_addr2;
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != IEEE80211_FC1_DIR_NODS)
+		return wh->i_addr1;
+	if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+		return wh->i_addr1;
+	return wh->i_addr3;
+}
+
+void
+ieee80211_note(struct ieee80211com *ic, const char *fmt, ...)
+{
+	char buf[128];		/* XXX */
+
+	__va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	__va_end(ap);
+
+	if_printf(ic->ic_ifp, "%s", buf);	/* NB: no \n */
+}
+
+void
+ieee80211_note_frame(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const char *fmt, ...)
+{
+	char buf[128];		/* XXX */
+	__va_list ap;
+
+	__va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	__va_end(ap);
+	if_printf(ic->ic_ifp, "[%6D] %s\n",
+		ieee80211_getbssid(ic, wh),":", buf);
+}
+
+void
+ieee80211_note_mac(struct ieee80211com *ic,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const char *fmt, ...)
+{
+	char buf[128];		/* XXX */
+	__va_list ap;
+
+	__va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	__va_end(ap);
+	if_printf(ic->ic_ifp, "[%6D] %s\n", mac, ":", buf);
+}
+
+static void
+ieee80211_discard_frame(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (ieee80211_getbssid(ic, wh)), ":");
+	if (type != NULL)
+		printf(" %s frame, ", type);
+	else
+		printf(" frame, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+
+static void
+ieee80211_discard_ie(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (ieee80211_getbssid(ic, wh)),":");
+	if (type != NULL)
+		printf(" %s information element, ", type);
+	else
+		printf(" information element, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+
+static void
+ieee80211_discard_mac(struct ieee80211com *ic,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const char *type, const char *fmt, ...)
+{
+	__va_list ap;
+
+	printf("[%6D] discard ", (mac), ":");
+	if (type != NULL)
+		printf(" %s frame, ", type);
+	else
+		printf(" frame, ");
+	__va_start(ap, fmt);
+	vprintf(fmt, ap);
+	__va_end(ap);
+	printf("\n");
+}
+#endif /* IEEE80211_DEBUG */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_ioctl.c src/sys/netproto/802_11/ieee80211_ioctl.c
--- src.preview/sys/netproto/802_11/ieee80211_ioctl.c	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_ioctl.c	2005-09-23 11:46:14.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,11 +28,10 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_ioctl.c,v 1.13 2004/03/30 22:57:57 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 ioctl support (FreeBSD-specific)
  */
@@ -43,11 +42,10 @@
 #include <sys/endian.h>
 #include <sys/param.h>
 #include <sys/kernel.h>
-#include <sys/proc.h>
 #include <sys/socket.h>
 #include <sys/sockio.h>
 #include <sys/systm.h>
-#include <sys/thread.h>
+#include <sys/proc.h>
  
 #include <net/if.h>
 #include <net/if_arp.h>
@@ -69,26 +67,130 @@
 
 #include <netproto/802_11/if_wavelan_ieee.h>
 
+#define	IS_UP(_ic) \
+	(((_ic)->ic_ifp->if_flags & IFF_UP) &&			\
+	    ((_ic)->ic_ifp->if_flags & IFF_RUNNING))
+#define	IS_UP_AUTO(_ic) \
+	(IS_UP(_ic) && (_ic)->ic_roaming == IEEE80211_ROAMING_AUTO)
+
 /*
  * XXX
  * Wireless LAN specific configuration interface, which is compatible
  * with wicontrol(8).
  */
 
+struct wi_read_ap_args {
+	int	i;		/* result count */
+	struct wi_apinfo *ap;	/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_ap_result(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_ap_args *sa = arg;
+	struct wi_apinfo *ap = sa->ap;
+	struct ieee80211_rateset *rs;
+	int j;
+
+	if ((caddr_t)(ap + 1) > sa->max)
+		return;
+	memset(ap, 0, sizeof(struct wi_apinfo));
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
+		ap->namelen = ic->ic_des_esslen;
+		if (ic->ic_des_esslen)
+			memcpy(ap->name, ic->ic_des_essid,
+			    ic->ic_des_esslen);
+	} else {
+		IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
+		ap->namelen = ni->ni_esslen;
+		if (ni->ni_esslen)
+			memcpy(ap->name, ni->ni_essid,
+			    ni->ni_esslen);
+	}
+	ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
+	ap->signal = ic->ic_node_getrssi(ni);
+	ap->capinfo = ni->ni_capinfo;
+	ap->interval = ni->ni_intval;
+	rs = &ni->ni_rates;
+	for (j = 0; j < rs->rs_nrates; j++) {
+		if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
+			ap->rate = (rs->rs_rates[j] &
+			    IEEE80211_RATE_VAL) * 5; /* XXX */
+		}
+	}
+	sa->i++;
+	sa->ap++;
+}
+
+struct wi_read_prism2_args {
+	int	i;		/* result count */
+	struct wi_scan_res *res;/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_prism2_result(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_prism2_args *sa = arg;
+	struct wi_scan_res *res = sa->res;
+
+	if ((caddr_t)(res + 1) > sa->max)
+		return;
+	res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
+	res->wi_noise = 0;
+	res->wi_signal = ic->ic_node_getrssi(ni);
+	IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
+	res->wi_interval = ni->ni_intval;
+	res->wi_capinfo = ni->ni_capinfo;
+	res->wi_ssid_len = ni->ni_esslen;
+	memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
+	/* NB: assumes wi_srates holds <= ni->ni_rates */
+	memcpy(res->wi_srates, ni->ni_rates.rs_rates,
+		sizeof(res->wi_srates));
+	if (ni->ni_rates.rs_nrates < 10)
+		res->wi_srates[ni->ni_rates.rs_nrates] = 0;
+	res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
+	res->wi_rsvd = 0;
+
+	sa->i++;
+	sa->res++;
+}
+
+struct wi_read_sigcache_args {
+	int	i;		/* result count */
+	struct wi_sigcache *wsc;/* current entry in result buffer */
+	caddr_t	max;		/* result buffer bound */
+};
+
+static void
+wi_read_sigcache(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct wi_read_sigcache_args *sa = arg;
+	struct wi_sigcache *wsc = sa->wsc;
+
+	if ((caddr_t)(wsc + 1) > sa->max)
+		return;
+	memset(wsc, 0, sizeof(struct wi_sigcache));
+	IEEE80211_ADDR_COPY(wsc->macsrc, ni->ni_macaddr);
+	wsc->signal = ic->ic_node_getrssi(ni);
+
+	sa->wsc++;
+	sa->i++;
+}
+
 int
-ieee80211_cfgget(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+ieee80211_cfgget(struct ieee80211com *ic, u_long cmd, caddr_t data, struct ucred *cr)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	int i, j, error;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct wi_req wreq;
 	struct wi_ltv_keys *keys;
-	struct wi_apinfo *ap;
-	struct ieee80211_node *ni;
-	struct ieee80211_rateset *rs;
-	struct wi_sigcache wsc;
-	struct wi_scan_p2_hdr *p2;
-	struct wi_scan_res *res;
 
 	error = copyin(ifr->ifr_data, &wreq, sizeof(wreq));
 	if (error)
@@ -139,7 +241,7 @@
 			i = 0;
 		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++)
 			if (isset(ic->ic_chan_active, i)) {
-				setbit((uint8_t *)wreq.wi_val, j);
+				setbit((u_int8_t *)wreq.wi_val, j);
 				wreq.wi_len = j / 16 + 1;
 			}
 		break;
@@ -150,13 +252,12 @@
 		break;
 	case WI_RID_CURRENT_CHAN:
 		wreq.wi_val[0] = htole16(
-			ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan));
+			ieee80211_chan2ieee(ic, ic->ic_curchan));
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_COMMS_QUALITY:
 		wreq.wi_val[0] = 0;				/* quality */
-		wreq.wi_val[1] =
-			htole16((*ic->ic_node_getrssi)(ic, ic->ic_bss));
+		wreq.wi_val[1] = htole16(ic->ic_node_getrssi(ic->ic_bss));
 		wreq.wi_val[2] = 0;				/* noise */
 		wreq.wi_len = 3;
 		break;
@@ -173,7 +274,7 @@
 		wreq.wi_len = IEEE80211_ADDR_LEN / 2;
 		break;
 	case WI_RID_TX_RATE:
-		if (ic->ic_fixed_rate == -1)
+		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
 			wreq.wi_val[0] = 0;	/* auto */
 		else
 			wreq.wi_val[0] = htole16(
@@ -201,7 +302,7 @@
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_ROAMING_MODE:
-		wreq.wi_val[0] = htole16(1);	/* enabled ... not supported */
+		wreq.wi_val[0] = htole16(ic->ic_roaming);	/* XXX map */
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_SYSTEM_SCALE:
@@ -222,8 +323,7 @@
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_WEP_AVAIL:
-		wreq.wi_val[0] =
-		    htole16((ic->ic_caps & IEEE80211_C_WEP) ? 1 : 0);
+		wreq.wi_val[0] = htole16(1);	/* always available */
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_CNFAUTHMODE:
@@ -232,11 +332,11 @@
 		break;
 	case WI_RID_ENCRYPTION:
 		wreq.wi_val[0] =
-		    htole16((ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0);
+		    htole16((ic->ic_flags & IEEE80211_F_PRIVACY) ? 1 : 0);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_TX_CRYPT_KEY:
-		wreq.wi_val[0] = htole16(ic->ic_wep_txkey);
+		wreq.wi_val[0] = htole16(ic->ic_def_txkey);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_DEFLT_CRYPT_KEYS:
@@ -250,124 +350,71 @@
 		}
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
 			keys->wi_keys[i].wi_keylen =
-			    htole16(ic->ic_nw_keys[i].wk_len);
+			    htole16(ic->ic_nw_keys[i].wk_keylen);
 			memcpy(keys->wi_keys[i].wi_keydat,
-			    ic->ic_nw_keys[i].wk_key, ic->ic_nw_keys[i].wk_len);
+			    ic->ic_nw_keys[i].wk_key,
+			    ic->ic_nw_keys[i].wk_keylen);
 		}
 		wreq.wi_len = sizeof(*keys) / 2;
 		break;
 	case WI_RID_MAX_DATALEN:
-		wreq.wi_val[0] = htole16(IEEE80211_MAX_LEN);	/* TODO: frag */
+		wreq.wi_val[0] = htole16(ic->ic_fragthreshold);
 		wreq.wi_len = 1;
 		break;
 	case WI_RID_IFACE_STATS:
 		/* XXX: should be implemented in lower drivers */
 		break;
 	case WI_RID_READ_APS:
-		if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
-			/*
-			 * Don't return results until active scan completes.
-			 */
-			if (ic->ic_state == IEEE80211_S_SCAN &&
-			    (ic->ic_flags & IEEE80211_F_ASCAN)) {
-				error = EINPROGRESS;
-				break;
-			}
-		}
-		i = 0;
-		ap = (void *)((char *)wreq.wi_val + sizeof(i));
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if ((caddr_t)(ap + 1) > (caddr_t)(&wreq + 1))
-				break;
-			memset(ap, 0, sizeof(*ap));
-			if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_macaddr);
-				ap->namelen = ic->ic_des_esslen;
-				if (ic->ic_des_esslen)
-					memcpy(ap->name, ic->ic_des_essid,
-					    ic->ic_des_esslen);
-			} else {
-				IEEE80211_ADDR_COPY(ap->bssid, ni->ni_bssid);
-				ap->namelen = ni->ni_esslen;
-				if (ni->ni_esslen)
-					memcpy(ap->name, ni->ni_essid,
-					    ni->ni_esslen);
-			}
-			ap->channel = ieee80211_chan2ieee(ic, ni->ni_chan);
-			ap->signal = (*ic->ic_node_getrssi)(ic, ni);
-			ap->capinfo = ni->ni_capinfo;
-			ap->interval = ni->ni_intval;
-			rs = &ni->ni_rates;
-			for (j = 0; j < rs->rs_nrates; j++) {
-				if (rs->rs_rates[j] & IEEE80211_RATE_BASIC) {
-					ap->rate = (rs->rs_rates[j] &
-					    IEEE80211_RATE_VAL) * 5; /* XXX */
-				}
-			}
-			i++;
-			ap++;
-		}
-		memcpy(wreq.wi_val, &i, sizeof(i));
-		wreq.wi_len = (sizeof(int) + sizeof(*ap) * i) / 2;
+		/*
+		 * Don't return results until active scan completes.
+		 */
+		if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+			struct wi_read_ap_args args;
+
+			args.i = 0;
+			args.ap = (void *)((char *)wreq.wi_val + sizeof(i));
+			args.max = (void *)(&wreq + 1);
+			ieee80211_iterate_nodes(&ic->ic_scan,
+				wi_read_ap_result, &args);
+			memcpy(wreq.wi_val, &args.i, sizeof(args.i));
+			wreq.wi_len = (sizeof(int) +
+				sizeof(struct wi_apinfo) * args.i) / 2;
+		} else
+			error = EINPROGRESS;
 		break;
 	case WI_RID_PRISM2:
-		wreq.wi_val[0] = 1;	/* XXX lie so SCAN_RES can give rates */
-		wreq.wi_len = sizeof(uint16_t) / 2;
+		/* NB: we lie so WI_RID_SCAN_RES can include rates */
+		wreq.wi_val[0] = 1;
+		wreq.wi_len = sizeof(u_int16_t) / 2;
 		break;
 	case WI_RID_SCAN_RES:			/* compatibility interface */
-		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
-		    ic->ic_state == IEEE80211_S_SCAN &&
-		    (ic->ic_flags & IEEE80211_F_ASCAN)) {
+		if ((ic->ic_flags & (IEEE80211_F_SCAN|IEEE80211_F_ASCAN)) == 0) {
+			struct wi_read_prism2_args args;
+			struct wi_scan_p2_hdr *p2;
+
+			/* NB: use Prism2 format so we can include rate info */
+			p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
+			args.i = 0;
+			args.res = (void *)&p2[1];
+			args.max = (void *)(&wreq + 1);
+			ieee80211_iterate_nodes(&ic->ic_scan,
+				wi_read_prism2_result, &args);
+			p2->wi_rsvd = 0;
+			p2->wi_reason = args.i;
+			wreq.wi_len = (sizeof(*p2) +
+				sizeof(struct wi_scan_res) * args.i) / 2;
+		} else
 			error = EINPROGRESS;
-			break;
-		}
-		/* NB: we use the Prism2 format so we can return rate info */
-		p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
-		res = (void *)&p2[1];
-		i = 0;
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if ((caddr_t)(res + 1) > (caddr_t)(&wreq + 1))
-				break;
-			res->wi_chan = ieee80211_chan2ieee(ic, ni->ni_chan);
-			res->wi_noise = 0;
-			res->wi_signal = (*ic->ic_node_getrssi)(ic, ni);
-			IEEE80211_ADDR_COPY(res->wi_bssid, ni->ni_bssid);
-			res->wi_interval = ni->ni_intval;
-			res->wi_capinfo = ni->ni_capinfo;
-			res->wi_ssid_len = ni->ni_esslen;
-			memcpy(res->wi_ssid, ni->ni_essid, IEEE80211_NWID_LEN);
-			/* NB: assumes wi_srates holds <= ni->ni_rates */
-			memcpy(res->wi_srates, ni->ni_rates.rs_rates,
-				sizeof(res->wi_srates));
-			if (ni->ni_rates.rs_nrates < 10)
-				res->wi_srates[ni->ni_rates.rs_nrates] = 0;
-			res->wi_rate = ni->ni_rates.rs_rates[ni->ni_txrate];
-			res->wi_rsvd = 0;
-			res++, i++;
-		}
-		p2->wi_rsvd = 0;
-		p2->wi_reason = i;
-		wreq.wi_len = (sizeof(*p2) + sizeof(*res) * i) / 2;
-		break;
-	case WI_RID_READ_CACHE:
-		i = 0;
-		TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-			if (i == (WI_MAX_DATALEN/sizeof(struct wi_sigcache))-1)
-				break;
-			IEEE80211_ADDR_COPY(wsc.macsrc, ni->ni_macaddr);
-			memset(&wsc.ipsrc, 0, sizeof(wsc.ipsrc));
-			wsc.signal = (*ic->ic_node_getrssi)(ic, ni);
-			wsc.noise = 0;
-			wsc.quality = 0;
-			memcpy((caddr_t)wreq.wi_val + sizeof(wsc) * i,
-			    &wsc, sizeof(wsc));
-			i++;
-		}
-		wreq.wi_len = sizeof(wsc) * i / 2;
 		break;
-	case WI_RID_SCAN_APS:
-		error = EINVAL;
+	case WI_RID_READ_CACHE: {
+		struct wi_read_sigcache_args args;
+		args.i = 0;
+		args.wsc = (struct wi_sigcache *) wreq.wi_val;
+		args.max = (void *)(&wreq + 1);
+		ieee80211_iterate_nodes(&ic->ic_scan, wi_read_sigcache, &args);
+		wreq.wi_len = sizeof(struct wi_sigcache) * args.i / 2;
 		break;
+	}
 	default:
 		error = EINVAL;
 		break;
@@ -399,25 +446,9 @@
  * the active list as the place to start the scan.
  */
 static int
-ieee80211_setupscan(struct ieee80211com *ic)
+ieee80211_setupscan(struct ieee80211com *ic, const u_int8_t chanlist[])
 {
-	u_char *chanlist = ic->ic_chan_active;
-	int i;
 
-	if (ic->ic_ibss_chan == NULL ||
-	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
-		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
-			if (isset(chanlist, i)) {
-				ic->ic_ibss_chan = &ic->ic_channels[i];
-				goto found;
-			}
-		return EINVAL;			/* no active channels */
-found:
-		;
-	}
-	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC ||
-	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan)))
-		ic->ic_bss->ni_chan = ic->ic_ibss_chan;
 	/*
 	 * XXX don't permit a scan to be started unless we
 	 * know the device is ready.  For the moment this means
@@ -426,13 +457,23 @@
 	 * scanning prior to being up but that'll require some
 	 * changes to the infrastructure.
 	 */
-	return (ic->ic_if.if_flags & IFF_UP) ? 0 : ENETRESET;
+	if (!IS_UP(ic))
+		return EINVAL;
+	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+	/*
+	 * We force the state to INIT before calling ieee80211_new_state
+	 * to get ieee80211_begin_scan called.  We really want to scan w/o
+	 * altering the current state but that's not possible right now.
+	 */
+	/* XXX handle proberequest case */
+	ic->ic_state = IEEE80211_S_INIT;	/* XXX bypass state machine */
+	return 0;
 }
 
 int
-ieee80211_cfgset(struct ifnet *ifp, u_long cmd, caddr_t data)
+ieee80211_cfgset(struct ieee80211com *ic, u_long cmd, caddr_t data)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	int i, j, len, error, rate;
 	struct ifreq *ifr = (struct ifreq *)data;
 	struct wi_ltv_keys *keys;
@@ -472,7 +513,9 @@
 		    isclr(ic->ic_chan_active, i))
 			return EINVAL;
 		ic->ic_ibss_chan = &ic->ic_channels[i];
-		if (ic->ic_flags & IEEE80211_F_SIBSS)
+		if (ic->ic_opmode == IEEE80211_M_MONITOR)
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		else
 			error = ENETRESET;
 		break;
 	case WI_RID_CURRENT_CHAN:
@@ -518,7 +561,7 @@
 		}
 		if (le16toh(wreq.wi_val[0]) != ic->ic_opmode) {
 			ic->ic_opmode = le16toh(wreq.wi_val[0]);
-			error = ENETRESET;
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		}
 		break;
 #if 0
@@ -535,7 +578,7 @@
 			return EINVAL;
 		if (wreq.wi_val[0] == 0) {
 			/* auto */
-			ic->ic_fixed_rate = -1;
+			ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
 			break;
 		}
 		rate = 2 * le16toh(wreq.wi_val[0]);
@@ -566,7 +609,7 @@
 		return EINVAL;
 	setrate:
 		ic->ic_fixed_rate = i;
-		error = ENETRESET;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_CUR_TX_RATE:
 		return EPERM;
@@ -586,14 +629,14 @@
 				ic->ic_flags |= IEEE80211_F_IBSSON;
 				if (ic->ic_opmode == IEEE80211_M_IBSS &&
 				    ic->ic_state == IEEE80211_S_SCAN)
-					error = ENETRESET;
+					error = IS_UP_AUTO(ic) ? ENETRESET : 0;
 			}
 		} else {
 			if (ic->ic_flags & IEEE80211_F_IBSSON) {
 				ic->ic_flags &= ~IEEE80211_F_IBSSON;
 				if (ic->ic_flags & IEEE80211_F_SIBSS) {
 					ic->ic_flags &= ~IEEE80211_F_SIBSS;
-					error = ENETRESET;
+					error = IS_UP_AUTO(ic) ? ENETRESET : 0;
 				}
 			}
 		}
@@ -607,8 +650,10 @@
 	case WI_RID_ROAMING_MODE:
 		if (len != 2)
 			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != 1)
+		i = le16toh(wreq.wi_val[0]);
+		if (i > IEEE80211_ROAMING_MANUAL)
 			return EINVAL;		/* not supported */
+		ic->ic_roaming = i;
 		break;
 	case WI_RID_SYSTEM_SCALE:
 		if (len != 2)
@@ -624,12 +669,12 @@
 				return EINVAL;
 			if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
 				ic->ic_flags |= IEEE80211_F_PMGTON;
-				error = ENETRESET;
+				error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 			}
 		} else {
 			if (ic->ic_flags & IEEE80211_F_PMGTON) {
 				ic->ic_flags &= ~IEEE80211_F_PMGTON;
-				error = ENETRESET;
+				error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 			}
 		}
 		break;
@@ -638,7 +683,7 @@
 			return EINVAL;
 		ic->ic_lintval = le16toh(wreq.wi_val[0]);
 		if (ic->ic_flags & IEEE80211_F_PMGTON)
-			error = ENETRESET;
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_CUR_BEACON_INT:
 		return EPERM;
@@ -647,8 +692,11 @@
 	case WI_RID_CNFAUTHMODE:
 		if (len != 2)
 			return EINVAL;
-		if (le16toh(wreq.wi_val[0]) != 1)
-			return EINVAL;		/* TODO: shared key auth */
+		i = le16toh(wreq.wi_val[0]);
+		if (i > IEEE80211_AUTH_WPA)
+			return EINVAL;
+		ic->ic_bss->ni_authmode = i;		/* XXX ENETRESET? */
+		error = ENETRESET;
 		break;
 	case WI_RID_ENCRYPTION:
 		if (len != 2)
@@ -656,13 +704,13 @@
 		if (wreq.wi_val[0] != 0) {
 			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
 				return EINVAL;
-			if ((ic->ic_flags & IEEE80211_F_WEPON) == 0) {
-				ic->ic_flags |= IEEE80211_F_WEPON;
+			if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0) {
+				ic->ic_flags |= IEEE80211_F_PRIVACY;
 				error = ENETRESET;
 			}
 		} else {
-			if (ic->ic_flags & IEEE80211_F_WEPON) {
-				ic->ic_flags &= ~IEEE80211_F_WEPON;
+			if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+				ic->ic_flags &= ~IEEE80211_F_PRIVACY;
 				error = ENETRESET;
 			}
 		}
@@ -673,7 +721,8 @@
 		i = le16toh(wreq.wi_val[0]);
 		if (i >= IEEE80211_WEP_NKID)
 			return EINVAL;
-		ic->ic_wep_txkey = i;
+		ic->ic_def_txkey = i;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_DEFLT_CRYPT_KEYS:
 		if (len != sizeof(struct wi_ltv_keys))
@@ -683,15 +732,20 @@
 			len = le16toh(keys->wi_keys[i].wi_keylen);
 			if (len != 0 && len < IEEE80211_WEP_KEYLEN)
 				return EINVAL;
-			if (len > sizeof(ic->ic_nw_keys[i].wk_key))
+			if (len > IEEE80211_KEYBUF_SIZE)
 				return EINVAL;
 		}
-		memset(ic->ic_nw_keys, 0, sizeof(ic->ic_nw_keys));
 		for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+			struct ieee80211_key *k = &ic->ic_nw_keys[i];
+
 			len = le16toh(keys->wi_keys[i].wi_keylen);
-			ic->ic_nw_keys[i].wk_len = len;
-			memcpy(ic->ic_nw_keys[i].wk_key,
-			    keys->wi_keys[i].wi_keydat, len);
+			k->wk_keylen = len;
+			k->wk_flags = IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV;
+			memset(k->wk_key, 0, sizeof(k->wk_key));
+			memcpy(k->wk_key, keys->wi_keys[i].wi_keydat, len);
+#if 0
+			k->wk_type = IEEE80211_CIPHER_WEP;
+#endif
 		}
 		error = ENETRESET;
 		break;
@@ -701,10 +755,8 @@
 		len = le16toh(wreq.wi_val[0]);
 		if (len < 350 /* ? */ || len > IEEE80211_MAX_LEN)
 			return EINVAL;
-		if (len != IEEE80211_MAX_LEN)
-			return EINVAL;		/* TODO: fragment */
 		ic->ic_fragthreshold = len;
-		error = ENETRESET;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
 		break;
 	case WI_RID_IFACE_STATS:
 		error = EPERM;
@@ -712,7 +764,7 @@
 	case WI_RID_SCAN_REQ:			/* XXX wicontrol */
 		if (ic->ic_opmode == IEEE80211_M_HOSTAP)
 			break;
-		error = ieee80211_setupscan(ic);
+		error = ieee80211_setupscan(ic, ic->ic_chan_avail);
 		if (error == 0)
 			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
 		break;
@@ -734,7 +786,7 @@
 		for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
 			if ((j / 8) >= len)
 				break;
-			if (isclr((uint8_t *)wreq.wi_val, j))
+			if (isclr((u_int8_t *)wreq.wi_val, j))
 				continue;
 			if (isclr(ic->ic_chan_active, i)) {
 				if (wreq.wi_type != WI_RID_CHANNEL_LIST)
@@ -744,9 +796,7 @@
 			}
 			setbit(chanlist, i);
 		}
-		memcpy(ic->ic_chan_active, chanlist,
-		    sizeof(ic->ic_chan_active));
-		error = ieee80211_setupscan(ic);
+		error = ieee80211_setupscan(ic, chanlist);
 		if (wreq.wi_type == WI_RID_CHANNEL_LIST) {
 			/* NB: ignore error from ieee80211_setupscan */
 			error = ENETRESET;
@@ -757,311 +807,1620 @@
 		error = EINVAL;
 		break;
 	}
+	if (error == ENETRESET && !IS_UP_AUTO(ic))
+		error = 0;
 	return error;
 }
 
-int
-ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr)
+static int
+cap2cipher(int flag)
+{
+	switch (flag) {
+	case IEEE80211_C_WEP:		return IEEE80211_CIPHER_WEP;
+	case IEEE80211_C_AES:		return IEEE80211_CIPHER_AES_OCB;
+	case IEEE80211_C_AES_CCM:	return IEEE80211_CIPHER_AES_CCM;
+	case IEEE80211_C_CKIP:		return IEEE80211_CIPHER_CKIP;
+	case IEEE80211_C_TKIP:		return IEEE80211_CIPHER_TKIP;
+	}
+	return -1;
+}
+
+static int
+ieee80211_ioctl_getkey(struct ieee80211com *ic, struct ieee80211req *ireq, struct ucred *cr)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_key ik;
+	struct ieee80211_key *wk;
+	const struct ieee80211_cipher *cip;
+	u_int kid;
+	int error;
+
+	if (ireq->i_len != sizeof(ik))
+		return EINVAL;
+	error = copyin(ireq->i_data, &ik, sizeof(ik));
+	if (error)
+		return error;
+	kid = ik.ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+		if (ni == NULL)
+			return EINVAL;		/* XXX */
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		IEEE80211_ADDR_COPY(&ik.ik_macaddr, ic->ic_bss->ni_macaddr);
+		ni = NULL;
+	}
+	cip = wk->wk_cipher;
+	ik.ik_type = cip->ic_cipher;
+	ik.ik_keylen = wk->wk_keylen;
+	ik.ik_flags = wk->wk_flags & (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV);
+	if (wk->wk_keyix == ic->ic_def_txkey)
+		ik.ik_flags |= IEEE80211_KEY_DEFAULT;
+	if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
+		/* NB: only root can read key data */
+		ik.ik_keyrsc = wk->wk_keyrsc;
+		ik.ik_keytsc = wk->wk_keytsc;
+		memcpy(ik.ik_keydata, wk->wk_key, wk->wk_keylen);
+		if (cip->ic_cipher == IEEE80211_CIPHER_TKIP) {
+			memcpy(ik.ik_keydata+wk->wk_keylen,
+				wk->wk_key + IEEE80211_KEYBUF_SIZE,
+				IEEE80211_MICBUF_SIZE);
+			ik.ik_keylen += IEEE80211_MICBUF_SIZE;
+		}
+	} else {
+		ik.ik_keyrsc = 0;
+		ik.ik_keytsc = 0;
+		memset(ik.ik_keydata, 0, sizeof(ik.ik_keydata));
+	}
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	return copyout(&ik, ireq->i_data, sizeof(ik));
+}
+
+static int
+ieee80211_ioctl_getchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+
+	if (sizeof(ic->ic_chan_active) > ireq->i_len)
+		ireq->i_len = sizeof(ic->ic_chan_active);
+	return copyout(&ic->ic_chan_active, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getchaninfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_chaninfo chans;	/* XXX off stack? */
+	int i, space;
+
+	/*
+	 * Since channel 0 is not available for DS, channel 1
+	 * is assigned to LSB on WaveLAN.
+	 */
+	if (ic->ic_phytype == IEEE80211_T_DS)
+		i = 1;
+	else
+		i = 0;
+	memset(&chans, 0, sizeof(chans));
+	for (; i <= IEEE80211_CHAN_MAX; i++)
+		if (isset(ic->ic_chan_avail, i)) {
+			struct ieee80211_channel *c = &ic->ic_channels[i];
+			chans.ic_chans[chans.ic_nchans].ic_freq = c->ic_freq;
+			chans.ic_chans[chans.ic_nchans].ic_flags = c->ic_flags;
+			chans.ic_nchans++;
+		}
+	space = __offsetof(struct ieee80211req_chaninfo,
+			ic_chans[chans.ic_nchans]);
+	if (space > ireq->i_len)
+		space = ireq->i_len;
+	return copyout(&chans, ireq->i_data, space);
+}
+
+static int
+ieee80211_ioctl_getwpaie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_wpaie wpaie;
+	int error;
+
+	if (ireq->i_len < IEEE80211_ADDR_LEN)
+		return EINVAL;
+	error = copyin(ireq->i_data, wpaie.wpa_macaddr, IEEE80211_ADDR_LEN);
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, wpaie.wpa_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	memset(wpaie.wpa_ie, 0, sizeof(wpaie.wpa_ie));
+	if (ni->ni_wpa_ie != NULL) {
+		int ielen = ni->ni_wpa_ie[1] + 2;
+		if (ielen > sizeof(wpaie.wpa_ie))
+			ielen = sizeof(wpaie.wpa_ie);
+		memcpy(wpaie.wpa_ie, ni->ni_wpa_ie, ielen);
+	}
+	ieee80211_free_node(ni);
+	if (ireq->i_len > sizeof(wpaie))
+		ireq->i_len = sizeof(wpaie);
+	return copyout(&wpaie, ireq->i_data, ireq->i_len);
+}
+
+static int
+ieee80211_ioctl_getstastats(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	u_int8_t macaddr[IEEE80211_ADDR_LEN];
+	const int off = __offsetof(struct ieee80211req_sta_stats, is_stats);
+	int error;
+
+	if (ireq->i_len < off)
+		return EINVAL;
+	error = copyin(ireq->i_data, macaddr, IEEE80211_ADDR_LEN);
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	if (ireq->i_len > sizeof(struct ieee80211req_sta_stats))
+		ireq->i_len = sizeof(struct ieee80211req_sta_stats);
+	/* NB: copy out only the statistics */
+	error = copyout(&ni->ni_stats, (u_int8_t *) ireq->i_data + off,
+			ireq->i_len - off);
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static void
+get_scan_result(struct ieee80211req_scan_result *sr,
+	const struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	memset(sr, 0, sizeof(*sr));
+	sr->isr_ssid_len = ni->ni_esslen;
+	if (ni->ni_wpa_ie != NULL)
+		sr->isr_ie_len += 2+ni->ni_wpa_ie[1];
+	if (ni->ni_wme_ie != NULL)
+		sr->isr_ie_len += 2+ni->ni_wme_ie[1];
+	sr->isr_len = sizeof(*sr) + sr->isr_ssid_len + sr->isr_ie_len;
+	sr->isr_len = roundup(sr->isr_len, sizeof(u_int32_t));
+	if (ni->ni_chan != IEEE80211_CHAN_ANYC) {
+		sr->isr_freq = ni->ni_chan->ic_freq;
+		sr->isr_flags = ni->ni_chan->ic_flags;
+	}
+	sr->isr_rssi = ic->ic_node_getrssi(ni);
+	sr->isr_intval = ni->ni_intval;
+	sr->isr_capinfo = ni->ni_capinfo;
+	sr->isr_erp = ni->ni_erp;
+	IEEE80211_ADDR_COPY(sr->isr_bssid, ni->ni_bssid);
+	sr->isr_nrates = ni->ni_rates.rs_nrates;
+	if (sr->isr_nrates > 15)
+		sr->isr_nrates = 15;
+	memcpy(sr->isr_rates, ni->ni_rates.rs_rates, sr->isr_nrates);
+}
+
+static int
+ieee80211_ioctl_getscanresults(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	union {
+		struct ieee80211req_scan_result res;
+		char data[512];		/* XXX shrink? */
+	} u;
+	struct ieee80211req_scan_result *sr = &u.res;
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	int error, space;
+	u_int8_t *p, *cp;
+
+	p = ireq->i_data;
+	space = ireq->i_len;
+	error = 0;
+	/* XXX locking */
+	nt =  &ic->ic_scan;
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		/* NB: skip pre-scan node state */ 
+		if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+			continue;
+		get_scan_result(sr, ni);
+		if (sr->isr_len > sizeof(u))
+			continue;		/* XXX */
+		if (space < sr->isr_len)
+			break;
+		cp = (u_int8_t *)(sr+1);
+		memcpy(cp, ni->ni_essid, ni->ni_esslen);
+		cp += ni->ni_esslen;
+		if (ni->ni_wpa_ie != NULL) {
+			memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+			cp += 2+ni->ni_wpa_ie[1];
+		}
+		if (ni->ni_wme_ie != NULL) {
+			memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+			cp += 2+ni->ni_wme_ie[1];
+		}
+		error = copyout(sr, p, sr->isr_len);
+		if (error)
+			break;
+		p += sr->isr_len;
+		space -= sr->isr_len;
+	}
+	ireq->i_len -= space;
+	return error;
+}
+
+struct stainforeq {
+	struct ieee80211com *ic;
+	struct ieee80211req_sta_info *si;
+	size_t	space;
+};
+
+static size_t
+sta_space(const struct ieee80211_node *ni, size_t *ielen)
+{
+	*ielen = 0;
+	if (ni->ni_wpa_ie != NULL)
+		*ielen += 2+ni->ni_wpa_ie[1];
+	if (ni->ni_wme_ie != NULL)
+		*ielen += 2+ni->ni_wme_ie[1];
+	return roundup(sizeof(struct ieee80211req_sta_info) + *ielen,
+		      sizeof(u_int32_t));
+}
+
+static void
+get_sta_space(void *arg, struct ieee80211_node *ni)
+{
+	struct stainforeq *req = arg;
+	struct ieee80211com *ic = ni->ni_ic;
+	size_t ielen;
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    ni->ni_associd == 0)	/* only associated stations */
+		return;
+	req->space += sta_space(ni, &ielen);
+}
+
+static void
+get_sta_info(void *arg, struct ieee80211_node *ni)
+{
+	struct stainforeq *req = arg;
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211req_sta_info *si;
+	size_t ielen, len;
+	u_int8_t *cp;
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    ni->ni_associd == 0)	/* only associated stations */
+		return;
+	if (ni->ni_chan == IEEE80211_CHAN_ANYC)	/* XXX bogus entry */
+		return;
+	len = sta_space(ni, &ielen);
+	if (len > req->space)
+		return;
+	si = req->si;
+	si->isi_len = len;
+	si->isi_ie_len = ielen;
+	si->isi_freq = ni->ni_chan->ic_freq;
+	si->isi_flags = ni->ni_chan->ic_flags;
+	si->isi_state = ni->ni_flags;
+	si->isi_authmode = ni->ni_authmode;
+	si->isi_rssi = ic->ic_node_getrssi(ni);
+	si->isi_capinfo = ni->ni_capinfo;
+	si->isi_erp = ni->ni_erp;
+	IEEE80211_ADDR_COPY(si->isi_macaddr, ni->ni_macaddr);
+	si->isi_nrates = ni->ni_rates.rs_nrates;
+	if (si->isi_nrates > 15)
+		si->isi_nrates = 15;
+	memcpy(si->isi_rates, ni->ni_rates.rs_rates, si->isi_nrates);
+	si->isi_txrate = ni->ni_txrate;
+	si->isi_associd = ni->ni_associd;
+	si->isi_txpower = ni->ni_txpower;
+	si->isi_vlan = ni->ni_vlan;
+	if (ni->ni_flags & IEEE80211_NODE_QOS) {
+		memcpy(si->isi_txseqs, ni->ni_txseqs, sizeof(ni->ni_txseqs));
+		memcpy(si->isi_rxseqs, ni->ni_rxseqs, sizeof(ni->ni_rxseqs));
+	} else {
+		si->isi_txseqs[0] = ni->ni_txseqs[0];
+		si->isi_rxseqs[0] = ni->ni_rxseqs[0];
+	}
+	/* NB: leave all cases in case we relax ni_associd == 0 check */
+	if (ieee80211_node_is_authorized(ni))
+		si->isi_inact = ic->ic_inact_run;
+	else if (ni->ni_associd != 0)
+		si->isi_inact = ic->ic_inact_auth;
+	else
+		si->isi_inact = ic->ic_inact_init;
+	si->isi_inact = (si->isi_inact - ni->ni_inact) * IEEE80211_INACT_WAIT;
+
+	cp = (u_int8_t *)(si+1);
+	if (ni->ni_wpa_ie != NULL) {
+		memcpy(cp, ni->ni_wpa_ie, 2+ni->ni_wpa_ie[1]);
+		cp += 2+ni->ni_wpa_ie[1];
+	}
+	if (ni->ni_wme_ie != NULL) {
+		memcpy(cp, ni->ni_wme_ie, 2+ni->ni_wme_ie[1]);
+		cp += 2+ni->ni_wme_ie[1];
+	}
+
+	req->si = (struct ieee80211req_sta_info *)(((u_int8_t *)si) + len);
+	req->space -= len;
+}
+
+static int
+ieee80211_ioctl_getstainfo(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct stainforeq req;
+	int error;
+
+	if (ireq->i_len < sizeof(struct stainforeq))
+		return EFAULT;
+
+	error = 0;
+	req.space = 0;
+	ieee80211_iterate_nodes(&ic->ic_sta, get_sta_space, &req);
+	if (req.space > ireq->i_len)
+		req.space = ireq->i_len;
+	if (req.space > 0) {
+		size_t space;
+		void *p;
+
+		space = req.space;
+		/* XXX M_WAITOK after driver lock released */
+		MALLOC(p, void *, space, M_TEMP, M_NOWAIT);
+		if (p == NULL)
+			return ENOMEM;
+		req.si = p;
+		ieee80211_iterate_nodes(&ic->ic_sta, get_sta_info, &req);
+		ireq->i_len = space - req.space;
+		error = copyout(p, ireq->i_data, ireq->i_len);
+		FREE(p, M_TEMP);
+	} else
+		ireq->i_len = 0;
+
+	return error;
+}
+
+static int
+ieee80211_ioctl_getstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_sta_txpow txpow;
+	int error;
+
+	if (ireq->i_len != sizeof(txpow))
+		return EINVAL;
+	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	txpow.it_txpow = ni->ni_txpower;
+	error = copyout(&txpow, ireq->i_data, sizeof(txpow));
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_getwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	struct wmeParams *wmep;
+	int ac;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return EINVAL;
+
+	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+	if (ac >= WME_NUM_AC)
+		ac = WME_AC_BE;
+	if (ireq->i_len & IEEE80211_WMEPARAM_BSS)
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+	else
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+		ireq->i_val = wmep->wmep_logcwmin;
+		break;
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+		ireq->i_val = wmep->wmep_logcwmax;
+		break;
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+		ireq->i_val = wmep->wmep_aifsn;
+		break;
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+		ireq->i_val = wmep->wmep_txopLimit;
+		break;
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+		ireq->i_val = wmep->wmep_acm;
+		break;
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+		ireq->i_val = !wmep->wmep_noackPolicy;
+		break;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_getmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+
+	return (acl == NULL ? EINVAL : acl->iac_getioctl(ic, ireq));
+}
+
+/*
+ * When building the kernel with -O2 on the i386 architecture, gcc
+ * seems to want to inline this function into ieee80211_ioctl()
+ * (which is the only routine that calls it). When this happens,
+ * ieee80211_ioctl() ends up consuming an additional 2K of stack
+ * space. (Exactly why it needs so much is unclear.) The problem
+ * is that it's possible for ieee80211_ioctl() to invoke other
+ * routines (including driver init functions) which could then find
+ * themselves perilously close to exhausting the stack.
+ *
+ * To avoid this, we deliberately prevent gcc from inlining this
+ * routine. Another way to avoid this is to use less agressive
+ * optimization when compiling this file (i.e. -O instead of -O2)
+ * but special-casing the compilation of this one module in the
+ * build system would be awkward.
+ */
+#ifdef __GNUC__
+__attribute__ ((noinline))
+#endif
+static int
+ieee80211_ioctl_get80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq, struct ucred *cr)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
 	int error = 0;
-	u_int kid, len;
-	struct ieee80211req *ireq;
-	struct ifreq *ifr;
-	uint8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+	u_int kid, len, m;
+	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
 	char tmpssid[IEEE80211_NWID_LEN];
-	struct ieee80211_channel *chan;
-	struct ifaddr *ifa;			/* XXX */
 
-	switch (cmd) {
-	case SIOCSIFMEDIA:
-	case SIOCGIFMEDIA:
-		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
-				&ic->ic_media, cmd);
-		break;
-	case SIOCG80211:
-		ireq = (struct ieee80211req *) data;
-		switch (ireq->i_type) {
-		case IEEE80211_IOC_SSID:
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
-				ireq->i_len = ic->ic_des_esslen;
-				memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
-				break;
-			default:
-				ireq->i_len = ic->ic_bss->ni_esslen;
-				memcpy(tmpssid, ic->ic_bss->ni_essid,
-					ireq->i_len);
-				break;
-			}
-			error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_SSID:
+		switch (ic->ic_state) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
+			ireq->i_len = ic->ic_des_esslen;
+			memcpy(tmpssid, ic->ic_des_essid, ireq->i_len);
 			break;
-		case IEEE80211_IOC_NUMSSIDS:
+		default:
+			ireq->i_len = ic->ic_bss->ni_esslen;
+			memcpy(tmpssid, ic->ic_bss->ni_essid,
+				ireq->i_len);
+			break;
+		}
+		error = copyout(tmpssid, ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_NUMSSIDS:
+		ireq->i_val = 1;
+		break;
+	case IEEE80211_IOC_WEP:
+		if ((ic->ic_flags & IEEE80211_F_PRIVACY) == 0)
+			ireq->i_val = IEEE80211_WEP_OFF;
+		else if (ic->ic_flags & IEEE80211_F_DROPUNENC)
+			ireq->i_val = IEEE80211_WEP_ON;
+		else
+			ireq->i_val = IEEE80211_WEP_MIXED;
+		break;
+	case IEEE80211_IOC_WEPKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		len = (u_int) ic->ic_nw_keys[kid].wk_keylen;
+		/* NB: only root can read WEP keys */
+		if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
+			bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
+		} else {
+			bzero(tmpkey, len);
+		}
+		ireq->i_len = len;
+		error = copyout(tmpkey, ireq->i_data, len);
+		break;
+	case IEEE80211_IOC_NUMWEPKEYS:
+		ireq->i_val = IEEE80211_WEP_NKID;
+		break;
+	case IEEE80211_IOC_WEPTXKEY:
+		ireq->i_val = ic->ic_def_txkey;
+		break;
+	case IEEE80211_IOC_AUTHMODE:
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			ireq->i_val = IEEE80211_AUTH_WPA;
+		else
+			ireq->i_val = ic->ic_bss->ni_authmode;
+		break;
+	case IEEE80211_IOC_CHANNEL:
+		ireq->i_val = ieee80211_chan2ieee(ic, ic->ic_curchan);
+		break;
+	case IEEE80211_IOC_POWERSAVE:
+		if (ic->ic_flags & IEEE80211_F_PMGTON)
+			ireq->i_val = IEEE80211_POWERSAVE_ON;
+		else
+			ireq->i_val = IEEE80211_POWERSAVE_OFF;
+		break;
+	case IEEE80211_IOC_POWERSAVESLEEP:
+		ireq->i_val = ic->ic_lintval;
+		break;
+	case IEEE80211_IOC_RTSTHRESHOLD:
+		ireq->i_val = ic->ic_rtsthreshold;
+		break;
+	case IEEE80211_IOC_PROTMODE:
+		ireq->i_val = ic->ic_protmode;
+		break;
+	case IEEE80211_IOC_TXPOWER:
+		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+			return EINVAL;
+		ireq->i_val = ic->ic_txpowlimit;
+		break;
+	case IEEE80211_IOC_MCASTCIPHER:
+		ireq->i_val = rsn->rsn_mcastcipher;
+		break;
+	case IEEE80211_IOC_MCASTKEYLEN:
+		ireq->i_val = rsn->rsn_mcastkeylen;
+		break;
+	case IEEE80211_IOC_UCASTCIPHERS:
+		ireq->i_val = 0;
+		for (m = 0x1; m != 0; m <<= 1)
+			if (rsn->rsn_ucastcipherset & m)
+				ireq->i_val |= 1<<cap2cipher(m);
+		break;
+	case IEEE80211_IOC_UCASTCIPHER:
+		ireq->i_val = rsn->rsn_ucastcipher;
+		break;
+	case IEEE80211_IOC_UCASTKEYLEN:
+		ireq->i_val = rsn->rsn_ucastkeylen;
+		break;
+	case IEEE80211_IOC_KEYMGTALGS:
+		ireq->i_val = rsn->rsn_keymgmtset;
+		break;
+	case IEEE80211_IOC_RSNCAPS:
+		ireq->i_val = rsn->rsn_caps;
+		break;
+	case IEEE80211_IOC_WPA:
+		switch (ic->ic_flags & IEEE80211_F_WPA) {
 			ireq->i_val = 1;
 			break;
-		case IEEE80211_IOC_WEP:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				ireq->i_val = IEEE80211_WEP_NOSUP; 
-			} else {
-				if (ic->ic_flags & IEEE80211_F_WEPON) {
-					ireq->i_val =
-					    IEEE80211_WEP_MIXED;
-				} else {
-					ireq->i_val =
-					    IEEE80211_WEP_OFF;
-				}
-			}
+		case IEEE80211_F_WPA2:
+			ireq->i_val = 2;
 			break;
-		case IEEE80211_IOC_WEPKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				error = EINVAL;
-				break;
-			}
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
-			}
-			len = (u_int) ic->ic_nw_keys[kid].wk_len;
-			/* NB: only root can read WEP keys */
-			if (suser_cred(cr, NULL_CRED_OKAY) == 0) {
-				bcopy(ic->ic_nw_keys[kid].wk_key, tmpkey, len);
-			} else {
-				bzero(tmpkey, len);
-			}
-			ireq->i_len = len;
-			error = copyout(tmpkey, ireq->i_data, len);
-			break;
-		case IEEE80211_IOC_NUMWEPKEYS:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
-				error = EINVAL;
-			else
-				ireq->i_val = IEEE80211_WEP_NKID;
-			break;
-		case IEEE80211_IOC_WEPTXKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0)
-				error = EINVAL;
-			else
-				ireq->i_val = ic->ic_wep_txkey;
-			break;
-		case IEEE80211_IOC_AUTHMODE:
-			ireq->i_val = IEEE80211_AUTH_OPEN;
-			break;
-		case IEEE80211_IOC_CHANNEL:
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
-				if (ic->ic_opmode == IEEE80211_M_STA)
-					chan = ic->ic_des_chan;
-				else
-					chan = ic->ic_ibss_chan;
-				break;
-			default:
-				chan = ic->ic_bss->ni_chan;
-				break;
-			}
-			ireq->i_val = ieee80211_chan2ieee(ic, chan);
-			break;
-		case IEEE80211_IOC_POWERSAVE:
-			if (ic->ic_flags & IEEE80211_F_PMGTON)
-				ireq->i_val = IEEE80211_POWERSAVE_ON;
-			else
-				ireq->i_val = IEEE80211_POWERSAVE_OFF;
-			break;
-		case IEEE80211_IOC_POWERSAVESLEEP:
-			ireq->i_val = ic->ic_lintval;
-			break;
-		case IEEE80211_IOC_RTSTHRESHOLD:
-			ireq->i_val = ic->ic_rtsthreshold;
-			break;
-		case IEEE80211_IOC_PROTMODE:
-			ireq->i_val = ic->ic_protmode;
-			break;
-		case IEEE80211_IOC_TXPOWER:
-			if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
-				error = EINVAL;
-			else
-				ireq->i_val = ic->ic_txpower;
+		case IEEE80211_F_WPA1 | IEEE80211_F_WPA2:
+			ireq->i_val = 3;
 			break;
 		default:
-			error = EINVAL;
+			ireq->i_val = 0;
 			break;
 		}
 		break;
-	case SIOCS80211:
-		error = suser_cred(cr, NULL_CRED_OKAY);
-		if (error)
-			break;
-		ireq = (struct ieee80211req *) data;
-		switch (ireq->i_type) {
-		case IEEE80211_IOC_SSID:
-			if (ireq->i_val != 0 ||
-			    ireq->i_len > IEEE80211_NWID_LEN) {
-				error = EINVAL;
-				break;
+	case IEEE80211_IOC_CHANLIST:
+		error = ieee80211_ioctl_getchanlist(ic, ireq);
+		break;
+	case IEEE80211_IOC_ROAMING:
+		ireq->i_val = ic->ic_roaming;
+		break;
+	case IEEE80211_IOC_PRIVACY:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_PRIVACY) != 0;
+		break;
+	case IEEE80211_IOC_DROPUNENCRYPTED:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_DROPUNENC) != 0;
+		break;
+	case IEEE80211_IOC_COUNTERMEASURES:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_COUNTERM) != 0;
+		break;
+	case IEEE80211_IOC_DRIVER_CAPS:
+		ireq->i_val = ic->ic_caps>>16;
+		ireq->i_len = ic->ic_caps&0xffff;
+		break;
+	case IEEE80211_IOC_WME:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_WME) != 0;
+		break;
+	case IEEE80211_IOC_HIDESSID:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_HIDESSID) != 0;
+		break;
+	case IEEE80211_IOC_APBRIDGE:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0;
+		break;
+	case IEEE80211_IOC_OPTIE:
+		if (ic->ic_opt_ie == NULL)
+			return EINVAL;
+		/* NB: truncate, caller can check length */
+		if (ireq->i_len > ic->ic_opt_ie_len)
+			ireq->i_len = ic->ic_opt_ie_len;
+		error = copyout(ic->ic_opt_ie, ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_WPAKEY:
+		error = ieee80211_ioctl_getkey(ic, ireq, cr);
+		break;
+	case IEEE80211_IOC_CHANINFO:
+		error = ieee80211_ioctl_getchaninfo(ic, ireq);
+		break;
+	case IEEE80211_IOC_BSSID:
+		if (ireq->i_len != IEEE80211_ADDR_LEN)
+			return EINVAL;
+		error = copyout(ic->ic_state == IEEE80211_S_RUN ?
+					ic->ic_bss->ni_bssid :
+					ic->ic_des_bssid,
+				ireq->i_data, ireq->i_len);
+		break;
+	case IEEE80211_IOC_WPAIE:
+		error = ieee80211_ioctl_getwpaie(ic, ireq);
+		break;
+	case IEEE80211_IOC_SCAN_RESULTS:
+		error = ieee80211_ioctl_getscanresults(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_STATS:
+		error = ieee80211_ioctl_getstastats(ic, ireq);
+		break;
+	case IEEE80211_IOC_TXPOWMAX:
+		ireq->i_val = ic->ic_bss->ni_txpower;
+		break;
+	case IEEE80211_IOC_STA_TXPOW:
+		error = ieee80211_ioctl_getstatxpow(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_INFO:
+		error = ieee80211_ioctl_getstainfo(ic, ireq);
+		break;
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
+		error = ieee80211_ioctl_getwmeparam(ic, ireq);
+		break;
+	case IEEE80211_IOC_DTIM_PERIOD:
+		ireq->i_val = ic->ic_dtim_period;
+		break;
+	case IEEE80211_IOC_BEACON_INTERVAL:
+		/* NB: get from ic_bss for station mode */
+		ireq->i_val = ic->ic_bss->ni_intval;
+		break;
+	case IEEE80211_IOC_PUREG:
+		ireq->i_val = (ic->ic_flags & IEEE80211_F_PUREG) != 0;
+		break;
+	case IEEE80211_IOC_FRAGTHRESHOLD:
+		ireq->i_val = ic->ic_fragthreshold;
+		break;
+	case IEEE80211_IOC_MACCMD:
+		error = ieee80211_ioctl_getmaccmd(ic, ireq);
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	return error;
+}
+
+static int
+ieee80211_ioctl_setoptie(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	int error;
+	void *ie;
+
+	/*
+	 * NB: Doing this for ap operation could be useful (e.g. for
+	 *     WPA and/or WME) except that it typically is worthless
+	 *     without being able to intervene when processing
+	 *     association response frames--so disallow it for now.
+	 */
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		return EINVAL;
+	if (ireq->i_len > IEEE80211_MAX_OPT_IE)
+		return EINVAL;
+	/* NB: data.length is validated by the wireless extensions code */
+	MALLOC(ie, void *, ireq->i_len, M_DEVBUF, M_WAITOK);
+	if (ie == NULL)
+		return ENOMEM;
+	error = copyin(ireq->i_data, ie, ireq->i_len);
+	/* XXX sanity check data? */
+	if (ic->ic_opt_ie != NULL)
+		FREE(ic->ic_opt_ie, M_DEVBUF);
+	ic->ic_opt_ie = ie;
+	ic->ic_opt_ie_len = ireq->i_len;
+	return 0;
+}
+
+static int
+ieee80211_ioctl_setkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_key ik;
+	struct ieee80211_node *ni;
+	struct ieee80211_key *wk;
+	u_int16_t kid;
+	int error;
+
+	if (ireq->i_len != sizeof(ik))
+		return EINVAL;
+	error = copyin(ireq->i_data, &ik, sizeof(ik));
+	if (error)
+		return error;
+	/* NB: cipher support is verified by ieee80211_crypt_newkey */
+	/* NB: this also checks ik->ik_keylen > sizeof(wk->wk_key) */
+	if (ik.ik_keylen > sizeof(ik.ik_keydata))
+		return E2BIG;
+	kid = ik.ik_keyix;
+	if (kid == IEEE80211_KEYIX_NONE) {
+		/* XXX unicast keys currently must be tx/rx */
+		if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV))
+			return EINVAL;
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			ni = ieee80211_ref_node(ic->ic_bss);
+			if (!IEEE80211_ADDR_EQ(ik.ik_macaddr, ni->ni_bssid)) {
+				ieee80211_free_node(ni);
+				return EADDRNOTAVAIL;
 			}
-			error = copyin(ireq->i_data, tmpssid, ireq->i_len);
-			if (error)
-				break;
-			memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
-			ic->ic_des_esslen = ireq->i_len;
-			memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
-			error = ENETRESET;
-			break;
-		case IEEE80211_IOC_WEP:
+		} else {
+			ni = ieee80211_find_node(&ic->ic_sta, ik.ik_macaddr);
+			if (ni == NULL)
+				return ENOENT;
+		}
+		wk = &ni->ni_ucastkey;
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		wk = &ic->ic_nw_keys[kid];
+		ni = NULL;
+	}
+	error = 0;
+	ieee80211_key_update_begin(ic);
+	if (ieee80211_crypto_newkey(ic, ik.ik_type, ik.ik_flags, wk)) {
+		wk->wk_keylen = ik.ik_keylen;
+		/* NB: MIC presence is implied by cipher type */
+		if (wk->wk_keylen > IEEE80211_KEYBUF_SIZE)
+			wk->wk_keylen = IEEE80211_KEYBUF_SIZE;
+		wk->wk_keyrsc = ik.ik_keyrsc;
+		wk->wk_keytsc = 0;			/* new key, reset */
+		memset(wk->wk_key, 0, sizeof(wk->wk_key));
+		memcpy(wk->wk_key, ik.ik_keydata, ik.ik_keylen);
+		if (!ieee80211_crypto_setkey(ic, wk,
+		    ni != NULL ? ni->ni_macaddr : ik.ik_macaddr))
+			error = EIO;
+		else if ((ik.ik_flags & IEEE80211_KEY_DEFAULT))
+			ic->ic_def_txkey = kid;
+	} else
+		error = ENXIO;
+	ieee80211_key_update_end(ic);
+	if (ni != NULL)
+		ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_delkey(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_del_key dk;
+	int kid, error;
+
+	if (ireq->i_len != sizeof(dk))
+		return EINVAL;
+	error = copyin(ireq->i_data, &dk, sizeof(dk));
+	if (error)
+		return error;
+	kid = dk.idk_keyix;
+	/* XXX u_int8_t -> u_int16_t */
+	if (dk.idk_keyix == (u_int8_t) IEEE80211_KEYIX_NONE) {
+		struct ieee80211_node *ni;
+
+		if (ic->ic_opmode == IEEE80211_M_STA) {
+			ni = ieee80211_ref_node(ic->ic_bss);
+			if (!IEEE80211_ADDR_EQ(dk.idk_macaddr, ni->ni_bssid)) {
+				ieee80211_free_node(ni);
+				return EADDRNOTAVAIL;
+			}
+		} else {
+			ni = ieee80211_find_node(&ic->ic_sta, dk.idk_macaddr);
+			if (ni == NULL)
+				return ENOENT;
+		}
+		/* XXX error return */
+		ieee80211_node_delucastkey(ni);
+		ieee80211_free_node(ni);
+	} else {
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		/* XXX error return */
+		ieee80211_crypto_delkey(ic, &ic->ic_nw_keys[kid]);
+	}
+	return 0;
+}
+
+static void
+domlme(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211req_mlme *mlme = arg;
+
+	if (ni->ni_associd != 0) {
+		IEEE80211_SEND_MGMT(ic, ni,
+			mlme->im_op == IEEE80211_MLME_DEAUTH ?
+				IEEE80211_FC0_SUBTYPE_DEAUTH :
+				IEEE80211_FC0_SUBTYPE_DISASSOC,
+			mlme->im_reason);
+	}
+	ieee80211_node_leave(ic, ni);
+}
+
+static int
+ieee80211_ioctl_setmlme(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_mlme mlme;
+	struct ieee80211_node *ni;
+	int error;
+
+	if (ireq->i_len != sizeof(mlme))
+		return EINVAL;
+	error = copyin(ireq->i_data, &mlme, sizeof(mlme));
+	if (error)
+		return error;
+	switch (mlme.im_op) {
+	case IEEE80211_MLME_ASSOC:
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return EINVAL;
+		/* XXX must be in S_SCAN state? */
+
+		if (ic->ic_des_esslen != 0) {
 			/*
-			 * These cards only support one mode so
-			 * we just turn wep on if what ever is
-			 * passed in is not OFF.
+			 * Desired ssid specified; must match both bssid and
+			 * ssid to distinguish ap advertising multiple ssid's.
 			 */
-			if (ireq->i_val == IEEE80211_WEP_OFF) {
-				ic->ic_flags &= ~IEEE80211_F_WEPON;
-			} else {
-				ic->ic_flags |= IEEE80211_F_WEPON;
-			}
-			error = ENETRESET;
+			ni = ieee80211_find_node_with_ssid(&ic->ic_scan,
+				mlme.im_macaddr,
+				ic->ic_des_esslen, ic->ic_des_essid);
+		} else {
+			/*
+			 * Normal case; just match bssid.
+			 */
+			ni = ieee80211_find_node(&ic->ic_scan, mlme.im_macaddr);
+		}
+		if (ni == NULL)
+			return EINVAL;
+		if (!ieee80211_sta_join(ic, ni)) {
+			ieee80211_free_node(ni);
+			return EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_DISASSOC:
+	case IEEE80211_MLME_DEAUTH:
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			/* XXX not quite right */
+			ieee80211_new_state(ic, IEEE80211_S_INIT,
+				mlme.im_reason);
 			break;
-		case IEEE80211_IOC_WEPKEY:
-			if ((ic->ic_caps & IEEE80211_C_WEP) == 0) {
-				error = EINVAL;
-				break;
-			}
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
-			} 
-			if (ireq->i_len > sizeof(tmpkey)) {
-				error = EINVAL;
-				break;
+		case IEEE80211_M_HOSTAP:
+			/* NB: the broadcast address means do 'em all */
+			if (!IEEE80211_ADDR_EQ(mlme.im_macaddr, ic->ic_ifp->if_broadcastaddr)) {
+				if ((ni = ieee80211_find_node(&ic->ic_sta,
+						mlme.im_macaddr)) == NULL)
+					return EINVAL;
+				domlme(&mlme, ni);
+				ieee80211_free_node(ni);
+			} else {
+				ieee80211_iterate_nodes(&ic->ic_sta,
+						domlme, &mlme);
 			}
-			memset(tmpkey, 0, sizeof(tmpkey));
-			error = copyin(ireq->i_data, tmpkey, ireq->i_len);
-			if (error)
-				break;
-			memcpy(ic->ic_nw_keys[kid].wk_key, tmpkey,
-				sizeof(tmpkey));
-			ic->ic_nw_keys[kid].wk_len = ireq->i_len;
-			error = ENETRESET;
 			break;
-		case IEEE80211_IOC_WEPTXKEY:
-			kid = (u_int) ireq->i_val;
-			if (kid >= IEEE80211_WEP_NKID) {
-				error = EINVAL;
-				break;
+		default:
+			return EINVAL;
+		}
+		break;
+	case IEEE80211_MLME_AUTHORIZE:
+	case IEEE80211_MLME_UNAUTHORIZE:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+			return EINVAL;
+		ni = ieee80211_find_node(&ic->ic_sta, mlme.im_macaddr);
+		if (ni == NULL)
+			return EINVAL;
+		if (mlme.im_op == IEEE80211_MLME_AUTHORIZE)
+			ieee80211_node_authorize(ni);
+		else
+			ieee80211_node_unauthorize(ni);
+		ieee80211_free_node(ni);
+		break;
+	default:
+		return EINVAL;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_macmac(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	u_int8_t mac[IEEE80211_ADDR_LEN];
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+	int error;
+
+	if (ireq->i_len != sizeof(mac))
+		return EINVAL;
+	error = copyin(ireq->i_data, mac, ireq->i_len);
+	if (error)
+		return error;
+	if (acl == NULL) {
+		acl = ieee80211_aclator_get("mac");
+		if (acl == NULL || !acl->iac_attach(ic))
+			return EINVAL;
+		ic->ic_acl = acl;
+	}
+	if (ireq->i_type == IEEE80211_IOC_ADDMAC)
+		acl->iac_add(ic, mac);
+	else
+		acl->iac_remove(ic, mac);
+	return 0;
+}
+
+static int
+ieee80211_ioctl_setmaccmd(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	const struct ieee80211_aclator *acl = ic->ic_acl;
+
+	switch (ireq->i_val) {
+	case IEEE80211_MACCMD_POLICY_OPEN:
+	case IEEE80211_MACCMD_POLICY_ALLOW:
+	case IEEE80211_MACCMD_POLICY_DENY:
+		if (acl == NULL) {
+			acl = ieee80211_aclator_get("mac");
+			if (acl == NULL || !acl->iac_attach(ic))
+				return EINVAL;
+			ic->ic_acl = acl;
+		}
+		acl->iac_setpolicy(ic, ireq->i_val);
+		break;
+	case IEEE80211_MACCMD_FLUSH:
+		if (acl != NULL)
+			acl->iac_flush(ic);
+		/* NB: silently ignore when not in use */
+		break;
+	case IEEE80211_MACCMD_DETACH:
+		if (acl != NULL) {
+			ic->ic_acl = NULL;
+			acl->iac_detach(ic);
+		}
+		break;
+	default:
+		if (acl == NULL)
+			return EINVAL;
+		else
+			return acl->iac_setioctl(ic, ireq);
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_setchanlist(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211req_chanlist list;
+	u_char chanlist[IEEE80211_CHAN_BYTES];
+	int i, j, error;
+
+	if (ireq->i_len != sizeof(list))
+		return EINVAL;
+	error = copyin(ireq->i_data, &list, sizeof(list));
+	if (error)
+		return error;
+	memset(chanlist, 0, sizeof(chanlist));
+	/*
+	 * Since channel 0 is not available for DS, channel 1
+	 * is assigned to LSB on WaveLAN.
+	 */
+	if (ic->ic_phytype == IEEE80211_T_DS)
+		i = 1;
+	else
+		i = 0;
+	for (j = 0; i <= IEEE80211_CHAN_MAX; i++, j++) {
+		/*
+		 * NB: silently discard unavailable channels so users
+		 *     can specify 1-255 to get all available channels.
+		 */
+		if (isset(list.ic_channels, j) && isset(ic->ic_chan_avail, i))
+			setbit(chanlist, i);
+	}
+	if (ic->ic_ibss_chan == NULL ||
+	    isclr(chanlist, ieee80211_chan2ieee(ic, ic->ic_ibss_chan))) {
+		for (i = 0; i <= IEEE80211_CHAN_MAX; i++)
+			if (isset(chanlist, i)) {
+				ic->ic_ibss_chan = &ic->ic_channels[i];
+				goto found;
 			}
-			ic->ic_wep_txkey = kid;
-			error = ENETRESET;
+		return EINVAL;			/* no active channels */
+found:
+		;
+	}
+	memcpy(ic->ic_chan_active, chanlist, sizeof(ic->ic_chan_active));
+	return IS_UP_AUTO(ic) ? ENETRESET : 0;
+}
+
+static int
+ieee80211_ioctl_setstatxpow(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_node *ni;
+	struct ieee80211req_sta_txpow txpow;
+	int error;
+
+	if (ireq->i_len != sizeof(txpow))
+		return EINVAL;
+	error = copyin(ireq->i_data, &txpow, sizeof(txpow));
+	if (error != 0)
+		return error;
+	ni = ieee80211_find_node(&ic->ic_sta, txpow.it_macaddr);
+	if (ni == NULL)
+		return EINVAL;		/* XXX */
+	ni->ni_txpower = txpow.it_txpow;
+	ieee80211_free_node(ni);
+	return error;
+}
+
+static int
+ieee80211_ioctl_setwmeparam(struct ieee80211com *ic, struct ieee80211req *ireq)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	struct wmeParams *wmep, *chanp;
+	int isbss, ac;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return EINVAL;
+
+	isbss = (ireq->i_len & IEEE80211_WMEPARAM_BSS);
+	ac = (ireq->i_len & IEEE80211_WMEPARAM_VAL);
+	if (ac >= WME_NUM_AC)
+		ac = WME_AC_BE;
+	if (isbss) {
+		chanp = &wme->wme_bssChanParams.cap_wmeParams[ac];
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[ac];
+	} else {
+		chanp = &wme->wme_chanParams.cap_wmeParams[ac];
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[ac];
+	}
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+		if (isbss) {
+			wmep->wmep_logcwmin = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_logcwmin = ireq->i_val;
+		} else {
+			wmep->wmep_logcwmin = chanp->wmep_logcwmin =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+		if (isbss) {
+			wmep->wmep_logcwmax = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_logcwmax = ireq->i_val;
+		} else {
+			wmep->wmep_logcwmax = chanp->wmep_logcwmax =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+		if (isbss) {
+			wmep->wmep_aifsn = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_aifsn = ireq->i_val;
+		} else {
+			wmep->wmep_aifsn = chanp->wmep_aifsn = ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+		if (isbss) {
+			wmep->wmep_txopLimit = ireq->i_val;
+			if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+				chanp->wmep_txopLimit = ireq->i_val;
+		} else {
+			wmep->wmep_txopLimit = chanp->wmep_txopLimit =
+				ireq->i_val;
+		}
+		break;
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+		wmep->wmep_acm = ireq->i_val;
+		if ((wme->wme_flags & WME_F_AGGRMODE) == 0)
+			chanp->wmep_acm = ireq->i_val;
+		break;
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (!bss only)*/
+		wmep->wmep_noackPolicy = chanp->wmep_noackPolicy =
+			(ireq->i_val) == 0;
+		break;
+	}
+	ieee80211_wme_updateparams(ic);
+	return 0;
+}
+
+static int
+cipher2cap(int cipher)
+{
+	switch (cipher) {
+	case IEEE80211_CIPHER_WEP:	return IEEE80211_C_WEP;
+	case IEEE80211_CIPHER_AES_OCB:	return IEEE80211_C_AES;
+	case IEEE80211_CIPHER_AES_CCM:	return IEEE80211_C_AES_CCM;
+	case IEEE80211_CIPHER_CKIP:	return IEEE80211_C_CKIP;
+	case IEEE80211_CIPHER_TKIP:	return IEEE80211_C_TKIP;
+	}
+	return 0;
+}
+
+static int
+ieee80211_ioctl_set80211(struct ieee80211com *ic, u_long cmd, struct ieee80211req *ireq)
+{
+	static const u_int8_t zerobssid[IEEE80211_ADDR_LEN];
+	struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	int error;
+	const struct ieee80211_authenticator *auth;
+	u_int8_t tmpkey[IEEE80211_KEYBUF_SIZE];
+	char tmpssid[IEEE80211_NWID_LEN];
+	u_int8_t tmpbssid[IEEE80211_ADDR_LEN];
+	struct ieee80211_key *k;
+	int j, caps;
+	u_int kid;
+
+	error = 0;
+	switch (ireq->i_type) {
+	case IEEE80211_IOC_SSID:
+		if (ireq->i_val != 0 ||
+		    ireq->i_len > IEEE80211_NWID_LEN)
+			return EINVAL;
+		error = copyin(ireq->i_data, tmpssid, ireq->i_len);
+		if (error)
 			break;
-#if 0
-		case IEEE80211_IOC_AUTHMODE:
-			sc->wi_authmode = ireq->i_val;
+		memset(ic->ic_des_essid, 0, IEEE80211_NWID_LEN);
+		ic->ic_des_esslen = ireq->i_len;
+		memcpy(ic->ic_des_essid, tmpssid, ireq->i_len);
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_WEP:
+		switch (ireq->i_val) {
+		case IEEE80211_WEP_OFF:
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+			break;
+		case IEEE80211_WEP_ON:
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+			break;
+		case IEEE80211_WEP_MIXED:
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
 			break;
-#endif
-		case IEEE80211_IOC_CHANNEL:
-			/* XXX 0xffff overflows 16-bit signed */
-			if (ireq->i_val == 0 ||
-			    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
-				ic->ic_des_chan = IEEE80211_CHAN_ANYC;
-			else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
-			    isclr(ic->ic_chan_active, ireq->i_val)) {
-				error = EINVAL;
-				break;
-			} else
-				ic->ic_ibss_chan = ic->ic_des_chan =
-					&ic->ic_channels[ireq->i_val];
-			switch (ic->ic_state) {
-			case IEEE80211_S_INIT:
-			case IEEE80211_S_SCAN:
-				error = ENETRESET;
-				break;
-			default:
-				if (ic->ic_opmode == IEEE80211_M_STA) {
-					if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
-					    ic->ic_bss->ni_chan != ic->ic_des_chan)
-						error = ENETRESET;
-				} else {
-					if (ic->ic_bss->ni_chan != ic->ic_ibss_chan)
-						error = ENETRESET;
-				}
-				break;
-			}
+		}
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_WEPKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID)
+			return EINVAL;
+		k = &ic->ic_nw_keys[kid];
+		if (ireq->i_len == 0) {
+			/* zero-len =>'s delete any existing key */
+			(void) ieee80211_crypto_delkey(ic, k);
 			break;
-		case IEEE80211_IOC_POWERSAVE:
-			switch (ireq->i_val) {
-			case IEEE80211_POWERSAVE_OFF:
-				if (ic->ic_flags & IEEE80211_F_PMGTON) {
-					ic->ic_flags &= ~IEEE80211_F_PMGTON;
-					error = ENETRESET;
-				}
-				break;
-			case IEEE80211_POWERSAVE_ON:
-				if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
-					error = EINVAL;
-				else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
-					ic->ic_flags |= IEEE80211_F_PMGTON;
-					error = ENETRESET;
-				}
-				break;
-			default:
-				error = EINVAL;
-				break;
-			}
+		}
+		if (ireq->i_len > sizeof(tmpkey))
+			return EINVAL;
+		memset(tmpkey, 0, sizeof(tmpkey));
+		error = copyin(ireq->i_data, tmpkey, ireq->i_len);
+		if (error)
 			break;
-		case IEEE80211_IOC_POWERSAVESLEEP:
-			if (ireq->i_val < 0) {
+		ieee80211_key_update_begin(ic);
+		k->wk_keyix = kid;	/* NB: force fixed key id */
+		if (ieee80211_crypto_newkey(ic, IEEE80211_CIPHER_WEP,
+		    IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV, k)) {
+			k->wk_keylen = ireq->i_len;
+			memcpy(k->wk_key, tmpkey, sizeof(tmpkey));
+			if  (!ieee80211_crypto_setkey(ic, k, ic->ic_myaddr))
 				error = EINVAL;
-				break;
-			}
-			ic->ic_lintval = ireq->i_val;
+		} else
+			error = EINVAL;
+		ieee80211_key_update_end(ic);
+		if (!error)			/* NB: for compatibility */
 			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_WEPTXKEY:
+		kid = (u_int) ireq->i_val;
+		if (kid >= IEEE80211_WEP_NKID &&
+		    (u_int16_t) kid != IEEE80211_KEYIX_NONE)
+			return EINVAL;
+		ic->ic_def_txkey = kid;
+		error = ENETRESET;	/* push to hardware */
+		break;
+	case IEEE80211_IOC_AUTHMODE:
+		switch (ireq->i_val) {
+		case IEEE80211_AUTH_WPA:
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+		case IEEE80211_AUTH_OPEN:	/* open */
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			auth = ieee80211_authenticator_get(ireq->i_val);
+			if (auth == NULL)
+				return EINVAL;
 			break;
-		case IEEE80211_IOC_RTSTHRESHOLD:
-			if (!(IEEE80211_RTS_MIN < ireq->i_val &&
-			      ireq->i_val < IEEE80211_RTS_MAX)) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_rtsthreshold = ireq->i_val;
+		default:
+			return EINVAL;
+		}
+		switch (ireq->i_val) {
+		case IEEE80211_AUTH_WPA:	/* WPA w/ 802.1x */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			ireq->i_val = IEEE80211_AUTH_8021X;
+			break;
+		case IEEE80211_AUTH_OPEN:	/* open */
+			ic->ic_flags &= ~(IEEE80211_F_WPA|IEEE80211_F_PRIVACY);
+			break;
+		case IEEE80211_AUTH_SHARED:	/* shared-key */
+		case IEEE80211_AUTH_8021X:	/* 802.1x */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* both require a key so mark the PRIVACY capability */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+			break;
+		case IEEE80211_AUTH_AUTO:	/* auto */
+			ic->ic_flags &= ~IEEE80211_F_WPA;
+			/* XXX PRIVACY handling? */
+			/* XXX what's the right way to do this? */
+			break;
+		}
+		/* NB: authenticator attach/detach happens on state change */
+		ic->ic_bss->ni_authmode = ireq->i_val;
+		/* XXX mixed/mode/usage? */
+		ic->ic_auth = auth;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_CHANNEL:
+		/* XXX 0xffff overflows 16-bit signed */
+		if (ireq->i_val == 0 ||
+		    ireq->i_val == (int16_t) IEEE80211_CHAN_ANY)
+			ic->ic_des_chan = IEEE80211_CHAN_ANYC;
+		else if ((u_int) ireq->i_val > IEEE80211_CHAN_MAX ||
+		    isclr(ic->ic_chan_active, ireq->i_val)) {
+			return EINVAL;
+		} else
+			ic->ic_ibss_chan = ic->ic_des_chan =
+				&ic->ic_channels[ireq->i_val];
+		switch (ic->ic_state) {
+		case IEEE80211_S_INIT:
+		case IEEE80211_S_SCAN:
 			error = ENETRESET;
 			break;
-		case IEEE80211_IOC_PROTMODE:
-			if (ireq->i_val > IEEE80211_PROT_RTSCTS) {
-				error = EINVAL;
-				break;
-			}
-			ic->ic_protmode = ireq->i_val;
-			/* NB: if not operating in 11g this can wait */
-			if (ic->ic_curmode == IEEE80211_MODE_11G)
+		default:
+			/*
+			 * If the desired channel has changed (to something
+			 * other than any) and we're not already scanning,
+			 * then kick the state machine.
+			 */
+			if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
+			    ic->ic_bss->ni_chan != ic->ic_des_chan &&
+			    (ic->ic_flags & IEEE80211_F_SCAN) == 0)
 				error = ENETRESET;
 			break;
-		case IEEE80211_IOC_TXPOWER:
-			if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0) {
-				error = EINVAL;
-				break;
+		}
+		if (error == ENETRESET &&
+			ic->ic_opmode == IEEE80211_M_MONITOR) {
+			if (IS_UP(ic)) {
+				/*
+				 * Monitor mode can switch directly.
+				 */
+				if (ic->ic_des_chan != IEEE80211_CHAN_ANYC)
+					ic->ic_curchan = ic->ic_des_chan;
+				error = ic->ic_reset(ic->ic_ifp);
+			} else
+				error = 0;
+		}
+		break;
+	case IEEE80211_IOC_POWERSAVE:
+		switch (ireq->i_val) {
+		case IEEE80211_POWERSAVE_OFF:
+			if (ic->ic_flags & IEEE80211_F_PMGTON) {
+				ic->ic_flags &= ~IEEE80211_F_PMGTON;
+				error = ENETRESET;
 			}
-			if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
-			      ireq->i_val < IEEE80211_TXPOWER_MAX)) {
+			break;
+		case IEEE80211_POWERSAVE_ON:
+			if ((ic->ic_caps & IEEE80211_C_PMGT) == 0)
 				error = EINVAL;
-				break;
+			else if ((ic->ic_flags & IEEE80211_F_PMGTON) == 0) {
+				ic->ic_flags |= IEEE80211_F_PMGTON;
+				error = ENETRESET;
 			}
-			ic->ic_txpower = ireq->i_val;
-			error = ENETRESET;
 			break;
 		default:
 			error = EINVAL;
 			break;
 		}
 		break;
+	case IEEE80211_IOC_POWERSAVESLEEP:
+		if (ireq->i_val < 0)
+			return EINVAL;
+		ic->ic_lintval = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_RTSTHRESHOLD:
+		if (!(IEEE80211_RTS_MIN <= ireq->i_val &&
+		      ireq->i_val <= IEEE80211_RTS_MAX))
+			return EINVAL;
+		ic->ic_rtsthreshold = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_PROTMODE:
+		if (ireq->i_val > IEEE80211_PROT_RTSCTS)
+			return EINVAL;
+		ic->ic_protmode = ireq->i_val;
+		/* NB: if not operating in 11g this can wait */
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_TXPOWER:
+		if ((ic->ic_caps & IEEE80211_C_TXPMGT) == 0)
+			return EINVAL;
+		if (!(IEEE80211_TXPOWER_MIN < ireq->i_val &&
+		      ireq->i_val < IEEE80211_TXPOWER_MAX))
+			return EINVAL;
+		ic->ic_txpowlimit = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	case IEEE80211_IOC_ROAMING:
+		if (!(IEEE80211_ROAMING_DEVICE <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_ROAMING_MANUAL))
+			return EINVAL;
+		ic->ic_roaming = ireq->i_val;
+		/* XXXX reset? */
+		break;
+	case IEEE80211_IOC_PRIVACY:
+		if (ireq->i_val) {
+			/* XXX check for key state? */
+			ic->ic_flags |= IEEE80211_F_PRIVACY;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_PRIVACY;
+		break;
+	case IEEE80211_IOC_DROPUNENCRYPTED:
+		if (ireq->i_val)
+			ic->ic_flags |= IEEE80211_F_DROPUNENC;
+		else
+			ic->ic_flags &= ~IEEE80211_F_DROPUNENC;
+		break;
+	case IEEE80211_IOC_WPAKEY:
+		error = ieee80211_ioctl_setkey(ic, ireq);
+		break;
+	case IEEE80211_IOC_DELKEY:
+		error = ieee80211_ioctl_delkey(ic, ireq);
+		break;
+	case IEEE80211_IOC_MLME:
+		error = ieee80211_ioctl_setmlme(ic, ireq);
+		break;
+	case IEEE80211_IOC_OPTIE:
+		error = ieee80211_ioctl_setoptie(ic, ireq);
+		break;
+	case IEEE80211_IOC_COUNTERMEASURES:
+		if (ireq->i_val) {
+			if ((ic->ic_flags & IEEE80211_F_WPA) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_COUNTERM;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_COUNTERM;
+		break;
+	case IEEE80211_IOC_WPA:
+		if (ireq->i_val > 3)
+			return EINVAL;
+		/* XXX verify ciphers available */
+		ic->ic_flags &= ~IEEE80211_F_WPA;
+		switch (ireq->i_val) {
+		case 1:
+			ic->ic_flags |= IEEE80211_F_WPA1;
+			break;
+		case 2:
+			ic->ic_flags |= IEEE80211_F_WPA2;
+			break;
+		case 3:
+			ic->ic_flags |= IEEE80211_F_WPA1 | IEEE80211_F_WPA2;
+			break;
+		}
+		error = ENETRESET;		/* XXX? */
+		break;
+	case IEEE80211_IOC_WME:
+		if (ireq->i_val) {
+			if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+				return EINVAL;
+			ic->ic_flags |= IEEE80211_F_WME;
+		} else
+			ic->ic_flags &= ~IEEE80211_F_WME;
+		error = ENETRESET;		/* XXX maybe not for station? */
+		break;
+	case IEEE80211_IOC_HIDESSID:
+		if (ireq->i_val)
+			ic->ic_flags |= IEEE80211_F_HIDESSID;
+		else
+			ic->ic_flags &= ~IEEE80211_F_HIDESSID;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_APBRIDGE:
+		if (ireq->i_val == 0)
+			ic->ic_flags |= IEEE80211_F_NOBRIDGE;
+		else
+			ic->ic_flags &= ~IEEE80211_F_NOBRIDGE;
+		break;
+	case IEEE80211_IOC_MCASTCIPHER:
+		if ((ic->ic_caps & cipher2cap(ireq->i_val)) == 0 &&
+		    !ieee80211_crypto_available(ireq->i_val))
+			return EINVAL;
+		rsn->rsn_mcastcipher = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_MCASTKEYLEN:
+		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+			return EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_mcastkeylen = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_UCASTCIPHERS:
+		/*
+		 * Convert user-specified cipher set to the set
+		 * we can support (via hardware or software).
+		 * NB: this logic intentionally ignores unknown and
+		 * unsupported ciphers so folks can specify 0xff or
+		 * similar and get all available ciphers.
+		 */
+		caps = 0;
+		for (j = 1; j < 32; j++)	/* NB: skip WEP */
+			if ((ireq->i_val & (1<<j)) &&
+			    ((ic->ic_caps & cipher2cap(j)) ||
+			     ieee80211_crypto_available(j)))
+				caps |= 1<<j;
+		if (caps == 0)			/* nothing available */
+			return EINVAL;
+		/* XXX verify ciphers ok for unicast use? */
+		/* XXX disallow if running as it'll have no effect */
+		rsn->rsn_ucastcipherset = caps;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_UCASTCIPHER:
+		if ((rsn->rsn_ucastcipherset & cipher2cap(ireq->i_val)) == 0)
+			return EINVAL;
+		rsn->rsn_ucastcipher = ireq->i_val;
+		break;
+	case IEEE80211_IOC_UCASTKEYLEN:
+		if (!(0 < ireq->i_val && ireq->i_val < IEEE80211_KEYBUF_SIZE))
+			return EINVAL;
+		/* XXX no way to verify driver capability */
+		rsn->rsn_ucastkeylen = ireq->i_val;
+		break;
+	case IEEE80211_IOC_DRIVER_CAPS:
+		/* NB: for testing */
+		ic->ic_caps = (((u_int16_t) ireq->i_val) << 16) |
+			       ((u_int16_t) ireq->i_len);
+		break;
+	case IEEE80211_IOC_KEYMGTALGS:
+		/* XXX check */
+		rsn->rsn_keymgmtset = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_RSNCAPS:
+		/* XXX check */
+		rsn->rsn_caps = ireq->i_val;
+		error = (ic->ic_flags & IEEE80211_F_WPA) ? ENETRESET : 0;
+		break;
+	case IEEE80211_IOC_BSSID:
+		/* NB: should only be set when in STA mode */
+		if (ic->ic_opmode != IEEE80211_M_STA)
+			return EINVAL;
+		if (ireq->i_len != sizeof(tmpbssid))
+			return EINVAL;
+		error = copyin(ireq->i_data, tmpbssid, ireq->i_len);
+		if (error)
+			break;
+		IEEE80211_ADDR_COPY(ic->ic_des_bssid, tmpbssid);
+		if (IEEE80211_ADDR_EQ(ic->ic_des_bssid, zerobssid))
+			ic->ic_flags &= ~IEEE80211_F_DESBSSID;
+		else
+			ic->ic_flags |= IEEE80211_F_DESBSSID;
+		error = ENETRESET;
+		break;
+	case IEEE80211_IOC_CHANLIST:
+		error = ieee80211_ioctl_setchanlist(ic, ireq);
+		break;
+	case IEEE80211_IOC_SCAN_REQ:
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP)	/* XXX ignore */
+			break;
+		error = ieee80211_setupscan(ic, ic->ic_chan_avail);
+		if (error == 0)		/* XXX background scan */
+			error = ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+		break;
+	case IEEE80211_IOC_ADDMAC:
+	case IEEE80211_IOC_DELMAC:
+		error = ieee80211_ioctl_macmac(ic, ireq);
+		break;
+	case IEEE80211_IOC_MACCMD:
+		error = ieee80211_ioctl_setmaccmd(ic, ireq);
+		break;
+	case IEEE80211_IOC_STA_TXPOW:
+		error = ieee80211_ioctl_setstatxpow(ic, ireq);
+		break;
+	case IEEE80211_IOC_WME_CWMIN:		/* WME: CWmin */
+	case IEEE80211_IOC_WME_CWMAX:		/* WME: CWmax */
+	case IEEE80211_IOC_WME_AIFS:		/* WME: AIFS */
+	case IEEE80211_IOC_WME_TXOPLIMIT:	/* WME: txops limit */
+	case IEEE80211_IOC_WME_ACM:		/* WME: ACM (bss only) */
+	case IEEE80211_IOC_WME_ACKPOLICY:	/* WME: ACK policy (bss only) */
+		error = ieee80211_ioctl_setwmeparam(ic, ireq);
+		break;
+	case IEEE80211_IOC_DTIM_PERIOD:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+		    ic->ic_opmode != IEEE80211_M_IBSS)
+			return EINVAL;
+		if (IEEE80211_DTIM_MIN <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_DTIM_MAX) {
+			ic->ic_dtim_period = ireq->i_val;
+			error = ENETRESET;		/* requires restart */
+		} else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_BEACON_INTERVAL:
+		if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+		    ic->ic_opmode != IEEE80211_M_IBSS)
+			return EINVAL;
+		if (IEEE80211_BINTVAL_MIN <= ireq->i_val &&
+		    ireq->i_val <= IEEE80211_BINTVAL_MAX) {
+			ic->ic_bintval = ireq->i_val;
+			error = ENETRESET;		/* requires restart */
+		} else
+			error = EINVAL;
+		break;
+	case IEEE80211_IOC_PUREG:
+		if (ireq->i_val)
+			ic->ic_flags |= IEEE80211_F_PUREG;
+		else
+			ic->ic_flags &= ~IEEE80211_F_PUREG;
+		/* NB: reset only if we're operating on an 11g channel */
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			error = ENETRESET;
+		break;
+	case IEEE80211_IOC_FRAGTHRESHOLD:
+		if ((ic->ic_caps & IEEE80211_C_TXFRAG) == 0 &&
+		    ireq->i_val != IEEE80211_FRAG_MAX)
+			return EINVAL;
+		if (!(IEEE80211_FRAG_MIN <= ireq->i_val &&
+		      ireq->i_val <= IEEE80211_FRAG_MAX))
+			return EINVAL;
+		ic->ic_fragthreshold = ireq->i_val;
+		error = IS_UP(ic) ? ic->ic_reset(ic->ic_ifp) : 0;
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+	if (error == ENETRESET && !IS_UP_AUTO(ic))
+		error = 0;
+	return error;
+}
+
+int
+ieee80211_ioctl(struct ieee80211com *ic, u_long cmd, caddr_t data,  struct ucred *cr)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	int error = 0;
+	struct ifreq *ifr;
+	struct ifaddr *ifa;			/* XXX */
+
+	switch (cmd) {
+	case SIOCSIFMEDIA:
+	case SIOCGIFMEDIA:
+		error = ifmedia_ioctl(ifp, (struct ifreq *) data,
+				&ic->ic_media, cmd);
+		break;
+	case SIOCG80211:
+		error = ieee80211_ioctl_get80211(ic, cmd,
+				(struct ieee80211req *) data, cr);
+		break;
+	case SIOCS80211:
+		error = suser_cred(cr, NULL_CRED_OKAY);
+		if (error == 0)
+			error = ieee80211_ioctl_set80211(ic, cmd,
+					(struct ieee80211req *) data);
+		break;
 	case SIOCGIFGENERIC:
-		error = ieee80211_cfgget(ifp, cmd, data, cr);
+		error = ieee80211_cfgget(ic, cmd, data, cr);
 		break;
 	case SIOCSIFGENERIC:
 		error = suser_cred(cr, NULL_CRED_OKAY);
 		if (error)
 			break;
-		error = ieee80211_cfgset(ifp, cmd, data);
+		error = ieee80211_cfgset(ic, cmd, data);
 		break;
 	case SIOCG80211STATS:
 		ifr = (struct ifreq *)data;
@@ -1101,14 +2460,14 @@
 		 */
 		case AF_IPX: {
 			struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
-			struct arpcom *ac = (struct arpcom *)ifp;
 
 			if (ipx_nullhost(*ina))
-				ina->x_host = *(union ipx_host *) ac->ac_enaddr;
+				ina->x_host = *(union ipx_host *)
+				    IFP2ENADDR(ifp);
 			else
 				bcopy((caddr_t) ina->x_host.c_host,
-				      (caddr_t) ac->ac_enaddr,
-				      sizeof(ac->ac_enaddr));
+				      (caddr_t) IFP2ENADDR(ifp),
+				      ETHER_ADDR_LEN);
 			/* fall thru... */
 		}
 #endif
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_ioctl.h src/sys/netproto/802_11/ieee80211_ioctl.h
--- src.preview/sys/netproto/802_11/ieee80211_ioctl.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_ioctl.h	2005-09-23 11:46:16.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,68 +29,343 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_ioctl.h,v 1.5 2004/03/30 22:57:57 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_ioctl.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_IOCTL_H_
-#define	_NETPROTO_802_11_IEEE80211_IOCTL_H_
+#ifndef _NET80211_IEEE80211_IOCTL_H_
+#define _NET80211_IEEE80211_IOCTL_H_
 
 /*
  * IEEE 802.11 ioctls.
  */
+#include <netproto/802_11/_ieee80211.h>
+#include <netproto/802_11/ieee80211.h>
+#include <netproto/802_11/ieee80211_crypto.h>
+
+/*
+ * Per/node (station) statistics available when operating as an AP.
+ */
+struct ieee80211_nodestats {
+	u_int32_t	ns_rx_data;		/* rx data frames */
+	u_int32_t	ns_rx_mgmt;		/* rx management frames */
+	u_int32_t	ns_rx_ctrl;		/* rx control frames */
+	u_int32_t	ns_rx_ucast;		/* rx unicast frames */
+	u_int32_t	ns_rx_mcast;		/* rx multi/broadcast frames */
+	u_int64_t	ns_rx_bytes;		/* rx data count (bytes) */
+	u_int64_t	ns_rx_beacons;		/* rx beacon frames */
+	u_int32_t	ns_rx_proberesp;	/* rx probe response frames */
+
+	u_int32_t	ns_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	ns_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	ns_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	ns_rx_demicfail;	/* rx demic failed */
+	u_int32_t	ns_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	ns_rx_defrag;		/* rx defragmentation failed */
+	u_int32_t	ns_rx_disassoc;		/* rx disassociation */
+	u_int32_t	ns_rx_deauth;		/* rx deauthentication */
+	u_int32_t	ns_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	ns_rx_unauth;		/* rx on unauthorized port */
+	u_int32_t	ns_rx_unencrypted;	/* rx unecrypted w/ privacy */
+
+	u_int32_t	ns_tx_data;		/* tx data frames */
+	u_int32_t	ns_tx_mgmt;		/* tx management frames */
+	u_int32_t	ns_tx_ucast;		/* tx unicast frames */
+	u_int32_t	ns_tx_mcast;		/* tx multi/broadcast frames */
+	u_int64_t	ns_tx_bytes;		/* tx data count (bytes) */
+	u_int32_t	ns_tx_probereq;		/* tx probe request frames */
+
+	u_int32_t	ns_tx_novlantag;	/* tx discard 'cuz no tag */
+	u_int32_t	ns_tx_vlanmismatch;	/* tx discard 'cuz bad tag */
+
+	u_int32_t	ns_ps_discard;		/* ps discard 'cuz of age */
 
+	/* MIB-related state */
+	u_int32_t	ns_tx_assoc;		/* [re]associations */
+	u_int32_t	ns_tx_assoc_fail;	/* [re]association failures */
+	u_int32_t	ns_tx_auth;		/* [re]authentications */
+	u_int32_t	ns_tx_auth_fail;	/* [re]authentication failures*/
+	u_int32_t	ns_tx_deauth;		/* deauthentications */
+	u_int32_t	ns_tx_deauth_code;	/* last deauth reason */
+	u_int32_t	ns_tx_disassoc;		/* disassociations */
+	u_int32_t	ns_tx_disassoc_code;	/* last disassociation reason */
+};
+
+/*
+ * Summary statistics.
+ */
 struct ieee80211_stats {
-	uint32_t	is_rx_badversion;	/* rx frame with bad version */
-	uint32_t	is_rx_tooshort;		/* rx frame too short */
-	uint32_t	is_rx_wrongbss;		/* rx from wrong bssid */
-	uint32_t	is_rx_dup;		/* rx discard 'cuz dup */
-	uint32_t	is_rx_wrongdir;		/* rx w/ wrong direction */
-	uint32_t	is_rx_mcastecho;	/* rx discard 'cuz mcast echo */
-	uint32_t	is_rx_notassoc;		/* rx discard 'cuz sta !assoc */
-	uint32_t	is_rx_nowep;		/* rx w/ wep but wep !config */
-	uint32_t	is_rx_wepfail;		/* rx wep processing failed */
-	uint32_t	is_rx_decap;		/* rx decapsulation failed */
-	uint32_t	is_rx_mgtdiscard;	/* rx discard mgt frames */
-	uint32_t	is_rx_ctl;		/* rx discard ctrl frames */
-	uint32_t	is_rx_rstoobig;		/* rx rate set truncated */
-	uint32_t	is_rx_elem_missing;	/* rx required element missing*/
-	uint32_t	is_rx_elem_toobig;	/* rx element too big */
-	uint32_t	is_rx_elem_toosmall;	/* rx element too small */
-	uint32_t	is_rx_elem_unknown;	/* rx element unknown */
-	uint32_t	is_rx_badchan;		/* rx frame w/ invalid chan */
-	uint32_t	is_rx_chanmismatch;	/* rx frame chan mismatch */
-	uint32_t	is_rx_nodealloc;	/* rx frame dropped */
-	uint32_t	is_rx_ssidmismatch;	/* rx frame ssid mismatch  */
-	uint32_t	is_rx_auth_unsupported;	/* rx w/ unsupported auth alg */
-	uint32_t	is_rx_auth_fail;	/* rx sta auth failure */
-	uint32_t	is_rx_assoc_bss;	/* rx assoc from wrong bssid */
-	uint32_t	is_rx_assoc_notauth;	/* rx assoc w/o auth */
-	uint32_t	is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
-	uint32_t	is_rx_assoc_norate;	/* rx assoc w/ no rate match */
-	uint32_t	is_rx_deauth;		/* rx deauthentication */
-	uint32_t	is_rx_disassoc;		/* rx disassociation */
-	uint32_t	is_rx_badsubtype;	/* rx frame w/ unknown subtype*/
-	uint32_t	is_rx_nombuf;		/* rx failed for lack of mbuf */
-	uint32_t	is_rx_decryptcrc;	/* rx decrypt failed on crc */
-	uint32_t	is_rx_ahdemo_mgt;	/* rx discard ahdemo mgt frame*/
-	uint32_t	is_rx_bad_auth;		/* rx bad auth request */
-	uint32_t	is_tx_nombuf;		/* tx failed for lack of mbuf */
-	uint32_t	is_tx_nonode;		/* tx failed for no node */
-	uint32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
-	uint32_t	is_scan_active;		/* active scans started */
-	uint32_t	is_scan_passive;	/* passive scans started */
-	uint32_t	is_node_timeout;	/* nodes timed out inactivity */
-	uint32_t	is_crypto_nomem;	/* no memory for crypto ctx */
+	u_int32_t	is_rx_badversion;	/* rx frame with bad version */
+	u_int32_t	is_rx_tooshort;		/* rx frame too short */
+	u_int32_t	is_rx_wrongbss;		/* rx from wrong bssid */
+	u_int32_t	is_rx_dup;		/* rx discard 'cuz dup */
+	u_int32_t	is_rx_wrongdir;		/* rx w/ wrong direction */
+	u_int32_t	is_rx_mcastecho;	/* rx discard 'cuz mcast echo */
+	u_int32_t	is_rx_notassoc;		/* rx discard 'cuz sta !assoc */
+	u_int32_t	is_rx_noprivacy;	/* rx w/ wep but privacy off */
+	u_int32_t	is_rx_wepfail;		/* rx wep processing failed */
+	u_int32_t	is_rx_decap;		/* rx decapsulation failed */
+	u_int32_t	is_rx_mgtdiscard;	/* rx discard mgt frames */
+	u_int32_t	is_rx_ctl;		/* rx discard ctrl frames */
+	u_int32_t	is_rx_beacon;		/* rx beacon frames */
+	u_int32_t	is_rx_rstoobig;		/* rx rate set truncated */
+	u_int32_t	is_rx_elem_missing;	/* rx required element missing*/
+	u_int32_t	is_rx_elem_toobig;	/* rx element too big */
+	u_int32_t	is_rx_elem_toosmall;	/* rx element too small */
+	u_int32_t	is_rx_elem_unknown;	/* rx element unknown */
+	u_int32_t	is_rx_badchan;		/* rx frame w/ invalid chan */
+	u_int32_t	is_rx_chanmismatch;	/* rx frame chan mismatch */
+	u_int32_t	is_rx_nodealloc;	/* rx frame dropped */
+	u_int32_t	is_rx_ssidmismatch;	/* rx frame ssid mismatch  */
+	u_int32_t	is_rx_auth_unsupported;	/* rx w/ unsupported auth alg */
+	u_int32_t	is_rx_auth_fail;	/* rx sta auth failure */
+	u_int32_t	is_rx_auth_countermeasures;/* rx auth discard 'cuz CM */
+	u_int32_t	is_rx_assoc_bss;	/* rx assoc from wrong bssid */
+	u_int32_t	is_rx_assoc_notauth;	/* rx assoc w/o auth */
+	u_int32_t	is_rx_assoc_capmismatch;/* rx assoc w/ cap mismatch */
+	u_int32_t	is_rx_assoc_norate;	/* rx assoc w/ no rate match */
+	u_int32_t	is_rx_assoc_badwpaie;	/* rx assoc w/ bad WPA IE */
+	u_int32_t	is_rx_deauth;		/* rx deauthentication */
+	u_int32_t	is_rx_disassoc;		/* rx disassociation */
+	u_int32_t	is_rx_badsubtype;	/* rx frame w/ unknown subtype*/
+	u_int32_t	is_rx_nobuf;		/* rx failed for lack of buf */
+	u_int32_t	is_rx_decryptcrc;	/* rx decrypt failed on crc */
+	u_int32_t	is_rx_ahdemo_mgt;	/* rx discard ahdemo mgt frame*/
+	u_int32_t	is_rx_bad_auth;		/* rx bad auth request */
+	u_int32_t	is_rx_unauth;		/* rx on unauthorized port */
+	u_int32_t	is_rx_badkeyid;		/* rx w/ incorrect keyid */
+	u_int32_t	is_rx_ccmpreplay;	/* rx seq# violation (CCMP) */
+	u_int32_t	is_rx_ccmpformat;	/* rx format bad (CCMP) */
+	u_int32_t	is_rx_ccmpmic;		/* rx MIC check failed (CCMP) */
+	u_int32_t	is_rx_tkipreplay;	/* rx seq# violation (TKIP) */
+	u_int32_t	is_rx_tkipformat;	/* rx format bad (TKIP) */
+	u_int32_t	is_rx_tkipmic;		/* rx MIC check failed (TKIP) */
+	u_int32_t	is_rx_tkipicv;		/* rx ICV check failed (TKIP) */
+	u_int32_t	is_rx_badcipher;	/* rx failed 'cuz key type */
+	u_int32_t	is_rx_nocipherctx;	/* rx failed 'cuz key !setup */
+	u_int32_t	is_rx_acl;		/* rx discard 'cuz acl policy */
+	u_int32_t	is_tx_nobuf;		/* tx failed for lack of buf */
+	u_int32_t	is_tx_nonode;		/* tx failed for no node */
+	u_int32_t	is_tx_unknownmgt;	/* tx of unknown mgt frame */
+	u_int32_t	is_tx_badcipher;	/* tx failed 'cuz key type */
+	u_int32_t	is_tx_nodefkey;		/* tx failed 'cuz no defkey */
+	u_int32_t	is_tx_noheadroom;	/* tx failed 'cuz no space */
+	u_int32_t	is_tx_fragframes;	/* tx frames fragmented */
+	u_int32_t	is_tx_frags;		/* tx fragments created */
+	u_int32_t	is_scan_active;		/* active scans started */
+	u_int32_t	is_scan_passive;	/* passive scans started */
+	u_int32_t	is_node_timeout;	/* nodes timed out inactivity */
+	u_int32_t	is_crypto_nomem;	/* no memory for crypto ctx */
+	u_int32_t	is_crypto_tkip;		/* tkip crypto done in s/w */
+	u_int32_t	is_crypto_tkipenmic;	/* tkip en-MIC done in s/w */
+	u_int32_t	is_crypto_tkipdemic;	/* tkip de-MIC done in s/w */
+	u_int32_t	is_crypto_tkipcm;	/* tkip counter measures */
+	u_int32_t	is_crypto_ccmp;		/* ccmp crypto done in s/w */
+	u_int32_t	is_crypto_wep;		/* wep crypto done in s/w */
+	u_int32_t	is_crypto_setkey_cipher;/* cipher rejected key */
+	u_int32_t	is_crypto_setkey_nokey;	/* no key index for setkey */
+	u_int32_t	is_crypto_delkey;	/* driver key delete failed */
+	u_int32_t	is_crypto_badcipher;	/* unknown cipher */
+	u_int32_t	is_crypto_nocipher;	/* cipher not available */
+	u_int32_t	is_crypto_attachfail;	/* cipher attach failed */
+	u_int32_t	is_crypto_swfallback;	/* cipher fallback to s/w */
+	u_int32_t	is_crypto_keyfail;	/* driver key alloc failed */
+	u_int32_t	is_crypto_enmicfail;	/* en-MIC failed */
+	u_int32_t	is_ibss_capmismatch;	/* merge failed-cap mismatch */
+	u_int32_t	is_ibss_norate;		/* merge failed-rate mismatch */
+	u_int32_t	is_ps_unassoc;		/* ps-poll for unassoc. sta */
+	u_int32_t	is_ps_badaid;		/* ps-poll w/ incorrect aid */
+	u_int32_t	is_ps_qempty;		/* ps-poll w/ nothing to send */
+	u_int32_t	is_ff_badhdr;		/* fast frame rx'd w/ bad hdr */
+	u_int32_t	is_ff_tooshort;		/* fast frame rx decap error */
+	u_int32_t	is_ff_split;		/* fast frame rx split error */
+	u_int32_t	is_ff_decap;		/* fast frames decap'd */
+	u_int32_t	is_ff_encap;		/* fast frames encap'd for tx */
+	u_int32_t	is_rx_badbintval;	/* rx frame w/ bogus bintval */
+	u_int32_t	is_spare[9];
+};
+
+/*
+ * Max size of optional information elements.  We artificially
+ * constrain this; it's limited only by the max frame size (and
+ * the max parameter size of the wireless extensions).
+ */
+#define	IEEE80211_MAX_OPT_IE	256
+
+/*
+ * WPA/RSN get/set key request.  Specify the key/cipher
+ * type and whether the key is to be used for sending and/or
+ * receiving.  The key index should be set only when working
+ * with global keys (use IEEE80211_KEYIX_NONE for ``no index'').
+ * Otherwise a unicast/pairwise key is specified by the bssid
+ * (on a station) or mac address (on an ap).  They key length
+ * must include any MIC key data; otherwise it should be no
+ more than IEEE80211_KEYBUF_SIZE.
+ */
+struct ieee80211req_key {
+	u_int8_t	ik_type;	/* key/cipher type */
+	u_int8_t	ik_pad;
+	u_int16_t	ik_keyix;	/* key index */
+	u_int8_t	ik_keylen;	/* key length in bytes */
+	u_int8_t	ik_flags;
+/* NB: IEEE80211_KEY_XMIT and IEEE80211_KEY_RECV defined elsewhere */
+#define	IEEE80211_KEY_DEFAULT	0x80	/* default xmit key */
+	u_int8_t	ik_macaddr[IEEE80211_ADDR_LEN];
+	u_int64_t	ik_keyrsc;	/* key receive sequence counter */
+	u_int64_t	ik_keytsc;	/* key transmit sequence counter */
+	u_int8_t	ik_keydata[IEEE80211_KEYBUF_SIZE+IEEE80211_MICBUF_SIZE];
+};
+
+/*
+ * Delete a key either by index or address.  Set the index
+ * to IEEE80211_KEYIX_NONE when deleting a unicast key.
+ */
+struct ieee80211req_del_key {
+	u_int8_t	idk_keyix;	/* key index */
+	u_int8_t	idk_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * MLME state manipulation request.  IEEE80211_MLME_ASSOC
+ * only makes sense when operating as a station.  The other
+ * requests can be used when operating as a station or an
+ * ap (to effect a station).
+ */
+struct ieee80211req_mlme {
+	u_int8_t	im_op;		/* operation to perform */
+#define	IEEE80211_MLME_ASSOC		1	/* associate station */
+#define	IEEE80211_MLME_DISASSOC		2	/* disassociate station */
+#define	IEEE80211_MLME_DEAUTH		3	/* deauthenticate station */
+#define	IEEE80211_MLME_AUTHORIZE	4	/* authorize station */
+#define	IEEE80211_MLME_UNAUTHORIZE	5	/* unauthorize station */
+	u_int8_t	im_ssid_len;	/* length of optional ssid */
+	u_int16_t	im_reason;	/* 802.11 reason code */
+	u_int8_t	im_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	im_ssid[IEEE80211_NWID_LEN];
+};
+
+/* 
+ * MAC ACL operations.
+ */
+enum {
+	IEEE80211_MACCMD_POLICY_OPEN	= 0,	/* set policy: no ACL's */
+	IEEE80211_MACCMD_POLICY_ALLOW	= 1,	/* set policy: allow traffic */
+	IEEE80211_MACCMD_POLICY_DENY	= 2,	/* set policy: deny traffic */
+	IEEE80211_MACCMD_FLUSH		= 3,	/* flush ACL database */
+	IEEE80211_MACCMD_DETACH		= 4,	/* detach ACL policy */
+	IEEE80211_MACCMD_POLICY		= 5,	/* get ACL policy */
+	IEEE80211_MACCMD_LIST		= 6,	/* get ACL database */
+};
+
+struct ieee80211req_maclist {
+	u_int8_t	ml_macaddr[IEEE80211_ADDR_LEN];
+};
+
+/*
+ * Set the active channel list.  Note this list is
+ * intersected with the available channel list in
+ * calculating the set of channels actually used in
+ * scanning.
+ */
+struct ieee80211req_chanlist {
+	u_int8_t	ic_channels[IEEE80211_CHAN_BYTES];
+};
+
+/*
+ * Get the active channel list info.
+ */
+struct ieee80211req_chaninfo {
+	u_int	ic_nchans;
+	struct ieee80211_channel ic_chans[IEEE80211_CHAN_MAX];
+};
+
+/*
+ * Retrieve the WPA/RSN information element for an associated station.
+ */
+struct ieee80211req_wpaie {
+	u_int8_t	wpa_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	wpa_ie[IEEE80211_MAX_OPT_IE];
+};
+
+/*
+ * Retrieve per-node statistics.
+ */
+struct ieee80211req_sta_stats {
+	union {
+		/* NB: explicitly force 64-bit alignment */
+		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
+		u_int64_t	pad;
+	} is_u;
+	struct ieee80211_nodestats is_stats;
+};
+
+/*
+ * Station information block; the mac address is used
+ * to retrieve other data like stats, unicast key, etc.
+ */
+struct ieee80211req_sta_info {
+	u_int16_t	isi_len;		/* length (mult of 4) */
+	u_int16_t	isi_freq;		/* MHz */
+	u_int16_t	isi_flags;		/* channel flags */
+	u_int16_t	isi_state;		/* state flags */
+	u_int8_t	isi_authmode;		/* authentication algorithm */
+	u_int8_t	isi_rssi;
+	u_int8_t	isi_capinfo;		/* capabilities */
+	u_int8_t	isi_erp;		/* ERP element */
+	u_int8_t	isi_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	isi_nrates;
+						/* negotiated rates */
+	u_int8_t	isi_rates[IEEE80211_RATE_MAXSIZE];
+	u_int8_t	isi_txrate;		/* index to isi_rates[] */
+	u_int16_t	isi_ie_len;		/* IE length */
+	u_int16_t	isi_associd;		/* assoc response */
+	u_int16_t	isi_txpower;		/* current tx power */
+	u_int16_t	isi_vlan;		/* vlan tag */
+	u_int16_t	isi_txseqs[17];		/* seq to be transmitted */
+	u_int16_t	isi_rxseqs[17];		/* seq previous for qos frames*/
+	u_int16_t	isi_inact;		/* inactivity timer */
+	/* XXX frag state? */
+	/* variable length IE data */
 };
 
 /*
+ * Retrieve per-station information; to retrieve all
+ * specify a mac address of ff:ff:ff:ff:ff:ff.
+ */
+struct ieee80211req_sta_req {
+	union {
+		/* NB: explicitly force 64-bit alignment */
+		u_int8_t	macaddr[IEEE80211_ADDR_LEN];
+		u_int64_t	pad;
+	} is_u;
+	struct ieee80211req_sta_info info[1];	/* variable length */
+};
+
+/*
+ * Get/set per-station tx power cap.
+ */
+struct ieee80211req_sta_txpow {
+	u_int8_t	it_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t	it_txpow;
+};
+
+/*
+ * WME parameters are set and return using i_val and i_len.
+ * i_val holds the value itself.  i_len specifies the AC
+ * and, as appropriate, then high bit specifies whether the
+ * operation is to be applied to the BSS or ourself.
+ */
+#define	IEEE80211_WMEPARAM_SELF	0x0000		/* parameter applies to self */
+#define	IEEE80211_WMEPARAM_BSS	0x8000		/* parameter applies to BSS */
+#define	IEEE80211_WMEPARAM_VAL	0x7fff		/* parameter value */
+
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+/*
  * FreeBSD-style ioctls.
  */
 /* the first member must be matched with struct ifreq */
 struct ieee80211req {
 	char		i_name[IFNAMSIZ];	/* if_name, e.g. "wi0" */
-	uint16_t	i_type;			/* req type */
+	u_int16_t	i_type;			/* req type */
 	int16_t		i_val;			/* Index or simple value */
 	int16_t		i_len;			/* Index or simple value */
 	void		*i_data;		/* Extra data */
@@ -98,38 +373,99 @@
 #define	SIOCS80211		 _IOW('i', 234, struct ieee80211req)
 #define	SIOCG80211		_IOWR('i', 235, struct ieee80211req)
 
-#define	IEEE80211_IOC_SSID		1
-#define	IEEE80211_IOC_NUMSSIDS		2
-#define	IEEE80211_IOC_WEP		3
-#define		IEEE80211_WEP_NOSUP	-1
-#define		IEEE80211_WEP_OFF	0
-#define		IEEE80211_WEP_ON	1
-#define		IEEE80211_WEP_MIXED	2
-#define	IEEE80211_IOC_WEPKEY		4
-#define	IEEE80211_IOC_NUMWEPKEYS	5
-#define	IEEE80211_IOC_WEPTXKEY		6
-#define	IEEE80211_IOC_AUTHMODE		7
-#define	IEEE80211_IOC_STATIONNAME	8
-#define	IEEE80211_IOC_CHANNEL		9
-#define	IEEE80211_IOC_POWERSAVE		10
-#define		IEEE80211_POWERSAVE_NOSUP	-1
-#define		IEEE80211_POWERSAVE_OFF		0
-#define		IEEE80211_POWERSAVE_CAM		1
-#define		IEEE80211_POWERSAVE_PSP		2
-#define		IEEE80211_POWERSAVE_PSP_CAM	3
-#define		IEEE80211_POWERSAVE_ON		IEEE80211_POWERSAVE_CAM
-#define	IEEE80211_IOC_POWERSAVESLEEP	11
+#define IEEE80211_IOC_SSID		1
+#define IEEE80211_IOC_NUMSSIDS		2
+#define IEEE80211_IOC_WEP		3
+#define 	IEEE80211_WEP_NOSUP	-1
+#define 	IEEE80211_WEP_OFF	0
+#define 	IEEE80211_WEP_ON	1
+#define 	IEEE80211_WEP_MIXED	2
+#define IEEE80211_IOC_WEPKEY		4
+#define IEEE80211_IOC_NUMWEPKEYS	5
+#define IEEE80211_IOC_WEPTXKEY		6
+#define IEEE80211_IOC_AUTHMODE		7
+#define IEEE80211_IOC_STATIONNAME	8
+#define IEEE80211_IOC_CHANNEL		9
+#define IEEE80211_IOC_POWERSAVE		10
+#define 	IEEE80211_POWERSAVE_NOSUP	-1
+#define 	IEEE80211_POWERSAVE_OFF		0
+#define 	IEEE80211_POWERSAVE_CAM		1
+#define 	IEEE80211_POWERSAVE_PSP		2
+#define 	IEEE80211_POWERSAVE_PSP_CAM	3
+#define 	IEEE80211_POWERSAVE_ON		IEEE80211_POWERSAVE_CAM
+#define IEEE80211_IOC_POWERSAVESLEEP	11
 #define	IEEE80211_IOC_RTSTHRESHOLD	12
-#define	IEEE80211_IOC_PROTMODE		13
-#define		IEEE80211_PROTMODE_OFF		0
-#define		IEEE80211_PROTMODE_CTS		1
-#define		IEEE80211_PROTMODE_RTSCTS	2
-#define	IEEE80211_IOC_TXPOWER		14
-
-#ifndef IEEE80211_CHAN_ANY
-#define	IEEE80211_CHAN_ANY	0xffff		/* token for ``any channel'' */
-#endif
+#define IEEE80211_IOC_PROTMODE		13
+#define 	IEEE80211_PROTMODE_OFF		0
+#define 	IEEE80211_PROTMODE_CTS		1
+#define 	IEEE80211_PROTMODE_RTSCTS	2
+#define	IEEE80211_IOC_TXPOWER		14	/* global tx power limit */
+#define	IEEE80211_IOC_BSSID		15
+#define	IEEE80211_IOC_ROAMING		16	/* roaming mode */
+#define	IEEE80211_IOC_PRIVACY		17	/* privacy invoked */
+#define	IEEE80211_IOC_DROPUNENCRYPTED	18	/* discard unencrypted frames */
+#define	IEEE80211_IOC_WPAKEY		19
+#define	IEEE80211_IOC_DELKEY		20
+#define	IEEE80211_IOC_MLME		21
+#define	IEEE80211_IOC_OPTIE		22	/* optional info. element */
+#define	IEEE80211_IOC_SCAN_REQ		23
+#define	IEEE80211_IOC_SCAN_RESULTS	24
+#define	IEEE80211_IOC_COUNTERMEASURES	25	/* WPA/TKIP countermeasures */
+#define	IEEE80211_IOC_WPA		26	/* WPA mode (0,1,2) */
+#define	IEEE80211_IOC_CHANLIST		27	/* channel list */
+#define	IEEE80211_IOC_WME		28	/* WME mode (on, off) */
+#define	IEEE80211_IOC_HIDESSID		29	/* hide SSID mode (on, off) */
+#define	IEEE80211_IOC_APBRIDGE		30	/* AP inter-sta bridging */
+#define	IEEE80211_IOC_MCASTCIPHER	31	/* multicast/default cipher */
+#define	IEEE80211_IOC_MCASTKEYLEN	32	/* multicast key length */
+#define	IEEE80211_IOC_UCASTCIPHERS	33	/* unicast cipher suites */
+#define	IEEE80211_IOC_UCASTCIPHER	34	/* unicast cipher */
+#define	IEEE80211_IOC_UCASTKEYLEN	35	/* unicast key length */
+#define	IEEE80211_IOC_DRIVER_CAPS	36	/* driver capabilities */
+#define	IEEE80211_IOC_KEYMGTALGS	37	/* key management algorithms */
+#define	IEEE80211_IOC_RSNCAPS		38	/* RSN capabilities */
+#define	IEEE80211_IOC_WPAIE		39	/* WPA information element */
+#define	IEEE80211_IOC_STA_STATS		40	/* per-station statistics */
+#define	IEEE80211_IOC_MACCMD		41	/* MAC ACL operation */
+#define	IEEE80211_IOC_CHANINFO		42	/* channel info list */
+#define	IEEE80211_IOC_TXPOWMAX		43	/* max tx power for channel */
+#define	IEEE80211_IOC_STA_TXPOW		44	/* per-station tx power limit */
+#define	IEEE80211_IOC_STA_INFO		45	/* station/neighbor info */
+#define	IEEE80211_IOC_WME_CWMIN		46	/* WME: ECWmin */
+#define	IEEE80211_IOC_WME_CWMAX		47	/* WME: ECWmax */
+#define	IEEE80211_IOC_WME_AIFS		48	/* WME: AIFSN */
+#define	IEEE80211_IOC_WME_TXOPLIMIT	49	/* WME: txops limit */
+#define	IEEE80211_IOC_WME_ACM		50	/* WME: ACM (bss only) */
+#define	IEEE80211_IOC_WME_ACKPOLICY	51	/* WME: ACK policy (!bss only)*/
+#define	IEEE80211_IOC_DTIM_PERIOD	52	/* DTIM period (beacons) */
+#define	IEEE80211_IOC_BEACON_INTERVAL	53	/* beacon interval (ms) */
+#define	IEEE80211_IOC_ADDMAC		54	/* add sta to MAC ACL table */
+#define	IEEE80211_IOC_DELMAC		55	/* del sta from MAC ACL table */
+#define	IEEE80211_IOC_PUREG		56	/* pure 11g (no 11b stations) */
+#define	IEEE80211_IOC_FRAGTHRESHOLD	73	/* tx fragmentation threshold */
+
+/*
+ * Scan result data returned for IEEE80211_IOC_SCAN_RESULTS.
+ */
+struct ieee80211req_scan_result {
+	u_int16_t	isr_len;		/* length (mult of 4) */
+	u_int16_t	isr_freq;		/* MHz */
+	u_int16_t	isr_flags;		/* channel flags */
+	u_int8_t	isr_noise;
+	u_int8_t	isr_rssi;
+	u_int8_t	isr_intval;		/* beacon interval */
+	u_int8_t	isr_capinfo;		/* capabilities */
+	u_int8_t	isr_erp;		/* ERP element */
+	u_int8_t	isr_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t	isr_nrates;
+	u_int8_t	isr_rates[IEEE80211_RATE_MAXSIZE];
+	u_int8_t	isr_ssid_len;		/* SSID length */
+	u_int8_t	isr_ie_len;		/* IE length */
+	u_int8_t	isr_pad[5];
+	/* variable length SSID followed by IE data */
+};
 
 #define	SIOCG80211STATS		_IOWR('i', 236, struct ifreq)
+#endif /* __FreeBSD__ */
 
-#endif /* _NETPROTO_802_11_IEEE80211_IOCTL_H_ */
+#endif /* _NET80211_IEEE80211_IOCTL_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_node.c src/sys/netproto/802_11/ieee80211_node.c
--- src.preview/sys/netproto/802_11/ieee80211_node.c	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_node.c	2005-09-23 11:46:17.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,126 +28,257 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_node.c,v 1.22 2004/04/05 04:15:55 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_node.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
-#include "opt_inet.h"
+#include <sys/cdefs.h>
 
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
 #include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
-#include <sys/thread.h>
-#include <sys/thread2.h>
 
-#include <machine/atomic.h>
+#include <sys/socket.h>
  
+#include <sys/thread.h>
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
 #include <net/ethernet.h>
-#include <net/if_llc.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
 #include <net/bpf.h>
 
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
-
-static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
-static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
-static void ieee80211_node_copy(struct ieee80211com *,
-		struct ieee80211_node *, const struct ieee80211_node *);
-static uint8_t ieee80211_node_getrssi(struct ieee80211com *,
-		struct ieee80211_node *);
-
-static void ieee80211_setup_node(struct ieee80211com *ic,
-		struct ieee80211_node *ni, uint8_t *macaddr);
-static void _ieee80211_free_node(struct ieee80211com *,
-		struct ieee80211_node *);
+/*
+ * Association id's are managed with a bit vector.
+ */
+#define	IEEE80211_AID_SET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] |= (1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_CLR(b, w) \
+	((w)[IEEE80211_AID(b) / 32] &= ~(1 << (IEEE80211_AID(b) % 32)))
+#define	IEEE80211_AID_ISSET(b, w) \
+	((w)[IEEE80211_AID(b) / 32] & (1 << (IEEE80211_AID(b) % 32)))
+
+static struct ieee80211_node *node_alloc(struct ieee80211_node_table *);
+static void node_cleanup(struct ieee80211_node *);
+static void node_free(struct ieee80211_node *);
+static u_int8_t node_getrssi(const struct ieee80211_node *);
+
+static void ieee80211_setup_node(struct ieee80211_node_table *,
+		struct ieee80211_node *, const u_int8_t *);
+static void _ieee80211_free_node(struct ieee80211_node *);
+static void ieee80211_free_allnodes(struct ieee80211_node_table *);
+
+static void ieee80211_timeout_scan_candidates(struct ieee80211_node_table *);
+static void ieee80211_timeout_stations(struct ieee80211_node_table *);
+
+static void ieee80211_set_tim(struct ieee80211_node *, int set);
+
+static void ieee80211_node_table_init(struct ieee80211com *ic,
+	struct ieee80211_node_table *nt, const char *name,
+	int inact, int keyixmax,
+	void (*timeout)(struct ieee80211_node_table *));
+static void ieee80211_node_table_cleanup(struct ieee80211_node_table *nt);
 
 MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
 
 void
-ieee80211_node_attach(struct ifnet *ifp)
+ieee80211_node_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
-	lwkt_token_init(&ic->ic_nodetoken);
-	TAILQ_INIT(&ic->ic_node);
-	ic->ic_node_alloc = ieee80211_node_alloc;
-	ic->ic_node_free = ieee80211_node_free;
-	ic->ic_node_copy = ieee80211_node_copy;
-	ic->ic_node_getrssi = ieee80211_node_getrssi;
-	ic->ic_scangen = 1;
+	ic->ic_node_alloc = node_alloc;
+	ic->ic_node_free = node_free;
+	ic->ic_node_cleanup = node_cleanup;
+	ic->ic_node_getrssi = node_getrssi;
+
+	/* default station inactivity timer setings */
+	ic->ic_inact_init = IEEE80211_INACT_INIT;
+	ic->ic_inact_auth = IEEE80211_INACT_AUTH;
+	ic->ic_inact_run = IEEE80211_INACT_RUN;
+	ic->ic_inact_probe = IEEE80211_INACT_PROBE;
+
+	/* NB: driver should override */
+	ic->ic_max_aid = IEEE80211_AID_DEF;
+	ic->ic_set_tim = ieee80211_set_tim;
 }
 
 void
-ieee80211_node_lateattach(struct ifnet *ifp)
+ieee80211_node_lateattach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct ieee80211_node *ni;
+	struct ieee80211_rsnparms *rsn;
 
-	ni = (*ic->ic_node_alloc)(ic);
-	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
-	ni->ni_chan = IEEE80211_CHAN_ANYC;
-	ic->ic_bss = ni;
-	ic->ic_txpower = IEEE80211_TXPOWER_MAX;
+	if (ic->ic_max_aid > IEEE80211_AID_MAX)
+		ic->ic_max_aid = IEEE80211_AID_MAX;
+	MALLOC(ic->ic_aid_bitmap, u_int32_t *,
+		howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t),
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_aid_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for AID bitmap!\n", __func__);
+		ic->ic_max_aid = 0;
+	}
+
+	/* XXX defer until using hostap/ibss mode */
+	ic->ic_tim_len = howmany(ic->ic_max_aid, 8) * sizeof(u_int8_t);
+	MALLOC(ic->ic_tim_bitmap, u_int8_t *, ic->ic_tim_len,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (ic->ic_tim_bitmap == NULL) {
+		/* XXX no way to recover */
+		printf("%s: no memory for TIM bitmap!\n", __func__);
+	}
+
+	ieee80211_node_table_init(ic, &ic->ic_sta, "station",
+		IEEE80211_INACT_INIT, ic->ic_crypto.cs_max_keyix,
+		ieee80211_timeout_stations);
+	ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
+		IEEE80211_INACT_SCAN, 0,
+		ieee80211_timeout_scan_candidates);
+
+	ieee80211_reset_bss(ic);
+	/*
+	 * Setup "global settings" in the bss node so that
+	 * each new station automatically inherits them.
+	 */
+	rsn = &ic->ic_bss->ni_rsn;
+	/* WEP, TKIP, and AES-CCM are always supported */
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_TKIP;
+	rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_CCM;
+	if (ic->ic_caps & IEEE80211_C_AES)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_AES_OCB;
+	if (ic->ic_caps & IEEE80211_C_CKIP)
+		rsn->rsn_ucastcipherset |= 1<<IEEE80211_CIPHER_CKIP;
+	/*
+	 * Default unicast cipher to WEP for 802.1x use.  If
+	 * WPA is enabled the management code will set these
+	 * values to reflect.
+	 */
+	rsn->rsn_ucastcipher = IEEE80211_CIPHER_WEP;
+	rsn->rsn_ucastkeylen = 104 / NBBY;
+	/*
+	 * WPA says the multicast cipher is the lowest unicast
+	 * cipher supported.  But we skip WEP which would
+	 * otherwise be used based on this criteria.
+	 */
+	rsn->rsn_mcastcipher = IEEE80211_CIPHER_TKIP;
+	rsn->rsn_mcastkeylen = 128 / NBBY;
+
+	/*
+	 * We support both WPA-PSK and 802.1x; the one used
+	 * is determined by the authentication mode and the
+	 * setting of the PSK state.
+	 */
+	rsn->rsn_keymgmtset = WPA_ASE_8021X_UNSPEC | WPA_ASE_8021X_PSK;
+	rsn->rsn_keymgmt = WPA_ASE_8021X_PSK;
+
+	ic->ic_auth = ieee80211_authenticator_get(ic->ic_bss->ni_authmode);
+}
+
+void
+ieee80211_node_detach(struct ieee80211com *ic)
+{
+
+	if (ic->ic_bss != NULL) {
+		ieee80211_free_node(ic->ic_bss);
+		ic->ic_bss = NULL;
+	}
+	ieee80211_node_table_cleanup(&ic->ic_scan);
+	ieee80211_node_table_cleanup(&ic->ic_sta);
+	if (ic->ic_aid_bitmap != NULL) {
+		FREE(ic->ic_aid_bitmap, M_DEVBUF);
+		ic->ic_aid_bitmap = NULL;
+	}
+	if (ic->ic_tim_bitmap != NULL) {
+		FREE(ic->ic_tim_bitmap, M_DEVBUF);
+		ic->ic_tim_bitmap = NULL;
+	}
+}
+
+/* 
+ * Port authorize/unauthorize interfaces for use by an authenticator.
+ */
+
+void
+ieee80211_node_authorize(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	ni->ni_flags |= IEEE80211_NODE_AUTH;
+	ni->ni_inact_reload = ic->ic_inact_run;
 }
 
 void
-ieee80211_node_detach(struct ifnet *ifp)
+ieee80211_node_unauthorize(struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	ni->ni_flags &= ~IEEE80211_NODE_AUTH;
+}
 
-	if (ic->ic_bss != NULL)
-		(*ic->ic_node_free)(ic, ic->ic_bss);
-	ieee80211_free_allnodes(ic);
-	lwkt_token_uninit(&ic->ic_nodetoken);
+/*
+ * Set/change the channel.  The rate set is also updated as
+ * to insure a consistent view by drivers.
+ */
+static __inline void
+ieee80211_set_chan(struct ieee80211com *ic,
+	struct ieee80211_node *ni, struct ieee80211_channel *chan)
+{
+	ni->ni_chan = chan;
+	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
 }
 
 /*
  * AP scanning support.
  */
 
+#ifdef IEEE80211_DEBUG
+static void
+dump_chanlist(const u_char chans[])
+{
+	const char *sep;
+	int i;
+
+	sep = " ";
+	for (i = 0; i < IEEE80211_CHAN_MAX; i++)
+		if (isset(chans, i)) {
+			printf("%s%u", sep, i);
+			sep = ", ";
+		}
+}
+#endif /* IEEE80211_DEBUG */
+
 /*
- * Initialize the active channel set based on the set
+ * Initialize the channel set to scan based on the
  * of available channels and the current PHY mode.
  */
 static void
-ieee80211_reset_scan(struct ifnet *ifp)
+ieee80211_reset_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
-	memcpy(ic->ic_chan_scan, ic->ic_chan_active,
-		sizeof(ic->ic_chan_active));
-	/* NB: hack, setup so next_scan starts with the first channel */
-	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
-		ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
+	/* XXX ic_des_chan should be handled with ic_chan_active */
+	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
+		memset(ic->ic_chan_scan, 0, sizeof(ic->ic_chan_scan));
+		setbit(ic->ic_chan_scan,
+			ieee80211_chan2ieee(ic, ic->ic_des_chan));
+	} else
+		memcpy(ic->ic_chan_scan, ic->ic_chan_active,
+			sizeof(ic->ic_chan_active));
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic)) {
+		printf("%s: scan set:", __func__);
+		dump_chanlist(ic->ic_chan_scan);
+		printf(" start chan %u\n",
+			ieee80211_chan2ieee(ic, ic->ic_curchan));
+	}
+#endif /* IEEE80211_DEBUG */
 }
 
 /*
  * Begin an active scan.
  */
 void
-ieee80211_begin_scan(struct ifnet *ifp)
+ieee80211_begin_scan(struct ieee80211com *ic, int reset)
 {
-	struct ieee80211com *ic = (void *)ifp;
 
+	ic->ic_scan.nt_scangen++;
 	/*
 	 * In all but hostap mode scanning starts off in
 	 * an active mode before switching to passive.
@@ -157,96 +288,175 @@
 		ic->ic_stats.is_scan_active++;
 	} else
 		ic->ic_stats.is_scan_passive++;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "begin %s scan\n",
-			(ic->ic_flags & IEEE80211_F_ASCAN) ?
-				"active" : "passive");
-	/*
-	 * Clear scan state and flush any previously seen
-	 * AP's.  Note that the latter assumes we don't act
-	 * as both an AP and a station, otherwise we'll
-	 * potentially flush state of stations associated
-	 * with us.
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		"begin %s scan in %s mode, scangen %u\n",
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive",
+		ieee80211_phymode_name[ic->ic_curmode], ic->ic_scan.nt_scangen);
+	/*
+	 * Clear scan state and flush any previously seen AP's.
 	 */
-	ieee80211_reset_scan(ifp);
-	ieee80211_free_allnodes(ic);
+	ieee80211_reset_scan(ic);
+	if (reset)
+		ieee80211_free_allnodes(&ic->ic_scan);
+
+	ic->ic_flags |= IEEE80211_F_SCAN;
 
 	/* Scan the next channel. */
-	ieee80211_next_scan(ifp);
+	ieee80211_next_scan(ic);
 }
 
 /*
  * Switch to the next channel marked for scanning.
  */
-void
-ieee80211_next_scan(struct ifnet *ifp)
+int
+ieee80211_next_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
 	struct ieee80211_channel *chan;
 
-	chan = ic->ic_bss->ni_chan;
-	for (;;) {
+	/*
+	 * Insure any previous mgt frame timeouts don't fire.
+	 * This assumes the driver does the right thing in
+	 * flushing anything queued in the driver and below.
+	 */
+	ic->ic_mgt_timer = 0;
+
+	chan = ic->ic_curchan;
+	do {
 		if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
 			chan = &ic->ic_channels[0];
 		if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
+			clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			    "%s: chan %d->%d\n", __func__,
+			    ieee80211_chan2ieee(ic, ic->ic_curchan),
+			    ieee80211_chan2ieee(ic, chan));
+			ic->ic_curchan = chan;
 			/*
-			 * Honor channels marked passive-only
-			 * during an active scan.
+			 * XXX drivers should do this as needed,
+			 * XXX for now maintain compatibility
 			 */
-			if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
-			    (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
-				break;
-		}
-		if (chan == ic->ic_bss->ni_chan) {
-			ieee80211_end_scan(ifp);
-			return;
+			ic->ic_bss->ni_rates =
+				ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
+			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+			return 1;
 		}
-	}
-	clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
-	IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
-	    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
-	    ieee80211_chan2ieee(ic, chan)));
-	ic->ic_bss->ni_chan = chan;
-	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+	} while (chan != ic->ic_curchan);
+	ieee80211_end_scan(ic);
+	return 0;
+}
+
+static __inline void
+copy_bss(struct ieee80211_node *nbss, const struct ieee80211_node *obss)
+{
+	/* propagate useful state */
+	nbss->ni_authmode = obss->ni_authmode;
+	nbss->ni_txpower = obss->ni_txpower;
+	nbss->ni_vlan = obss->ni_vlan;
+	nbss->ni_rsn = obss->ni_rsn;
+	/* XXX statistics? */
 }
 
 void
 ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
 {
+	struct ieee80211_node_table *nt;
 	struct ieee80211_node *ni;
-	struct ifnet *ifp = &ic->ic_if;
+	IEEE80211_LOCK_INFO;
 
-	ni = ic->ic_bss;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "creating ibss\n");
-	ic->ic_flags |= IEEE80211_F_SIBSS;
-	ni->ni_chan = chan;
-	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
-	IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+		"%s: creating ibss\n", __func__);
+
+	/*
+	 * Create the station/neighbor table.  Note that for adhoc
+	 * mode we make the initial inactivity timer longer since
+	 * we create nodes only through discovery and they typically
+	 * are long-lived associations.
+	 */
+	nt = &ic->ic_sta;
+	IEEE80211_NODE_LOCK(nt);
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+		nt->nt_name = "station";
+		nt->nt_inact_init = ic->ic_inact_init;
+	} else {
+		nt->nt_name = "neighbor";
+		nt->nt_inact_init = ic->ic_inact_run;
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	ni = ieee80211_alloc_node(&ic->ic_sta, ic->ic_myaddr);
+	if (ni == NULL) {
+		/* XXX recovery? */
+		return;
+	}
 	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
-	if (ic->ic_opmode == IEEE80211_M_IBSS)
-		ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
 	ni->ni_esslen = ic->ic_des_esslen;
 	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
-	ni->ni_rssi = 0;
-	ni->ni_rstamp = 0;
-	memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
-	ni->ni_intval = ic->ic_lintval;
-	ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
-	if (ic->ic_flags & IEEE80211_F_WEPON)
+	copy_bss(ni, ic->ic_bss);
+	ni->ni_intval = ic->ic_bintval;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
 		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
 	if (ic->ic_phytype == IEEE80211_T_FH) {
 		ni->ni_fhdwell = 200;	/* XXX */
 		ni->ni_fhindex = 1;
 	}
-	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		ic->ic_flags |= IEEE80211_F_SIBSS;
+		ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS;	/* XXX */
+		if (ic->ic_flags & IEEE80211_F_DESBSSID)
+			IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid);
+		else
+			ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
+	}
+	/* 
+	 * Fix the channel and related attributes.
+	 */
+	ieee80211_set_chan(ic, ni, chan);
+	ic->ic_curchan = chan;
+	ic->ic_curmode = ieee80211_chan2mode(ic, chan);
+	/*
+	 * Do mode-specific rate setup.
+	 */
+	if (ic->ic_curmode == IEEE80211_MODE_11G) {
+		/*
+		 * Use a mixed 11b/11g rate set.
+		 */
+		ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11G);
+	} else if (ic->ic_curmode == IEEE80211_MODE_11B) {
+		/*
+		 * Force pure 11b rate set.
+		 */
+		ieee80211_set11gbasicrates(&ni->ni_rates, IEEE80211_MODE_11B);
+	}
+
+	(void) ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+}
+
+void
+ieee80211_reset_bss(struct ieee80211com *ic)
+{
+	struct ieee80211_node *ni, *obss;
+
+	ieee80211_node_table_reset(&ic->ic_scan);
+	ieee80211_node_table_reset(&ic->ic_sta);
+
+	ni = ieee80211_alloc_node(&ic->ic_scan, ic->ic_myaddr);
+	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
+	obss = ic->ic_bss;
+	ic->ic_bss = ieee80211_ref_node(ni);
+	if (obss != NULL) {
+		copy_bss(ni, obss);
+		ni->ni_intval = ic->ic_bintval;
+		ieee80211_free_node(obss);
+	}
 }
 
+/* XXX tunable */
+#define	STA_FAILS_MAX	2		/* assoc failures before ignored */
+
 static int
-ieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
+ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = (void *)ifp;
-        uint8_t rate;
+        u_int8_t rate;
         int fail;
 
 	fail = 0;
@@ -262,7 +472,7 @@
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
 			fail |= 0x02;
 	}
-	if (ic->ic_flags & IEEE80211_F_WEPON) {
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
 		if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
 			fail |= 0x04;
 	} else {
@@ -270,7 +480,7 @@
 		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
 			fail |= 0x04;
 	}
-	rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
+	rate = ieee80211_fix_rate(ni, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
 	if (rate & IEEE80211_RATE_BASIC)
 		fail |= 0x08;
 	if (ic->ic_des_esslen != 0 &&
@@ -280,10 +490,14 @@
 	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
 	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
 		fail |= 0x20;
+	if (ni->ni_fails >= STA_FAILS_MAX)
+		fail |= 0x40;
 #ifdef IEEE80211_DEBUG
-	if (ifp->if_flags & IFF_DEBUG) {
-		printf(" %c %6D", fail ? '-' : '+', ni->ni_macaddr, ":");
-		printf(" %6D%c", ni->ni_bssid, ":", fail & 0x20 ? '!' : ' ');
+	if (ieee80211_msg_scan(ic)) {
+		printf(" %c %6D", fail ? '-' : '+',
+		    (ni->ni_macaddr), ":");
+		printf(" %6D%c", (ni->ni_bssid), ":",
+		    fail & 0x20 ? '!' : ' ');
 		printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
 			fail & 0x01 ? '!' : ' ');
 		printf(" %+4d", ni->ni_rssi);
@@ -305,49 +519,155 @@
 	return fail;
 }
 
+static __inline u_int8_t
+maxrate(const struct ieee80211_node *ni)
+{
+	const struct ieee80211_rateset *rs = &ni->ni_rates;
+	/* NB: assumes rate set is sorted (happens on frame receive) */
+	return rs->rs_rates[rs->rs_nrates-1] & IEEE80211_RATE_VAL;
+}
+
+/*
+ * Compare the capabilities of two nodes and decide which is
+ * more desirable (return >0 if a is considered better).  Note
+ * that we assume compatibility/usability has already been checked
+ * so we don't need to (e.g. validate whether privacy is supported).
+ * Used to select the best scan candidate for association in a BSS.
+ */
+static int
+ieee80211_node_compare(struct ieee80211com *ic,
+		       const struct ieee80211_node *a,
+		       const struct ieee80211_node *b)
+{
+#define ABS(x) (x < 0 ? -x : x)
+	u_int8_t maxa, maxb;
+	u_int8_t rssia, rssib;
+	int weight;
+
+	/* privacy support preferred */
+	if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
+	    (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
+		return 1;
+	if ((a->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0 &&
+	    (b->ni_capinfo & IEEE80211_CAPINFO_PRIVACY))
+		return -1;
+
+	/* compare count of previous failures */
+	weight = b->ni_fails - a->ni_fails;
+	if (ABS(weight) > 1)
+		return weight;
+
+	rssia = ic->ic_node_getrssi(a);
+	rssib = ic->ic_node_getrssi(b);
+	if (ABS(rssib - rssia) < 5) {
+		/* best/max rate preferred if signal level close enough XXX */
+		maxa = maxrate(a);
+		maxb = maxrate(b);
+		if (maxa != maxb)
+			return maxa - maxb;
+		/* XXX use freq for channel preference */
+		/* for now just prefer 5Ghz band to all other bands */
+		if (IEEE80211_IS_CHAN_5GHZ(a->ni_chan) &&
+		   !IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+			return 1;
+		if (!IEEE80211_IS_CHAN_5GHZ(a->ni_chan) &&
+		     IEEE80211_IS_CHAN_5GHZ(b->ni_chan))
+			return -1;
+	}
+	/* all things being equal, use signal level */
+	return rssia - rssib;
+#undef ABS
+}
+
+/*
+ * Mark an ongoing scan stopped.
+ */
+void
+ieee80211_cancel_scan(struct ieee80211com *ic)
+{
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "%s: end %s scan\n",
+		__func__,
+		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive");
+
+	ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN);
+}
+
 /*
  * Complete a scan of potential channels.
  */
 void
-ieee80211_end_scan(struct ifnet *ifp)
+ieee80211_end_scan(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
-	struct ieee80211_node *ni, *nextbs, *selbs;
-	int i, fail;
+	struct ieee80211_node_table *nt = &ic->ic_scan;
+	struct ieee80211_node *ni, *selbs;
+	IEEE80211_LOCK_INFO;
 
-	ic->ic_flags &= ~IEEE80211_F_ASCAN;
-	ni = TAILQ_FIRST(&ic->ic_node);
+	ieee80211_cancel_scan(ic);
+	ieee80211_notify_scan_done(ic);
 
 	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
-		/* XXX off stack? */
-		u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
+		u_int8_t maxrssi[IEEE80211_CHAN_MAX];	/* XXX off stack? */
+		int i, bestchan;
+		u_int8_t rssi;
+
 		/*
 		 * The passive scan to look for existing AP's completed,
 		 * select a channel to camp on.  Identify the channels
 		 * that already have one or more AP's and try to locate
-		 * an unnoccupied one.  If that fails, pick a random
-		 * channel from the active set.
+		 * an unoccupied one.  If that fails, pick a channel that
+		 * looks to be quietest.
 		 */
-		for (; ni != NULL; ni = nextbs) {
-			ieee80211_ref_node(ni);
-			nextbs = TAILQ_NEXT(ni, ni_list);
-			setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
-			ieee80211_free_node(ic, ni);
+		memset(maxrssi, 0, sizeof(maxrssi));
+		IEEE80211_NODE_LOCK(nt);
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+			rssi = ic->ic_node_getrssi(ni);
+			i = ieee80211_chan2ieee(ic, ni->ni_chan);
+			if (rssi > maxrssi[i])
+				maxrssi[i] = rssi;
 		}
+		IEEE80211_NODE_UNLOCK(nt);
+		/* XXX select channel more intelligently */
+		bestchan = -1;
 		for (i = 0; i < IEEE80211_CHAN_MAX; i++)
-			if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
-				break;
-		if (i == IEEE80211_CHAN_MAX) {
-			fail = arc4random() & 3;	/* random 0-3 */
-			for (i = 0; i < IEEE80211_CHAN_MAX; i++)
-				if (isset(ic->ic_chan_active, i) && fail-- == 0)
+			if (isset(ic->ic_chan_active, i)) {
+				/*
+				 * If the channel is unoccupied the max rssi
+				 * should be zero; just take it.  Otherwise
+				 * track the channel with the lowest rssi and
+				 * use that when all channels appear occupied.
+				 */
+				if (maxrssi[i] == 0) {
+					bestchan = i;
 					break;
+				}
+				if (bestchan == -1 ||
+				    maxrssi[i] < maxrssi[bestchan])
+					bestchan = i;
+			}
+		if (bestchan != -1) {
+			ieee80211_create_ibss(ic, &ic->ic_channels[bestchan]);
+			return;
 		}
-		ieee80211_create_ibss(ic, &ic->ic_channels[i]);
-		return;
+		/* no suitable channel, should not happen */
 	}
-	if (ni == NULL) {
-		IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__));
+
+	/*
+	 * When manually sequencing the state machine; scan just once
+	 * regardless of whether we have a candidate or not.  The
+	 * controlling application is expected to setup state and
+	 * initiate an association.
+	 */
+	if (ic->ic_roaming == IEEE80211_ROAMING_MANUAL)
+		return;
+	/*
+	 * Automatic sequencing; look for a candidate and
+	 * if found join the network.
+	 */
+	/* NB: unlocked read should be ok */
+	if (TAILQ_FIRST(&nt->nt_node) == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN,
+			"%s: no scan candidate\n", __func__);
   notfound:
 		if (ic->ic_opmode == IEEE80211_M_IBSS &&
 		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
@@ -356,347 +676,1665 @@
 			return;
 		}
 		/*
+		 * Decrement the failure counts so entries will be
+		 * reconsidered the next time around.  We really want
+		 * to do this only for sta's where we've previously
+		 had some success.
+		 */
+		IEEE80211_NODE_LOCK(nt);
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
+			if (ni->ni_fails)
+				ni->ni_fails--;
+		IEEE80211_NODE_UNLOCK(nt);
+		/*
 		 * Reset the list of channels to scan and start again.
 		 */
-		ieee80211_reset_scan(ifp);
-		ieee80211_next_scan(ifp);
+		ieee80211_reset_scan(ic);
+		ic->ic_flags |= IEEE80211_F_SCAN;
+		ieee80211_next_scan(ic);
 		return;
 	}
 	selbs = NULL;
-	if (ifp->if_flags & IFF_DEBUG)
-		if_printf(ifp, "\tmacaddr          bssid         chan  rssi rate flag  wep  essid\n");
-	for (; ni != NULL; ni = nextbs) {
-		ieee80211_ref_node(ni);
-		nextbs = TAILQ_NEXT(ni, ni_list);
-		if (ni->ni_fails) {
-			/*
-			 * The configuration of the access points may change
-			 * during my scan.  So delete the entry for the AP
-			 * and retry to associate if there is another beacon.
-			 */
-			if (ni->ni_fails++ > 2)
-				ieee80211_free_node(ic, ni);
-			continue;
-		}
-		if (ieee80211_match_bss(ifp, ni) == 0) {
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_SCAN, "\t%s\n",
+	    "macaddr          bssid         chan  rssi rate flag  wep  essid");
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		if (ieee80211_match_bss(ic, ni) == 0) {
 			if (selbs == NULL)
 				selbs = ni;
-			else if (ni->ni_rssi > selbs->ni_rssi) {
-				ieee80211_unref_node(&selbs);
+			else if (ieee80211_node_compare(ic, ni, selbs) > 0)
 				selbs = ni;
-			} else
-				ieee80211_unref_node(&ni);
-		} else {
-			ieee80211_unref_node(&ni);
 		}
 	}
+	if (selbs != NULL)		/* NB: grab ref while dropping lock */
+		(void) ieee80211_ref_node(selbs);
+	IEEE80211_NODE_UNLOCK(nt);
 	if (selbs == NULL)
 		goto notfound;
-	(*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
+	if (!ieee80211_sta_join(ic, selbs)) {
+		ieee80211_free_node(selbs);
+		goto notfound;
+	}
+}
+/*
+ * Handle 802.11 ad hoc network merge.  The
+ * convention, set by the Wireless Ethernet Compatibility Alliance
+ * (WECA), is that an 802.11 station will change its BSSID to match
+ * the "oldest" 802.11 ad hoc network, on the same channel, that
+ * has the station's desired SSID.  The "oldest" 802.11 network
+ * sends beacons with the greatest TSF timestamp.
+ *
+ * The caller is assumed to validate TSF's before attempting a merge.
+ *
+ * Return !0 if the BSSID changed, 0 otherwise.
+ */
+int
+ieee80211_ibss_merge(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+
+	if (ni == ic->ic_bss ||
+	    IEEE80211_ADDR_EQ(ni->ni_bssid, ic->ic_bss->ni_bssid)) {
+		/* unchanged, nothing to do */
+		return 0;
+	}
+	if (ieee80211_match_bss(ic, ni) != 0) {	/* capabilities mismatch */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "%s: merge failed, capabilities mismatch\n", __func__);
+		ic->ic_stats.is_ibss_capmismatch++;
+		return 0;
+	}
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		"%s: new bssid %6D: %s preamble, %s slot time%s\n", __func__,
+		(ni->ni_bssid), ":",
+		ic->ic_flags&IEEE80211_F_SHPREAMBLE ? "short" : "long",
+		ic->ic_flags&IEEE80211_F_SHSLOT ? "short" : "long",
+		ic->ic_flags&IEEE80211_F_USEPROT ? ", protection" : ""
+	);
+	return ieee80211_sta_join(ic, ieee80211_ref_node(ni));
+}
+
+/*
+ * Join the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+int
+ieee80211_sta_join(struct ieee80211com *ic, struct ieee80211_node *selbs)
+{
+	struct ieee80211_node *obss;
+	IEEE80211_LOCK_INFO;
+
 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
-		ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
-		    IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
-		if (ic->ic_bss->ni_rates.rs_nrates == 0) {
-			selbs->ni_fails++;
-			ieee80211_unref_node(&selbs);
-			goto notfound;
-		}
-		ieee80211_unref_node(&selbs);
-		/*
-		 * Discard scan set; the nodes have a refcnt of zero
-		 * and have not asked the driver to setup private
-		 * node state.  Let them be repopulated on demand either
-		 * through transmission (ieee80211_find_txnode) or receipt
-		 * of a probe response (to be added).
+		struct ieee80211_node_table *nt;
+		/*
+		 * Delete unusable rates; we've already checked
+		 * that the negotiated rate set is acceptable.
 		 */
-		ieee80211_free_allnodes(ic);
-		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
-	} else {
-		ieee80211_unref_node(&selbs);
-		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+		ieee80211_fix_rate(selbs, IEEE80211_F_DODEL);
+		/*
+		 * Fillin the neighbor table; it will already
+		 * exist if we are simply switching mastership.
+		 * XXX ic_sta always setup so this is unnecessary?
+		 */
+		nt = &ic->ic_sta;
+		IEEE80211_NODE_LOCK(nt);
+		nt->nt_name = "neighbor";
+		nt->nt_inact_init = ic->ic_inact_run;
+		IEEE80211_NODE_UNLOCK(nt);
 	}
+
+	/*
+	 * Committed to selbs, setup state.
+	 */
+	obss = ic->ic_bss;
+	ic->ic_bss = selbs;		/* NB: caller assumed to bump refcnt */
+	if (obss != NULL)
+		ieee80211_free_node(obss);
+	/*
+	 * Set the erp state (mostly the slot time) to deal with
+	 * the auto-select case; this should be redundant if the
+	 * mode is locked.
+	 */ 
+	ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan);
+	ic->ic_curchan = selbs->ni_chan;
+	ieee80211_reset_erp(ic);
+	ieee80211_wme_initparams(ic);
+
+	if (ic->ic_opmode == IEEE80211_M_STA)
+		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
+	else
+		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+	return 1;
+}
+
+/*
+ * Leave the specified IBSS/BSS network.  The node is assumed to
+ * be passed in with a held reference.
+ */
+void
+ieee80211_sta_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	ic->ic_node_cleanup(ni);
+	ieee80211_notify_node_leave(ic, ni);
 }
 
 static struct ieee80211_node *
-ieee80211_node_alloc(struct ieee80211com *ic)
+node_alloc(struct ieee80211_node_table *nt)
 {
 	struct ieee80211_node *ni;
+
 	MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
 		M_80211_NODE, M_NOWAIT | M_ZERO);
 	return ni;
 }
 
+/*
+ * Reclaim any resources in a node and reset any critical
+ * state.  Typically nodes are free'd immediately after,
+ * but in some cases the storage may be reused so we need
+ * to insure consistent state (should probably fix that).
+ */
 static void
-ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+node_cleanup(struct ieee80211_node *ni)
 {
-	FREE(ni, M_80211_NODE);
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+	struct ieee80211com *ic = ni->ni_ic;
+	int i, qlen;
+
+	/* NB: preserve ni_table */
+	if (ni->ni_flags & IEEE80211_NODE_PWR_MGT) {
+		ic->ic_ps_sta--;
+		ni->ni_flags &= ~IEEE80211_NODE_PWR_MGT;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		    "[%6D] power save mode off, %u sta's in ps mode\n",
+		    (ni->ni_macaddr), ":", ic->ic_ps_sta);
+	}
+	/*
+	 * Clear AREF flag that marks the authorization refcnt bump
+	 * has happened.  This is probably not needed as the node
+	 * should always be removed from the table so not found but
+	 * do it just in case.
+	 */
+	ni->ni_flags &= ~IEEE80211_NODE_AREF;
+
+	/*
+	 * Drain power save queue and, if needed, clear TIM.
+	 */
+	IEEE80211_NODE_SAVEQ_DRAIN(ni, qlen);
+	if (qlen != 0 && ic->ic_set_tim != NULL)
+		ic->ic_set_tim(ni, 0);
+
+	ni->ni_associd = 0;
+	if (ni->ni_challenge != NULL) {
+		FREE(ni->ni_challenge, M_DEVBUF);
+		ni->ni_challenge = NULL;
+	}
+	/*
+	 * Preserve SSID, WPA, and WME ie's so the bss node is
+	 * reusable during a re-auth/re-assoc state transition.
+	 * If we remove these data they will not be recreated
+	 * because they come from a probe-response or beacon frame
+	 * which cannot be expected prior to the association-response.
+	 * This should not be an issue when operating in other modes
+	 * as stations leaving always go through a full state transition
+	 * which will rebuild this state.
+	 *
+	 * XXX does this leave us open to inheriting old state?
+	 */
+	for (i = 0; i < N(ni->ni_rxfrag); i++)
+		if (ni->ni_rxfrag[i] != NULL) {
+			m_freem(ni->ni_rxfrag[i]);
+			ni->ni_rxfrag[i] = NULL;
+		}
+	/*
+	 * Must be careful here to remove any key map entry w/o a LOR.
+	 */
+	ieee80211_node_delucastkey(ni);
+#undef N
 }
 
 static void
-ieee80211_node_copy(struct ieee80211com *ic,
-	struct ieee80211_node *dst, const struct ieee80211_node *src)
+node_free(struct ieee80211_node *ni)
 {
-	*dst = *src;
+	struct ieee80211com *ic = ni->ni_ic;
+
+	ic->ic_node_cleanup(ni);
+	if (ni->ni_wpa_ie != NULL)
+		FREE(ni->ni_wpa_ie, M_DEVBUF);
+	if (ni->ni_wme_ie != NULL)
+		FREE(ni->ni_wme_ie, M_DEVBUF);
+	IEEE80211_NODE_SAVEQ_DESTROY(ni);
+	FREE(ni, M_80211_NODE);
 }
 
-static uint8_t
-ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
+static u_int8_t
+node_getrssi(const struct ieee80211_node *ni)
 {
 	return ni->ni_rssi;
 }
 
 static void
-ieee80211_setup_node(struct ieee80211com *ic,
-	struct ieee80211_node *ni, uint8_t *macaddr)
+ieee80211_setup_node(struct ieee80211_node_table *nt,
+	struct ieee80211_node *ni, const u_int8_t *macaddr)
 {
-	lwkt_tokref ilock;
+	struct ieee80211com *ic = nt->nt_ic;
+	IEEE80211_LOCK_INFO;
 	int hash;
 
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %p<%6D> in %s table\n", __func__, ni,
+		(macaddr), ":", nt->nt_name);
+
 	IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
 	hash = IEEE80211_NODE_HASH(macaddr);
-	ni->ni_refcnt = 1;		/* mark referenced */
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
-	LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
-	/* 
-	 * Note we don't enable the inactive timer when acting
-	 * as a station.  Nodes created in this mode represent
-	 * AP's identified while scanning.  If we time them out
-	 * then several things happen: we can't return the data
-	 * to users to show the list of AP's we encountered, and
-	 * more importantly, we'll incorrectly deauthenticate
-	 * ourself because the inactivity timer will kick us off. 
-	 */
-	if (ic->ic_opmode != IEEE80211_M_STA)
-		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
-	lwkt_reltoken(&ilock);
+	ieee80211_node_initref(ni);		/* mark referenced */
+	ni->ni_chan = IEEE80211_CHAN_ANYC;
+	ni->ni_authmode = IEEE80211_AUTH_OPEN;
+	ni->ni_txpower = ic->ic_txpowlimit;	/* max power */
+	ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey, IEEE80211_KEYIX_NONE);
+	ni->ni_inact_reload = nt->nt_inact_init;
+	ni->ni_inact = ni->ni_inact_reload;
+	IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_INSERT_TAIL(&nt->nt_node, ni, ni_list);
+	LIST_INSERT_HEAD(&nt->nt_hash[hash], ni, ni_hash);
+	ni->ni_table = nt;
+	ni->ni_ic = ic;
+	IEEE80211_NODE_UNLOCK(nt);
 }
 
 struct ieee80211_node *
-ieee80211_alloc_node(struct ieee80211com *ic, uint8_t *macaddr)
+ieee80211_alloc_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
 {
-	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_node_alloc(nt);
 	if (ni != NULL)
-		ieee80211_setup_node(ic, ni, macaddr);
+		ieee80211_setup_node(nt, ni, macaddr);
 	else
 		ic->ic_stats.is_rx_nodealloc++;
 	return ni;
 }
 
+/*
+ * Craft a temporary node suitable for sending a management frame
+ * to the specified station.  We craft only as much state as we
+ * need to do the work since the node will be immediately reclaimed
+ * once the send completes.
+ */
+struct ieee80211_node *
+ieee80211_tmp_node(struct ieee80211com *ic, const u_int8_t *macaddr)
+{
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_node_alloc(&ic->ic_sta);
+	if (ni != NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+			"%s %p<%6D>\n", __func__, ni, macaddr, ":");
+
+		IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
+		IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
+		ieee80211_node_initref(ni);		/* mark referenced */
+		ni->ni_txpower = ic->ic_bss->ni_txpower;
+		/* NB: required by ieee80211_fix_rate */
+		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ieee80211_crypto_resetkey(ic, &ni->ni_ucastkey,
+			IEEE80211_KEYIX_NONE);
+		/* XXX optimize away */
+		IEEE80211_NODE_SAVEQ_INIT(ni, "unknown");
+
+		ni->ni_table = NULL;		/* NB: pedantic */
+		ni->ni_ic = ic;
+	} else {
+		/* XXX msg */
+		ic->ic_stats.is_rx_nodealloc++;
+	}
+	return ni;
+}
+
 struct ieee80211_node *
-ieee80211_dup_bss(struct ieee80211com *ic, uint8_t *macaddr)
+ieee80211_dup_bss(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
 {
-	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+
+	ni = ic->ic_node_alloc(nt);
 	if (ni != NULL) {
-		ieee80211_setup_node(ic, ni, macaddr);
+		ieee80211_setup_node(nt, ni, macaddr);
 		/*
 		 * Inherit from ic_bss.
 		 */
+		ni->ni_authmode = ic->ic_bss->ni_authmode;
+		ni->ni_txpower = ic->ic_bss->ni_txpower;
+		ni->ni_vlan = ic->ic_bss->ni_vlan;	/* XXX?? */
 		IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
-		ni->ni_chan = ic->ic_bss->ni_chan;
+		ieee80211_set_chan(ic, ni, ic->ic_bss->ni_chan);
+		ni->ni_rsn = ic->ic_bss->ni_rsn;
 	} else
 		ic->ic_stats.is_rx_nodealloc++;
 	return ni;
 }
 
 static struct ieee80211_node *
-_ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+_ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, const char *func, int line)
+#else
+_ieee80211_find_node(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr)
+#endif
 {
 	struct ieee80211_node *ni;
 	int hash;
 
+	IEEE80211_NODE_LOCK_ASSERT(nt);
+
 	hash = IEEE80211_NODE_HASH(macaddr);
-	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
+	LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
 		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
-			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
+			ieee80211_ref_node(ni);	/* mark referenced */
+#ifdef IEEE80211_DEBUG_REFCNT
+			IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+			    "%s (%s:%u) %p<%6D> refcnt %d\n", __func__,
+			    func, line,
+			    ni, (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+#endif
 			return ni;
 		}
 	}
 	return NULL;
 }
+#ifdef IEEE80211_DEBUG_REFCNT
+#define	_ieee80211_find_node(nt, mac) \
+	_ieee80211_find_node_debug(nt, mac, func, line)
+#endif
 
 struct ieee80211_node *
-ieee80211_find_node(struct ieee80211com *ic, uint8_t *macaddr)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, const char *func, int line)
+#else
+ieee80211_find_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+#endif
 {
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	ni = _ieee80211_find_node(ic, macaddr);
-	lwkt_reltoken(&ilock);
+	IEEE80211_NODE_LOCK(nt);
+	ni = _ieee80211_find_node(nt, macaddr);
+	IEEE80211_NODE_UNLOCK(nt);
 	return ni;
 }
 
 /*
- * Return a reference to the appropriate node for sending
- * a data frame.  This handles node discovery in adhoc networks.
+ * Fake up a node; this handles node discovery in adhoc mode.
+ * Note that for the driver's benefit we we treat this like
+ * an association so the driver has an opportunity to setup
+ * it's private state.
  */
 struct ieee80211_node *
-ieee80211_find_txnode(struct ieee80211com *ic, uint8_t *macaddr)
+ieee80211_fakeup_adhoc_node(struct ieee80211_node_table *nt,
+	const u_int8_t macaddr[IEEE80211_ADDR_LEN])
 {
+	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
-
-	/*
-	 * The destination address should be in the node table
-	 * unless we are operating in station mode or this is a
-	 * multicast/broadcast frame.
-	 */
-	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
-		return ic->ic_bss;
 
-	/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	ni = _ieee80211_find_node(ic, macaddr);
-	lwkt_reltoken(&ilock);
-	ni = _ieee80211_find_node(ic, macaddr);
-	if (ni == NULL &&
-	    (ic->ic_opmode == IEEE80211_M_IBSS ||
-	     ic->ic_opmode == IEEE80211_M_AHDEMO)) {
-		/*
-		 * Fake up a node; this handles node discovery in
-		 * adhoc mode.  Note that for the driver's benefit
-		 * we we treat this like an association so the driver
-		 * has an opportunity to setup it's private state.
-		 *
-		 * XXX need better way to handle this; issue probe
-		 *     request so we can deduce rate set, etc.
-		 */
-		ni = ieee80211_dup_bss(ic, macaddr);
-		if (ni != NULL) {
-			/* XXX no rate negotiation; just dup */
-			ni->ni_rates = ic->ic_bss->ni_rates;
-			if (ic->ic_newassoc)
-				(*ic->ic_newassoc)(ic, ni, 1);
-		}
+	ni = ieee80211_dup_bss(nt, macaddr);
+	if (ni != NULL) {
+		/* XXX no rate negotiation; just dup */
+		ni->ni_rates = ic->ic_bss->ni_rates;
+		if (ic->ic_newassoc != NULL)
+			ic->ic_newassoc(ni, 1);
+		/* XXX not right for 802.1x/WPA */
+		ieee80211_node_authorize(ni);
 	}
 	return ni;
 }
 
-/*
- * Like find but search based on the channel too.
- */
-struct ieee80211_node *
-ieee80211_lookup_node(struct ieee80211com *ic,
-	uint8_t *macaddr, struct ieee80211_channel *chan)
-{
-	struct ieee80211_node *ni;
-	int hash;
-	lwkt_tokref ilock;
-
-	hash = IEEE80211_NODE_HASH(macaddr);
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
-		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
-		    ni->ni_chan == chan) {
-			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
-			break;
+#ifdef IEEE80211_DEBUG
+static void
+dump_probe_beacon(u_int8_t subtype, int isnew,
+	const u_int8_t mac[IEEE80211_ADDR_LEN],
+	const struct ieee80211_scanparams *sp)
+{
+
+	printf("[%6D] %s%s on chan %u (bss chan %u) ",
+	    mac, ":", isnew ? "new " : "",
+	    ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT],
+	    sp->chan, sp->bchan);
+	ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]);
+	printf("\n");
+
+	if (isnew) {
+		printf("[%6D] caps 0x%x bintval %u erp 0x%x", 
+			mac, ":", sp->capinfo, sp->bintval, sp->erp);
+		if (sp->country != NULL) {
+#ifdef __FreeBSD__
+			printf(" country info %*D",
+				sp->country[1], sp->country+2, " ");
+#else
+			int i;
+			printf(" country info");
+			for (i = 0; i < sp->country[1]; i++)
+				printf(" %02x", sp->country[i+2]);
+#endif
 		}
+		printf("\n");
 	}
-	lwkt_reltoken(&ilock);
-	return ni;
 }
+#endif /* IEEE80211_DEBUG */
 
 static void
-_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+saveie(u_int8_t **iep, const u_int8_t *ie)
 {
-	KASSERT(ni != ic->ic_bss, ("freeing bss node"));
 
-	TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
-	LIST_REMOVE(ni, ni_hash);
-	if (TAILQ_EMPTY(&ic->ic_node))
-		ic->ic_inact_timer = 0;
-	(*ic->ic_node_free)(ic, ni);
+	if (ie == NULL)
+		*iep = NULL;
+	else
+		ieee80211_saveie(iep, ie);
 }
 
+/*
+ * Process a beacon or probe response frame.
+ */
 void
-ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
+ieee80211_add_scan(struct ieee80211com *ic,
+	const struct ieee80211_scanparams *sp,
+	const struct ieee80211_frame *wh,
+	int subtype, int rssi, int rstamp)
 {
-	lwkt_tokref ilock;
-
-	KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
+#define	ISPROBE(_st)	((_st) == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+	struct ieee80211_node_table *nt = &ic->ic_scan;
+	struct ieee80211_node *ni;
+	int newnode = 0;
 
-	/* XXX DF atomic op */
-	crit_enter();
-	--ni->ni_refcnt;
-	if (ni->ni_refcnt == 0) {
-		crit_exit();
-		lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-		_ieee80211_free_node(ic, ni);
-		lwkt_reltoken(&ilock);
-	} else {
-		crit_exit();
-	}
+	ni = ieee80211_find_node(nt, wh->i_addr2);
+	if (ni == NULL) {
+		/*
+		 * Create a new entry.
+		 */
+		ni = ic->ic_node_alloc(nt);
+		if (ni == NULL) {
+			ic->ic_stats.is_rx_nodealloc++;
+			return;
+		}
+		ieee80211_setup_node(nt, ni, wh->i_addr2);
+		/*
+		 * XXX inherit from ic_bss.
+		 */
+		ni->ni_authmode = ic->ic_bss->ni_authmode;
+		ni->ni_txpower = ic->ic_bss->ni_txpower;
+		ni->ni_vlan = ic->ic_bss->ni_vlan;	/* XXX?? */
+		ieee80211_set_chan(ic, ni, ic->ic_curchan);
+		ni->ni_rsn = ic->ic_bss->ni_rsn;
+		newnode = 1;
+	}
+#ifdef IEEE80211_DEBUG
+	if (ieee80211_msg_scan(ic) && (ic->ic_flags & IEEE80211_F_SCAN))
+		dump_probe_beacon(subtype, newnode, wh->i_addr2, sp);
+#endif
+	/* XXX ap beaconing multiple ssid w/ same bssid */
+	if (sp->ssid[1] != 0 &&
+	    (ISPROBE(subtype) || ni->ni_esslen == 0)) {
+		ni->ni_esslen = sp->ssid[1];
+		memset(ni->ni_essid, 0, sizeof(ni->ni_essid));
+		memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
+	}
+	ni->ni_scangen = ic->ic_scan.nt_scangen;
+	IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+	ni->ni_rssi = rssi;
+	ni->ni_rstamp = rstamp;
+	memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp));
+	ni->ni_intval = sp->bintval;
+	ni->ni_capinfo = sp->capinfo;
+	ni->ni_chan = &ic->ic_channels[sp->chan];
+	ni->ni_fhdwell = sp->fhdwell;
+	ni->ni_fhindex = sp->fhindex;
+	ni->ni_erp = sp->erp;
+	if (sp->tim != NULL) {
+		struct ieee80211_tim_ie *ie =
+		    (struct ieee80211_tim_ie *) sp->tim;
+
+		ni->ni_dtim_count = ie->tim_count;
+		ni->ni_dtim_period = ie->tim_period;
+	}
+	/*
+	 * Record the byte offset from the mac header to
+	 * the start of the TIM information element for
+	 * use by hardware and/or to speedup software
+	 * processing of beacon frames.
+	 */
+	ni->ni_timoff = sp->timoff;
+	/*
+	 * Record optional information elements that might be
+	 * used by applications or drivers.
+	 */
+	saveie(&ni->ni_wme_ie, sp->wme);
+	saveie(&ni->ni_wpa_ie, sp->wpa);
+
+	/* NB: must be after ni_chan is setup */
+	ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
+
+	if (!newnode)
+		ieee80211_free_node(ni);
+#undef ISPROBE
+}
+
+/*
+ * Do node discovery in adhoc mode on receipt of a beacon
+ * or probe response frame.  Note that for the driver's
+ * benefit we we treat this like an association so the
+ * driver has an opportunity to setup it's private state.
+ */
+struct ieee80211_node *
+ieee80211_add_neighbor(struct ieee80211com *ic,
+	const struct ieee80211_frame *wh,
+	const struct ieee80211_scanparams *sp)
+{
+	struct ieee80211_node *ni;
+
+	ni = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);/* XXX alloc_node? */
+	if (ni != NULL) {
+		ni->ni_esslen = sp->ssid[1];
+		memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]);
+		IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+		memcpy(ni->ni_tstamp.data, sp->tstamp, sizeof(ni->ni_tstamp));
+		ni->ni_intval = sp->bintval;
+		ni->ni_capinfo = sp->capinfo;
+		ni->ni_chan = ic->ic_bss->ni_chan;
+		ni->ni_fhdwell = sp->fhdwell;
+		ni->ni_fhindex = sp->fhindex;
+		ni->ni_erp = sp->erp;
+		ni->ni_timoff = sp->timoff;
+		if (sp->wme != NULL)
+			ieee80211_saveie(&ni->ni_wme_ie, sp->wme);
+		if (sp->wpa != NULL)
+			ieee80211_saveie(&ni->ni_wpa_ie, sp->wpa);
+
+		/* NB: must be after ni_chan is setup */
+		ieee80211_setup_rates(ni, sp->rates, sp->xrates, IEEE80211_F_DOSORT);
+
+		if (ic->ic_newassoc != NULL)
+			ic->ic_newassoc(ni, 1);
+		/* XXX not right for 802.1x/WPA */
+		ieee80211_node_authorize(ni);
+	}
+	return ni;
+}
+
+#define	IS_CTL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+#define	IS_PSPOLL(wh) \
+	((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL)
+/*
+ * Locate the node for sender, track state, and then pass the
+ * (referenced) node up to the 802.11 layer for its use.  We
+ * are required to pass some node so we fall back to ic_bss
+ * when this frame is from an unknown sender.  The 802.11 layer
+ * knows this means the sender wasn't in the node table and
+ * acts accordingly. 
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_rxnode_debug(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh, const char *func, int line)
+#else
+ieee80211_find_rxnode(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh)
+#endif
+{
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	IEEE80211_LOCK_INFO;
+
+	/* XXX may want scanned nodes in the neighbor table for adhoc */
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    (ic->ic_flags & IEEE80211_F_SCAN))
+		nt = &ic->ic_scan;
+	else
+		nt = &ic->ic_sta;
+	/* XXX check ic_bss first in station mode */
+	/* XXX 4-address frames? */
+	IEEE80211_NODE_LOCK(nt);
+	if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+		ni = _ieee80211_find_node(nt, wh->i_addr1);
+	else
+		ni = _ieee80211_find_node(nt, wh->i_addr2);
+	if (ni == NULL)
+		ni = ieee80211_ref_node(ic->ic_bss);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	return ni;
+}
+
+/*
+ * Like ieee80211_find_rxnode but use the supplied h/w
+ * key index as a hint to locate the node in the key
+ * mapping table.  If an entry is present at the key
+ * index we return it; otherwise do a normal lookup and
+ * update the mapping table if the station has a unicast
+ * key assigned to it.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_rxnode_withkey_debug(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh, ieee80211_keyix keyix,
+	const char *func, int line)
+#else
+ieee80211_find_rxnode_withkey(struct ieee80211com *ic,
+	const struct ieee80211_frame_min *wh, ieee80211_keyix keyix)
+#endif
+{
+	struct ieee80211_node_table *nt;
+	struct ieee80211_node *ni;
+	IEEE80211_LOCK_INFO;
+
+	if (ic->ic_opmode == IEEE80211_M_STA ||
+	    ic->ic_opmode == IEEE80211_M_MONITOR ||
+	    (ic->ic_flags & IEEE80211_F_SCAN))
+		nt = &ic->ic_scan;
+	else
+		nt = &ic->ic_sta;
+	IEEE80211_NODE_LOCK(nt);
+	if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax)
+		ni = nt->nt_keyixmap[keyix];
+	else
+		ni = NULL;
+	if (ni == NULL) {
+		if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/)
+			ni = _ieee80211_find_node(nt, wh->i_addr1);
+		else
+			ni = _ieee80211_find_node(nt, wh->i_addr2);
+		if (ni == NULL)
+			ni = ieee80211_ref_node(ic->ic_bss);
+		if (nt->nt_keyixmap != NULL) {
+			/*
+			 * If the station has a unicast key cache slot
+			 * assigned update the key->node mapping table.
+			 */
+			keyix = ni->ni_ucastkey.wk_rxkeyix;
+			/* XXX can keyixmap[keyix] != NULL? */
+			if (keyix < nt->nt_keyixmax &&
+			    nt->nt_keyixmap[keyix] == NULL) {
+				IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+				    "%s: add key map entry %p<%6D> refcnt %d\n",
+				    __func__, ni, ni->ni_macaddr,":",
+				    ieee80211_node_refcnt(ni)+1);
+				nt->nt_keyixmap[keyix] = ieee80211_ref_node(ni);
+			}
+		}
+	} else {
+		ieee80211_ref_node(ni);
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	return ni;
+}
+#undef IS_PSPOLL
+#undef IS_CTL
+
+/*
+ * Return a reference to the appropriate node for sending
+ * a data frame.  This handles node discovery in adhoc networks.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_txnode_debug(struct ieee80211com *ic, const u_int8_t *macaddr,
+	const char *func, int line)
+#else
+ieee80211_find_txnode(struct ieee80211com *ic, const u_int8_t *macaddr)
+#endif
+{
+	struct ieee80211_node_table *nt = &ic->ic_sta;
+	struct ieee80211_node *ni;
+	IEEE80211_LOCK_INFO;
+
+	/*
+	 * The destination address should be in the node table
+	 * unless this is a multicast/broadcast frame.  We can
+	 * also optimize station mode operation, all frames go
+	 * to the bss node.
+	 */
+	/* XXX can't hold lock across dup_bss 'cuz of recursive locking */
+	IEEE80211_NODE_LOCK(nt);
+	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr))
+		ni = ieee80211_ref_node(ic->ic_bss);
+	else
+		ni = _ieee80211_find_node(nt, macaddr);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	if (ni == NULL) {
+		if (ic->ic_opmode == IEEE80211_M_IBSS ||
+		    ic->ic_opmode == IEEE80211_M_AHDEMO) {
+			/*
+			 * In adhoc mode cons up a node for the destination.
+			 * Note that we need an additional reference for the
+			 * caller to be consistent with _ieee80211_find_node.
+			 */
+			ni = ieee80211_fakeup_adhoc_node(nt, macaddr);
+			if (ni != NULL)
+				(void) ieee80211_ref_node(ni);
+		} else {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+				"[%6D] no node, discard frame (%s)\n",
+				(macaddr), ":", __func__);
+			ic->ic_stats.is_tx_nonode++;
+		}
+	}
+	return ni;
+}
+
+/*
+ * Like find but search based on the channel too.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_with_channel_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, struct ieee80211_channel *chan,
+	const char *func, int line)
+#else
+ieee80211_find_node_with_channel(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, struct ieee80211_channel *chan)
+#endif
+{
+	struct ieee80211_node *ni;
+	int hash;
+	IEEE80211_LOCK_INFO;
+
+	hash = IEEE80211_NODE_HASH(macaddr);
+	IEEE80211_NODE_LOCK(nt);
+	LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+		    ni->ni_chan == chan) {
+			ieee80211_ref_node(ni);		/* mark referenced */
+			IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+			    "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+			    func, line,
+#else
+			    "%s %p<%6D> refcnt %d\n", __func__,
+#endif
+			    ni, (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+			break;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+	return ni;
+}
+
+/*
+ * Like find but search based on the ssid too.
+ */
+struct ieee80211_node *
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_find_node_with_ssid_debug(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid,
+	const char *func, int line)
+#else
+ieee80211_find_node_with_ssid(struct ieee80211_node_table *nt,
+	const u_int8_t *macaddr, u_int ssidlen, const u_int8_t *ssid)
+#endif
+{
+#define	MATCH_SSID(ni, ssid, ssidlen) \
+	(ni->ni_esslen == ssidlen && memcmp(ni->ni_essid, ssid, ssidlen) == 0)
+	static const u_int8_t zeromac[IEEE80211_ADDR_LEN];
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni;
+	int hash;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_NODE_LOCK(nt);
+	/*
+	 * A mac address that is all zero means match only the ssid;
+	 * otherwise we must match both.
+	 */
+	if (IEEE80211_ADDR_EQ(macaddr, zeromac)) {
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+			if (MATCH_SSID(ni, ssid, ssidlen))
+				break;
+		}
+	} else {
+		hash = IEEE80211_NODE_HASH(macaddr);
+		LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) {
+			if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) &&
+			    MATCH_SSID(ni, ssid, ssidlen))
+				break;
+		}
+	}
+	if (ni != NULL) {
+		ieee80211_ref_node(ni);	/* mark referenced */
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+#ifdef IEEE80211_DEBUG_REFCNT
+		    "%s (%s:%u) %p<%s> refcnt %d\n", __func__,
+		    func, line,
+#else
+		    "%s %p<%6D> refcnt %d\n", __func__,
+#endif
+		     ni, (ni->ni_macaddr), ":",
+		     ieee80211_node_refcnt(ni));
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+	return ni;
+#undef MATCH_SSID
+}
+
+static void
+_ieee80211_free_node(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_node_table *nt = ni->ni_table;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %p<%6D> in %s table\n", __func__, ni,
+		(ni->ni_macaddr), ":",
+		nt != NULL ? nt->nt_name : "<gone>");
+
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	if (nt != NULL) {
+		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+		LIST_REMOVE(ni, ni_hash);
+	}
+	ic->ic_node_free(ni);
 }
 
 void
-ieee80211_free_allnodes(struct ieee80211com *ic)
+#ifdef IEEE80211_DEBUG_REFCNT
+ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line)
+#else
+ieee80211_free_node(struct ieee80211_node *ni)
+#endif
+{
+	struct ieee80211_node_table *nt = ni->ni_table;
+	IEEE80211_LOCK_INFO;
+
+#ifdef IEEE80211_DEBUG_REFCNT
+	IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+		"%s (%s:%u) %p<%6D> refcnt %d\n", __func__, func, line, ni,
+		 (ni->ni_macaddr), ":", ieee80211_node_refcnt(ni)-1);
+#endif
+	if (nt != NULL) {
+		IEEE80211_NODE_LOCK(nt);
+		if (ieee80211_node_dectestref(ni)) {
+			/*
+			 * Last reference, reclaim state.
+			 */
+			_ieee80211_free_node(ni);
+		} else if (ieee80211_node_refcnt(ni) == 1 &&
+		    nt->nt_keyixmap != NULL) {
+			ieee80211_keyix keyix;
+			/*
+			 * Check for a last reference in the key mapping table.
+			 */
+			keyix = ni->ni_ucastkey.wk_rxkeyix;
+			if (keyix < nt->nt_keyixmax &&
+			    nt->nt_keyixmap[keyix] == ni) {
+				IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+				    "%s: %p<%6D> clear key map entry", __func__,
+				    ni, ni->ni_macaddr,":");
+				nt->nt_keyixmap[keyix] = NULL;
+				ieee80211_node_decref(ni); /* XXX needed? */
+				_ieee80211_free_node(ni);
+			}
+		}
+		IEEE80211_NODE_UNLOCK(nt);
+	} else {
+		if (ieee80211_node_dectestref(ni))
+			_ieee80211_free_node(ni);
+	}
+}
+
+/*
+ * Reclaim a unicast key and clear any key cache state.
+ */
+int
+ieee80211_node_delucastkey(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ieee80211_node_table *nt = &ic->ic_sta;
+	struct ieee80211_node *nikey;
+	ieee80211_keyix keyix;
+	int isowned = 1, status;
+	IEEE80211_LOCK_INFO;
+
+	/*
+	 * NB: We must beware of LOR here; deleting the key
+	 * can cause the crypto layer to block traffic updates
+	 * which can generate a LOR against the node table lock;
+	 * grab it here and stash the key index for our use below.
+	 *
+	 * Must also beware of recursion on the node table lock.
+	 * When called from node_cleanup we may already have
+	 * the node table lock held.  Unfortunately there's no
+	 * way to separate out this path so we must do this
+	 * conditionally.
+	 */
+#if 0
+	isowned = IEEE80211_NODE_IS_LOCKED(nt);
+	if (!isowned)
+		IEEE80211_NODE_LOCK(nt);
+#endif
+	keyix = ni->ni_ucastkey.wk_rxkeyix;
+	status = ieee80211_crypto_delkey(ic, &ni->ni_ucastkey);
+	if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax) {
+		nikey = nt->nt_keyixmap[keyix];
+		nt->nt_keyixmap[keyix] = NULL;;
+	} else
+		nikey = NULL;
+	if (!isowned)
+		IEEE80211_NODE_UNLOCK(&ic->ic_sta);
+
+	if (nikey != NULL) {
+		KASSERT(nikey == ni,
+			("key map out of sync, ni %p nikey %p", ni, nikey));
+		IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+			"%s: delete key map entry %p<%6D> refcnt %d\n",
+			__func__, ni, ni->ni_macaddr,":",
+			ieee80211_node_refcnt(ni)-1);
+		ieee80211_free_node(ni);
+	}
+	return status;
+}
+
+/*
+ * Reclaim a node.  If this is the last reference count then
+ * do the normal free work.  Otherwise remove it from the node
+ * table and mark it gone by clearing the back-reference.
+ */
+static void
+node_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+	ieee80211_keyix keyix;
+
+	IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+		"%s: remove %p<%6D> from %s table, refcnt %d\n",
+		__func__, ni, (ni->ni_macaddr), ":",
+		nt->nt_name, ieee80211_node_refcnt(ni)-1);
+	/*
+	 * Clear any entry in the unicast key mapping table.
+	 * We need to do it here so rx lookups don't find it
+	 * in the mapping table even if it's not in the hash
+	 * table.  We cannot depend on the mapping table entry
+	 * being cleared because the node may not be free'd.
+	 */
+	keyix = ni->ni_ucastkey.wk_rxkeyix;
+	if (nt->nt_keyixmap != NULL && keyix < nt->nt_keyixmax &&
+	    nt->nt_keyixmap[keyix] == ni) {
+		IEEE80211_DPRINTF(ni->ni_ic, IEEE80211_MSG_NODE,
+			"%s: %p<%6D> clear key map entry\n",
+			__func__, ni, (ni->ni_macaddr), ":");
+		nt->nt_keyixmap[keyix] = NULL;
+		ieee80211_node_decref(ni);	/* NB: don't need free */
+	}
+	if (!ieee80211_node_dectestref(ni)) {
+		/*
+		 * Other references are present, just remove the
+		 * node from the table so it cannot be found.  When
+		 * the references are dropped storage will be
+		 * reclaimed.
+		 */
+		TAILQ_REMOVE(&nt->nt_node, ni, ni_list);
+		LIST_REMOVE(ni, ni_hash);
+		ni->ni_table = NULL;		/* clear reference */
+	} else
+		_ieee80211_free_node(ni);
+}
+
+static void
+ieee80211_free_allnodes_locked(struct ieee80211_node_table *nt)
 {
+	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
-	lwkt_tokref ilock;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
-		_ieee80211_free_node(ic, ni);  
-	lwkt_reltoken(&ilock);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s: free all nodes in %s table\n", __func__, nt->nt_name);
+
+	while ((ni = TAILQ_FIRST(&nt->nt_node)) != NULL) {
+		if (ni->ni_associd != 0) {
+			if (ic->ic_auth->ia_node_leave != NULL)
+				ic->ic_auth->ia_node_leave(ic, ni);
+			IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+		}
+		node_reclaim(nt, ni);
+	}
+	ieee80211_reset_erp(ic);
+}
+
+static void
+ieee80211_free_allnodes(struct ieee80211_node_table *nt)
+{
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_NODE_LOCK(nt);
+	ieee80211_free_allnodes_locked(nt);
+	IEEE80211_NODE_UNLOCK(nt);
+}
+
+/*
+ * Timeout entries in the scan cache.
+ */
+static void
+ieee80211_timeout_scan_candidates(struct ieee80211_node_table *nt)
+{
+	struct ieee80211com *ic = nt->nt_ic;
+	struct ieee80211_node *ni, *tni;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_NODE_LOCK(nt);
+	ni = ic->ic_bss;
+	/* XXX belongs elsewhere */
+	if (ni->ni_rxfrag[0] != NULL && ticks > ni->ni_rxfragstamp + hz) {
+		m_freem(ni->ni_rxfrag[0]);
+		ni->ni_rxfrag[0] = NULL;
+	}
+#ifndef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar)			\
+		for ((var) = TAILQ_FIRST((head));			\
+		(var) && ((tvar) = TAILQ_NEXT((var), field), 1);	\
+		(var) = (tvar))
+#endif
+
+	TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, tni) {
+		if (ni->ni_inact && --ni->ni_inact == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+			    "[%6D] scan candidate purged from cache "
+			    "(refcnt %u)\n", (ni->ni_macaddr), ":",
+			    ieee80211_node_refcnt(ni));
+			node_reclaim(nt, ni);
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
 }
 
 /*
- * Timeout inactive nodes.  Note that we cannot hold the node
- * lock while sending a frame as this would lead to a LOR.
- * Instead we use a generation number to mark nodes that we've
- * scanned and drop the lock and restart a scan if we have to
- * time out a node.  Since we are single-threaded by virtue of
+ * Timeout inactive stations and do related housekeeping.
+ * Note that we cannot hold the node lock while sending a
+ * frame as this would lead to a LOR.  Instead we use a
+ * generation number to mark nodes that we've scanned and
+ * drop the lock and restart a scan if we have to time out
+ * a node.  Since we are single-threaded by virtue of
  * controlling the inactivity timer we can be sure this will
  * process each node only once.
  */
-void
-ieee80211_timeout_nodes(struct ieee80211com *ic)
+static void
+ieee80211_timeout_stations(struct ieee80211_node_table *nt)
 {
+	struct ieee80211com *ic = nt->nt_ic;
 	struct ieee80211_node *ni;
-	lwkt_tokref ilock;
-	u_int gen = ic->ic_scangen++;		/* NB: ok 'cuz single-threaded*/
-
+	u_int gen;
+	int isadhoc;
+	IEEE80211_LOCK_INFO;
+	IEEE80211_SCANLOCK_INFO;
+
+	isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS ||
+		   ic->ic_opmode == IEEE80211_M_AHDEMO);
+	IEEE80211_SCAN_LOCK(nt);
+	gen = nt->nt_scangen++;
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s: %s scangen %u\n", __func__, nt->nt_name, gen);
 restart:
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
 		if (ni->ni_scangen == gen)	/* previously handled */
 			continue;
 		ni->ni_scangen = gen;
-		if (++ni->ni_inact > IEEE80211_INACT_MAX) {
-			IEEE80211_DPRINTF(("station %6D timed out "
-			    "due to inactivity (%u secs)\n",
-			    ni->ni_macaddr, ":", ni->ni_inact));
+		/*
+		 * Ignore entries for which have yet to receive an
+		 * authentication frame.  These are transient and
+		 * will be reclaimed when the last reference to them
+		 * goes away (when frame xmits complete).
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		    (ni->ni_flags & IEEE80211_NODE_AREF) == 0)
+			continue;
+		/*
+		 * Free fragment if not needed anymore
+		 * (last fragment older than 1s).
+		 * XXX doesn't belong here
+		 */
+		if (ni->ni_rxfrag[0] != NULL &&
+		    ticks > ni->ni_rxfragstamp + hz) {
+			m_freem(ni->ni_rxfrag[0]);
+			ni->ni_rxfrag[0] = NULL;
+		}
+		/*
+		 * Special case ourself; we may be idle for extended periods
+		 * of time and regardless reclaiming our state is wrong.
+		 */
+		if (ni == ic->ic_bss)
+			continue;
+		ni->ni_inact--;
+		if (ni->ni_associd != 0 || isadhoc) {
 			/*
-			 * Send a deauthenticate frame.
+			 * Age frames on the power save queue. The
+			 * aging interval is 4 times the listen
+			 * interval specified by the station.  This
+			 * number is factored into the age calculations
+			 * when the frame is placed on the queue.  We
+			 * store ages as time differences we can check
+			 * and/or adjust only the head of the list.
+			 */
+			if (IEEE80211_NODE_SAVEQ_QLEN(ni) != 0) {
+				struct mbuf *m;
+				int discard = 0;
+
+				IEEE80211_NODE_SAVEQ_LOCK(ni);
+				while (IF_POLL(&ni->ni_savedq, m) != NULL &&
+				     M_AGE_GET(m) < IEEE80211_INACT_WAIT) {
+IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER, "[%6D] discard frame, age %u\n", (ni->ni_macaddr), ":", M_AGE_GET(m));/*XXX*/
+					_IEEE80211_NODE_SAVEQ_DEQUEUE_HEAD(ni, m);
+					m_freem(m);
+					discard++;
+				}
+				if (m != NULL)
+					M_AGE_SUB(m, IEEE80211_INACT_WAIT);
+				IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+				if (discard != 0) {
+					IEEE80211_DPRINTF(ic,
+					    IEEE80211_MSG_POWER,
+					    "[%6D] discard %u frames for age\n",
+					    (ni->ni_macaddr), ":",
+					    discard);
+					IEEE80211_NODE_STAT_ADD(ni,
+						ps_discard, discard);
+					if (IEEE80211_NODE_SAVEQ_QLEN(ni) == 0)
+						ic->ic_set_tim(ni, 0);
+			}
+			/*
+			 * Probe the station before time it out.  We
+			 * send a null data frame which may not be
+			 * universally supported by drivers (need it
+			 * for ps-poll support so it should be...).
+			 */
+			if (0 < ni->ni_inact &&
+			    ni->ni_inact <= ic->ic_inact_probe) {
+				ieee80211_ref_node(ni);
+				IEEE80211_NODE_UNLOCK(nt);
+				ieee80211_send_nulldata(ni);
+				/* XXX stat? */
+				goto restart;
+			}
+		}
+		if (ni->ni_inact <= 0) {
+			/*
+			 * Send a deauthenticate frame and drop the station.
+			 * This is somewhat complicated due to reference counts
+			 * and locking.  At this point a station will typically
+			 * have a reference count of 1.  ieee80211_node_leave
+			 * will do a "free" of the node which will drop the
+			 * reference count.  But in the meantime a reference
+			 * wil be held by the deauth frame.  The actual reclaim
+			 * of the node will happen either after the tx is
+			 * completed or by ieee80211_node_leave.
 			 *
-			 * Drop the node lock before sending the
-			 * deauthentication frame in case the driver takes     
-			 * a lock, as this will result in a LOR between the     
-			 * node lock and the driver lock.
+			 * Separately we must drop the node lock before sending
+			 * in case the driver takes a lock, as this will result
+			 * in  LOR between the node lock and the driver lock.
 			 */
-			lwkt_reltoken(&ilock);
-			IEEE80211_SEND_MGMT(ic, ni,
-			    IEEE80211_FC0_SUBTYPE_DEAUTH,
-			    IEEE80211_REASON_AUTH_EXPIRE);
-			ieee80211_free_node(ic, ni);
+			IEEE80211_NODE_UNLOCK(nt);
+			if (ni->ni_associd != 0) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_AUTH_EXPIRE);
+			}
+			ieee80211_node_leave(ic, ni);
 			ic->ic_stats.is_node_timeout++;
 			goto restart;
 		}
 	}
-	if (!TAILQ_EMPTY(&ic->ic_node))
-		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
-	lwkt_reltoken(&ilock);
+	IEEE80211_NODE_UNLOCK(nt);
+
+	IEEE80211_SCAN_UNLOCK(nt);
+
+	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
+}
+
+void
+ieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg)
+{
+	struct ieee80211_node *ni;
+	u_int gen;
+	IEEE80211_LOCK_INFO;
+	IEEE80211_SCANLOCK_INFO;
+
+	IEEE80211_SCAN_LOCK(nt);
+	gen = nt->nt_scangen++;
+restart:
+	IEEE80211_NODE_LOCK(nt);
+	TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+		if (ni->ni_scangen != gen) {
+			ni->ni_scangen = gen;
+			(void) ieee80211_ref_node(ni);
+			IEEE80211_NODE_UNLOCK(nt);
+			(*f)(arg, ni);
+			ieee80211_free_node(ni);
+			goto restart;
+		}
+	}
+	IEEE80211_NODE_UNLOCK(nt);
+
+	IEEE80211_SCAN_UNLOCK(nt);
+}
+
+void
+ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+{
+	printf("0x%p: mac %6D refcnt %d\n", ni,
+		(ni->ni_macaddr), ":", ieee80211_node_refcnt(ni));
+	printf("\tscangen %u authmode %u flags 0x%x\n",
+		ni->ni_scangen, ni->ni_authmode, ni->ni_flags);
+	printf("\tassocid 0x%x txpower %u vlan %u\n",
+		ni->ni_associd, ni->ni_txpower, ni->ni_vlan);
+	printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n",
+		ni->ni_txseqs[0],
+		ni->ni_rxseqs[0] >> IEEE80211_SEQ_SEQ_SHIFT,
+		ni->ni_rxseqs[0] & IEEE80211_SEQ_FRAG_MASK,
+		ni->ni_rxfragstamp);
+	printf("\trstamp %u rssi %u intval %u capinfo 0x%x\n",
+		ni->ni_rstamp, ni->ni_rssi, ni->ni_intval, ni->ni_capinfo);
+	printf("\tbssid %6D essid \"%.*s\" channel %u:0x%x\n",
+		(ni->ni_bssid), ":",
+		ni->ni_esslen, ni->ni_essid,
+		ni->ni_chan->ic_freq, ni->ni_chan->ic_flags);
+	printf("\tfails %u inact %u txrate %u\n",
+		ni->ni_fails, ni->ni_inact, ni->ni_txrate);
+}
+
+void
+ieee80211_dump_nodes(struct ieee80211_node_table *nt)
+{
+	ieee80211_iterate_nodes(nt,
+		(ieee80211_iter_func *) ieee80211_dump_node, nt);
+}
+
+/*
+ * Handle a station joining an 11g network.
+ */
+static void
+ieee80211_node_join_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	/*
+	 * Station isn't capable of short slot time.  Bump
+	 * the count of long slot time stations and disable
+	 * use of short slot time.  Note that the actual switch
+	 * over to long slot time use may not occur until the
+	 * next beacon transmission (per sec. 7.3.1.4 of 11g).
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		ic->ic_longslotsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] station needs long slot time, count %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_longslotsta);
+		/* XXX vap's w/ conflicting needs won't work */
+		ieee80211_set_shortslottime(ic, 0);
+	}
+	/*
+	 * If the new station is not an ERP station
+	 * then bump the counter and enable protection
+	 * if configured.
+	 */
+	if (!ieee80211_iserp_rateset(ic, &ni->ni_rates)) {
+		ic->ic_nonerpsta++;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] station is !ERP, %d non-ERP stations associated\n",
+		    (ni->ni_macaddr), ":", ic->ic_nonerpsta);
+		/*
+		 * If protection is configured, enable it.
+		 */
+		if (ic->ic_protmode != IEEE80211_PROT_NONE) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "%s: enable use of protection\n", __func__);
+			ic->ic_flags |= IEEE80211_F_USEPROT;
+		}
+		/*
+		 * If station does not support short preamble
+		 * then we must enable use of Barker preamble.
+		 */
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			    "[%6D] station needs long preamble\n",
+			    (ni->ni_macaddr), ":");
+			ic->ic_flags |= IEEE80211_F_USEBARKER;
+			ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		}
+	} else
+		ni->ni_flags |= IEEE80211_NODE_ERP;
+}
+
+void
+ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, int resp)
+{
+	int newassoc;
+
+	if (ni->ni_associd == 0) {
+		u_int16_t aid;
+
+		/*
+		 * It would be good to search the bitmap
+		 * more efficiently, but this will do for now.
+		 */
+		for (aid = 1; aid < ic->ic_max_aid; aid++) {
+			if (!IEEE80211_AID_ISSET(aid,
+			    ic->ic_aid_bitmap))
+				break;
+		}
+		if (aid >= ic->ic_max_aid) {
+			IEEE80211_SEND_MGMT(ic, ni, resp,
+			    IEEE80211_REASON_ASSOC_TOOMANY);
+			ieee80211_node_leave(ic, ni);
+			return;
+		}
+		ni->ni_associd = aid | 0xc000;
+		IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap);
+		ic->ic_sta_assoc++;
+		newassoc = 1;
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			ieee80211_node_join_11g(ic, ni);
+	} else
+		newassoc = 0;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+	    "[%6D] station %sassociated at aid %d: %s preamble, %s slot time%s%s\n",
+	    (ni->ni_macaddr), ":", newassoc ? "" : "re",
+	    IEEE80211_NODE_AID(ni),
+	    ic->ic_flags & IEEE80211_F_SHPREAMBLE ? "short" : "long",
+	    ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long",
+	    ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "",
+	    ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : ""
+	);
+
+	/* give driver a chance to setup state like ni_txrate */
+	if (ic->ic_newassoc != NULL)
+		ic->ic_newassoc(ni, newassoc);
+	ni->ni_inact_reload = ic->ic_inact_auth;
+	ni->ni_inact = ni->ni_inact_reload;
+	IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS);
+	/* tell the authenticator about new station */
+	if (ic->ic_auth->ia_node_join != NULL)
+		ic->ic_auth->ia_node_join(ic, ni);
+	ieee80211_notify_node_join(ic, ni, newassoc);
+}
+
+/*
+ * Handle a station leaving an 11g network.
+ */
+static void
+ieee80211_node_leave_11g(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+
+	KASSERT(ic->ic_curmode == IEEE80211_MODE_11G,
+	     ("not in 11g, bss %u:0x%x, curmode %u", ni->ni_chan->ic_freq,
+	      ni->ni_chan->ic_flags, ic->ic_curmode));
+
+	/*
+	 * If a long slot station do the slot time bookkeeping.
+	 */
+	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) == 0) {
+		KASSERT(ic->ic_longslotsta > 0,
+		    ("bogus long slot station count %d", ic->ic_longslotsta));
+		ic->ic_longslotsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] long slot time station leaves, count now %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_longslotsta);
+		if (ic->ic_longslotsta == 0) {
+			/*
+			 * Re-enable use of short slot time if supported
+			 * and not operating in IBSS mode (per spec).
+			 */
+			if ((ic->ic_caps & IEEE80211_C_SHSLOT) &&
+			    ic->ic_opmode != IEEE80211_M_IBSS) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "%s: re-enable use of short slot time\n",
+				    __func__);
+				ieee80211_set_shortslottime(ic, 1);
+			}
+		}
+	}
+	/*
+	 * If a non-ERP station do the protection-related bookkeeping.
+	 */
+	if ((ni->ni_flags & IEEE80211_NODE_ERP) == 0) {
+		KASSERT(ic->ic_nonerpsta > 0,
+		    ("bogus non-ERP station count %d", ic->ic_nonerpsta));
+		ic->ic_nonerpsta--;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+		    "[%6D] non-ERP station leaves, count now %d\n",
+		    (ni->ni_macaddr), ":", ic->ic_nonerpsta);
+		if (ic->ic_nonerpsta == 0) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				"%s: disable use of protection\n", __func__);
+			ic->ic_flags &= ~IEEE80211_F_USEPROT;
+			/* XXX verify mode? */
+			if (ic->ic_caps & IEEE80211_C_SHPREAMBLE) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+				    "%s: re-enable use of short preamble\n",
+				    __func__);
+				ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+				ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+			}
+		}
+	}
 }
 
+/*
+ * Handle bookkeeping for station deauthentication/disassociation
+ * when operating as an ap.
+ */
 void
-ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
+ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni)
 {
+	struct ieee80211_node_table *nt = ni->ni_table;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG,
+	    "[%6D] station with aid %d leaves\n",
+	    (ni->ni_macaddr), ":", IEEE80211_NODE_AID(ni));
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		ic->ic_opmode == IEEE80211_M_IBSS ||
+		ic->ic_opmode == IEEE80211_M_AHDEMO,
+		("unexpected operating mode %u", ic->ic_opmode));
+	/*
+	 * If node wasn't previously associated all
+	 * we need to do is reclaim the reference.
+	 */
+	/* XXX ibss mode bypasses 11g and notification */
+	if (ni->ni_associd == 0)
+		goto done;
+	/*
+	 * Tell the authenticator the station is leaving.
+	 * Note that we must do this before yanking the
+	 * association id as the authenticator uses the
+	 * associd to locate it's state block.
+	 */
+	if (ic->ic_auth->ia_node_leave != NULL)
+		ic->ic_auth->ia_node_leave(ic, ni);
+	IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap);
+	ni->ni_associd = 0;
+	ic->ic_sta_assoc--;
+
+	if (ic->ic_curmode == IEEE80211_MODE_11G)
+		ieee80211_node_leave_11g(ic, ni);
+	/*
+	 * Cleanup station state.  In particular clear various
+	 * state that might otherwise be reused if the node
+	 * is reused before the reference count goes to zero
+	 * (and memory is reclaimed).
+	 */
+	ieee80211_sta_leave(ic, ni);
+done:
+	/*
+	 * Remove the node from any table it's recorded in and
+	 * drop the caller's reference.  Removal from the table
+	 * is important to insure the node is not reprocessed
+	 * for inactivity.
+	 */
+	if (nt != NULL) {
+		IEEE80211_NODE_LOCK(nt);
+		node_reclaim(nt, ni);
+		IEEE80211_NODE_UNLOCK(nt);
+	} else
+		ieee80211_free_node(ni);
+}
+
+u_int8_t
+ieee80211_getrssi(struct ieee80211com *ic)
+{
+#define	NZ(x)	((x) == 0 ? 1 : (x))
+	struct ieee80211_node_table *nt = &ic->ic_sta;
+	u_int32_t rssi_samples, rssi_total;
 	struct ieee80211_node *ni;
-	struct lwkt_tokref ilock;
 
-	lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
-		(*f)(arg, ni);
-	lwkt_reltoken(&ilock);
+	rssi_total = 0;
+	rssi_samples = 0;
+	switch (ic->ic_opmode) {
+	case IEEE80211_M_IBSS:		/* average of all ibss neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
+			if (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ni);
+			}
+		break;
+	case IEEE80211_M_AHDEMO:	/* average of all neighbors */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list) {
+			rssi_samples++;
+			rssi_total += ic->ic_node_getrssi(ni);
+		}
+		break;
+	case IEEE80211_M_HOSTAP:	/* average of all associated stations */
+		/* XXX locking */
+		TAILQ_FOREACH(ni, &nt->nt_node, ni_list)
+			if (IEEE80211_AID(ni->ni_associd) != 0) {
+				rssi_samples++;
+				rssi_total += ic->ic_node_getrssi(ni);
+			}
+		break;
+	case IEEE80211_M_MONITOR:	/* XXX */
+	case IEEE80211_M_STA:		/* use stats from associated ap */
+	default:
+		if (ic->ic_bss != NULL)
+			rssi_total = ic->ic_node_getrssi(ic->ic_bss);
+		rssi_samples = 1;
+		break;
+	}
+	return rssi_total / NZ(rssi_samples);
+#undef NZ
+}
+
+/*
+ * Indicate whether there are frames queued for a station in power-save mode.
+ */
+static void
+ieee80211_set_tim(struct ieee80211_node *ni, int set)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	u_int16_t aid;
+	IEEE80211_LOCK_INFO;
+
+	KASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP ||
+		ic->ic_opmode == IEEE80211_M_IBSS,
+		("operating mode %u", ic->ic_opmode));
+
+	aid = IEEE80211_AID(ni->ni_associd);
+	KASSERT(aid < ic->ic_max_aid,
+		("bogus aid %u, max %u", aid, ic->ic_max_aid));
+
+	IEEE80211_BEACON_LOCK(ic);
+	if (set != (isset(ic->ic_tim_bitmap, aid) != 0)) {
+		if (set) {
+			setbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending++;
+		} else {
+			clrbit(ic->ic_tim_bitmap, aid);
+			ic->ic_ps_pending--;
+		}
+		ic->ic_flags |= IEEE80211_F_TIMUPDATE;
+	}
+	IEEE80211_BEACON_UNLOCK(ic);
+}
+
+/*
+ * Node table support.
+ */
+
+static void
+ieee80211_node_table_init(struct ieee80211com *ic,
+	struct ieee80211_node_table *nt,
+	const char *name, int inact, int keyixmax,
+	void (*timeout)(struct ieee80211_node_table *))
+{
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"%s %s table, inact %u\n", __func__, name, inact);
+
+	nt->nt_ic = ic;
+	/* XXX need unit */
+	IEEE80211_NODE_LOCK_INIT(nt, ic->ic_ifp->if_xname);
+	IEEE80211_SCAN_LOCK_INIT(nt, ic->ic_ifp->if_xname);
+	TAILQ_INIT(&nt->nt_node);
+	nt->nt_name = name;
+	nt->nt_scangen = 1;
+	nt->nt_inact_init = inact;
+	nt->nt_timeout = timeout;
+	nt->nt_keyixmax = keyixmax;
+	if (nt->nt_keyixmax > 0) {
+		MALLOC(nt->nt_keyixmap, struct ieee80211_node **,
+			keyixmax * sizeof(struct ieee80211_node *),
+			M_80211_NODE, M_NOWAIT | M_ZERO);
+		if (nt->nt_keyixmap == NULL)
+			if_printf(ic->ic_ifp,
+			    "Cannot allocate key index map with %u entries\n",
+			    keyixmax);
+	} else
+		nt->nt_keyixmap = NULL;
+}
+
+void
+ieee80211_node_table_reset(struct ieee80211_node_table *nt)
+{
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+		"%s %s table\n", __func__, nt->nt_name);
+
+	IEEE80211_NODE_LOCK(nt);
+	nt->nt_inact_timer = 0;
+	ieee80211_free_allnodes_locked(nt);
+	IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+ieee80211_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_DPRINTF(nt->nt_ic, IEEE80211_MSG_NODE,
+		"%s %s table\n", __func__, nt->nt_name);
+
+	IEEE80211_NODE_LOCK(nt);
+	ieee80211_free_allnodes_locked(nt);
+	if (nt->nt_keyixmap != NULL) {
+		/* XXX verify all entries are NULL */
+		int i;
+		for (i = 0; i < nt->nt_keyixmax; i++)
+			if (nt->nt_keyixmap[i] != NULL)
+				printf("%s: %s[%u] still active\n", __func__,
+					nt->nt_name, i);
+		FREE(nt->nt_keyixmap, M_80211_NODE);
+		nt->nt_keyixmap = NULL;
+	}
+	IEEE80211_SCAN_LOCK_DESTROY(nt);
+	IEEE80211_NODE_LOCK_DESTROY(nt);
 }
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_node.h src/sys/netproto/802_11/ieee80211_node.h
--- src.preview/sys/netproto/802_11/ieee80211_node.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_node.h	2005-09-23 11:46:17.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,31 +29,57 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_node.h,v 1.10 2004/04/05 22:10:26 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_node.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
+#ifndef _NET80211_IEEE80211_NODE_H_
+#define _NET80211_IEEE80211_NODE_H_
 
-#ifndef _NETPROTO_802_11_IEEE80211_NODE_H_
-#define	_NETPROTO_802_11_IEEE80211_NODE_H_
+#include <netproto/802_11/ieee80211_ioctl.h>		/* for ieee80211_nodestats */
 
-#define	IEEE80211_PSCAN_WAIT 	5		/* passive scan wait */
-#define	IEEE80211_TRANS_WAIT 	5		/* transition wait */
-#define	IEEE80211_INACT_WAIT	5		/* inactivity timer interval */
-#define	IEEE80211_INACT_MAX	(300/IEEE80211_INACT_WAIT)
+/*
+ * Each ieee80211com instance has a single timer that fires once a
+ * second.  This is used to initiate various work depending on the
+ * state of the instance: scanning (passive or active), ``transition''
+ * (waiting for a response to a management frame when operating
+ * as a station), and node inactivity processing (when operating
+ * as an AP).  For inactivity processing each node has a timeout
+ * set in it's ni_inact field that is decremented on each timeout
+ * and the node is reclaimed when the counter goes to zero.  We
+ * use different inactivity timeout values depending on whether
+ * the node is associated and authorized (either by 802.1x or
+ * open/shared key authentication) or associated but yet to be
+ * authorized.  The latter timeout is shorter to more aggressively
+ * reclaim nodes that leave part way through the 802.1x exchange.
+ */
+#define	IEEE80211_INACT_WAIT	15		/* inactivity interval (secs) */
+#define	IEEE80211_INACT_INIT	(30/IEEE80211_INACT_WAIT)	/* initial */
+#define	IEEE80211_INACT_AUTH	(180/IEEE80211_INACT_WAIT)	/* associated but not authorized */
+#define	IEEE80211_INACT_RUN	(300/IEEE80211_INACT_WAIT)	/* authorized */
+#define	IEEE80211_INACT_PROBE	(30/IEEE80211_INACT_WAIT)	/* probe */
+#define	IEEE80211_INACT_SCAN	(300/IEEE80211_INACT_WAIT)	/* scanned */
+
+#define	IEEE80211_TRANS_WAIT 	5		/* mgt frame tx timer (secs) */
 
 #define	IEEE80211_NODE_HASHSIZE	32
 /* simple hash is enough for variation of macaddr */
 #define	IEEE80211_NODE_HASH(addr)	\
-	(((uint8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE)
-
-#define	IEEE80211_RATE_SIZE	8		/* 802.11 standard */
-#define	IEEE80211_RATE_MAXSIZE	15		/* max rates we'll handle */
+	(((const u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % \
+		IEEE80211_NODE_HASHSIZE)
 
-struct ieee80211_rateset {
-	uint8_t			rs_nrates;
-	uint8_t			rs_rates[IEEE80211_RATE_MAXSIZE];
+struct ieee80211_rsnparms {
+	u_int8_t	rsn_mcastcipher;	/* mcast/group cipher */
+	u_int8_t	rsn_mcastkeylen;	/* mcast key length */
+	u_int8_t	rsn_ucastcipherset;	/* unicast cipher set */
+	u_int8_t	rsn_ucastcipher;	/* selected unicast cipher */
+	u_int8_t	rsn_ucastkeylen;	/* unicast key length */
+	u_int8_t	rsn_keymgmtset;		/* key mangement algorithms */
+	u_int8_t	rsn_keymgmt;		/* selected key mgmt algo */
+	u_int16_t	rsn_caps;		/* capabilities */
 };
 
+struct ieee80211_node_table;
+struct ieee80211com;
+
 /*
  * Node specific information.  Note that drivers are expected
  * to derive from this structure to add device-specific per-node
@@ -61,96 +87,244 @@
  * the ieee80211com structure.
  */
 struct ieee80211_node {
+	struct ieee80211com	*ni_ic;
+	struct ieee80211_node_table *ni_table;
 	TAILQ_ENTRY(ieee80211_node)	ni_list;
 	LIST_ENTRY(ieee80211_node)	ni_hash;
 	u_int			ni_refcnt;
 	u_int			ni_scangen;	/* gen# for timeout scan */
+	u_int8_t		ni_authmode;	/* authentication algorithm */
+	u_int16_t		ni_flags;	/* special-purpose state */
+#define	IEEE80211_NODE_AUTH	0x0001		/* authorized for data */
+#define	IEEE80211_NODE_QOS	0x0002		/* QoS enabled */
+#define	IEEE80211_NODE_ERP	0x0004		/* ERP enabled */
+/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
+#define	IEEE80211_NODE_PWR_MGT	0x0010		/* power save mode enabled */
+#define	IEEE80211_NODE_AREF	0x0020		/* authentication ref held */
+	u_int16_t		ni_associd;	/* assoc response */
+	u_int16_t		ni_txpower;	/* current transmit power */
+	u_int16_t		ni_vlan;	/* vlan tag */
+	u_int32_t		*ni_challenge;	/* shared-key challenge */
+	u_int8_t		*ni_wpa_ie;	/* captured WPA/RSN ie */
+	u_int8_t		*ni_wme_ie;	/* captured WME ie */
+	u_int16_t		ni_txseqs[17];	/* tx seq per-tid */
+	u_int16_t		ni_rxseqs[17];	/* rx seq previous per-tid*/
+	u_int32_t		ni_rxfragstamp;	/* time stamp of last rx frag */
+	struct mbuf		*ni_rxfrag[3];	/* rx frag reassembly */
+	struct ieee80211_rsnparms ni_rsn;	/* RSN/WPA parameters */
+	struct ieee80211_key	ni_ucastkey;	/* unicast key */
 
 	/* hardware */
-	uint32_t		ni_rstamp;	/* recv timestamp */
-	uint8_t			ni_rssi;	/* recv ssi */
+	u_int32_t		ni_rstamp;	/* recv timestamp */
+	u_int8_t		ni_rssi;	/* recv ssi */
 
 	/* header */
-	uint8_t			ni_macaddr[IEEE80211_ADDR_LEN];
-	uint8_t			ni_bssid[IEEE80211_ADDR_LEN];
+	u_int8_t		ni_macaddr[IEEE80211_ADDR_LEN];
+	u_int8_t		ni_bssid[IEEE80211_ADDR_LEN];
 
 	/* beacon, probe response */
-	uint8_t			ni_tstamp[8];	/* from last rcv'd beacon */
-	uint16_t		ni_intval;	/* beacon interval */
-	uint16_t		ni_capinfo;	/* capabilities */
-	uint8_t			ni_esslen;
-	uint8_t			ni_essid[IEEE80211_NWID_LEN];
+	union {
+		u_int8_t	data[8];
+		u_int64_t	tsf;
+	} ni_tstamp;				/* from last rcv'd beacon */
+	u_int16_t		ni_intval;	/* beacon interval */
+	u_int16_t		ni_capinfo;	/* capabilities */
+	u_int8_t		ni_esslen;
+	u_int8_t		ni_essid[IEEE80211_NWID_LEN];
 	struct ieee80211_rateset ni_rates;	/* negotiated rate set */
-	uint8_t			*ni_country;	/* country information XXX */
-	struct ieee80211_channel *ni_chan;
-	uint16_t		ni_fhdwell;	/* FH only */
-	uint8_t			ni_fhindex;	/* FH only */
-	uint8_t			ni_erp;		/* 11g only */
-
-#ifdef notyet
-	/* DTIM and contention free period (CFP) */
-	uint8_t			ni_dtimperiod;
-	uint8_t			ni_cfpperiod;	/* # of DTIMs between CFPs */
-	uint16_t		ni_cfpduremain;	/* remaining cfp duration */
-	uint16_t		ni_cfpmaxduration;/* max CFP duration in TU */
-	uint16_t		ni_nextdtim;	/* time to next DTIM */
-	uint16_t		ni_timoffset;
-#endif
+	struct ieee80211_channel *ni_chan;	/* XXX multiple uses */
+	u_int16_t		ni_fhdwell;	/* FH only */
+	u_int8_t		ni_fhindex;	/* FH only */
+	u_int8_t		ni_erp;		/* ERP from beacon/probe resp */
+	u_int16_t		ni_timoff;	/* byte offset to TIM ie */
+	u_int8_t		ni_dtim_period;	/* DTIM period */
+	u_int8_t		ni_dtim_count;	/* DTIM count for last bcn */
 
 	/* others */
-	uint16_t		ni_associd;	/* assoc response */
-	uint16_t		ni_txseq;	/* seq to be transmitted */
-	uint16_t		ni_rxseq;	/* seq previous received */
 	int			ni_fails;	/* failure count to associate */
-	int			ni_inact;	/* inactivity mark count */
+	short			ni_inact;	/* inactivity mark count */
+	short			ni_inact_reload;/* inactivity reload value */
 	int			ni_txrate;	/* index to ni_rates[] */
+	struct	ifqueue		ni_savedq;	/* ps-poll queue */
+	struct ieee80211_nodestats ni_stats;	/* per-node statistics */
 };
+MALLOC_DECLARE(M_80211_NODE);
+
+#define	IEEE80211_NODE_AID(ni)	IEEE80211_AID(ni->ni_associd)
+
+#define	IEEE80211_NODE_STAT(ni,stat)	(ni->ni_stats.ns_##stat++)
+#define	IEEE80211_NODE_STAT_ADD(ni,stat,v)	(ni->ni_stats.ns_##stat += v)
+#define	IEEE80211_NODE_STAT_SET(ni,stat,v)	(ni->ni_stats.ns_##stat = v)
 
 static __inline struct ieee80211_node *
 ieee80211_ref_node(struct ieee80211_node *ni)
 {
-	atomic_add_int(&ni->ni_refcnt, 1);
+	ieee80211_node_incref(ni);
 	return ni;
 }
 
 static __inline void
 ieee80211_unref_node(struct ieee80211_node **ni)
 {
-	atomic_subtract_int(&(*ni)->ni_refcnt, 1);
+	ieee80211_node_decref(*ni);
 	*ni = NULL;			/* guard against use */
 }
 
 struct ieee80211com;
 
-#ifdef MALLOC_DECLARE
-MALLOC_DECLARE(M_80211_NODE);
+void	ieee80211_node_attach(struct ieee80211com *);
+void	ieee80211_node_lateattach(struct ieee80211com *);
+void	ieee80211_node_detach(struct ieee80211com *);
+
+static __inline int
+ieee80211_node_is_authorized(const struct ieee80211_node *ni)
+{
+	return (ni->ni_flags & IEEE80211_NODE_AUTH);
+}
+
+void	ieee80211_node_authorize(struct ieee80211_node *);
+void	ieee80211_node_unauthorize(struct ieee80211_node *);
+
+void	ieee80211_begin_scan(struct ieee80211com *, int);
+int	ieee80211_next_scan(struct ieee80211com *);
+void	ieee80211_create_ibss(struct ieee80211com*, struct ieee80211_channel *);
+void	ieee80211_reset_bss(struct ieee80211com *);
+void	ieee80211_cancel_scan(struct ieee80211com *);
+void	ieee80211_end_scan(struct ieee80211com *);
+int	ieee80211_ibss_merge(struct ieee80211_node *);
+int	ieee80211_sta_join(struct ieee80211com *, struct ieee80211_node *);
+void	ieee80211_sta_leave(struct ieee80211com *, struct ieee80211_node *);
+
+/*
+ * Table of ieee80211_node instances.  Each ieee80211com
+ * has at least one for holding the scan candidates.
+ * When operating as an access point or in ibss mode there
+ * is a second table for associated stations or neighbors.
+ */
+struct ieee80211_node_table {
+	struct ieee80211com	*nt_ic;		/* back reference */
+	ieee80211_node_lock_t	nt_nodelock;	/* on node table */
+	TAILQ_HEAD(, ieee80211_node) nt_node;	/* information of all nodes */
+	LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
+	const char		*nt_name;	/* for debugging */
+	ieee80211_scan_lock_t	nt_scanlock;	/* on nt_scangen */
+	u_int			nt_scangen;	/* gen# for timeout scan */
+	int			nt_inact_timer;	/* inactivity timer */
+	int			nt_inact_init;	/* initial node inact setting */
+	struct ieee80211_node	**nt_keyixmap;	/* key ix -> node map */
+	int			nt_keyixmax;	/* keyixmap size */
+
+	void			(*nt_timeout)(struct ieee80211_node_table *);
+};
+void	ieee80211_node_table_reset(struct ieee80211_node_table *);
+
+struct ieee80211_node *ieee80211_alloc_node(
+		struct ieee80211_node_table *, const u_int8_t *);
+struct ieee80211_node *ieee80211_tmp_node(struct ieee80211com *,
+		const u_int8_t *macaddr);
+struct ieee80211_node *ieee80211_dup_bss(struct ieee80211_node_table *,
+		const u_int8_t *);
+#ifdef IEEE80211_DEBUG_REFCNT
+void	ieee80211_free_node_debug(struct ieee80211_node *,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_debug(
+		struct ieee80211_node_table *, const u_int8_t *,
+		const char *func, int line);
+struct ieee80211_node * ieee80211_find_rxnode_debug(
+		struct ieee80211com *, const struct ieee80211_frame_min *,
+		const char *func, int line);
+struct ieee80211_node * ieee80211_find_rxnode_withkey_debug(
+		struct ieee80211com *,
+		const struct ieee80211_frame_min *, u_int16_t keyix,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_txnode_debug(
+		struct ieee80211com *, const u_int8_t *,
+		const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_with_channel_debug(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		struct ieee80211_channel *, const char *func, int line);
+struct ieee80211_node *ieee80211_find_node_with_ssid_debug(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		u_int ssidlen, const u_int8_t *ssid,
+		const char *func, int line);
+#define	ieee80211_free_node(ni) \
+	ieee80211_free_node_debug(ni, __func__, __LINE__)
+#define	ieee80211_find_node(nt, mac) \
+	ieee80211_find_node_debug(nt, mac, __func__, __LINE__)
+#define	ieee80211_find_rxnode(nt, wh) \
+	ieee80211_find_rxnode_debug(nt, wh, __func__, __LINE__)
+#define	ieee80211_find_rxnode_withkey(nt, wh, keyix) \
+	ieee80211_find_rxnode_withkey_debug(nt, wh, keyix, __func__, __LINE__)
+#define	ieee80211_find_txnode(nt, mac) \
+	ieee80211_find_txnode_debug(nt, mac, __func__, __LINE__)
+#define	ieee80211_find_node_with_channel(nt, mac, c) \
+	ieee80211_find_node_with_channel_debug(nt, mac, c, __func__, __LINE__)
+#define	ieee80211_find_node_with_ssid(nt, mac, sl, ss) \
+	ieee80211_find_node_with_ssid_debug(nt, mac, sl, ss, __func__, __LINE__)
+#else
+void	ieee80211_free_node(struct ieee80211_node *);
+struct ieee80211_node *ieee80211_find_node(
+		struct ieee80211_node_table *, const u_int8_t *);
+struct ieee80211_node * ieee80211_find_rxnode(
+		struct ieee80211com *, const struct ieee80211_frame_min *);
+struct ieee80211_node * ieee80211_find_rxnode_withkey(struct ieee80211com *,
+		const struct ieee80211_frame_min *, u_int16_t keyix);
+struct ieee80211_node *ieee80211_find_txnode(
+		struct ieee80211com *, const u_int8_t *);
+struct ieee80211_node *ieee80211_find_node_with_channel(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		struct ieee80211_channel *);
+struct ieee80211_node *ieee80211_find_node_with_ssid(
+		struct ieee80211_node_table *, const u_int8_t *macaddr,
+		u_int ssidlen, const u_int8_t *ssid);
 #endif
+int	ieee80211_node_delucastkey(struct ieee80211_node *);
 
-extern	void ieee80211_node_attach(struct ifnet *);
-extern	void ieee80211_node_lateattach(struct ifnet *);
-extern	void ieee80211_node_detach(struct ifnet *);
-
-extern	void ieee80211_begin_scan(struct ifnet *);
-extern	void ieee80211_next_scan(struct ifnet *);
-extern	void ieee80211_end_scan(struct ifnet *);
-extern	struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_find_node(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node *ieee80211_find_txnode(struct ieee80211com *,
-		uint8_t *);
-extern	struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *,
-		uint8_t *macaddr, struct ieee80211_channel *);
-extern	void ieee80211_free_node(struct ieee80211com *,
-		struct ieee80211_node *);
-extern	void ieee80211_free_allnodes(struct ieee80211com *);
 typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
-extern	void ieee80211_iterate_nodes(struct ieee80211com *ic,
+void	ieee80211_iterate_nodes(struct ieee80211_node_table *,
 		ieee80211_iter_func *, void *);
-extern	void ieee80211_timeout_nodes(struct ieee80211com *);
 
-extern	void ieee80211_create_ibss(struct ieee80211com* ,
-		struct ieee80211_channel *);
-#endif /* _NETPROTO_802_11_IEEE80211_NODE_H_ */
+void	ieee80211_dump_node(struct ieee80211_node_table *,
+		struct ieee80211_node *);
+void	ieee80211_dump_nodes(struct ieee80211_node_table *);
+
+struct ieee80211_node *ieee80211_fakeup_adhoc_node(
+		struct ieee80211_node_table *, const u_int8_t macaddr[]);
+void	ieee80211_node_join(struct ieee80211com *, struct ieee80211_node *,int);
+void	ieee80211_node_leave(struct ieee80211com *, struct ieee80211_node *);
+u_int8_t ieee80211_getrssi(struct ieee80211com *ic);
+
+/*
+ * Parameters supplied when adding/updating an entry in a
+ * scan cache.  Pointer variables should be set to NULL
+ * if no data is available.  Pointer references can be to
+ * local data; any information that is saved will be copied.
+ * All multi-byte values must be in host byte order.
+ */
+struct ieee80211_scanparams {
+	u_int16_t	capinfo;	/* 802.11 capabilities */
+	u_int16_t	fhdwell;	/* FHSS dwell interval */
+	u_int8_t	chan;		/* */
+	u_int8_t	bchan;
+	u_int8_t	fhindex;
+	u_int8_t	erp;
+	u_int16_t	bintval;
+	u_int8_t	timoff;
+	u_int8_t	*tim;
+	u_int8_t	*tstamp;
+	u_int8_t	*country;
+	u_int8_t	*ssid;
+	u_int8_t	*rates;
+	u_int8_t	*xrates;
+	u_int8_t	*wpa;
+	u_int8_t	*wme;
+};
+
+void	ieee80211_add_scan(struct ieee80211com *,
+		const struct ieee80211_scanparams *,
+		const struct ieee80211_frame *,
+		int subtype, int rssi, int rstamp);
+struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211com *,
+		const struct ieee80211_frame *,
+		const struct ieee80211_scanparams *);
+#endif /* _NET80211_IEEE80211_NODE_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_output.c src/sys/netproto/802_11/ieee80211_output.c
--- src.preview/sys/netproto/802_11/ieee80211_output.c	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_output.c	2005-09-23 11:46:18.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,45 +28,114 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_output.c,v 1.10 2004/04/02 23:25:39 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_output.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 #include "opt_inet.h"
 
 #include <sys/param.h>
 #include <sys/systm.h> 
 #include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
 #include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
 
-#include <machine/atomic.h>
+#include <sys/socket.h>
+#include <sys/thread.h>
  
+#include <net/bpf.h>
+#include <net/ethernet.h>
 #include <net/if.h>
-#include <net/if_dl.h>
+#include <net/if_llc.h>
 #include <net/if_media.h>
+#include <net/if_var.h>
 #include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
+#include <net/vlan/if_vlan_var.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
-
 #ifdef INET
 #include <netinet/in.h> 
 #include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#ifdef IEEE80211_DEBUG
+/*
+ * Decide if an outbound management frame should be
+ * printed when debugging is enabled.  This filters some
+ * of the less interesting frames that come frequently
+ * (e.g. beacons).
+ */
+static __inline int
+doprint(struct ieee80211com *ic, int subtype)
+{
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		return (ic->ic_opmode == IEEE80211_M_IBSS);
+	}
+	return 1;
+}
 #endif
 
 /*
+ * Set the direction field and address fields of an outgoing
+ * non-QoS frame.  Note this should be called early on in
+ * constructing a frame as it sets i_fc[1]; other bits can
+ * then be or'd in.
+ */
+static void
+ieee80211_send_setup(struct ieee80211com *ic,
+	struct ieee80211_node *ni,
+	struct ieee80211_frame *wh,
+	int type,
+	const u_int8_t sa[IEEE80211_ADDR_LEN],
+	const u_int8_t da[IEEE80211_ADDR_LEN],
+	const u_int8_t bssid[IEEE80211_ADDR_LEN])
+{
+#define	WH4(wh)	((struct ieee80211_frame_addr4 *)wh)
+
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
+	if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
+		switch (ic->ic_opmode) {
+		case IEEE80211_M_STA:
+			wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
+			IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
+			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
+			IEEE80211_ADDR_COPY(wh->i_addr3, da);
+			break;
+		case IEEE80211_M_IBSS:
+		case IEEE80211_M_AHDEMO:
+			wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+			IEEE80211_ADDR_COPY(wh->i_addr1, da);
+			IEEE80211_ADDR_COPY(wh->i_addr2, sa);
+			IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
+			break;
+		case IEEE80211_M_HOSTAP:
+			wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
+			IEEE80211_ADDR_COPY(wh->i_addr1, da);
+			IEEE80211_ADDR_COPY(wh->i_addr2, bssid);
+			IEEE80211_ADDR_COPY(wh->i_addr3, sa);
+			break;
+		case IEEE80211_M_MONITOR:	/* NB: to quiet compiler */
+			break;
+		}
+	} else {
+		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+		IEEE80211_ADDR_COPY(wh->i_addr1, da);
+		IEEE80211_ADDR_COPY(wh->i_addr2, sa);
+		IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
+	}
+	*(u_int16_t *)&wh->i_dur[0] = 0;
+	/* NB: use non-QoS tid */
+	*(u_int16_t *)&wh->i_seq[0] =
+	    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+	ni->ni_txseqs[0]++;
+#undef WH4
+}
+
+/*
  * Send a management frame to the specified node.  The node pointer
  * must have a reference as the pointer will be passed to the driver
  * and potentially held for a long time.  If the frame is successfully
@@ -74,14 +143,13 @@
  * reference (and potentially free'ing up any associated storage).
  */
 static int
-ieee80211_mgmt_output(struct ifnet *ifp, struct ieee80211_node *ni,
+ieee80211_mgmt_output(struct ieee80211com *ic, struct ieee80211_node *ni,
     struct mbuf *m, int type)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_frame *wh;
 
 	KASSERT(ni != NULL, ("null node"));
-	ni->ni_inact = 0;
 
 	/*
 	 * Yech, hack alert!  We want to pass the node down to the
@@ -102,32 +170,29 @@
 	m->m_pkthdr.rcvif = (void *)ni;
 
 	wh = mtod(m, struct ieee80211_frame *);
-	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | type;
-	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
-	*(uint16_t *)wh->i_dur = 0;
-	*(uint16_t *)wh->i_seq =
-	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
-	ni->ni_txseq++;
-	IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr);
-	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
-	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
-
-	if (ifp->if_flags & IFF_DEBUG) {
-		/* avoid to print too many frames */
-		if (ic->ic_opmode == IEEE80211_M_IBSS ||
+	ieee80211_send_setup(ic, ni, wh, 
+		IEEE80211_FC0_TYPE_MGT | type,
+		ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
+	if ((m->m_flags & M_LINK0) != 0 && ni->ni_challenge != NULL) {
+		m->m_flags &= ~M_LINK0;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			"[%6D] encrypting frame (%s)\n",
+			(wh->i_addr1), ":", __func__);
+		wh->i_fc[1] |= IEEE80211_FC1_WEP;
+	}
 #ifdef IEEE80211_DEBUG
-		    ieee80211_debug > 1 ||
-#endif
-		    (type & IEEE80211_FC0_SUBTYPE_MASK) !=
-		    IEEE80211_FC0_SUBTYPE_PROBE_RESP)
-			if_printf(ifp, "sending %s to %6D on channel %u\n",
-			    ieee80211_mgt_subtype_name[
-			    (type & IEEE80211_FC0_SUBTYPE_MASK)
-			    >> IEEE80211_FC0_SUBTYPE_SHIFT],
-			    ni->ni_macaddr, ":",
-			    ieee80211_chan2ieee(ic, ni->ni_chan));
+	/* avoid printing too many frames */
+	if ((ieee80211_msg_debug(ic) && doprint(ic, type)) ||
+	    ieee80211_msg_dumppkts(ic)) {
+		printf("[%6D] send %s on channel %u\n",
+		    (wh->i_addr1), ":",
+		    ieee80211_mgt_subtype_name[
+			(type & IEEE80211_FC0_SUBTYPE_MASK) >>
+				IEEE80211_FC0_SUBTYPE_SHIFT],
+		    ieee80211_chan2ieee(ic, ic->ic_curchan));
 	}
-
+#endif
+	IEEE80211_NODE_STAT(ni, tx_mgmt);
 	IF_ENQUEUE(&ic->ic_mgtq, m);
 	ifp->if_timer = 1;
 	(*ifp->if_start)(ifp);
@@ -135,41 +200,344 @@
 }
 
 /*
- * Encapsulate an outbound data frame.  The mbuf chain is updated and
- * a reference to the destination node is returned.  If an error is
- * encountered NULL is returned and the node reference will also be NULL.
- * 
- * NB: The caller is responsible for free'ing a returned node reference.
- *     The convention is ic_bss is not reference counted; the caller must
- *     maintain that.
+ * Send a null data frame to the specified node.
+ *
+ * NB: the caller is assumed to have setup a node reference
+ *     for use; this is necessary to deal with a race condition
+ *     when probing for inactive stations.
+ */
+int
+ieee80211_send_nulldata(struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	struct ifnet *ifp = ic->ic_ifp;
+	struct mbuf *m;
+	struct ieee80211_frame *wh;
+
+	MGETHDR(m, M_NOWAIT, MT_HEADER);
+	if (m == NULL) {
+		/* XXX debug msg */
+		ic->ic_stats.is_tx_nobuf++;
+		ieee80211_unref_node(&ni);
+		return ENOMEM;
+	}
+	m->m_pkthdr.rcvif = (void *) ni;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	ieee80211_send_setup(ic, ni, wh,
+		IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
+		ic->ic_myaddr, ni->ni_macaddr, ni->ni_bssid);
+	/* NB: power management bit is never sent by an AP */
+	if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
+	    ic->ic_opmode != IEEE80211_M_HOSTAP)
+		wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
+	m->m_len = m->m_pkthdr.len = sizeof(struct ieee80211_frame);
+
+	IEEE80211_NODE_STAT(ni, tx_data);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+	    "[%6D] send null data frame on channel %u, pwr mgt %s\n",
+	    (ni->ni_macaddr), ":",
+	    ieee80211_chan2ieee(ic, ic->ic_curchan),
+	    wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
+
+	IF_ENQUEUE(&ic->ic_mgtq, m);		/* cheat */
+	(*ifp->if_start)(ifp);
+
+	return 0;
+}
+
+/* 
+ * Assign priority to a frame based on any vlan tag assigned
+ * to the station and/or any Diffserv setting in an IP header.
+ * Finally, if an ACM policy is setup (in station mode) it's
+ * applied.
+ */
+int
+ieee80211_classify(struct ieee80211com *ic, struct mbuf *m, struct ieee80211_node *ni)
+{
+	int v_wme_ac, d_wme_ac, ac;
+#ifdef INET
+	struct ether_header *eh;
+#endif
+
+	if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0) {
+		ac = WME_AC_BE;
+		goto done;
+	}
+
+	/* 
+	 * If node has a vlan tag then all traffic
+	 * to it must have a matching tag.
+	 */
+	v_wme_ac = 0;
+	if (ni->ni_vlan != 0) {
+#if 1
+		IEEE80211_NODE_STAT(ni, tx_novlantag);
+		return 1;
+#else
+		 struct m_tag *mtag = VLAN_OUTPUT_TAG(ic->ic_ifp, m);
+		 if (mtag == NULL) {
+			IEEE80211_NODE_STAT(ni, tx_novlantag);
+			return 1;
+		}
+		if (EVL_VLANOFTAG(VLAN_TAG_VALUE(mtag)) !=
+		    EVL_VLANOFTAG(ni->ni_vlan)) {
+			IEEE80211_NODE_STAT(ni, tx_vlanmismatch);
+			return 1;
+		}
+		/* map vlan priority to AC */
+		switch (EVL_PRIOFTAG(ni->ni_vlan)) {
+		case 1:
+		case 2:
+			v_wme_ac = WME_AC_BK;
+			break;
+		case 0:
+		case 3:
+			v_wme_ac = WME_AC_BE;
+			break;
+		case 4:
+		case 5:
+			v_wme_ac = WME_AC_VI;
+			break;
+		case 6:
+		case 7:
+			v_wme_ac = WME_AC_VO;
+			break;
+		}
+#endif
+	}
+
+#ifdef INET
+	eh = mtod(m, struct ether_header *);
+	if (eh->ether_type == htons(ETHERTYPE_IP)) {
+		const struct ip *ip = (struct ip *)
+			(mtod(m, u_int8_t *) + sizeof (*eh));
+		/*
+		 * IP frame, map the TOS field.
+		 */
+		switch (ip->ip_tos) {
+		case 0x08:
+		case 0x20:
+			d_wme_ac = WME_AC_BK;	/* background */
+			break;
+		case 0x28:
+		case 0xa0:
+			d_wme_ac = WME_AC_VI;	/* video */
+			break;
+		case 0x30:			/* voice */
+		case 0xe0:
+		case 0x88:			/* XXX UPSD */
+		case 0xb8:
+			d_wme_ac = WME_AC_VO;
+			break;
+		default:
+			d_wme_ac = WME_AC_BE;
+			break;
+		}
+	} else {
+#endif /* INET */
+		d_wme_ac = WME_AC_BE;
+#ifdef INET
+	}
+#endif
+	/*
+	 * Use highest priority AC.
+	 */
+	if (v_wme_ac > d_wme_ac)
+		ac = v_wme_ac;
+	else
+		ac = d_wme_ac;
+
+	/*
+	 * Apply ACM policy.
+	 */
+	if (ic->ic_opmode == IEEE80211_M_STA) {
+		static const int acmap[4] = {
+			WME_AC_BK,	/* WME_AC_BE */
+			WME_AC_BK,	/* WME_AC_BK */
+			WME_AC_BE,	/* WME_AC_VI */
+			WME_AC_VI,	/* WME_AC_VO */
+		};
+		while (ac != WME_AC_BK &&
+		    ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].wmep_acm)
+			ac = acmap[ac];
+	}
+done:
+	M_WME_SETAC(m, ac);
+	return 0;
+}
+
+/*
+ * Insure there is sufficient contiguous space to encapsulate the
+ * 802.11 data frame.  If room isn't already there, arrange for it.
+ * Drivers and cipher modules assume we have done the necessary work
+ * and fail rudely if they don't find the space they need.
+ */
+static struct mbuf *
+ieee80211_mbuf_adjust(struct ieee80211com *ic, int hdrsize,
+	struct ieee80211_key *key, struct mbuf *m)
+{
+#define	TO_BE_RECLAIMED	(sizeof(struct ether_header) - sizeof(struct llc))
+	int needed_space = hdrsize;
+
+	if (key != NULL) {
+		/* XXX belongs in crypto code? */
+		needed_space += key->wk_cipher->ic_header;
+		/* XXX frags */
+	}
+	/*
+	 * We know we are called just before stripping an Ethernet
+	 * header and prepending an LLC header.  This means we know
+	 * there will be
+	 *	sizeof(struct ether_header) - sizeof(struct llc)
+	 * bytes recovered to which we need additional space for the
+	 * 802.11 header and any crypto header.
+	 */
+	/* XXX check trailing space and copy instead? */
+	if (M_LEADINGSPACE(m) < needed_space - TO_BE_RECLAIMED) {
+		struct mbuf *n = m_gethdr(M_NOWAIT, m->m_type);
+		if (n == NULL) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+			    "%s: cannot expand storage\n", __func__);
+			ic->ic_stats.is_tx_nobuf++;
+			m_freem(m);
+			return NULL;
+		}
+		KASSERT(needed_space <= MHLEN,
+		    ("not enough room, need %u got %zu\n", needed_space, MHLEN));
+		/*
+		 * Setup new mbuf to have leading space to prepend the
+		 * 802.11 header and any crypto header bits that are
+		 * required (the latter are added when the driver calls
+		 * back to ieee80211_crypto_encap to do crypto encapsulation).
+		 */
+		/* NB: must be first 'cuz it clobbers m_data */
+		m_move_pkthdr(n, m);
+		n->m_len = 0;			/* NB: m_gethdr does not set */
+		n->m_data += needed_space;
+		/*
+		 * Pull up Ethernet header to create the expected layout.
+		 * We could use m_pullup but that's overkill (i.e. we don't
+		 * need the actual data) and it cannot fail so do it inline
+		 */
+		/* NB: struct ether_header is known to be contiguous */
+		n->m_len += sizeof(struct ether_header);
+		m->m_len -= sizeof(struct ether_header);
+		m->m_data += sizeof(struct ether_header);
+		/*
+		 * Replace the head of the chain.
+		 */
+		n->m_next = m;
+		m = n;
+	}
+	return m;
+#undef TO_BE_RECLAIMED
+}
+
+#define	KEY_UNDEFINED(k)	((k).wk_cipher == &ieee80211_cipher_none)
+/*
+ * Return the transmit key to use in sending a unicast frame.
+ * If a unicast key is set we use that.  When no unicast key is set
+ * we fall back to the default transmit key.
+ */ 
+static __inline struct ieee80211_key *
+ieee80211_crypto_getucastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (KEY_UNDEFINED(ni->ni_ucastkey)) {
+		if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+		    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+			return NULL;
+		return &ic->ic_nw_keys[ic->ic_def_txkey];
+	} else {
+		return &ni->ni_ucastkey;
+	}
+}
+
+/*
+ * Return the transmit key to use in sending a multicast frame.
+ * Multicast traffic always uses the group key which is installed as
+ * the default tx key.
+ */ 
+static __inline struct ieee80211_key *
+ieee80211_crypto_getmcastkey(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	if (ic->ic_def_txkey == IEEE80211_KEYIX_NONE ||
+	    KEY_UNDEFINED(ic->ic_nw_keys[ic->ic_def_txkey]))
+		return NULL;
+	return &ic->ic_nw_keys[ic->ic_def_txkey];
+}
+
+/*
+ * Encapsulate an outbound data frame.  The mbuf chain is updated.
+ * If an error is encountered NULL is returned.  The caller is required
+ * to provide a node reference and pullup the ethernet header in the
+ * first mbuf.
  */
 struct mbuf *
-ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
+ieee80211_encap(struct ieee80211com *ic, struct mbuf *m,
+	struct ieee80211_node *ni)
 {
-	struct ieee80211com *ic = (void *)ifp;
 	struct ether_header eh;
 	struct ieee80211_frame *wh;
-	struct ieee80211_node *ni = NULL;
+	struct ieee80211_key *key;
 	struct llc *llc;
+	int hdrsize, datalen, addqos;
 
-	if (m->m_len < sizeof(struct ether_header)) {
-		m = m_pullup(m, sizeof(struct ether_header));
-		if (m == NULL) {
-			ic->ic_stats.is_tx_nombuf++;
-			goto bad;
-		}
-	}
+	KASSERT(m->m_len >= sizeof(eh), ("no ethernet header!"));
 	memcpy(&eh, mtod(m, caddr_t), sizeof(struct ether_header));
 
-	ni = ieee80211_find_txnode(ic, eh.ether_dhost);
-	if (ni == NULL) {
-		IEEE80211_DPRINTF(("%s: no node for dst %6D, discard frame\n",
-			__func__, eh.ether_dhost, ":"));
-		ic->ic_stats.is_tx_nonode++; 
+	/*
+	 * Insure space for additional headers.  First identify
+	 * transmit key to use in calculating any buffer adjustments
+	 * required.  This is also used below to do privacy
+	 * encapsulation work.  Then calculate the 802.11 header
+	 * size and any padding required by the driver.
+	 *
+	 * Note key may be NULL if we fall back to the default
+	 * transmit key and that is not set.  In that case the
+	 * buffer may not be expanded as needed by the cipher
+	 * routines, but they will/should discard it.
+	 */
+	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
+		if (ic->ic_opmode == IEEE80211_M_STA ||
+		    !IEEE80211_IS_MULTICAST(eh.ether_dhost))
+			key = ieee80211_crypto_getucastkey(ic, ni);
+		else
+			key = ieee80211_crypto_getmcastkey(ic, ni);
+		if (key == NULL && eh.ether_type != htons(ETHERTYPE_PAE)) {
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_CRYPTO,
+			    "[%6D] no default transmit key (%s) deftxkey %u\n",
+			    (eh.ether_dhost), ":",  __func__,
+			    ic->ic_def_txkey);
+			ic->ic_stats.is_tx_nodefkey++;
+		}
+	} else
+		key = NULL;
+	/* XXX 4-address format */
+	/*
+	 * XXX Some ap's don't handle QoS-encapsulated EAPOL
+	 * frames so suppress use.  This may be an issue if other
+	 * ap's require all data frames to be QoS-encapsulated
+	 * once negotiated in which case we'll need to make this
+	 * configurable.
+	 */
+	addqos = (ni->ni_flags & IEEE80211_NODE_QOS) &&
+		 eh.ether_type != htons(ETHERTYPE_PAE);
+	if (addqos)
+		hdrsize = sizeof(struct ieee80211_qosframe);
+	else
+		hdrsize = sizeof(struct ieee80211_frame);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		hdrsize = roundup(hdrsize, sizeof(u_int32_t));
+	m = ieee80211_mbuf_adjust(ic, hdrsize, key, m);
+	if (m == NULL) {
+		/* NB: ieee80211_mbuf_adjust handles msgs+statistics */
 		goto bad;
 	}
-	ni->ni_inact = 0;
 
+	/* NB: this could be optimized because of ieee80211_mbuf_adjust */
 	m_adj(m, sizeof(struct ether_header) - sizeof(struct llc));
 	llc = mtod(m, struct llc *);
 	llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP;
@@ -178,17 +546,16 @@
 	llc->llc_snap.org_code[1] = 0;
 	llc->llc_snap.org_code[2] = 0;
 	llc->llc_snap.ether_type = eh.ether_type;
-	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
+	datalen = m->m_pkthdr.len;		/* NB: w/o 802.11 header */
+
+	M_PREPEND(m, hdrsize, MB_DONTWAIT);
 	if (m == NULL) {
-		ic->ic_stats.is_tx_nombuf++;
+		ic->ic_stats.is_tx_nobuf++;
 		goto bad;
 	}
 	wh = mtod(m, struct ieee80211_frame *);
 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
-	*(uint16_t *)wh->i_dur = 0;
-	*(uint16_t *)wh->i_seq =
-	    htole16(ni->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
-	ni->ni_txseq++;
+	*(u_int16_t *)wh->i_dur = 0;
 	switch (ic->ic_opmode) {
 	case IEEE80211_M_STA:
 		wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
@@ -201,7 +568,11 @@
 		wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
 		IEEE80211_ADDR_COPY(wh->i_addr1, eh.ether_dhost);
 		IEEE80211_ADDR_COPY(wh->i_addr2, eh.ether_shost);
-		IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+		/*
+		 * NB: always use the bssid from ic_bss as the
+		 *     neighbor's may be stale after an ibss merge
+		 */
+		IEEE80211_ADDR_COPY(wh->i_addr3, ic->ic_bss->ni_bssid);
 		break;
 	case IEEE80211_M_HOSTAP:
 		wh->i_fc[1] = IEEE80211_FC1_DIR_FROMDS;
@@ -212,22 +583,66 @@
 	case IEEE80211_M_MONITOR:
 		goto bad;
 	}
-	*pni = ni;
+	if (m->m_flags & M_MORE_DATA)
+		wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
+	if (addqos) {
+		struct ieee80211_qosframe *qwh =
+			(struct ieee80211_qosframe *) wh;
+		int ac, tid;
+
+		ac = M_WME_GETAC(m);
+		/* map from access class/queue to 11e header priorty value */
+		tid = WME_AC_TO_TID(ac);
+		qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+		if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
+			qwh->i_qos[0] |= 1 << IEEE80211_QOS_ACKPOLICY_S;
+		qwh->i_qos[1] = 0;
+		qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+
+		*(u_int16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[tid]++;
+	} else {
+		*(u_int16_t *)wh->i_seq =
+		    htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
+		ni->ni_txseqs[0]++;
+	}
+	if (key != NULL) {
+		/*
+		 * IEEE 802.1X: send EAPOL frames always in the clear.
+		 * WPA/WPA2: encrypt EAPOL keys when pairwise keys are set.
+		 */
+		if (eh.ether_type != htons(ETHERTYPE_PAE) ||
+		    ((ic->ic_flags & IEEE80211_F_WPA) &&
+		     (ic->ic_opmode == IEEE80211_M_STA ?
+		      !KEY_UNDEFINED(*key) : !KEY_UNDEFINED(ni->ni_ucastkey)))) {
+			wh->i_fc[1] |= IEEE80211_FC1_WEP;
+			/* XXX do fragmentation */
+			if (!ieee80211_crypto_enmic(ic, key, m, 0)) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_OUTPUT,
+				    "[%6D] enmic failed, discard frame\n",
+				    (eh.ether_dhost), ":");
+				ic->ic_stats.is_crypto_enmicfail++;
+				goto bad;
+			}
+		}
+	}
+
+	IEEE80211_NODE_STAT(ni, tx_data);
+	IEEE80211_NODE_STAT_ADD(ni, tx_bytes, datalen);
+
 	return m;
 bad:
 	if (m != NULL)
 		m_freem(m);
-	if (ni && ni != ic->ic_bss)
-		ieee80211_free_node(ic, ni);
-	*pni = NULL;
 	return NULL;
 }
 
 /*
  * Add a supported rates element id to a frame.
  */
-uint8_t *
-ieee80211_add_rates(uint8_t *frm, const struct ieee80211_rateset *rs)
+static u_int8_t *
+ieee80211_add_rates(u_int8_t *frm, const struct ieee80211_rateset *rs)
 {
 	int nrates;
 
@@ -243,8 +658,8 @@
 /*
  * Add an extended supported rates element id to a frame.
  */
-uint8_t *
-ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
+static u_int8_t *
+ieee80211_add_xrates(u_int8_t *frm, const struct ieee80211_rateset *rs)
 {
 	/*
 	 * Add an extended supported rates element if operating in 11g mode.
@@ -262,8 +677,8 @@
 /* 
  * Add an ssid elemet to a frame.
  */
-static uint8_t *
-ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len)
+static u_int8_t *
+ieee80211_add_ssid(u_int8_t *frm, const u_int8_t *ssid, u_int len)
 {
 	*frm++ = IEEE80211_ELEMID_SSID;
 	*frm++ = len;
@@ -271,17 +686,359 @@
 	return frm + len;
 }
 
-static struct mbuf *
-ieee80211_getmbuf(int flags, int type, u_int pktlen)
+/*
+ * Add an erp element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_erp(u_int8_t *frm, struct ieee80211com *ic)
 {
-	struct mbuf *m;
+	u_int8_t erp;
+
+	*frm++ = IEEE80211_ELEMID_ERP;
+	*frm++ = 1;
+	erp = 0;
+	if (ic->ic_nonerpsta != 0)
+		erp |= IEEE80211_ERP_NON_ERP_PRESENT;
+	if (ic->ic_flags & IEEE80211_F_USEPROT)
+		erp |= IEEE80211_ERP_USE_PROTECTION;
+	if (ic->ic_flags & IEEE80211_F_USEBARKER)
+		erp |= IEEE80211_ERP_LONG_PREAMBLE;
+	*frm++ = erp;
+	return frm;
+}
 
-	KASSERT(pktlen <= MCLBYTES, ("802.11 packet too large: %u", pktlen));
-	if (pktlen <= MHLEN)
-		MGETHDR(m, flags, type);
+static u_int8_t *
+ieee80211_setup_wpa_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	WPA_OUI_BYTES		0x00, 0x50, 0xf2
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t oui[4] = { WPA_OUI_BYTES, WPA_OUI_TYPE };
+	static const u_int8_t cipher_suite[][4] = {
+		{ WPA_OUI_BYTES, WPA_CSE_WEP40 },	/* NB: 40-bit */
+		{ WPA_OUI_BYTES, WPA_CSE_TKIP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX WRAP */
+		{ WPA_OUI_BYTES, WPA_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ WPA_OUI_BYTES, WPA_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ WPA_OUI_BYTES, WPA_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ WPA_OUI_BYTES, WPA_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_VENDOR;
+	*frm++ = 0;				/* length filled in below */
+	memcpy(frm, oui, sizeof(oui));		/* WPA OUI */
+	frm += sizeof(oui);
+	ADDSHORT(frm, WPA_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
 	else
-		m = m_getcl(flags, type, M_PKTHDR);
-	return m;
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	if (rsn->rsn_caps != 0 && rsn->rsn_caps != RSN_CAP_PREAUTH)
+		ADDSHORT(frm, rsn->rsn_caps);
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("WPA IE too big, %u > %zu",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSHORT
+#undef ADDSELECTOR
+#undef WPA_OUI_BYTES
+}
+
+static u_int8_t *
+ieee80211_setup_rsn_ie(struct ieee80211com *ic, u_int8_t *ie)
+{
+#define	RSN_OUI_BYTES		0x00, 0x0f, 0xac
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+#define	ADDSELECTOR(frm, sel) do {		\
+	memcpy(frm, sel, 4);			\
+	frm += 4;				\
+} while (0)
+	static const u_int8_t cipher_suite[][4] = {
+		{ RSN_OUI_BYTES, RSN_CSE_WEP40 },	/* NB: 40-bit */
+		{ RSN_OUI_BYTES, RSN_CSE_TKIP },
+		{ RSN_OUI_BYTES, RSN_CSE_WRAP },
+		{ RSN_OUI_BYTES, RSN_CSE_CCMP },
+		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
+		{ RSN_OUI_BYTES, RSN_CSE_NULL },
+	};
+	static const u_int8_t wep104_suite[4] =
+		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };
+	static const u_int8_t key_mgt_unspec[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
+	static const u_int8_t key_mgt_psk[4] =
+		{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
+	const struct ieee80211_rsnparms *rsn = &ic->ic_bss->ni_rsn;
+	u_int8_t *frm = ie;
+	u_int8_t *selcnt;
+
+	*frm++ = IEEE80211_ELEMID_RSN;
+	*frm++ = 0;				/* length filled in below */
+	ADDSHORT(frm, RSN_VERSION);
+
+	/* XXX filter out CKIP */
+
+	/* multicast cipher */
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP &&
+	    rsn->rsn_mcastkeylen >= 13)
+		ADDSELECTOR(frm, wep104_suite);
+	else
+		ADDSELECTOR(frm, cipher_suite[rsn->rsn_mcastcipher]);
+
+	/* unicast cipher list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_AES_CCM)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM]);
+	}
+	if (rsn->rsn_ucastcipherset & (1<<IEEE80211_CIPHER_TKIP)) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, cipher_suite[IEEE80211_CIPHER_TKIP]);
+	}
+
+	/* authenticator selector list */
+	selcnt = frm;
+	ADDSHORT(frm, 0);			/* selector count */
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_UNSPEC) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_unspec);
+	}
+	if (rsn->rsn_keymgmtset & WPA_ASE_8021X_PSK) {
+		selcnt[0]++;
+		ADDSELECTOR(frm, key_mgt_psk);
+	}
+
+	/* optional capabilities */
+	ADDSHORT(frm, rsn->rsn_caps);
+	/* XXX PMKID */
+
+	/* calculate element length */
+	ie[1] = frm - ie - 2;
+	KASSERT(ie[1]+2 <= sizeof(struct ieee80211_ie_wpa),
+		("RSN IE too big, %u > %zu",
+		ie[1]+2, sizeof(struct ieee80211_ie_wpa)));
+	return frm;
+#undef ADDSELECTOR
+#undef ADDSHORT
+#undef RSN_OUI_BYTES
+}
+
+/*
+ * Add a WPA/RSN element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wpa(u_int8_t *frm, struct ieee80211com *ic)
+{
+
+	KASSERT(ic->ic_flags & IEEE80211_F_WPA, ("no WPA/RSN!"));
+	if (ic->ic_flags & IEEE80211_F_WPA2)
+		frm = ieee80211_setup_rsn_ie(ic, frm);
+	if (ic->ic_flags & IEEE80211_F_WPA1)
+		frm = ieee80211_setup_wpa_ie(ic, frm);
+	return frm;
+}
+
+#define	WME_OUI_BYTES		0x00, 0x50, 0xf2
+/*
+ * Add a WME information element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+	static const struct ieee80211_wme_info info = {
+		.wme_id		= IEEE80211_ELEMID_VENDOR,
+		.wme_len	= sizeof(struct ieee80211_wme_info) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_INFO_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+		.wme_info	= 0,
+	};
+	memcpy(frm, &info, sizeof(info));
+	return frm + sizeof(info); 
+}
+
+/*
+ * Add a WME parameters element to a frame.
+ */
+static u_int8_t *
+ieee80211_add_wme_param(u_int8_t *frm, struct ieee80211_wme_state *wme)
+{
+#define	SM(_v, _f)	(((_v) << _f##_S) & _f)
+#define	ADDSHORT(frm, v) do {			\
+	frm[0] = (v) & 0xff;			\
+	frm[1] = (v) >> 8;			\
+	frm += 2;				\
+} while (0)
+	/* NB: this works 'cuz a param has an info at the front */
+	static const struct ieee80211_wme_info param = {
+		.wme_id		= IEEE80211_ELEMID_VENDOR,
+		.wme_len	= sizeof(struct ieee80211_wme_param) - 2,
+		.wme_oui	= { WME_OUI_BYTES },
+		.wme_type	= WME_OUI_TYPE,
+		.wme_subtype	= WME_PARAM_OUI_SUBTYPE,
+		.wme_version	= WME_VERSION,
+	};
+	int i;
+
+	memcpy(frm, &param, sizeof(param));
+	frm += __offsetof(struct ieee80211_wme_info, wme_info);
+	*frm++ = wme->wme_bssChanParams.cap_info;	/* AC info */
+	*frm++ = 0;					/* reserved field */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		const struct wmeParams *ac =
+		       &wme->wme_bssChanParams.cap_wmeParams[i];
+		*frm++ = SM(i, WME_PARAM_ACI)
+		       | SM(ac->wmep_acm, WME_PARAM_ACM)
+		       | SM(ac->wmep_aifsn, WME_PARAM_AIFSN)
+		       ;
+		*frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
+		       | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN)
+		       ;
+		ADDSHORT(frm, ac->wmep_txopLimit);
+	}
+	return frm;
+#undef SM
+#undef ADDSHORT
+}
+#undef WME_OUI_BYTES
+
+/*
+ * Send a probe request frame with the specified ssid
+ * and any optional information element data.
+ */
+int
+ieee80211_send_probereq(struct ieee80211_node *ni,
+	const u_int8_t sa[IEEE80211_ADDR_LEN],
+	const u_int8_t da[IEEE80211_ADDR_LEN],
+	const u_int8_t bssid[IEEE80211_ADDR_LEN],
+	const u_int8_t *ssid, size_t ssidlen,
+	const void *optie, size_t optielen)
+{
+	struct ieee80211com *ic = ni->ni_ic;
+	enum ieee80211_phymode mode;
+	struct ieee80211_frame *wh;
+	struct mbuf *m;
+	u_int8_t *frm;
+
+	/*
+	 * Hold a reference on the node so it doesn't go away until after
+	 * the xmit is complete all the way in the driver.  On error we
+	 * will remove our reference.
+	 */
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n",
+		__func__, __LINE__,
+		ni, (ni->ni_macaddr), ":",
+		ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
+	/*
+	 * prreq frame format
+	 *	[tlv] ssid
+	 *	[tlv] supported rates
+	 *	[tlv] extended supported rates
+	 *	[tlv] user-specified ie's
+	 */
+	m = ieee80211_getmgtframe(&frm,
+		 2 + IEEE80211_NWID_LEN
+	       + 2 + IEEE80211_RATE_SIZE
+	       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+	       + (optie != NULL ? optielen : 0)
+	);
+	if (m == NULL) {
+		ic->ic_stats.is_tx_nobuf++;
+		ieee80211_free_node(ni);
+		return ENOMEM;
+	}
+
+	frm = ieee80211_add_ssid(frm, ssid, ssidlen);
+	mode = ieee80211_chan2mode(ic, ic->ic_curchan);
+	frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+	frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
+
+	if (optie != NULL) {
+		memcpy(frm, optie, optielen);
+		frm += optielen;
+	}
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+
+	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
+	if (m == NULL)
+		return ENOMEM;
+	KASSERT(m->m_pkthdr.rcvif == NULL, ("rcvif not null"));
+	m->m_pkthdr.rcvif = (void *)ni;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	ieee80211_send_setup(ic, ni, wh,
+		IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
+		sa, da, bssid);
+	/* XXX power management? */
+
+	IEEE80211_NODE_STAT(ni, tx_probereq);
+	IEEE80211_NODE_STAT(ni, tx_mgmt);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_DEBUG | IEEE80211_MSG_DUMPPKTS,
+	    "[%6D] send probe req on channel %u\n",
+	    (wh->i_addr1), ":",
+	    ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+	IF_ENQUEUE(&ic->ic_mgtq, m);
+	(ic->ic_ifp->if_start)(ic->ic_ifp);
+	return 0;
 }
 
 /*
@@ -294,12 +1051,10 @@
 	int type, int arg)
 {
 #define	senderr(_x, _v)	do { ic->ic_stats._v++; ret = _x; goto bad; } while (0)
-	struct ifnet *ifp = &ic->ic_if;
 	struct mbuf *m;
-	uint8_t *frm;
-	enum ieee80211_phymode mode;
-	uint16_t capinfo;
-	int ret, timer;
+	u_int8_t *frm;
+	u_int16_t capinfo;
+	int has_challenge, is_shared_key, ret, timer, status;
 
 	KASSERT(ni != NULL, ("null node"));
 
@@ -308,34 +1063,15 @@
 	 * the xmit is complete all the way in the driver.  On error we
 	 * will remove our reference.
 	 */
-	if (ni != ic->ic_bss)
-		ieee80211_ref_node(ni);
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_NODE,
+		"ieee80211_ref_node (%s:%u) %p<%6D> refcnt %d\n",
+		__func__, __LINE__,
+		ni, (ni->ni_macaddr), ":",
+		ieee80211_node_refcnt(ni)+1);
+	ieee80211_ref_node(ni);
+
 	timer = 0;
 	switch (type) {
-	case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
-		/*
-		 * prreq frame format
-		 *	[tlv] ssid
-		 *	[tlv] supported rates
-		 *	[tlv] extended supported rates
-		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 2 + ic->ic_des_esslen
-		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
-		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
-		frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen);
-		mode = ieee80211_chan2mode(ic, ni->ni_chan);
-		frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
-		frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
-
-		timer = IEEE80211_TRANS_WAIT;
-		break;
-
 	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
 		/*
 		 * probe response frame format
@@ -346,39 +1082,50 @@
 		 *	[tlv] supported rates
 		 *	[tlv] parameter set (FH/DS)
 		 *	[tlv] parameter set (IBSS)
+		 *	[tlv] extended rate phy (ERP)
 		 *	[tlv] extended supported rates
+		 *	[tlv] WPA
+		 *	[tlv] WME (optional)
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 8 + 2 + 2 + 2
-		       + 2 + ni->ni_esslen
+		m = ieee80211_getmgtframe(&frm,
+			 8
+		       + sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
+		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
-		       + (ic->ic_phytype == IEEE80211_T_FH ? 7 : 3)
+		       + 7	/* max(7,3) */
 		       + 6
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 3
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       /* XXX !WPA1+WPA2 fits w/o a cluster */
+		       + (ic->ic_flags & IEEE80211_F_WPA ?
+				2*sizeof(struct ieee80211_ie_wpa) : 0)
+		       + sizeof(struct ieee80211_wme_param)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		memset(frm, 0, 8);	/* timestamp should be filled later */
 		frm += 8;
-		*(uint16_t *)frm = htole16(ic->ic_bss->ni_intval);
+		*(u_int16_t *)frm = htole16(ic->ic_bss->ni_intval);
 		frm += 2;
 		if (ic->ic_opmode == IEEE80211_M_IBSS)
 			capinfo = IEEE80211_CAPINFO_IBSS;
 		else
 			capinfo = IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
-		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		*(uint16_t *)frm = htole16(capinfo);
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
 		frm = ieee80211_add_ssid(frm, ic->ic_bss->ni_essid,
 				ic->ic_bss->ni_esslen);
-		frm = ieee80211_add_rates(frm, &ic->ic_bss->ni_rates);
+		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 
 		if (ic->ic_phytype == IEEE80211_T_FH) {
                         *frm++ = IEEE80211_ELEMID_FHPARMS;
@@ -386,58 +1133,105 @@
                         *frm++ = ni->ni_fhdwell & 0x00ff;
                         *frm++ = (ni->ni_fhdwell >> 8) & 0x00ff;
                         *frm++ = IEEE80211_FH_CHANSET(
-			    ieee80211_chan2ieee(ic, ni->ni_chan));
+			    ieee80211_chan2ieee(ic, ic->ic_curchan));
                         *frm++ = IEEE80211_FH_CHANPAT(
-			    ieee80211_chan2ieee(ic, ni->ni_chan));
+			    ieee80211_chan2ieee(ic, ic->ic_curchan));
                         *frm++ = ni->ni_fhindex;
 		} else {
 			*frm++ = IEEE80211_ELEMID_DSPARMS;
 			*frm++ = 1;
-			*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+			*frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
 		}
 
 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
 			*frm++ = IEEE80211_ELEMID_IBSSPARMS;
 			*frm++ = 2;
 			*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
-		} else {	/* IEEE80211_M_HOSTAP */
-			/* TODO: TIM */
-			*frm++ = IEEE80211_ELEMID_TIM;
-			*frm++ = 4;	/* length */
-			*frm++ = 0;	/* DTIM count */
-			*frm++ = 1;	/* DTIM period */
-			*frm++ = 0;	/* bitmap control */
-			*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
 		}
-		frm = ieee80211_add_xrates(frm, &ic->ic_bss->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if (ic->ic_flags & IEEE80211_F_WPA)
+			frm = ieee80211_add_wpa(frm, ic);
+		if (ic->ic_curmode == IEEE80211_MODE_11G)
+			frm = ieee80211_add_erp(frm, ic);
+		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
+		if (ic->ic_flags & IEEE80211_F_WME)
+			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH:
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		status = arg >> 16;
+		arg &= 0xffff;
+		has_challenge = ((arg == IEEE80211_AUTH_SHARED_CHALLENGE ||
+		    arg == IEEE80211_AUTH_SHARED_RESPONSE) &&
+		    ni->ni_challenge != NULL);
+
+		/*
+		 * Deduce whether we're doing open authentication or
+		 * shared key authentication.  We do the latter if
+		 * we're in the middle of a shared key authentication
+		 * handshake or if we're initiating an authentication
+		 * request and configured to use shared key.
+		 */
+		is_shared_key = has_challenge ||
+		     arg >= IEEE80211_AUTH_SHARED_RESPONSE ||
+		     (arg == IEEE80211_AUTH_SHARED_REQUEST &&
+		      ic->ic_bss->ni_authmode == IEEE80211_AUTH_SHARED);
+
+		m = ieee80211_getmgtframe(&frm,
+			  3 * sizeof(u_int16_t)
+			+ (has_challenge && status == IEEE80211_STATUS_SUCCESS ?
+				sizeof(u_int16_t)+IEEE80211_CHALLENGE_LEN : 0)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2 * 3);
-		m->m_pkthdr.len = m->m_len = 6;
-		frm = mtod(m, uint8_t *);
-		/* TODO: shared key auth */
-		((uint16_t *)frm)[0] = htole16(IEEE80211_AUTH_ALG_OPEN);
-		((uint16_t *)frm)[1] = htole16(arg);	/* sequence number */
-		((uint16_t *)frm)[2] = 0;		/* status */
+			senderr(ENOMEM, is_tx_nobuf);
+
+		((u_int16_t *)frm)[0] =
+		    (is_shared_key) ? htole16(IEEE80211_AUTH_ALG_SHARED)
+		                    : htole16(IEEE80211_AUTH_ALG_OPEN);
+		((u_int16_t *)frm)[1] = htole16(arg);	/* sequence number */
+		((u_int16_t *)frm)[2] = htole16(status);/* status */
+
+		if (has_challenge && status == IEEE80211_STATUS_SUCCESS) {
+			((u_int16_t *)frm)[3] =
+			    htole16((IEEE80211_CHALLENGE_LEN << 8) |
+			    IEEE80211_ELEMID_CHALLENGE);
+			memcpy(&((u_int16_t *)frm)[4], ni->ni_challenge,
+			    IEEE80211_CHALLENGE_LEN);
+			m->m_pkthdr.len = m->m_len =
+				4 * sizeof(u_int16_t) + IEEE80211_CHALLENGE_LEN;
+			if (arg == IEEE80211_AUTH_SHARED_RESPONSE) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+				    "[%6D] request encrypt frame (%s)\n",
+				    (ni->ni_macaddr), ":", __func__);
+				m->m_flags |= M_LINK0; /* WEP-encrypt, please */
+			}
+		} else
+			m->m_pkthdr.len = m->m_len = 3 * sizeof(u_int16_t);
+
+		/* XXX not right for shared key */
+		if (status == IEEE80211_STATUS_SUCCESS)
+			IEEE80211_NODE_STAT(ni, tx_auth);
+		else
+			IEEE80211_NODE_STAT(ni, tx_auth_fail);
+
 		if (ic->ic_opmode == IEEE80211_M_STA)
 			timer = IEEE80211_TRANS_WAIT;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DEAUTH:
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D deauthenticate (reason %d)\n",
-			    ni->ni_macaddr, ":", arg);
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_AUTH,
+			"[%6D] send station deauthenticate (reason %d)\n",
+			(ni->ni_macaddr), ":", arg);
+		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2);
-		m->m_pkthdr.len = m->m_len = 2;
-		*mtod(m, uint16_t *) = htole16(arg);	/* reason */
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+		IEEE80211_NODE_STAT(ni, tx_deauth);
+		IEEE80211_NODE_STAT_SET(ni, tx_deauth_code, arg);
+
+		ieee80211_node_unauthorize(ni);		/* port closed */
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
@@ -450,39 +1244,43 @@
 		 *	[tlv] ssid
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME
+		 *	[tlv] user-specified ie's
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 sizeof(capinfo)
-		       + sizeof(uint16_t)
+		m = ieee80211_getmgtframe(&frm,
+			 sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
 		       + IEEE80211_ADDR_LEN
-		       + 2 + ni->ni_esslen
+		       + 2 + IEEE80211_NWID_LEN
 		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + sizeof(struct ieee80211_wme_info)
+		       + (ic->ic_opt_ie != NULL ? ic->ic_opt_ie_len : 0)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		capinfo = 0;
 		if (ic->ic_opmode == IEEE80211_M_IBSS)
 			capinfo |= IEEE80211_CAPINFO_IBSS;
 		else		/* IEEE80211_M_STA */
 			capinfo |= IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		/*
 		 * NB: Some 11a AP's reject the request when
 		 *     short premable is set.
 		 */
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
-		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
+		    (ic->ic_caps & IEEE80211_C_SHSLOT))
 			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
-		*(uint16_t *)frm = htole16(capinfo);
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(uint16_t *)frm = htole16(ic->ic_lintval);
+		*(u_int16_t *)frm = htole16(ic->ic_lintval);
 		frm += 2;
 
 		if (type == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) {
@@ -493,7 +1291,13 @@
 		frm = ieee80211_add_ssid(frm, ni->ni_essid, ni->ni_esslen);
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+			frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+		if (ic->ic_opt_ie != NULL) {
+			memcpy(frm, ic->ic_opt_ie, ic->ic_opt_ie_len);
+			frm += ic->ic_opt_ie_len;
+		}
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 
 		timer = IEEE80211_TRANS_WAIT;
 		break;
@@ -507,67 +1311,397 @@
 		 *	[2] association ID
 		 *	[tlv] supported rates
 		 *	[tlv] extended supported rates
+		 *	[tlv] WME (if enabled and STA enabled)
 		 */
-		m = ieee80211_getmbuf(MB_DONTWAIT, MT_DATA,
-			 sizeof(capinfo)
-		       + sizeof(uint16_t)
-		       + sizeof(uint16_t)
+		m = ieee80211_getmgtframe(&frm,
+			 sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
+		       + sizeof(u_int16_t)
 		       + 2 + IEEE80211_RATE_SIZE
-		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+		       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		       + sizeof(struct ieee80211_wme_param)
+		);
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		m->m_data += sizeof(struct ieee80211_frame);
-		frm = mtod(m, uint8_t *);
+			senderr(ENOMEM, is_tx_nobuf);
 
 		capinfo = IEEE80211_CAPINFO_ESS;
-		if (ic->ic_flags & IEEE80211_F_WEPON)
+		if (ic->ic_flags & IEEE80211_F_PRIVACY)
 			capinfo |= IEEE80211_CAPINFO_PRIVACY;
 		if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
-		    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
 			capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
-		*(uint16_t *)frm = htole16(capinfo);
+		if (ic->ic_flags & IEEE80211_F_SHSLOT)
+			capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+		*(u_int16_t *)frm = htole16(capinfo);
 		frm += 2;
 
-		*(uint16_t *)frm = htole16(arg);	/* status */
+		*(u_int16_t *)frm = htole16(arg);	/* status */
 		frm += 2;
 
-		if (arg == IEEE80211_STATUS_SUCCESS)
-			*(uint16_t *)frm = htole16(ni->ni_associd);
+		if (arg == IEEE80211_STATUS_SUCCESS) {
+			*(u_int16_t *)frm = htole16(ni->ni_associd);
+			IEEE80211_NODE_STAT(ni, tx_assoc);
+		} else
+			IEEE80211_NODE_STAT(ni, tx_assoc_fail);
 		frm += 2;
 
 		frm = ieee80211_add_rates(frm, &ni->ni_rates);
 		frm = ieee80211_add_xrates(frm, &ni->ni_rates);
-		m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *);
+		if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_wme_ie != NULL)
+			frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
-		if (ifp->if_flags & IFF_DEBUG)
-			if_printf(ifp, "station %6D disassociate (reason %d)\n",
-			    ni->ni_macaddr, ":", arg);
-		MGETHDR(m, MB_DONTWAIT, MT_DATA);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ASSOC,
+			"[%6D] send station disassociate (reason %d)\n",
+			(ni->ni_macaddr), ":", arg);
+		m = ieee80211_getmgtframe(&frm, sizeof(u_int16_t));
 		if (m == NULL)
-			senderr(ENOMEM, is_tx_nombuf);
-		MH_ALIGN(m, 2);
-		m->m_pkthdr.len = m->m_len = 2;
-		*mtod(m, uint16_t *) = htole16(arg);	/* reason */
+			senderr(ENOMEM, is_tx_nobuf);
+		*(u_int16_t *)frm = htole16(arg);	/* reason */
+		m->m_pkthdr.len = m->m_len = sizeof(u_int16_t);
+
+		IEEE80211_NODE_STAT(ni, tx_disassoc);
+		IEEE80211_NODE_STAT_SET(ni, tx_disassoc_code, arg);
 		break;
 
 	default:
-		IEEE80211_DPRINTF(("%s: invalid mgmt frame type %u\n",
-			__func__, type));
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"[%6D] invalid mgmt frame type %u\n",
+			(ni->ni_macaddr), ":", type);
 		senderr(EINVAL, is_tx_unknownmgt);
 		/* NOTREACHED */
 	}
 
-	ret = ieee80211_mgmt_output(ifp, ni, m, type);
+	ret = ieee80211_mgmt_output(ic, ni, m, type);
 	if (ret == 0) {
 		if (timer)
 			ic->ic_mgt_timer = timer;
 	} else {
 bad:
-		if (ni != ic->ic_bss)		/* remove ref we added */
-			ieee80211_free_node(ic, ni);
+		ieee80211_free_node(ni);
 	}
 	return ret;
 #undef senderr
 }
+
+/*
+ * Allocate a beacon frame and fillin the appropriate bits.
+ */
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo)
+{
+	struct ifnet *ifp = ic->ic_ifp;
+	struct ieee80211_frame *wh;
+	struct mbuf *m;
+	int pktlen;
+	u_int8_t *frm, *efrm;
+	u_int16_t capinfo;
+	struct ieee80211_rateset *rs;
+
+	/*
+	 * beacon frame format
+	 *	[8] time stamp
+	 *	[2] beacon interval
+	 *	[2] cabability information
+	 *	[tlv] ssid
+	 *	[tlv] supported rates
+	 *	[3] parameter set (DS)
+	 *	[tlv] parameter set (IBSS/TIM)
+	 *	[tlv] extended rate phy (ERP)
+	 *	[tlv] extended supported rates
+	 *	[tlv] WME parameters
+	 *	[tlv] WPA/RSN parameters
+	 * XXX Vendor-specific OIDs (e.g. Atheros)
+	 * NB: we allocate the max space required for the TIM bitmap.
+	 */
+	rs = &ni->ni_rates;
+	pktlen =   8					/* time stamp */
+		 + sizeof(u_int16_t)			/* beacon interval */
+		 + sizeof(u_int16_t)			/* capabilities */
+		 + 2 + ni->ni_esslen			/* ssid */
+	         + 2 + IEEE80211_RATE_SIZE		/* supported rates */
+	         + 2 + 1				/* DS parameters */
+		 + 2 + 4 + ic->ic_tim_len		/* DTIM/IBSSPARMS */
+		 + 2 + 1				/* ERP */
+	         + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE)
+		 + (ic->ic_caps & IEEE80211_C_WME ?	/* WME */
+			sizeof(struct ieee80211_wme_param) : 0)
+		 + (ic->ic_caps & IEEE80211_C_WPA ?	/* WPA 1+2 */
+			2*sizeof(struct ieee80211_ie_wpa) : 0)
+		 ;
+	m = ieee80211_getmgtframe(&frm, pktlen);
+	if (m == NULL) {
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"%s: cannot get buf; size %u\n", __func__, pktlen);
+		ic->ic_stats.is_tx_nobuf++;
+		return NULL;
+	}
+
+	memset(frm, 0, 8);	/* XXX timestamp is set by hardware/driver */
+	frm += 8;
+	*(u_int16_t *)frm = htole16(ni->ni_intval);
+	frm += 2;
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	bo->bo_caps = (u_int16_t *)frm;
+	*(u_int16_t *)frm = htole16(capinfo);
+	frm += 2;
+	*frm++ = IEEE80211_ELEMID_SSID;
+	if ((ic->ic_flags & IEEE80211_F_HIDESSID) == 0) {
+		*frm++ = ni->ni_esslen;
+		memcpy(frm, ni->ni_essid, ni->ni_esslen);
+		frm += ni->ni_esslen;
+	} else
+		*frm++ = 0;
+	frm = ieee80211_add_rates(frm, rs);
+	if (ic->ic_curmode != IEEE80211_MODE_FH) {
+		*frm++ = IEEE80211_ELEMID_DSPARMS;
+		*frm++ = 1;
+		*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+	}
+	bo->bo_tim = frm;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
+		*frm++ = 2;
+		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
+		bo->bo_tim_len = 0;
+	} else {
+		struct ieee80211_tim_ie *tie = (struct ieee80211_tim_ie *) frm;
+
+		tie->tim_ie = IEEE80211_ELEMID_TIM;
+		tie->tim_len = 4;	/* length */
+		tie->tim_count = 0;	/* DTIM count */ 
+		tie->tim_period = ic->ic_dtim_period;	/* DTIM period */
+		tie->tim_bitctl = 0;	/* bitmap control */
+		tie->tim_bitmap[0] = 0;	/* Partial Virtual Bitmap */
+		frm += sizeof(struct ieee80211_tim_ie);
+		bo->bo_tim_len = 1;
+	}
+	bo->bo_trailer = frm;
+	if (ic->ic_flags & IEEE80211_F_WME) {
+		bo->bo_wme = frm;
+		frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+		ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+	}
+	if (ic->ic_flags & IEEE80211_F_WPA)
+		frm = ieee80211_add_wpa(frm, ic);
+	if (ic->ic_curmode == IEEE80211_MODE_11G)
+		frm = ieee80211_add_erp(frm, ic);
+	efrm = ieee80211_add_xrates(frm, rs);
+	bo->bo_trailer_len = efrm - bo->bo_trailer;
+	m->m_pkthdr.len = m->m_len = efrm - mtod(m, u_int8_t *);
+
+	M_PREPEND(m, sizeof(struct ieee80211_frame), MB_DONTWAIT);
+	KASSERT(m != NULL, ("no space for 802.11 header?"));
+	wh = mtod(m, struct ieee80211_frame *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+	    IEEE80211_FC0_SUBTYPE_BEACON;
+	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+	*(u_int16_t *)wh->i_dur = 0;
+	IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	*(u_int16_t *)wh->i_seq = 0;
+
+	return m;
+}
+
+/*
+ * Update the dynamic parts of a beacon frame based on the current state.
+ */
+int
+ieee80211_beacon_update(struct ieee80211com *ic, struct ieee80211_node *ni,
+	struct ieee80211_beacon_offsets *bo, struct mbuf *m, int mcast)
+{
+	int len_changed = 0;
+	u_int16_t capinfo;
+	IEEE80211_LOCK_INFO;
+
+	IEEE80211_BEACON_LOCK(ic);
+	/* XXX faster to recalculate entirely or just changes? */
+	if (ic->ic_opmode == IEEE80211_M_IBSS)
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	else
+		capinfo = IEEE80211_CAPINFO_ESS;
+	if (ic->ic_flags & IEEE80211_F_PRIVACY)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	*bo->bo_caps = htole16(capinfo);
+
+	if (ic->ic_flags & IEEE80211_F_WME) {
+		struct ieee80211_wme_state *wme = &ic->ic_wme;
+
+		/*
+		 * Check for agressive mode change.  When there is
+		 * significant high priority traffic in the BSS
+		 * throttle back BE traffic by using conservative
+		 * parameters.  Otherwise BE uses agressive params
+		 * to optimize performance of legacy/non-QoS traffic.
+		 */
+		if (wme->wme_flags & WME_F_AGGRMODE) {
+			if (wme->wme_hipri_traffic >
+			    wme->wme_hipri_switch_thresh) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+				    "%s: traffic %u, disable aggressive mode\n",
+				    __func__, wme->wme_hipri_traffic);
+				wme->wme_flags &= ~WME_F_AGGRMODE;
+				ieee80211_wme_updateparams_locked(ic);
+					wme->wme_hipri_switch_hysteresis;
+			} else
+				wme->wme_hipri_traffic = 0;
+		} else {
+			if (wme->wme_hipri_traffic <=
+			    wme->wme_hipri_switch_thresh) {
+				IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+				    "%s: traffic %u, enable aggressive mode\n",
+				    __func__, wme->wme_hipri_traffic);
+				wme->wme_flags |= WME_F_AGGRMODE;
+				ieee80211_wme_updateparams_locked(ic);
+				wme->wme_hipri_traffic = 0;
+			} else
+				wme->wme_hipri_traffic =
+					wme->wme_hipri_switch_hysteresis;
+		}
+		if (ic->ic_flags & IEEE80211_F_WMEUPDATE) {
+			(void) ieee80211_add_wme_param(bo->bo_wme, wme);
+			ic->ic_flags &= ~IEEE80211_F_WMEUPDATE;
+		}
+	}
+
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {	/* NB: no IBSS support*/
+		struct ieee80211_tim_ie *tie =
+			(struct ieee80211_tim_ie *) bo->bo_tim;
+		if (ic->ic_flags & IEEE80211_F_TIMUPDATE) {
+			u_int timlen, timoff, i;
+			/* 
+			 * ATIM/DTIM needs updating.  If it fits in the
+			 * current space allocated then just copy in the
+			 * new bits.  Otherwise we need to move any trailing
+			 * data to make room.  Note that we know there is
+			 * contiguous space because ieee80211_beacon_allocate
+			 * insures there is space in the mbuf to write a
+			 * maximal-size virtual bitmap (based on ic_max_aid).
+			 */
+			/*
+			 * Calculate the bitmap size and offset, copy any
+			 * trailer out of the way, and then copy in the
+			 * new bitmap and update the information element.
+			 * Note that the tim bitmap must contain at least
+			 * one byte and any offset must be even.
+			 */
+			if (ic->ic_ps_pending != 0) {
+				timoff = 128;		/* impossibly large */
+				for (i = 0; i < ic->ic_tim_len; i++)
+					if (ic->ic_tim_bitmap[i]) {
+						timoff = i &~ 1;
+						break;
+					}
+				KASSERT(timoff != 128, ("tim bitmap empty!"));
+				for (i = ic->ic_tim_len-1; i >= timoff; i--)
+					if (ic->ic_tim_bitmap[i])
+						break;
+				timlen = 1 + (i - timoff);
+			} else {
+				timoff = 0;
+				timlen = 1;
+			}
+			if (timlen != bo->bo_tim_len) {
+				/* copy up/down trailer */
+				ovbcopy(bo->bo_trailer, tie->tim_bitmap+timlen,
+					bo->bo_trailer_len);
+				bo->bo_trailer = tie->tim_bitmap+timlen;
+				bo->bo_wme = bo->bo_trailer;
+				bo->bo_tim_len = timlen;
+
+				/* update information element */
+				tie->tim_len = 3 + timlen;
+				tie->tim_bitctl = timoff;
+				len_changed = 1;
+			}
+			memcpy(tie->tim_bitmap, ic->ic_tim_bitmap + timoff,
+				bo->bo_tim_len);
+
+			ic->ic_flags &= ~IEEE80211_F_TIMUPDATE;
+
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+				"%s: TIM updated, pending %u, off %u, len %u\n",
+				__func__, ic->ic_ps_pending, timoff, timlen);
+		}
+		/* count down DTIM period */
+		if (tie->tim_count == 0)
+			tie->tim_count = tie->tim_period - 1;
+		else
+			tie->tim_count--;
+		/* update state for buffered multicast frames on DTIM */
+		if (mcast && (tie->tim_count == 1 || tie->tim_period == 1))
+			tie->tim_bitctl |= 1;
+		else
+			tie->tim_bitctl &= ~1;
+	}
+	IEEE80211_BEACON_UNLOCK(ic);
+
+	return len_changed;
+}
+
+/*
+ * Save an outbound packet for a node in power-save sleep state.
+ * The new packet is placed on the node's saved queue, and the TIM
+ * is changed, if necessary.
+ */
+void
+ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, 
+		  struct mbuf *m)
+{
+	int qlen, age;
+
+	IEEE80211_NODE_SAVEQ_LOCK(ni);
+	if (IF_QFULL(&ni->ni_savedq)) {
+		IF_DROP(&ni->ni_savedq);
+		IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+			"[%6D] pwr save q overflow, drops %d (size %d)\n",
+			(ni->ni_macaddr),  ":",
+			ni->ni_savedq.ifq_drops, IEEE80211_PS_MAX_QUEUE);
+#ifdef IEEE80211_DEBUG
+		if (ieee80211_msg_dumppkts(ic))
+			ieee80211_dump_pkt(mtod(m, caddr_t), m->m_len, -1, -1);
+#endif
+		m_freem(m);
+		return;
+	}
+	/*
+	 * Tag the frame with it's expiry time and insert
+	 * it in the queue.  The aging interval is 4 times
+	 * the listen interval specified by the station. 
+	 * Frames that sit around too long are reclaimed
+	 * using this information.
+	 */
+	/* XXX handle overflow? */
+	age = ((ni->ni_intval * ic->ic_bintval) << 2) / 1024; /* TU -> secs */
+	_IEEE80211_NODE_SAVEQ_ENQUEUE(ni, m, qlen, age);
+	IEEE80211_NODE_SAVEQ_UNLOCK(ni);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_POWER,
+		"[%6D] save frame, %u now queued\n",
+		(ni->ni_macaddr), ":", qlen);
+
+	if (qlen == 1)
+		ic->ic_set_tim(ni, 1);
+}
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_proto.c src/sys/netproto/802_11/ieee80211_proto.c
--- src.preview/sys/netproto/802_11/ieee80211_proto.c	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_proto.c	2005-09-23 11:46:20.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -28,11 +28,10 @@
  * 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.
- *
- * $FreeBSD: src/sys/net80211/ieee80211_proto.c,v 1.8 2004/04/02 20:22:25 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.c,v 1.1 2004/07/26 16:30:17 joerg Exp $
  */
 
+#include <sys/cdefs.h>
+
 /*
  * IEEE 802.11 protocol support.
  */
@@ -40,35 +39,20 @@
 #include "opt_inet.h"
 
 #include <sys/param.h>
-#include <sys/systm.h> 
-#include <sys/mbuf.h>   
-#include <sys/malloc.h>
 #include <sys/kernel.h>
+#include <sys/systm.h> 
+ 
 #include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/endian.h>
-#include <sys/errno.h>
-#include <sys/bus.h>
-#include <sys/proc.h>
-#include <sys/sysctl.h>
+#include <sys/thread.h>
 
-#include <machine/atomic.h>
- 
 #include <net/if.h>
-#include <net/if_dl.h>
 #include <net/if_media.h>
-#include <net/if_arp.h>
-#include <net/ethernet.h>
-#include <net/if_llc.h>
 
 #include <netproto/802_11/ieee80211_var.h>
 
-#include <net/bpf.h>
-
-#ifdef INET
-#include <netinet/in.h> 
-#include <netinet/if_ether.h>
-#endif
+/* XXX tunables */
+#define	AGGRESSIVE_MODE_SWITCH_HYSTERESIS	3	/* pkts / 100ms */
+#define	HIGH_PRI_SWITCH_THRESH			10	/* pkts / 100ms */
 
 #define	IEEE80211_RATE2MBS(r)	(((r) & IEEE80211_RATE_VAL) / 2)
 
@@ -78,6 +62,12 @@
 	"beacon",	"atim",		"disassoc",	"auth",
 	"deauth",	"reserved#13",	"reserved#14",	"reserved#15"
 };
+const char *ieee80211_ctl_subtype_name[] = {
+	"reserved#0",	"reserved#1",	"reserved#2",	"reserved#3",
+	"reserved#3",	"reserved#5",	"reserved#6",	"reserved#7",
+	"reserved#8",	"reserved#9",	"ps_poll",	"rts",
+	"cts",		"ack",		"cf_end",	"cf_end_ack"
+};
 const char *ieee80211_state_name[IEEE80211_S_MAX] = {
 	"INIT",		/* IEEE80211_S_INIT */
 	"SCAN",		/* IEEE80211_S_SCAN */
@@ -85,24 +75,34 @@
 	"ASSOC",	/* IEEE80211_S_ASSOC */
 	"RUN"		/* IEEE80211_S_RUN */
 };
+const char *ieee80211_wme_acnames[] = {
+	"WME_AC_BE",
+	"WME_AC_BK",
+	"WME_AC_VI",
+	"WME_AC_VO",
+	"WME_UPSD",
+};
 
 static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int);
 
 void
-ieee80211_proto_attach(struct ifnet *ifp)
+ieee80211_proto_attach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+	struct ifnet *ifp = ic->ic_ifp;
 
-	ifp->if_hdrlen = sizeof(struct ieee80211_frame);
+	/* XXX room for crypto  */
+	ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4);
 
-#ifdef notdef
 	ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
-#else
-	ic->ic_rtsthreshold = IEEE80211_RTS_MAX;
-#endif
-	ic->ic_fragthreshold = 2346;		/* XXX not used yet */
-	ic->ic_fixed_rate = -1;			/* no fixed rate */
+	ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
+	ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
 	ic->ic_protmode = IEEE80211_PROT_CTSONLY;
+	ic->ic_roaming = IEEE80211_ROAMING_AUTO;
+
+	ic->ic_wme.wme_hipri_switch_hysteresis =
+		AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
+
+	lwkt_token_init(&ic->ic_mgtq.ifq_mtx);
 
 	/* protocol state change handler */
 	ic->ic_newstate = ieee80211_newstate;
@@ -113,18 +113,125 @@
 }
 
 void
-ieee80211_proto_detach(struct ifnet *ifp)
+ieee80211_proto_detach(struct ieee80211com *ic)
 {
-	struct ieee80211com *ic = (void *)ifp;
+
+	/*
+	 * This should not be needed as we detach when reseting
+	 * the state but be conservative here since the
+	 * authenticator may do things like spawn kernel threads.
+	 */
+	if (ic->ic_auth->ia_detach)
+		ic->ic_auth->ia_detach(ic);
 
 	IF_DRAIN(&ic->ic_mgtq);
+	lwkt_token_uninit(&ic->ic_mgtq.ifq_mtx);
+
+	/*
+	 * Detach any ACL'ator.
+	 */
+	if (ic->ic_acl != NULL)
+		ic->ic_acl->iac_detach(ic);
+}
+
+/*
+ * Simple-minded authenticator module support.
+ */
+
+#define	IEEE80211_AUTH_MAX	(IEEE80211_AUTH_WPA+1)
+/* XXX well-known names */
+static const char *auth_modnames[IEEE80211_AUTH_MAX] = {
+	"wlan_internal",	/* IEEE80211_AUTH_NONE */
+	"wlan_internal",	/* IEEE80211_AUTH_OPEN */
+	"wlan_internal",	/* IEEE80211_AUTH_SHARED */
+	"wlan_xauth",		/* IEEE80211_AUTH_8021X	 */
+	"wlan_internal",	/* IEEE80211_AUTH_AUTO */
+	"wlan_xauth",		/* IEEE80211_AUTH_WPA */
+};
+static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX];
+
+static const struct ieee80211_authenticator auth_internal = {
+	.ia_name		= "wlan_internal",
+	.ia_attach		= NULL,
+	.ia_detach		= NULL,
+	.ia_node_join		= NULL,
+	.ia_node_leave		= NULL,
+};
+
+/*
+ * Setup internal authenticators once; they are never unregistered.
+ */
+static void
+ieee80211_auth_setup(void)
+{
+	ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal);
+	ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal);
+}
+SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL);
+
+const struct ieee80211_authenticator *
+ieee80211_authenticator_get(int auth)
+{
+	if (auth >= IEEE80211_AUTH_MAX)
+		return NULL;
+	if (authenticators[auth] == NULL)
+		ieee80211_load_module(auth_modnames[auth]);
+	return authenticators[auth];
+}
+
+void
+ieee80211_authenticator_register(int type,
+	const struct ieee80211_authenticator *auth)
+{
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = auth;
+}
+
+void
+ieee80211_authenticator_unregister(int type)
+{
+
+	if (type >= IEEE80211_AUTH_MAX)
+		return;
+	authenticators[type] = NULL;
+}
+
+/*
+ * Very simple-minded ACL module support.
+ */
+/* XXX just one for now */
+static	const struct ieee80211_aclator *acl = NULL;
+
+void
+ieee80211_aclator_register(const struct ieee80211_aclator *iac)
+{
+	printf("wlan: %s acl policy registered\n", iac->iac_name);
+	acl = iac;
 }
 
 void
-ieee80211_print_essid(uint8_t *essid, int len)
+ieee80211_aclator_unregister(const struct ieee80211_aclator *iac)
 {
+	if (acl == iac)
+		acl = NULL;
+	printf("wlan: %s acl policy unregistered\n", iac->iac_name);
+}
+
+const struct ieee80211_aclator *
+ieee80211_aclator_get(const char *name)
+{
+	if (acl == NULL)
+		ieee80211_load_module("wlan_acl");
+	return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL;
+}
+
+void
+ieee80211_print_essid(const u_int8_t *essid, int len)
+{
+	const u_int8_t *p; 
 	int i;
-	uint8_t *p; 
 
 	if (len > IEEE80211_NWID_LEN)
 		len = IEEE80211_NWID_LEN;
@@ -146,28 +253,33 @@
 }
 
 void
-ieee80211_dump_pkt(uint8_t *buf, int len, int rate, int rssi)
+ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi)
 {
-	struct ieee80211_frame *wh;
+	const struct ieee80211_frame *wh;
 	int i;
 
-	wh = (struct ieee80211_frame *)buf;
+	wh = (const struct ieee80211_frame *)buf;
 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
 	case IEEE80211_FC1_DIR_NODS:
-		printf("NODS %6D->%6D(%6D)", wh->i_addr2, ":",
-		       wh->i_addr1, ":", wh->i_addr3, ":");
+		printf("NODS %6D", (wh->i_addr2), ":");
+		printf("->%6D", (wh->i_addr1), ":");
+		printf("(%6D)", (wh->i_addr3), ":");
 		break;
 	case IEEE80211_FC1_DIR_TODS:
-		printf("TODS %6D->%6D(%6D)", wh->i_addr2, ":",
-		       wh->i_addr3, ":", wh->i_addr1, ":");
+		printf("TODS %6D", (wh->i_addr2), ":");
+		printf("->%6D", (wh->i_addr3), ":");
+		printf("(%6D)", (wh->i_addr1), ":");
 		break;
 	case IEEE80211_FC1_DIR_FROMDS:
-		printf("FRDS %6D->%6D(%6D)", wh->i_addr3, ":",
-		       wh->i_addr1, ":", wh->i_addr2, ":");
+		printf("FRDS %6D", (wh->i_addr3), ":");
+		printf("->%6D", (wh->i_addr1), ":");
+		printf("(%6D)", (wh->i_addr2), ":");
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
-		printf("DSDS %6D->%6D(%6D->%6D)", (uint8_t *)&wh[1], ":",
-		       wh->i_addr3, ":", wh->i_addr2, ":", wh->i_addr1, ":");
+		printf("DSDS %6D", ((const u_int8_t *)&wh[1]), ":");
+		printf("->%6D", (wh->i_addr3), ":");
+		printf("(%6D", (wh->i_addr2), ":");
+		printf("->%6D)", (wh->i_addr1), ":");
 		break;
 	}
 	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
@@ -183,8 +295,13 @@
 		printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
 		break;
 	}
-	if (wh->i_fc[1] & IEEE80211_FC1_WEP)
-		printf(" WEP");
+	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+		int i;
+		printf(" WEP [IV");
+		for (i = 0; i < IEEE80211_WEP_IVLEN; i++)
+			printf(" %.02x", buf[sizeof(*wh)+i]);
+		printf(" KID %u]", buf[sizeof(*wh)+i] >> 6);
+	}
 	if (rate >= 0)
 		printf(" %dM", rate / 2);
 	if (rssi >= 0)
@@ -201,16 +318,24 @@
 }
 
 int
-ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags)
+ieee80211_fix_rate(struct ieee80211_node *ni, int flags)
 {
 #define	RV(v)	((v) & IEEE80211_RATE_VAL)
+	struct ieee80211com *ic = ni->ni_ic;
 	int i, j, ignore, error;
-	int okrate, badrate;
+	int okrate, badrate, fixedrate;
 	struct ieee80211_rateset *srs, *nrs;
-	uint8_t r;
+	u_int8_t r;
 
+	/*
+	 * If the fixed rate check was requested but no
+	 * fixed has been defined then just remove it.
+	 */
+	if ((flags & IEEE80211_F_DOFRATE) &&
+	    ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE)
+		flags &= ~IEEE80211_F_DOFRATE;
 	error = 0;
-	okrate = badrate = 0;
+	okrate = badrate = fixedrate = 0;
 	srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
 	nrs = &ni->ni_rates;
 	for (i = 0; i < nrs->rs_nrates; ) {
@@ -231,17 +356,10 @@
 		badrate = r;
 		if (flags & IEEE80211_F_DOFRATE) {
 			/*
-			 * Apply fixed rate constraint.  Note that we do
-			 * not apply the constraint to basic rates as
-			 * otherwise we may not be able to associate if
-			 * the rate set we submit to the AP is invalid
-			 * (e.g. fix rate at 36Mb/s which is not a basic
-			 * rate for 11a operation).
+			 * Check any fixed rate is included. 
 			 */
-			if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 &&
-			    ic->ic_fixed_rate >= 0 &&
-			    r != RV(srs->rs_rates[ic->ic_fixed_rate]))
-				ignore++;
+			if (r == RV(srs->rs_rates[ic->ic_fixed_rate]))
+				fixedrate = r;
 		}
 		if (flags & IEEE80211_F_DONEGO) {
 			/*
@@ -291,24 +409,445 @@
 			okrate = nrs->rs_rates[i];
 		i++;
 	}
-	if (okrate == 0 || error != 0)
+	if (okrate == 0 || error != 0 ||
+	    ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0))
 		return badrate | IEEE80211_RATE_BASIC;
 	else
 		return RV(okrate);
 #undef RV
 }
 
+/*
+ * Reset 11g-related state.
+ */
+void
+ieee80211_reset_erp(struct ieee80211com *ic)
+{
+	ic->ic_flags &= ~IEEE80211_F_USEPROT;
+	ic->ic_nonerpsta = 0;
+	ic->ic_longslotsta = 0;
+	/*
+	 * Short slot time is enabled only when operating in 11g
+	 * and not in an IBSS.  We must also honor whether or not
+	 * the driver is capable of doing it.
+	 */
+	ieee80211_set_shortslottime(ic,
+		ic->ic_curmode == IEEE80211_MODE_11A ||
+		(ic->ic_curmode == IEEE80211_MODE_11G &&
+		ic->ic_opmode == IEEE80211_M_HOSTAP &&
+		(ic->ic_caps & IEEE80211_C_SHSLOT)));
+	/*
+	 * Set short preamble and ERP barker-preamble flags.
+	 */
+	if (ic->ic_curmode == IEEE80211_MODE_11A ||
+	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) {
+		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
+	} else {
+		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
+		ic->ic_flags |= IEEE80211_F_USEBARKER;
+	}
+}
+
+/*
+ * Set the short slot time state and notify the driver.
+ */
+void
+ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff)
+{
+	if (onoff)
+		ic->ic_flags |= IEEE80211_F_SHSLOT;
+	else
+		ic->ic_flags &= ~IEEE80211_F_SHSLOT;
+	/* notify driver */
+	if (ic->ic_updateslot != NULL)
+		ic->ic_updateslot(ic->ic_ifp);
+}
+
+/*
+ * Check if the specified rate set supports ERP.
+ * NB: the rate set is assumed to be sorted.
+ */
+int
+ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs)
+{
+#define N(a)	(sizeof(a) / sizeof(a[0]))
+	static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 };
+	int i, j;
+
+	if (rs->rs_nrates < N(rates))
+		return 0;
+	for (i = 0; i < N(rates); i++) {
+		for (j = 0; j < rs->rs_nrates; j++) {
+			int r = rs->rs_rates[j] & IEEE80211_RATE_VAL;
+			if (rates[i] == r)
+				goto next;
+			if (r > rates[i])
+				return 0;
+		}
+		return 0;
+	next:
+		;
+	}
+	return 1;
+#undef N
+}
+
+/*
+ * Mark the basic rates for the 11g rate table based on the
+ * operating mode.  For real 11g we mark all the 11b rates
+ * and 6, 12, and 24 OFDM.  For 11b compatibility we mark only
+ * 11b rates.  There's also a pseudo 11a-mode used to mark only
+ * the basic OFDM rates.
+ */
+void
+ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode)
+{
+	static const struct ieee80211_rateset basic[] = {
+	    { 0 },			/* IEEE80211_MODE_AUTO */
+	    { 3, { 12, 24, 48 } },	/* IEEE80211_MODE_11A */
+	    { 2, { 2, 4 } },		/* IEEE80211_MODE_11B */
+	    { 4, { 2, 4, 11, 22 } },	/* IEEE80211_MODE_11G (mixed b/g) */
+	    { 0 },			/* IEEE80211_MODE_FH */
+					/* IEEE80211_MODE_PUREG (not yet) */
+	    { 7, { 2, 4, 11, 22, 12, 24, 48 } },
+	};
+	int i, j;
+
+	for (i = 0; i < rs->rs_nrates; i++) {
+		rs->rs_rates[i] &= IEEE80211_RATE_VAL;
+		for (j = 0; j < basic[mode].rs_nrates; j++)
+			if (basic[mode].rs_rates[j] == rs->rs_rates[i]) {
+				rs->rs_rates[i] |= IEEE80211_RATE_BASIC;
+				break;
+			}
+	}
+}
+
+/*
+ * WME protocol support.  The following parameters come from the spec.
+ */
+typedef struct phyParamType {
+	u_int8_t aifsn; 
+	u_int8_t logcwmin;
+	u_int8_t logcwmax; 
+	u_int16_t txopLimit;
+	u_int8_t acm;
+} paramType;
+
+static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
+	{ 3, 4, 6 },		/* IEEE80211_MODE_AUTO */
+	{ 3, 4, 6 },		/* IEEE80211_MODE_11A */ 
+	{ 3, 5, 7 },		/* IEEE80211_MODE_11B */ 
+	{ 3, 4, 6 },		/* IEEE80211_MODE_11G */ 
+	{ 3, 5, 7 },		/* IEEE80211_MODE_FH */ 
+	{ 2, 3, 5 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 3, 5 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
+	{ 7, 4, 10 },		/* IEEE80211_MODE_AUTO */
+	{ 7, 4, 10 },		/* IEEE80211_MODE_11A */ 
+	{ 7, 5, 10 },		/* IEEE80211_MODE_11B */ 
+	{ 7, 4, 10 },		/* IEEE80211_MODE_11G */ 
+	{ 7, 5, 10 },		/* IEEE80211_MODE_FH */ 
+	{ 7, 3, 10 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 7, 3, 10 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_11A */ 
+	{ 1, 4, 5, 188 },	/* IEEE80211_MODE_11B */ 
+	{ 1, 3, 4,  94 },	/* IEEE80211_MODE_11G */ 
+	{ 1, 4, 5, 188 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_AUTO */
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_11A */ 
+	{ 1, 3, 4, 102 },	/* IEEE80211_MODE_11B */ 
+	{ 1, 2, 3,  47 },	/* IEEE80211_MODE_11G */ 
+	{ 1, 3, 4, 102 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+
+static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
+	{ 3, 4, 10 },		/* IEEE80211_MODE_AUTO */
+	{ 3, 4, 10 },		/* IEEE80211_MODE_11A */ 
+	{ 3, 5, 10 },		/* IEEE80211_MODE_11B */ 
+	{ 3, 4, 10 },		/* IEEE80211_MODE_11G */ 
+	{ 3, 5, 10 },		/* IEEE80211_MODE_FH */ 
+	{ 2, 3, 10 },		/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 3, 10 },		/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_11A */ 
+	{ 2, 4, 5, 188 },	/* IEEE80211_MODE_11B */ 
+	{ 2, 3, 4,  94 },	/* IEEE80211_MODE_11G */ 
+	{ 2, 4, 5, 188 },	/* IEEE80211_MODE_FH */ 
+	{ 2, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 2, 2, 3,  94 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_AUTO */
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_11A */ 
+	{ 2, 3, 4, 102 },	/* IEEE80211_MODE_11B */ 
+	{ 2, 2, 3,  47 },	/* IEEE80211_MODE_11G */ 
+	{ 2, 3, 4, 102 },	/* IEEE80211_MODE_FH */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_A */ 
+	{ 1, 2, 2,  47 },	/* IEEE80211_MODE_TURBO_G */ 
+};
+
+void
+ieee80211_wme_initparams(struct ieee80211com *ic)
+{
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	const paramType *pPhyParam, *pBssPhyParam;
+	struct wmeParams *wmep;
+	int i;
+
+	if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+		return;
+
+	for (i = 0; i < WME_NUM_AC; i++) {
+		switch (i) {
+		case WME_AC_BK:
+			pPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+			pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode];
+			break;
+		case WME_AC_VI:
+			pPhyParam = &phyParamForAC_VI[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode];
+			break;
+		case WME_AC_VO:
+			pPhyParam = &phyParamForAC_VO[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode];
+			break;
+		case WME_AC_BE:
+		default:
+			pPhyParam = &phyParamForAC_BE[ic->ic_curmode];
+			pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode];
+			break;
+		}
+
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+			wmep->wmep_acm = pPhyParam->acm;
+			wmep->wmep_aifsn = pPhyParam->aifsn;	
+			wmep->wmep_logcwmin = pPhyParam->logcwmin;	
+			wmep->wmep_logcwmax = pPhyParam->logcwmax;		
+			wmep->wmep_txopLimit = pPhyParam->txopLimit;
+		} else {
+			wmep->wmep_acm = pBssPhyParam->acm;
+			wmep->wmep_aifsn = pBssPhyParam->aifsn;	
+			wmep->wmep_logcwmin = pBssPhyParam->logcwmin;	
+			wmep->wmep_logcwmax = pBssPhyParam->logcwmax;		
+			wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+
+		}	
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s chan [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[i]
+			, wmep->wmep_acm
+			, wmep->wmep_aifsn
+			, wmep->wmep_logcwmin
+			, wmep->wmep_logcwmax
+			, wmep->wmep_txopLimit
+		);
+
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		wmep->wmep_acm = pBssPhyParam->acm;
+		wmep->wmep_aifsn = pBssPhyParam->aifsn;	
+		wmep->wmep_logcwmin = pBssPhyParam->logcwmin;	
+		wmep->wmep_logcwmax = pBssPhyParam->logcwmax;		
+		wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s  bss [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[i]
+			, wmep->wmep_acm
+			, wmep->wmep_aifsn
+			, wmep->wmep_logcwmin
+			, wmep->wmep_logcwmax
+			, wmep->wmep_txopLimit
+		);
+	}
+	/* NB: check ic_bss to avoid NULL deref on initial attach */
+	if (ic->ic_bss != NULL) {
+		/*
+		 * Calculate agressive mode switching threshold based
+		 * on beacon interval.  This doesn't need locking since
+		 * we're only called before entering the RUN state at
+		 * which point we start sending beacon frames.
+		 */
+		wme->wme_hipri_switch_thresh =
+			(HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100;
+		ieee80211_wme_updateparams(ic);
+	}
+}
+
+/*
+ * Update WME parameters for ourself and the BSS.
+ */
+void
+ieee80211_wme_updateparams_locked(struct ieee80211com *ic)
+{
+	static const paramType phyParam[IEEE80211_MODE_MAX] = {
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_AUTO */ 
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_11A */ 
+		{ 2, 5, 10, 64 },	/* IEEE80211_MODE_11B */ 
+		{ 2, 4, 10, 64 },	/* IEEE80211_MODE_11G */ 
+		{ 2, 5, 10, 64 },	/* IEEE80211_MODE_FH */ 
+		{ 1, 3, 10, 64 },	/* IEEE80211_MODE_TURBO_A */ 
+		{ 1, 3, 10, 64 },	/* IEEE80211_MODE_TURBO_G */ 
+	};
+	struct ieee80211_wme_state *wme = &ic->ic_wme;
+	const struct wmeParams *wmep;
+	struct wmeParams *chanp, *bssp;
+	int i;
+
+       	/* set up the channel access parameters for the physical device */
+	for (i = 0; i < WME_NUM_AC; i++) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+
+		chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
+		wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+		chanp->wmep_aifsn = wmep->wmep_aifsn;
+		chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+		chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+		chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+	}
+
+	/*
+	 * This implements agressive mode as found in certain
+	 * vendors' AP's.  When there is significant high
+	 * priority (VI/VO) traffic in the BSS throttle back BE
+	 * traffic by using conservative parameters.  Otherwise
+	 * BE uses agressive params to optimize performance of
+	 * legacy/non-QoS traffic.
+	 */
+        if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	     (wme->wme_flags & WME_F_AGGRMODE) == 0) ||
+	    (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+	     (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) ||
+	    (ic->ic_flags & IEEE80211_F_WME) == 0) {
+		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+		chanp->wmep_aifsn = bssp->wmep_aifsn =
+			phyParam[ic->ic_curmode].aifsn;
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+			phyParam[ic->ic_curmode].logcwmin;
+		chanp->wmep_logcwmax = bssp->wmep_logcwmax =
+			phyParam[ic->ic_curmode].logcwmax;
+		chanp->wmep_txopLimit = bssp->wmep_txopLimit =
+			(ic->ic_caps & IEEE80211_C_BURST) ?
+				phyParam[ic->ic_curmode].txopLimit : 0;		
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s [acm %u aifsn %u log2(cwmin) %u "
+			"log2(cwmax) %u txpoLimit %u]\n", __func__
+			, ieee80211_wme_acnames[WME_AC_BE]
+			, chanp->wmep_acm
+			, chanp->wmep_aifsn
+			, chanp->wmep_logcwmin
+			, chanp->wmep_logcwmax
+			, chanp->wmep_txopLimit
+		);
+	}
+	
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+	    ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) == 0) {
+        	static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = {
+              		3,	/* IEEE80211_MODE_AUTO */
+              		3,	/* IEEE80211_MODE_11A */
+              		4,	/* IEEE80211_MODE_11B */
+              		3,	/* IEEE80211_MODE_11G */
+              		4,	/* IEEE80211_MODE_FH */
+              		3,	/* IEEE80211_MODE_TURBO_A */
+              		3,	/* IEEE80211_MODE_TURBO_G */
+		};
+		chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+		bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+		chanp->wmep_logcwmin = bssp->wmep_logcwmin = 
+			logCwMin[ic->ic_curmode];
+		IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+			"%s: %s log2(cwmin) %u\n", __func__
+			, ieee80211_wme_acnames[WME_AC_BE]
+			, chanp->wmep_logcwmin
+		);
+    	}	
+	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {	/* XXX ibss? */
+		/*
+		 * Arrange for a beacon update and bump the parameter
+		 * set number so associated stations load the new values.
+		 */
+		wme->wme_bssChanParams.cap_info =
+			(wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT;
+		ic->ic_flags |= IEEE80211_F_WMEUPDATE;
+	}
+
+	wme->wme_update(ic);
+
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME,
+		"%s: WME params updated, cap_info 0x%x\n", __func__,
+		ic->ic_opmode == IEEE80211_M_STA ?
+			wme->wme_wmeChanParams.cap_info :
+			wme->wme_bssChanParams.cap_info);
+}
+
+void
+ieee80211_wme_updateparams(struct ieee80211com *ic)
+{
+	IEEE80211_LOCK_INFO;
+
+	if (ic->ic_caps & IEEE80211_C_WME) {
+		IEEE80211_BEACON_LOCK(ic);
+		ieee80211_wme_updateparams_locked(ic);
+		IEEE80211_BEACON_UNLOCK(ic);
+	}
+}
+
+static void
+sta_disassoc(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = arg;
+
+	if (ni->ni_associd != 0) {
+		IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC,
+			IEEE80211_REASON_ASSOC_LEAVE);
+		ieee80211_node_leave(ic, ni);
+	}
+}
+
+static void
+sta_deauth(void *arg, struct ieee80211_node *ni)
+{
+	struct ieee80211com *ic = arg;
+
+	IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+		IEEE80211_REASON_ASSOC_LEAVE);
+}
+
 static int
-ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt)
+ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
 {
-	struct ifnet *ifp = &ic->ic_if;
+	struct ifnet *ifp = ic->ic_ifp;
 	struct ieee80211_node *ni;
 	enum ieee80211_state ostate;
-	struct lwkt_tokref ilock;
+	IEEE80211_LOCK_INFO;
 
 	ostate = ic->ic_state;
-	IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__,
-		ieee80211_state_name[ostate], ieee80211_state_name[nstate]));
+	IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
+		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
 	ic->ic_state = nstate;			/* state transition */
 	ni = ic->ic_bss;			/* NB: no reference held */
 	switch (nstate) {
@@ -322,22 +861,16 @@
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_DISASSOC,
 				    IEEE80211_REASON_ASSOC_LEAVE);
+				ieee80211_sta_leave(ic, ni);
 				break;
 			case IEEE80211_M_HOSTAP:
-				lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-					if (ni->ni_associd == 0)
-						continue;
-					IEEE80211_SEND_MGMT(ic, ni,
-					    IEEE80211_FC0_SUBTYPE_DISASSOC,
-					    IEEE80211_REASON_ASSOC_LEAVE);
-				}
-				lwkt_reltoken(&ilock);
+				ieee80211_iterate_nodes(&ic->ic_sta,
+					sta_disassoc, ic);
 				break;
 			default:
 				break;
 			}
-			/* FALLTHRU */
+			goto reset;
 		case IEEE80211_S_ASSOC:
 			switch (ic->ic_opmode) {
 			case IEEE80211_M_STA:
@@ -346,42 +879,32 @@
 				    IEEE80211_REASON_AUTH_LEAVE);
 				break;
 			case IEEE80211_M_HOSTAP:
-				lwkt_gettoken(&ilock, &ic->ic_nodetoken);
-				TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
-					IEEE80211_SEND_MGMT(ic, ni,
-					    IEEE80211_FC0_SUBTYPE_DEAUTH,
-					    IEEE80211_REASON_AUTH_LEAVE);
-				}
-				lwkt_reltoken(&ilock);
+				ieee80211_iterate_nodes(&ic->ic_sta,
+					sta_deauth, ic);
 				break;
 			default:
 				break;
 			}
-			/* FALLTHRU */
-		case IEEE80211_S_AUTH:
+			goto reset;
 		case IEEE80211_S_SCAN:
+			ieee80211_cancel_scan(ic);
+			goto reset;
+		case IEEE80211_S_AUTH:
+		reset:
 			ic->ic_mgt_timer = 0;
 			IF_DRAIN(&ic->ic_mgtq);
-			if (ic->ic_wep_ctx != NULL) {
-				free(ic->ic_wep_ctx, M_DEVBUF);
-				ic->ic_wep_ctx = NULL;
-			}
-			ieee80211_free_allnodes(ic);
+			ieee80211_reset_bss(ic);
 			break;
 		}
+		if (ic->ic_auth->ia_detach != NULL)
+			ic->ic_auth->ia_detach(ic);
 		break;
 	case IEEE80211_S_SCAN:
-		ic->ic_flags &= ~IEEE80211_F_SIBSS;
-		/* initialize bss for probe request */
-		IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr);
-		IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr);
-		ni->ni_rates = ic->ic_sup_rates[
-			ieee80211_chan2mode(ic, ni->ni_chan)];
-		ni->ni_associd = 0;
-		ni->ni_rstamp = 0;
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
+			if ((ic->ic_opmode == IEEE80211_M_HOSTAP ||
+			     ic->ic_opmode == IEEE80211_M_IBSS ||
+			     ic->ic_opmode == IEEE80211_M_AHDEMO) &&
 			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
 				/*
 				 * AP operation and we already have a channel;
@@ -389,51 +912,57 @@
 				 */
 				ieee80211_create_ibss(ic, ic->ic_des_chan);
 			} else {
-				ieee80211_begin_scan(ifp);
+				ieee80211_begin_scan(ic, arg);
 			}
 			break;
 		case IEEE80211_S_SCAN:
-			/* scan next */
-			if (ic->ic_flags & IEEE80211_F_ASCAN) {
-				IEEE80211_SEND_MGMT(ic, ni,
-				    IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0);
+			/*
+			 * Scan next. If doing an active scan and the
+			 * channel is not marked passive-only then send
+			 * a probe request.  Otherwise just listen for
+			 * beacons on the channel.
+			 */
+			if ((ic->ic_flags & IEEE80211_F_ASCAN) &&
+			    (ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) {
+				ieee80211_send_probereq(ni,
+					ic->ic_myaddr, ifp->if_broadcastaddr,
+					ifp->if_broadcastaddr,
+					ic->ic_des_essid, ic->ic_des_esslen,
+					ic->ic_opt_ie, ic->ic_opt_ie_len);
 			}
 			break;
 		case IEEE80211_S_RUN:
 			/* beacon miss */
-			if (ifp->if_flags & IFF_DEBUG) {
-				/* XXX bssid clobbered above */
-				if_printf(ifp, "no recent beacons from %6D;"
-				    " rescanning\n",
-				    ic->ic_bss->ni_bssid, ":");
-			}
-			ieee80211_free_allnodes(ic);
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE,
+				"no recent beacons from %6D; rescanning\n",
+				(ic->ic_bss->ni_bssid), ":");
+			ieee80211_sta_leave(ic, ni);
+			ic->ic_flags &= ~IEEE80211_F_SIBSS;	/* XXX */
 			/* FALLTHRU */
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
 			/* timeout restart scan */
-			ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr);
+			ni = ieee80211_find_node(&ic->ic_scan,
+				ic->ic_bss->ni_macaddr);
 			if (ni != NULL) {
 				ni->ni_fails++;
 				ieee80211_unref_node(&ni);
 			}
-			ieee80211_begin_scan(ifp);
+			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO)
+				ieee80211_begin_scan(ic, arg);
 			break;
 		}
 		break;
 	case IEEE80211_S_AUTH:
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
-			break;
 		case IEEE80211_S_SCAN:
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_AUTH, 1);
 			break;
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
-			switch (mgt) {
+			switch (arg) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				/* ??? */
 				IEEE80211_SEND_MGMT(ic, ni,
@@ -445,16 +974,19 @@
 			}
 			break;
 		case IEEE80211_S_RUN:
-			switch (mgt) {
+			switch (arg) {
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				IEEE80211_SEND_MGMT(ic, ni,
 				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
 				ic->ic_state = ostate;	/* stay RUN */
 				break;
 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
-				/* try to reauth */
-				IEEE80211_SEND_MGMT(ic, ni,
-				    IEEE80211_FC0_SUBTYPE_AUTH, 1);
+				ieee80211_sta_leave(ic, ni);
+				if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+					/* try to reauth */
+					IEEE80211_SEND_MGMT(ic, ni,
+					    IEEE80211_FC0_SUBTYPE_AUTH, 1);
+				}
 				break;
 			}
 			break;
@@ -465,49 +997,88 @@
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
 		case IEEE80211_S_ASSOC:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				"%s: invalid transition\n", __func__);
 			break;
 		case IEEE80211_S_AUTH:
 			IEEE80211_SEND_MGMT(ic, ni,
 			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
 			break;
 		case IEEE80211_S_RUN:
-			IEEE80211_SEND_MGMT(ic, ni,
-			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+			ieee80211_sta_leave(ic, ni);
+			if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) {
+				IEEE80211_SEND_MGMT(ic, ni,
+				    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+			}
 			break;
 		}
 		break;
 	case IEEE80211_S_RUN:
+		if (ic->ic_flags & IEEE80211_F_WPA) {
+			/* XXX validate prerequisites */
+		}
 		switch (ostate) {
 		case IEEE80211_S_INIT:
+			if (ic->ic_opmode == IEEE80211_M_MONITOR)
+				break;
+			/* fall thru... */
 		case IEEE80211_S_AUTH:
+			IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY,
+				"%s: invalid transition\n", __func__);
+			/* fall thru... */
 		case IEEE80211_S_RUN:
-			IEEE80211_DPRINTF(("%s: invalid transition\n",
-				__func__));
 			break;
 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
 		case IEEE80211_S_ASSOC:		/* infra mode */
 			KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates,
 				("%s: bogus xmit rate %u setup\n", __func__,
 					ni->ni_txrate));
-			if (ifp->if_flags & IFF_DEBUG) {
-				if_printf(ifp, " ");
+#ifdef IEEE80211_DEBUG
+			if (ieee80211_msg_debug(ic)) {
 				if (ic->ic_opmode == IEEE80211_M_STA)
-					printf("associated ");
+					if_printf(ifp, "associated ");
 				else
-					printf("synchronized ");
-				printf("with %6D ssid ", ni->ni_bssid, ":");
+					if_printf(ifp, "synchronized ");
+				printf("with %6D ssid ",
+				    (ni->ni_bssid), ":");
 				ieee80211_print_essid(ic->ic_bss->ni_essid,
 				    ni->ni_esslen);
 				printf(" channel %d start %uMb\n",
-					ieee80211_chan2ieee(ic, ni->ni_chan),
+					ieee80211_chan2ieee(ic, ic->ic_curchan),
 					IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate]));
 			}
+#endif
 			ic->ic_mgt_timer = 0;
-			(*ifp->if_start)(ifp);
+			if (ic->ic_opmode == IEEE80211_M_STA)
+				ieee80211_notify_node_join(ic, ni, 
+					arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
+			(*ifp->if_start)(ifp);	/* XXX not authorized yet */
 			break;
 		}
+		/*
+		 * Start/stop the authenticator when operating as an
+		 * AP.  We delay until here to allow configuration to
+		 * happen out of order.
+		 */
+		if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */
+		    ic->ic_auth->ia_attach != NULL) {
+			/* XXX check failure */
+			ic->ic_auth->ia_attach(ic);
+		} else if (ic->ic_auth->ia_detach != NULL) {
+			ic->ic_auth->ia_detach(ic);
+		}
+		/*
+		 * When 802.1x is not in use mark the port authorized
+		 * at this point so traffic can flow.
+		 */
+		if (ni->ni_authmode != IEEE80211_AUTH_8021X)
+			ieee80211_node_authorize(ni);
+		/*
+		 * Enable inactivity processing.
+		 * XXX
+		 */
+		ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT;
+		ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT;
 		break;
 	}
 	return 0;
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_proto.h src/sys/netproto/802_11/ieee80211_proto.h
--- src.preview/sys/netproto/802_11/ieee80211_proto.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_proto.h	2005-09-23 11:46:21.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,10 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_proto.h,v 1.4 2003/08/19 22:17:03 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_proto.h,v 1.1 2004/07/26 16:30:17 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_PROTO_H_
-#define	_NETPROTO_802_11_IEEE80211_PROTO_H_
+#ifndef _NET80211_IEEE80211_PROTO_H_
+#define _NET80211_IEEE80211_PROTO_H_
 
 /*
  * 802.11 protocol implementation definitions.
@@ -53,28 +51,206 @@
 	((*(_ic)->ic_send_mgmt)(_ic, _ni, _type, _arg))
 
 extern	const char *ieee80211_mgt_subtype_name[];
+extern	const char *ieee80211_phymode_name[];
 
-extern	void ieee80211_proto_attach(struct ifnet *);
-extern	void ieee80211_proto_detach(struct ifnet *);
+void	ieee80211_proto_attach(struct ieee80211com *);
+void	ieee80211_proto_detach(struct ieee80211com *);
 
 struct ieee80211_node;
-extern	void ieee80211_input(struct ifnet *, struct mbuf *,
-		struct ieee80211_node *, int, uint32_t);
-extern	void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
-		struct ieee80211_node *, int, int, uint32_t);
-extern	int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
+int	ieee80211_input(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *, int, u_int32_t);
+int	ieee80211_setup_rates(struct ieee80211_node *ni,
+		const u_int8_t *rates, const u_int8_t *xrates, int flags);
+void	ieee80211_saveie(u_int8_t **, const u_int8_t *);
+void	ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *, int, int, u_int32_t);
+int	ieee80211_send_nulldata(struct ieee80211_node *);
+int	ieee80211_send_probereq(struct ieee80211_node *ni,
+		const u_int8_t sa[IEEE80211_ADDR_LEN],
+		const u_int8_t da[IEEE80211_ADDR_LEN],
+		const u_int8_t bssid[IEEE80211_ADDR_LEN],
+		const u_int8_t *ssid, size_t ssidlen,
+		const void *optie, size_t optielen);
+int	ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
 		int, int);
-extern	struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
-		struct ieee80211_node **);
-extern	struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);
-extern	uint8_t *ieee80211_add_rates(uint8_t *frm,
-		const struct ieee80211_rateset *);
+int	ieee80211_classify(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *);
+struct mbuf *ieee80211_encap(struct ieee80211com *, struct mbuf *,
+		struct ieee80211_node *);
+void	ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, 
+		struct mbuf *);
+
+void	ieee80211_reset_erp(struct ieee80211com *);
+void	ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
+int	ieee80211_iserp_rateset(struct ieee80211com *,
+		struct ieee80211_rateset *);
+void	ieee80211_set11gbasicrates(struct ieee80211_rateset *,
+		enum ieee80211_phymode);
+
+ * Return the size of the 802.11 header for a management or data frame.
+ */
+static __inline int
+ieee80211_hdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+	int size = sizeof(struct ieee80211_frame);
+
+	/* NB: we don't handle control frames */
+	KASSERT((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL,
+		("%s: control frame", __func__));
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+		size += IEEE80211_ADDR_LEN;
+	if (IEEE80211_QOS_HAS_SEQ(wh))
+		size += sizeof(u_int16_t);
+	return size;
+}
+
+/*
+ * Return the size of the 802.11 header; handles any type of frame.
+ */
+static __inline int
+ieee80211_anyhdrsize(const void *data)
+{
+	const struct ieee80211_frame *wh = data;
+
+	if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+		switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+		case IEEE80211_FC0_SUBTYPE_CTS:
+		case IEEE80211_FC0_SUBTYPE_ACK:
+			return sizeof(struct ieee80211_frame_ack);
+		}
+		return sizeof(struct ieee80211_frame_min);
+	} else
+		return ieee80211_hdrsize(data);
+}
+
+/*
+ * Template for an in-kernel authenticator.  Authenticators
+ * register with the protocol code and are typically loaded
+ * as separate modules as needed.
+ */
+struct ieee80211_authenticator {
+	const char *ia_name;		/* printable name */
+	int	(*ia_attach)(struct ieee80211com *);
+	void	(*ia_detach)(struct ieee80211com *);
+	void	(*ia_node_join)(struct ieee80211com *,
+				struct ieee80211_node *);
+	void	(*ia_node_leave)(struct ieee80211com *,
+				struct ieee80211_node *);
+};
+void	ieee80211_authenticator_register(int type,
+		const struct ieee80211_authenticator *);
+void	ieee80211_authenticator_unregister(int type);
+const struct ieee80211_authenticator *ieee80211_authenticator_get(int auth);
+
+struct ieee80211req;
+/*
+ * Template for an MAC ACL policy module.  Such modules
+ * register with the protocol code and are passed the sender's
+ * address of each received frame for validation.
+ */
+struct ieee80211_aclator {
+	const char *iac_name;		/* printable name */
+	int	(*iac_attach)(struct ieee80211com *);
+	void	(*iac_detach)(struct ieee80211com *);
+	int	(*iac_check)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_add)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_remove)(struct ieee80211com *,
+			const u_int8_t mac[IEEE80211_ADDR_LEN]);
+	int	(*iac_flush)(struct ieee80211com *);
+	int	(*iac_setpolicy)(struct ieee80211com *, int);
+	int	(*iac_getpolicy)(struct ieee80211com *);
+	int	(*iac_setioctl)(struct ieee80211com *, struct ieee80211req *);
+	int	(*iac_getioctl)(struct ieee80211com *, struct ieee80211req *);
+};
+void	ieee80211_aclator_register(const struct ieee80211_aclator *);
+void	ieee80211_aclator_unregister(const struct ieee80211_aclator *);
+const struct ieee80211_aclator *ieee80211_aclator_get(const char *name);
+
+/* flags for ieee80211_fix_rate() */
+#define	IEEE80211_F_DOSORT	0x00000001	/* sort rate list */
+#define	IEEE80211_F_DOFRATE	0x00000002	/* use fixed rate */
+#define	IEEE80211_F_DONEGO	0x00000004	/* calc negotiated rate */
+#define	IEEE80211_F_DODEL	0x00000008	/* delete ignore rate */
+int	ieee80211_fix_rate(struct ieee80211_node *, int);
+
+/*
+ * WME/WMM support.
+ */
+struct wmeParams {
+	u_int8_t	wmep_acm;
+	u_int8_t	wmep_aifsn;
+	u_int8_t	wmep_logcwmin;		/* log2(cwmin) */
+	u_int8_t	wmep_logcwmax;		/* log2(cwmax) */
+	u_int8_t	wmep_txopLimit;
+	u_int8_t	wmep_noackPolicy;	/* 0 (ack), 1 (no ack) */
+};
+#define	IEEE80211_TXOP_TO_US(_txop)	((_txop)<<5)
+#define	IEEE80211_US_TO_TXOP(_us)	((_us)>>5)
+
+struct chanAccParams {
+	u_int8_t	cap_info;		/* version of the current set */
+	struct wmeParams cap_wmeParams[WME_NUM_AC];
+};
+
+struct ieee80211_wme_state {
+	u_int	wme_flags;
+#define	WME_F_AGGRMODE	0x00000001	/* STATUS: WME agressive mode */
+	u_int	wme_hipri_traffic;	/* VI/VO frames in beacon interval */
+	u_int	wme_hipri_switch_thresh;/* agressive mode switch thresh */
+	u_int	wme_hipri_switch_hysteresis;/* agressive mode switch hysteresis */
+
+	struct wmeParams wme_params[4];		/* from assoc resp for each AC*/
+	struct chanAccParams wme_wmeChanParams;	/* WME params applied to self */
+	struct chanAccParams wme_wmeBssChanParams;/* WME params bcast to stations */
+	struct chanAccParams wme_chanParams;	/* params applied to self */
+	struct chanAccParams wme_bssChanParams;	/* params bcast to stations */
+
+	int	(*wme_update)(struct ieee80211com *);
+};
+
+void	ieee80211_wme_initparams(struct ieee80211com *);
+void	ieee80211_wme_updateparams(struct ieee80211com *);
+void	ieee80211_wme_updateparams_locked(struct ieee80211com *);
+
 #define	ieee80211_new_state(_ic, _nstate, _arg) \
 	(((_ic)->ic_newstate)((_ic), (_nstate), (_arg)))
-extern	uint8_t *ieee80211_add_xrates(uint8_t *frm,
-		const struct ieee80211_rateset *);
-extern	void ieee80211_print_essid(uint8_t *, int);
-extern	void ieee80211_dump_pkt(uint8_t *, int, int, int);
+void	ieee80211_print_essid(const u_int8_t *, int);
+void	ieee80211_dump_pkt(const u_int8_t *, int, int, int);
 
 extern	const char *ieee80211_state_name[IEEE80211_S_MAX];
-#endif /* _NETPROTO_802_11_IEEE80211_PROTO_H_ */
+extern	const char *ieee80211_wme_acnames[];
+
+/*
+ * Beacon frames constructed by ieee80211_beacon_alloc
+ * have the following structure filled in so drivers
+ * can update the frame later w/ minimal overhead.
+ */
+struct ieee80211_beacon_offsets {
+	u_int16_t	*bo_caps;	/* capabilities */
+	u_int8_t	*bo_tim;	/* start of atim/dtim */
+	u_int8_t	*bo_wme;	/* start of WME parameters */
+	u_int8_t	*bo_trailer;	/* start of fixed-size trailer */
+	u_int16_t	bo_tim_len;	/* atim/dtim length in bytes */
+	u_int16_t	bo_trailer_len;	/* trailer length in bytes */
+};
+struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *);
+int	ieee80211_beacon_update(struct ieee80211com *,
+		struct ieee80211_node *, struct ieee80211_beacon_offsets *,
+		struct mbuf *, int broadcast);
+
+/*
+ * Notification methods called from the 802.11 state machine.
+ * Note that while these are defined here, their implementation
+ * is OS-specific.
+ */
+void	ieee80211_notify_node_join(struct ieee80211com *,
+		struct ieee80211_node *, int newassoc);
+void	ieee80211_notify_node_leave(struct ieee80211com *,
+		struct ieee80211_node *);
+void	ieee80211_notify_scan_done(struct ieee80211com *);
+#endif /* _NET80211_IEEE80211_PROTO_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_radiotap.h src/sys/netproto/802_11/ieee80211_radiotap.h
--- src.preview/sys/netproto/802_11/ieee80211_radiotap.h	2004-07-26 12:30:17.000000000 -0400
+++ src/sys/netproto/802_11/ieee80211_radiotap.h	2005-09-23 11:24:22.000000000 -0400
@@ -1,8 +1,7 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.3 2004/04/05 22:13:21 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.3 2003/11/16 09:02:42 dyoung Exp $ */
-/* $DragonFly: src/sys/netproto/802_11/ieee80211_radiotap.h,v 1.1 2004/07/26 16:30:17 joerg Exp $ */
+/* $FreeBSD$ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.10 2005/01/04 00:34:58 dyoung Exp $ */
 
-/*
+/*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -30,9 +29,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
  * OF SUCH DAMAGE.
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211RADIOTAP_H_
-#define	_NETPROTO_802_11_IEEE80211RADIOTAP_H_
+#ifndef _NET_IF_IEEE80211RADIOTAP_H_
+#define _NET_IF_IEEE80211RADIOTAP_H_
 
 /* A generic radio capture format is desirable. There is one for
  * Linux, but it is neither rigidly defined (there were not even
@@ -48,26 +46,31 @@
  * function of...") that I cannot set false expectations for lawyerly
  * readers.
  */
-#ifdef _KERNEL
+#if defined(__KERNEL__) || defined(_KERNEL)
 #ifndef DLT_IEEE802_11_RADIO
 #define	DLT_IEEE802_11_RADIO	127	/* 802.11 plus WLAN header */
 #endif
-#endif /* _KERNEL */
+#endif /* defined(__KERNEL__) || defined(_KERNEL) */
+
+/* XXX tcpdump/libpcap do not tolerate variable-length headers,
+ * yet, so we pad every radiotap header to 64 bytes. Ugh.
+ */
+#define IEEE80211_RADIOTAP_HDRLEN	64
 
 /* The radio capture header precedes the 802.11 header. */
 struct ieee80211_radiotap_header {
-	uint8_t		it_version;	/* Version 0. Only increases
+	u_int8_t	it_version;	/* Version 0. Only increases
 					 * for drastic changes,
 					 * introduction of compatible
 					 * new fields does not count.
 					 */
-	uint8_t		it_pad;
-	uint16_t	it_len;         /* length of the whole
+	u_int8_t	it_pad;
+	u_int16_t       it_len;         /* length of the whole
 					 * header in bytes, including
 					 * it_version, it_pad,
 					 * it_len, and data fields.
 					 */
-	uint32_t	it_present;     /* A bitmap telling which
+	u_int32_t       it_present;     /* A bitmap telling which
 					 * fields are present. Set bit 31
 					 * (0x80000000) to extend the
 					 * bitmap by another 32 bits.
@@ -76,87 +79,87 @@
 					 */
 } __attribute__((__packed__));
 
-/* Name                                 Data type	Units
- * ----                                 ---------	-----
+/* Name                                 Data type       Units
+ * ----                                 ---------       -----
  *
- * IEEE80211_RADIOTAP_TSFT              uint64_t	microseconds
+ * IEEE80211_RADIOTAP_TSFT              u_int64_t       microseconds
  *
  *      Value in microseconds of the MAC's 64-bit 802.11 Time
  *      Synchronization Function timer when the first bit of the
  *      MPDU arrived at the MAC. For received frames, only.
  *
- * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t	MHz, bitmap
+ * IEEE80211_RADIOTAP_CHANNEL           2 x u_int16_t   MHz, bitmap
  *
  *      Tx/Rx frequency in MHz, followed by flags (see below).
  *
- * IEEE80211_RADIOTAP_FHSS              uint16_t	see below
+ * IEEE80211_RADIOTAP_FHSS              u_int16_t       see below
  *
  *      For frequency-hopping radios, the hop set (first byte)
  *      and pattern (second byte).
  *
- * IEEE80211_RADIOTAP_RATE              uint8_t		500kb/s
+ * IEEE80211_RADIOTAP_RATE              u_int8_t        500kb/s
  *
  *      Tx/Rx data rate
  *
- * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      RF signal power at the antenna, decibel difference from
  *      one milliwatt.
  *
- * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      RF noise power at the antenna, decibel difference from one
  *      milliwatt.
  *
- * IEEE80211_RADIOTAP_DB_ANTSIGNAL      uint8_t		decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u_int8_t        decibel (dB)
  *
  *      RF signal power at the antenna, decibel difference from an
  *      arbitrary, fixed reference.
  *
- * IEEE80211_RADIOTAP_DB_ANTNOISE       uint8_t		decibel (dB)
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u_int8_t        decibel (dB)
  *
  *      RF noise power at the antenna, decibel difference from an
  *      arbitrary, fixed reference point.
  *
- * IEEE80211_RADIOTAP_BARKER_CODE_LOCK  uint16_t	unitless
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      u_int16_t       unitless
  *
  *      Quality of Barker code lock. Unitless. Monotonically
  *      nondecreasing with "better" lock strength. Called "Signal
  *      Quality" in datasheets.  (Is there a standard way to measure
  *      this?)
  *
- * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_t	unitless
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    u_int16_t       unitless
  *
  *      Transmit power expressed as unitless distance from max
  *      power set at factory calibration.  0 is max power.
  *      Monotonically nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t	decibels (dB)
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION u_int16_t       decibels (dB)
  *
  *      Transmit power expressed as decibel distance from max power
  *      set at factory calibration.  0 is max power.  Monotonically
  *      nondecreasing with lower power levels.
  *
- * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t		decibels from
- *							one milliwatt (dBm)
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      int8_t          decibels from
+ *                                                      one milliwatt (dBm)
  *
  *      Transmit power expressed as dBm (decibels from a 1 milliwatt
  *      reference). This is the absolute power level measured at
  *      the antenna port.
  *
- * IEEE80211_RADIOTAP_FLAGS             uint8_t		bitmap
+ * IEEE80211_RADIOTAP_FLAGS             u_int8_t        bitmap
  *
  *      Properties of transmitted and received frames. See flags
  *      defined below.
  *
- * IEEE80211_RADIOTAP_ANTENNA           uint8_t		antenna index
+ * IEEE80211_RADIOTAP_ANTENNA           u_int8_t        antenna index
  *
  *      Unitless indication of the Rx/Tx antenna for this packet.
  *      The first antenna is antenna 0.
  *
- * IEEE80211_RADIOTAP_FCS           	uint32_t	data
+ * IEEE80211_RADIOTAP_FCS           	u_int32_t       data
  *
  *	FCS from frame in network byte order.
  */
@@ -175,18 +178,17 @@
 	IEEE80211_RADIOTAP_ANTENNA = 11,
 	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
 	IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
-	IEEE80211_RADIOTAP_FCS = 14,
 	IEEE80211_RADIOTAP_EXT = 31,
 };
 
 #ifndef _KERNEL
 /* Channel flags. */
-#define	IEEE80211_CHAN_TURBO    0x0010  /* Turbo channel */
-#define	IEEE80211_CHAN_CCK      0x0020  /* CCK channel */
-#define	IEEE80211_CHAN_OFDM     0x0040  /* OFDM channel */
-#define	IEEE80211_CHAN_2GHZ     0x0080  /* 2 GHz spectrum channel. */
-#define	IEEE80211_CHAN_5GHZ     0x0100  /* 5 GHz spectrum channel */
-#define	IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
+#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
+#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
+#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
+#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
+#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
+#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
 #define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
 #define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
 #endif /* !_KERNEL */
@@ -205,5 +207,10 @@
 #define	IEEE80211_RADIOTAP_F_FRAG	0x08	/* sent/received
 						 * with fragmentation
 						 */
+#define	IEEE80211_RADIOTAP_F_FCS	0x10	/* frame includes FCS */
+#define	IEEE80211_RADIOTAP_F_DATAPAD	0x20	/* frame has padding between
+						 * 802.11 header and payload
+						 * (to 32-bit boundary)
+						 */
 
-#endif /* _NETPROTO_802_11_IEEE80211RADIOTAP_H_ */
+#endif /* _NET_IF_IEEE80211RADIOTAP_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_var.h src/sys/netproto/802_11/ieee80211_var.h
--- src.preview/sys/netproto/802_11/ieee80211_var.h	2005-01-25 19:37:40.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_var.h	2005-09-23 11:46:23.000000000 -0400
@@ -1,6 +1,6 @@
-/*
+/*-
  * Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,249 +29,262 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
- * $FreeBSD: src/sys/net80211/ieee80211_var.h,v 1.15 2004/04/05 22:10:26 sam Exp $
- * $DragonFly: src/sys/netproto/802_11/ieee80211_var.h,v 1.2 2005/01/26 00:37:40 joerg Exp $
+ * $FreeBSD$
  */
-
-#ifndef _NETPROTO_802_11_IEEE80211_VAR_H_
-#define	_NETPROTO_802_11_IEEE80211_VAR_H_
+#ifndef _NET80211_IEEE80211_VAR_H_
+#define _NET80211_IEEE80211_VAR_H_
 
 /*
  * Definitions for IEEE 802.11 drivers.
  */
+#define	IEEE80211_DEBUG
+#undef	IEEE80211_DEBUG_REFCNT			/* node refcnt stuff */
 
+/* NB: portability glue must go first */
+#ifdef __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#elif __FreeBSD__
+#include <net80211/ieee80211_freebsd.h>
+#elif __DragonFly__
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#elif __linux__
+#include <net80211/ieee80211_linux.h>
+#else
+#error	"No support for your operating system!"
+#endif
+
+#include <netproto/802_11/_ieee80211.h>
 #include <netproto/802_11/ieee80211.h>
 #include <netproto/802_11/ieee80211_crypto.h>
 #include <netproto/802_11/ieee80211_ioctl.h>		/* for ieee80211_stats */
 #include <netproto/802_11/ieee80211_node.h>
 #include <netproto/802_11/ieee80211_proto.h>
 
-#define	IEEE80211_CHAN_MAX	255
-#define	IEEE80211_CHAN_ANY	0xffff		/* token for ``any channel'' */
-#define	IEEE80211_CHAN_ANYC \
-	((struct ieee80211_channel *) IEEE80211_CHAN_ANY)
-
-#define	IEEE80211_TXPOWER_MAX	100	/* max power */
-#define	IEEE80211_TXPOWER_MIN	0	/* kill radio (if possible) */
-
-enum ieee80211_phytype {
-	IEEE80211_T_DS,			/* direct sequence spread spectrum */
-	IEEE80211_T_FH,			/* frequency hopping */
-	IEEE80211_T_OFDM,		/* frequency division multiplexing */
-	IEEE80211_T_TURBO,		/* high rate OFDM, aka turbo mode */
-};
-#define	IEEE80211_T_CCK	IEEE80211_T_DS	/* more common nomenclature */
+#define	IEEE80211_TXPOWER_MAX	100	/* .5 dbM (XXX units?) */
+#define	IEEE80211_TXPOWER_MIN	0	/* kill radio */
 
-/* XXX not really a mode; there are really multiple PHY's */
-enum ieee80211_phymode {
-	IEEE80211_MODE_AUTO	= 0,	/* autoselect */
-	IEEE80211_MODE_11A	= 1,	/* 5GHz, OFDM */
-	IEEE80211_MODE_11B	= 2,	/* 2GHz, CCK */
-	IEEE80211_MODE_11G	= 3,	/* 2GHz, OFDM */
-	IEEE80211_MODE_FH	= 4,	/* 2GHz, GFSK */
-	IEEE80211_MODE_TURBO	= 5,	/* 5GHz, OFDM, 2x clock */
-};
-#define	IEEE80211_MODE_MAX	(IEEE80211_MODE_TURBO+1)
+#define	IEEE80211_DTIM_MAX	15	/* max DTIM period */
+#define	IEEE80211_DTIM_MIN	1	/* min DTIM period */
+#define	IEEE80211_DTIM_DEFAULT	1	/* default DTIM period */
 
-enum ieee80211_opmode {
-	IEEE80211_M_STA		= 1,	/* infrastructure station */
-	IEEE80211_M_IBSS 	= 0,	/* IBSS (adhoc) station */
-	IEEE80211_M_AHDEMO	= 3,	/* Old lucent compatible adhoc demo */
-	IEEE80211_M_HOSTAP	= 6,	/* Software Access Point */
-	IEEE80211_M_MONITOR	= 8	/* Monitor mode */
-};
+/* NB: min+max come from WiFi requirements */
+#define	IEEE80211_BINTVAL_MAX	1000	/* max beacon interval (TU's) */
+#define	IEEE80211_BINTVAL_MIN	25	/* min beacon interval (TU's) */
+#define	IEEE80211_BINTVAL_DEFAULT 100	/* default beacon interval (TU's) */
 
-/*
- * 802.11g protection mode.
- */
-enum ieee80211_protmode {
-	IEEE80211_PROT_NONE	= 0,	/* no protection */
-	IEEE80211_PROT_CTSONLY	= 1,	/* CTS to self */
-	IEEE80211_PROT_RTSCTS	= 2,	/* RTS-CTS */
-};
+#define	IEEE80211_PS_SLEEP	0x1	/* STA is in power saving mode */
+#define	IEEE80211_PS_MAX_QUEUE	50	/* maximum saved packets */
 
-/*
- * Channels are specified by frequency and attributes.
- */
-struct ieee80211_channel {
-	uint16_t	ic_freq;	/* setting in Mhz */
-	uint16_t	ic_flags;	/* see below */
-};
+#define	IEEE80211_FIXED_RATE_NONE	-1
 
-/* bits 0-3 are for private use by drivers */
-/* channel attributes */
-#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */
-#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */
-#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */
-#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */
-#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */
-#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */
-#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */
-#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */
+#define	IEEE80211_RTS_DEFAULT		IEEE80211_RTS_MAX
+#define	IEEE80211_FRAG_DEFAULT		IEEE80211_FRAG_MAX
 
-/*
- * Useful combinations of channel characteristics.
- */
-#define	IEEE80211_CHAN_FHSS \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
-#define	IEEE80211_CHAN_A \
-	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
-#define	IEEE80211_CHAN_B \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
-#define	IEEE80211_CHAN_PUREG \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
-#define	IEEE80211_CHAN_G \
-	(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
-#define	IEEE80211_CHAN_T \
-	(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
-
-#define	IEEE80211_IS_CHAN_FHSS(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
-#define	IEEE80211_IS_CHAN_A(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
-#define	IEEE80211_IS_CHAN_B(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
-#define	IEEE80211_IS_CHAN_PUREG(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
-#define	IEEE80211_IS_CHAN_G(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
-#define	IEEE80211_IS_CHAN_T(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
-
-#define	IEEE80211_IS_CHAN_2GHZ(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0)
-#define	IEEE80211_IS_CHAN_5GHZ(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0)
-#define	IEEE80211_IS_CHAN_OFDM(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_OFDM) != 0)
-#define	IEEE80211_IS_CHAN_CCK(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_CCK) != 0)
-#define	IEEE80211_IS_CHAN_GFSK(_c) \
-	(((_c)->ic_flags & IEEE80211_CHAN_GFSK) != 0)
-
-/* ni_chan encoding for FH phy */
-#define	IEEE80211_FH_CHANMOD	80
-#define	IEEE80211_FH_CHAN(set,pat)	(((set)-1)*IEEE80211_FH_CHANMOD+(pat))
-#define	IEEE80211_FH_CHANSET(chan)	((chan)/IEEE80211_FH_CHANMOD+1)
-#define	IEEE80211_FH_CHANPAT(chan)	((chan)%IEEE80211_FH_CHANMOD)
+#define	IEEE80211_MS_TO_TU(x)	(((x) * 1000) / 1024)
+#define	IEEE80211_TU_TO_MS(x)	(((x) * 1024) / 1000)
+
+struct ieee80211_aclator;
+struct sysctl_ctx_list;
 
 struct ieee80211com {
-	struct arpcom		ic_ac;
+	SLIST_ENTRY(ieee80211com) ic_next;
+	struct ifnet		*ic_ifp;	/* associated device */
+	struct ieee80211_stats	ic_stats;	/* statistics */
+	struct sysctl_ctx_list	*ic_sysctl;	/* dynamic sysctl context */
+	u_int32_t		ic_debug;	/* debug msg flags */
+	int			ic_vap;		/* virtual AP index */
+	ieee80211_beacon_lock_t	ic_beaconlock;	/* beacon update lock */
+
+	int			(*ic_reset)(struct ifnet *);
 	void			(*ic_recv_mgmt)(struct ieee80211com *,
 				    struct mbuf *, struct ieee80211_node *,
-				    int, int, uint32_t);
+				    int, int, u_int32_t);
 	int			(*ic_send_mgmt)(struct ieee80211com *,
 				    struct ieee80211_node *, int, int);
 	int			(*ic_newstate)(struct ieee80211com *,
 				    enum ieee80211_state, int);
-	void			(*ic_newassoc)(struct ieee80211com *,
-				    struct ieee80211_node *, int);
-	uint8_t			ic_myaddr[IEEE80211_ADDR_LEN];
+	void			(*ic_newassoc)(struct ieee80211_node *, int);
+	void			(*ic_updateslot)(struct ifnet *);
+	void			(*ic_set_tim)(struct ieee80211_node *, int);
+	u_int8_t		ic_myaddr[IEEE80211_ADDR_LEN];
 	struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
 	struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
-	u_char			ic_chan_avail[roundup(IEEE80211_CHAN_MAX,NBBY)];
-	u_char			ic_chan_active[roundup(IEEE80211_CHAN_MAX, NBBY)];
-	u_char			ic_chan_scan[roundup(IEEE80211_CHAN_MAX,NBBY)];
+	u_int8_t		ic_chan_avail[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_active[IEEE80211_CHAN_BYTES];
+	u_int8_t		ic_chan_scan[IEEE80211_CHAN_BYTES];
+	struct ieee80211_node_table ic_scan;	/* scan candidates */
 	struct ifqueue		ic_mgtq;
-	uint32_t		ic_flags;	/* state flags */
-	uint32_t		ic_caps;	/* capabilities */
-	uint16_t		ic_modecaps;	/* set of mode capabilities */
-	uint16_t		ic_curmode;	/* current mode */
+	u_int32_t		ic_flags;	/* state flags */
+	u_int32_t		ic_flags_ext;	/* extended state flags */
+	u_int32_t		ic_caps;	/* capabilities */
+	u_int16_t		ic_modecaps;	/* set of mode capabilities */
+	u_int16_t		ic_curmode;	/* current mode */
 	enum ieee80211_phytype	ic_phytype;	/* XXX wrong for multi-mode */
 	enum ieee80211_opmode	ic_opmode;	/* operation mode */
 	enum ieee80211_state	ic_state;	/* 802.11 state */
 	enum ieee80211_protmode	ic_protmode;	/* 802.11g protection mode */
+	enum ieee80211_roamingmode ic_roaming;	/* roaming mode */
+	struct ieee80211_node_table ic_sta;	/* stations/neighbors */
+	u_int32_t		*ic_aid_bitmap;	/* association id map */
+	u_int16_t		ic_max_aid;
+	u_int16_t		ic_sta_assoc;	/* stations associated */
+	u_int16_t		ic_ps_sta;	/* stations in power save */
+	u_int16_t		ic_ps_pending;	/* ps sta's w/ pending frames */
+	u_int8_t		*ic_tim_bitmap;	/* power-save stations w/ data*/
+	u_int16_t		ic_tim_len;	/* ic_tim_bitmap size (bytes) */
+	u_int8_t		ic_dtim_period;	/* DTIM period */
+	u_int8_t		ic_dtim_count;	/* DTIM count for last bcn */
 	struct ifmedia		ic_media;	/* interface media config */
 	struct bpf_if		*ic_rawbpf;	/* packet filter structure */
 	struct ieee80211_node	*ic_bss;	/* information for this node */
 	struct ieee80211_channel *ic_ibss_chan;
+	struct ieee80211_channel *ic_curchan;	/* current channel */
 	int			ic_fixed_rate;	/* index to ic_sup_rates[] */
-	uint16_t		ic_rtsthreshold;
-	uint16_t		ic_fragthreshold;
-	struct lwkt_token	ic_nodetoken;	/* on node table */
-	u_int			ic_scangen;	/* gen# for timeout scan */
-	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211com *);
-	void			(*ic_node_free)(struct ieee80211com *,
-					struct ieee80211_node *);
-	void			(*ic_node_copy)(struct ieee80211com *,
-					struct ieee80211_node *,
-					const struct ieee80211_node *);
-	uint8_t			(*ic_node_getrssi)(struct ieee80211com *,
-					struct ieee80211_node *);
-	TAILQ_HEAD(, ieee80211_node) ic_node;	/* information of all nodes */
-	LIST_HEAD(, ieee80211_node) ic_hash[IEEE80211_NODE_HASHSIZE];
-	uint16_t		ic_lintval;	/* listen interval */
-	uint16_t		ic_holdover;	/* PM hold over duration */
-	uint16_t		ic_txmin;	/* min tx retry count */
-	uint16_t		ic_txmax;	/* max tx retry count */
-	uint16_t		ic_txlifetime;	/* tx lifetime */
-	uint16_t		ic_txpower;	/* tx power setting (dbM) */
-	uint16_t		ic_bmisstimeout;/* beacon miss threshold (ms) */
+	u_int16_t		ic_rtsthreshold;
+	u_int16_t		ic_fragthreshold;
+	struct ieee80211_node	*(*ic_node_alloc)(struct ieee80211_node_table*);
+	void			(*ic_node_free)(struct ieee80211_node *);
+	void			(*ic_node_cleanup)(struct ieee80211_node *);
+	u_int8_t		(*ic_node_getrssi)(const struct ieee80211_node*);
+	u_int16_t		ic_lintval;	/* listen interval */
+	u_int16_t		ic_bintval;	/* beacon interval */
+	u_int16_t		ic_holdover;	/* PM hold over duration */
+	u_int16_t		ic_txmin;	/* min tx retry count */
+	u_int16_t		ic_txmax;	/* max tx retry count */
+	u_int16_t		ic_txlifetime;	/* tx lifetime */
+	u_int16_t		ic_txpowlimit;	/* global tx power limit */
+	u_int16_t		ic_bmisstimeout;/* beacon miss threshold (ms) */
+	u_int16_t		ic_nonerpsta;	/* # non-ERP stations */
+	u_int16_t		ic_longslotsta;	/* # long slot time stations */
 	int			ic_mgt_timer;	/* mgmt timeout */
 	int			ic_inact_timer;	/* inactivity timer wait */
 	int			ic_des_esslen;
-	uint8_t			ic_des_essid[IEEE80211_NWID_LEN];
+	u_int8_t		ic_des_essid[IEEE80211_NWID_LEN];
 	struct ieee80211_channel *ic_des_chan;	/* desired channel */
-	uint8_t			ic_des_bssid[IEEE80211_ADDR_LEN];
-	struct ieee80211_wepkey	ic_nw_keys[IEEE80211_WEP_NKID];
-	int			ic_wep_txkey;	/* default tx key index */
-	void			*ic_wep_ctx;	/* wep crypt context */
-	uint32_t		ic_iv;		/* initial vector for wep */
-	struct ieee80211_stats	ic_stats;	/* statistics */
+	u_int8_t		ic_des_bssid[IEEE80211_ADDR_LEN];
+	void			*ic_opt_ie;	/* user-specified IE's */
+	u_int16_t		ic_opt_ie_len;	/* length of ni_opt_ie */
+	/*
+	 * Inactivity timer settings for nodes.
+	 */
+	int			ic_inact_init;	/* initial setting */
+	int			ic_inact_auth;	/* auth but not assoc setting */
+	int			ic_inact_run;	/* authorized setting */
+	int			ic_inact_probe;	/* inactive probe time */
+
+	/*
+	 * WME/WMM state.
+	 */
+	struct ieee80211_wme_state ic_wme;
+
+	/*
+	 * Cipher state/configuration.
+	 */
+	struct ieee80211_crypto_state ic_crypto;
+#define	ic_nw_keys	ic_crypto.cs_nw_keys	/* XXX compatibility */
+#define	ic_def_txkey	ic_crypto.cs_def_txkey	/* XXX compatibility */
+
+	/*
+	 * 802.1x glue.  When an authenticator attaches it
+	 * fills in this section.  We assume that when ic_ec
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_authenticator *ic_auth;
+	struct eapolcom		*ic_ec;	
+
+	/*
+	 * Access control glue.  When a control agent attaches
+	 * it fills in this section.  We assume that when ic_ac
+	 * is setup that the methods are safe to call.
+	 */
+	const struct ieee80211_aclator *ic_acl;
+	void			*ic_as;
 };
-#define	ic_if		ic_ac.ac_if
-#define	ic_softc	ic_if.if_softc
 
 #define	IEEE80211_ADDR_EQ(a1,a2)	(memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0)
 #define	IEEE80211_ADDR_COPY(dst,src)	memcpy(dst,src,IEEE80211_ADDR_LEN)
 
 /* ic_flags */
-#define	IEEE80211_F_ASCAN	0x00000001	/* STATUS: active scan */
-#define	IEEE80211_F_SIBSS	0x00000002	/* STATUS: start IBSS */
-#define	IEEE80211_F_WEPON	0x00000100	/* CONF: WEP enabled */
-#define	IEEE80211_F_IBSSON	0x00000200	/* CONF: IBSS creation enable */
-#define	IEEE80211_F_PMGTON	0x00000400	/* CONF: Power mgmt enable */
-#define	IEEE80211_F_DESBSSID	0x00000800	/* CONF: des_bssid is set */
-#define	IEEE80211_F_SCANAP	0x00001000	/* CONF: Scanning AP */
-#define	IEEE80211_F_ROAMING	0x00002000	/* CONF: roaming enabled */
-#define	IEEE80211_F_SWRETRY	0x00004000	/* CONF: sw tx retry enabled */
-#define	IEEE80211_F_TXPMGT	0x00018000	/* STATUS: tx power */
-#define	IEEE80211_F_TXPOW_OFF	0x00000000	/* TX Power: radio disabled */
-#define	IEEE80211_F_TXPOW_FIXED	0x00008000	/* TX Power: fixed rate */
-#define	IEEE80211_F_TXPOW_AUTO	0x00010000	/* TX Power: undefined */
-#define	IEEE80211_F_SHSLOT	0x00020000	/* CONF: short slot time */
-#define	IEEE80211_F_SHPREAMBLE	0x00040000	/* CONF: short preamble */
+/* NB: bits 0x4c available */
+#define	IEEE80211_F_FF		0x00000001	/* CONF: ATH FF enabled */
+#define	IEEE80211_F_TURBOP	0x00000002	/* CONF: ATH Turbo enabled*/
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */
+#define	IEEE80211_F_PRIVACY	0x00000010	/* CONF: privacy enabled */
+#define	IEEE80211_F_PUREG	0x00000020	/* CONF: 11g w/o 11b sta's */
+#define	IEEE80211_F_SCAN	0x00000080	/* STATUS: scanning */
+#define	IEEE80211_F_ASCAN	0x00000100	/* STATUS: active scan */
+#define	IEEE80211_F_SIBSS	0x00000200	/* STATUS: start IBSS */
+/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
+#define	IEEE80211_F_SHSLOT	0x00000400	/* STATUS: use short slot time*/
+#define	IEEE80211_F_PMGTON	0x00000800	/* CONF: Power mgmt enable */
+#define	IEEE80211_F_DESBSSID	0x00001000	/* CONF: des_bssid is set */
+#define	IEEE80211_F_WME		0x00002000	/* CONF: enable WME use */
+#define	IEEE80211_F_BGSCAN	0x00004000	/* CONF: bg scan enabled (???)*/
+#define	IEEE80211_F_SWRETRY	0x00008000	/* CONF: sw tx retry enabled */
+#define IEEE80211_F_TXPOW_FIXED	0x00010000	/* TX Power: fixed rate */
+#define	IEEE80211_F_IBSSON	0x00020000	/* CONF: IBSS creation enable */
+#define	IEEE80211_F_SHPREAMBLE	0x00040000	/* STATUS: use short preamble */
+#define	IEEE80211_F_DATAPAD	0x00080000	/* CONF: do alignment pad */
 #define	IEEE80211_F_USEPROT	0x00100000	/* STATUS: protection enabled */
 #define	IEEE80211_F_USEBARKER	0x00200000	/* STATUS: use barker preamble*/
+#define	IEEE80211_F_TIMUPDATE	0x00400000	/* STATUS: update beacon tim */
+#define	IEEE80211_F_WPA1	0x00800000	/* CONF: WPA enabled */
+#define	IEEE80211_F_WPA2	0x01000000	/* CONF: WPA2 enabled */
+#define	IEEE80211_F_WPA		0x01800000	/* CONF: WPA/WPA2 enabled */
+#define	IEEE80211_F_DROPUNENC	0x02000000	/* CONF: drop unencrypted */
+#define	IEEE80211_F_COUNTERM	0x04000000	/* CONF: TKIP countermeasures */
+#define	IEEE80211_F_HIDESSID	0x08000000	/* CONF: hide SSID in beacon */
+#define	IEEE80211_F_NOBRIDGE	0x10000000	/* CONF: dis. internal bridge */
+#define	IEEE80211_F_WMEUPDATE	0x20000000	/* STATUS: update beacon wme */
+
+/* ic_flags_ext */
+#define	IEEE80211_FEXT_WDS	0x00000001	/* CONF: 4 addr allowed */
+/* 0x00000006 reserved */
+#define	IEEE80211_FEXT_BGSCAN	0x00000008	/* STATUS: enable full bgscan completion */
 
 /* ic_caps */
 #define	IEEE80211_C_WEP		0x00000001	/* CAPABILITY: WEP available */
-#define	IEEE80211_C_IBSS	0x00000002	/* CAPABILITY: IBSS available */
-#define	IEEE80211_C_PMGT	0x00000004	/* CAPABILITY: Power mgmt */
-#define	IEEE80211_C_HOSTAP	0x00000008	/* CAPABILITY: HOSTAP avail */
-#define	IEEE80211_C_AHDEMO	0x00000010	/* CAPABILITY: Old Adhoc Demo */
-#define	IEEE80211_C_SWRETRY	0x00000020	/* CAPABILITY: sw tx retry */
-#define	IEEE80211_C_TXPMGT	0x00000040	/* CAPABILITY: tx power mgmt */
-#define	IEEE80211_C_SHSLOT	0x00000080	/* CAPABILITY: short slottime */
-#define	IEEE80211_C_SHPREAMBLE	0x00000100	/* CAPABILITY: short preamble */
-#define	IEEE80211_C_MONITOR	0x00000200	/* CAPABILITY: monitor mode */
-
-/* flags for ieee80211_fix_rate() */
-#define	IEEE80211_F_DOSORT	0x00000001	/* sort rate list */
-#define	IEEE80211_F_DOFRATE	0x00000002	/* use fixed rate */
-#define	IEEE80211_F_DONEGO	0x00000004	/* calc negotiated rate */
-#define	IEEE80211_F_DODEL	0x00000008	/* delete ignore rate */
-
-void	ieee80211_ifattach(struct ifnet *);
-void	ieee80211_ifdetach(struct ifnet *);
-void	ieee80211_media_init(struct ifnet *, ifm_change_cb_t, ifm_stat_cb_t);
+#define	IEEE80211_C_TKIP	0x00000002	/* CAPABILITY: TKIP available */
+#define	IEEE80211_C_AES		0x00000004	/* CAPABILITY: AES OCB avail */
+#define	IEEE80211_C_AES_CCM	0x00000008	/* CAPABILITY: AES CCM avail */
+#define	IEEE80211_C_CKIP	0x00000020	/* CAPABILITY: CKIP available */
+#define	IEEE80211_C_FF		0x00000040	/* CAPABILITY: ATH FF avail */
+#define	IEEE80211_C_TURBOP	0x00000080	/* CAPABILITY: ATH Turbo avail*/
+#define	IEEE80211_C_IBSS	0x00000100	/* CAPABILITY: IBSS available */
+#define	IEEE80211_C_PMGT	0x00000200	/* CAPABILITY: Power mgmt */
+#define	IEEE80211_C_HOSTAP	0x00000400	/* CAPABILITY: HOSTAP avail */
+#define	IEEE80211_C_AHDEMO	0x00000800	/* CAPABILITY: Old Adhoc Demo */
+#define	IEEE80211_C_SWRETRY	0x00001000	/* CAPABILITY: sw tx retry */
+#define	IEEE80211_C_TXPMGT	0x00002000	/* CAPABILITY: tx power mgmt */
+#define	IEEE80211_C_SHSLOT	0x00004000	/* CAPABILITY: short slottime */
+#define	IEEE80211_C_SHPREAMBLE	0x00008000	/* CAPABILITY: short preamble */
+#define	IEEE80211_C_MONITOR	0x00010000	/* CAPABILITY: monitor mode */
+#define	IEEE80211_C_TKIPMIC	0x00020000	/* CAPABILITY: TKIP MIC avail */
+#define	IEEE80211_C_WPA1	0x00800000	/* CAPABILITY: WPA1 avail */
+#define	IEEE80211_C_WPA2	0x01000000	/* CAPABILITY: WPA2 avail */
+#define	IEEE80211_C_WPA		0x01800000	/* CAPABILITY: WPA1+WPA2 avail*/
+#define	IEEE80211_C_BURST	0x02000000	/* CAPABILITY: frame bursting */
+#define	IEEE80211_C_WME		0x04000000	/* CAPABILITY: WME avail */
+#define	IEEE80211_C_WDS		0x08000000	/* CAPABILITY: 4-addr support */
+/* 0x10000000 reserved */
+#define	IEEE80211_C_BGSCAN	0x20000000	/* CAPABILITY: bg scanning */
+#define	IEEE80211_C_TXFRAG	0x40000000	/* CAPABILITY: tx fragments */
+/* XXX protection/barker? */
+
+#define	IEEE80211_C_CRYPTO	0x0000002f	/* CAPABILITY: crypto alg's */
+
+void	ieee80211_ifattach(struct ieee80211com *);
+void	ieee80211_ifdetach(struct ieee80211com *);
+void	ieee80211_announce(struct ieee80211com *);
+void	ieee80211_media_init(struct ieee80211com *,
+		ifm_change_cb_t, ifm_stat_cb_t);
+struct ieee80211com *ieee80211_find_vap(const u_int8_t mac[IEEE80211_ADDR_LEN]);
 int	ieee80211_media_change(struct ifnet *);
 void	ieee80211_media_status(struct ifnet *, struct ifmediareq *);
-int	ieee80211_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *);
-int	ieee80211_cfgget(struct ifnet *, u_long, caddr_t, struct ucred *);
-int	ieee80211_cfgset(struct ifnet *, u_long, caddr_t);
-void	ieee80211_watchdog(struct ifnet *);
-int	ieee80211_fix_rate(struct ieee80211com *, struct ieee80211_node *, int);
+int	ieee80211_ioctl(struct ieee80211com *, u_long, caddr_t, struct ucred *);
+int	ieee80211_cfgget(struct ieee80211com *, u_long, caddr_t, struct ucred *);
+int	ieee80211_cfgset(struct ieee80211com *, u_long, caddr_t);
+void	ieee80211_watchdog(struct ieee80211com *);
 int	ieee80211_rate2media(struct ieee80211com *, int,
 		enum ieee80211_phymode);
 int	ieee80211_media2rate(int);
@@ -282,14 +295,123 @@
 enum ieee80211_phymode ieee80211_chan2mode(struct ieee80211com *,
 		struct ieee80211_channel *);
 
-#define	IEEE80211_DEBUG
+/* 
+ * Key update synchronization methods.  XXX should not be visible.
+ */
+static __inline void
+ieee80211_key_update_begin(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_begin(ic);
+}
+static __inline void
+ieee80211_key_update_end(struct ieee80211com *ic)
+{
+	ic->ic_crypto.cs_key_update_end(ic);
+}
+
+/*
+ * XXX these need to be here for IEEE80211_F_DATAPAD
+ */
+
+/*
+ * Return the space occupied by the 802.11 header and any
+ * padding required by the driver.  This works for a
+ * management or data frame.
+ */
+static __inline int
+ieee80211_hdrspace(struct ieee80211com *ic, const void *data)
+{
+	int size = ieee80211_hdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof(u_int32_t));
+	return size;
+}
+
+/*
+ * Like ieee80211_hdrspace, but handles any type of frame.
+ */
+static __inline int
+ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data)
+{
+	int size = ieee80211_anyhdrsize(data);
+	if (ic->ic_flags & IEEE80211_F_DATAPAD)
+		size = roundup(size, sizeof(u_int32_t));
+	return size;
+}
+
+#define	IEEE80211_MSG_DEBUG	0x40000000	/* IFF_DEBUG equivalent */
+#define	IEEE80211_MSG_DUMPPKTS	0x20000000	/* IFF_LINK2 equivalant */
+#define	IEEE80211_MSG_CRYPTO	0x10000000	/* crypto work */
+#define	IEEE80211_MSG_INPUT	0x08000000	/* input handling */
+#define	IEEE80211_MSG_XRATE	0x04000000	/* rate set handling */
+#define	IEEE80211_MSG_ELEMID	0x02000000	/* element id parsing */
+#define	IEEE80211_MSG_NODE	0x01000000	/* node handling */
+#define	IEEE80211_MSG_ASSOC	0x00800000	/* association handling */
+#define	IEEE80211_MSG_AUTH	0x00400000	/* authentication handling */
+#define	IEEE80211_MSG_SCAN	0x00200000	/* scanning */
+#define	IEEE80211_MSG_OUTPUT	0x00100000	/* output handling */
+#define	IEEE80211_MSG_STATE	0x00080000	/* state machine */
+#define	IEEE80211_MSG_POWER	0x00040000	/* power save handling */
+#define	IEEE80211_MSG_DOT1X	0x00020000	/* 802.1x authenticator */
+#define	IEEE80211_MSG_DOT1XSM	0x00010000	/* 802.1x state machine */
+#define	IEEE80211_MSG_RADIUS	0x00008000	/* 802.1x radius client */
+#define	IEEE80211_MSG_RADDUMP	0x00004000	/* dump 802.1x radius packets */
+#define	IEEE80211_MSG_RADKEYS	0x00002000	/* dump 802.1x keys */
+#define	IEEE80211_MSG_WPA	0x00001000	/* WPA/RSN protocol */
+#define	IEEE80211_MSG_ACL	0x00000800	/* ACL handling */
+#define	IEEE80211_MSG_WME	0x00000400	/* WME protocol */
+#define	IEEE80211_MSG_SUPERG	0x00000200	/* Atheros SuperG protocol */
+#define	IEEE80211_MSG_DOTH	0x00000100	/* 802.11h support */
+#define	IEEE80211_MSG_INACT	0x00000080	/* inactivity handling */
+#define	IEEE80211_MSG_ROAM	0x00000040	/* sta-mode roaming */
+
+#define	IEEE80211_MSG_ANY	0xffffffff	/* anything */
+
 #ifdef IEEE80211_DEBUG
-extern	int ieee80211_debug;
-#define	IEEE80211_DPRINTF(X)	if (ieee80211_debug) printf X
-#define	IEEE80211_DPRINTF2(X)	if (ieee80211_debug>1) printf X
+#define	ieee80211_msg(_ic, _m)	((_ic)->ic_debug & (_m))
+#define	IEEE80211_DPRINTF(_ic, _m, _fmt, ...) do {			\
+	if (ieee80211_msg(_ic, _m))					\
+		ieee80211_note(_ic, _fmt, __VA_ARGS__);		\
+} while (0)
+#define	IEEE80211_NOTE(_ic, _m, _ni, _fmt, ...) do {			\
+	if (ieee80211_msg(_ic, _m))					\
+		ieee80211_note_mac(_ic, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\
+} while (0)
+#define	IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...) do {		\
+	if (ieee80211_msg(_ic, _m))					\
+		ieee80211_note_mac(_ic, _mac, _fmt, __VA_ARGS__);	\
+} while (0)
+#define	IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...) do {		\
+	if (ieee80211_msg(_ic, _m))					\
+		ieee80211_note_frame(_ic, _wh, _fmt, __VA_ARGS__);	\
+} while (0)
+void	ieee80211_note(struct ieee80211com *ic, const char *fmt, ...);
+void	ieee80211_note_mac(struct ieee80211com *ic,
+		const u_int8_t mac[IEEE80211_ADDR_LEN], const char *fmt, ...);
+void	ieee80211_note_frame(struct ieee80211com *ic,
+		const struct ieee80211_frame *wh, const char *fmt, ...);
+#define	ieee80211_msg_debug(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_DEBUG)
+#define	ieee80211_msg_dumppkts(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_DUMPPKTS)
+#define	ieee80211_msg_input(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_INPUT)
+#define	ieee80211_msg_radius(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADIUS)
+#define	ieee80211_msg_dumpradius(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADDUMP)
+#define	ieee80211_msg_dumpradkeys(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_RADKEYS)
+#define	ieee80211_msg_scan(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_SCAN)
+#define	ieee80211_msg_assoc(_ic) \
+	((_ic)->ic_debug & IEEE80211_MSG_ASSOC)
 #else
-#define	IEEE80211_DPRINTF(X)
-#define	IEEE80211_DPRINTF2(X)
+#define	IEEE80211_DPRINTF(_ic, _m, _fmt, ...)
+#define	IEEE80211_NOTE_FRAME(_ic, _m, _wh, _fmt, ...)
+#define	IEEE80211_NOTE_MAC(_ic, _m, _mac, _fmt, ...)
+#define	ieee80211_msg_dumppkts(_ic)	0
+#define	ieee80211_msg(_ic, _m)		0
 #endif
 
-#endif /* _NETPROTO_802_11_IEEE80211_VAR_H_ */
+#endif /* _NET80211_IEEE80211_VAR_H_ */
diff -N -u -r src.preview/sys/netproto/802_11/ieee80211_xauth.c src/sys/netproto/802_11/ieee80211_xauth.c
--- src.preview/sys/netproto/802_11/ieee80211_xauth.c	1969-12-31 19:00:00.000000000 -0500
+++ src/sys/netproto/802_11/ieee80211_xauth.c	2005-09-23 11:24:22.000000000 -0400
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2004 Video54 Technologies, Inc.
+ * Copyright (c) 2004-2005 Sam Leffler, Errno Consulting
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * External authenticator placeholder module.
+ *
+ * This support is optional; it is only used when the 802.11 layer's
+ * authentication mode is set to use 802.1x or WPA is enabled separately
+ * (for WPA-PSK).  If compiled as a module this code does not need
+ * to be present unless 802.1x/WPA is in use.
+ *
+ * The authenticator hooks into the 802.11 layer.  At present we use none
+ * of the available callbacks--the user mode authenticator process works
+ * entirely from messages about stations joining and leaving.
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h> 
+#include <sys/mbuf.h>   
+#include <sys/module.h>
+
+#include <sys/socket.h>
+#include <sys/thread.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <net/route.h>
+
+#include <netproto/802_11/ieee80211_var.h>
+
+/*
+ * One module handles everything for now.  May want
+ * to split things up for embedded applications.
+ */
+static const struct ieee80211_authenticator xauth = {
+	.ia_name	= "external",
+	.ia_attach	= NULL,
+	.ia_detach	= NULL,
+	.ia_node_join	= NULL,
+	.ia_node_leave	= NULL,
+};
+
+/*
+ * Module glue.
+ */
+static int
+wlan_xauth_modevent(module_t mod, int type, void *unused)
+{
+	switch (type) {
+	case MOD_LOAD:
+		ieee80211_authenticator_register(IEEE80211_AUTH_8021X, &xauth);
+		ieee80211_authenticator_register(IEEE80211_AUTH_WPA, &xauth);
+		return 0;
+	case MOD_UNLOAD:
+		ieee80211_authenticator_unregister(IEEE80211_AUTH_8021X);
+		ieee80211_authenticator_unregister(IEEE80211_AUTH_WPA);
+		return 0;
+	}
+	return EINVAL;
+}
+
+static moduledata_t wlan_xauth_mod = {
+	"wlan_xauth",
+	wlan_xauth_modevent,
+	0
+};
+DECLARE_MODULE(wlan_xauth, wlan_xauth_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);
+MODULE_VERSION(wlan_xauth, 1);
+MODULE_DEPEND(wlan_xauth, wlan, 1, 1, 1);
diff -N -u -r src.preview/sys/netproto/ipsec/ipsec_input.c src/sys/netproto/ipsec/ipsec_input.c
--- src.preview/sys/netproto/ipsec/ipsec_input.c	2005-06-17 15:12:23.000000000 -0400
+++ src/sys/netproto/ipsec/ipsec_input.c	2005-09-26 10:49:26.000000000 -0400
@@ -55,6 +55,7 @@
 #include <sys/errno.h>
 #include <sys/syslog.h>
 #include <sys/in_cksum.h>
+#include <sys/thread2.h>
 
 #include <net/if.h>
 #include <net/route.h>
diff -N -u -r src.preview/sys/netproto/ipsec/ipsec_output.c src/sys/netproto/ipsec/ipsec_output.c
--- src.preview/sys/netproto/ipsec/ipsec_output.c	2005-06-17 15:12:23.000000000 -0400
+++ src/sys/netproto/ipsec/ipsec_output.c	2005-09-26 10:49:32.000000000 -0400
@@ -43,6 +43,7 @@
 #include <sys/errno.h>
 #include <sys/syslog.h>
 #include <sys/in_cksum.h>
+#include <sys/thread2.h>
 
 #include <net/if.h>
 #include <net/route.h>
diff -N -u -r src.preview/sys/netproto/ipsec/key.c src/sys/netproto/ipsec/key.c
--- src.preview/sys/netproto/ipsec/key.c	2005-06-10 19:59:31.000000000 -0400
+++ src/sys/netproto/ipsec/key.c	2005-09-26 10:49:38.000000000 -0400
@@ -54,6 +54,7 @@
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/syslog.h>
+#include <sys/thread2.h>
 
 #include <net/if.h>
 #include <net/route.h>
diff -N -u -r src.preview/sys/netproto/ipsec/keysock.c src/sys/netproto/ipsec/keysock.c
--- src.preview/sys/netproto/ipsec/keysock.c	2005-06-02 20:22:27.000000000 -0400
+++ src/sys/netproto/ipsec/keysock.c	2005-09-26 10:49:43.000000000 -0400
@@ -48,6 +48,7 @@
 #include <sys/socketvar.h>
 #include <sys/sysctl.h>
 #include <sys/systm.h>
+#include <sys/thread2.h>
 
 #include <net/raw_cb.h>
 #include <net/route.h>
diff -N -u -r src.preview/sys/netproto/ipsec/xform_ah.c src/sys/netproto/ipsec/xform_ah.c
--- src.preview/sys/netproto/ipsec/xform_ah.c	2005-06-17 15:12:23.000000000 -0400
+++ src/sys/netproto/ipsec/xform_ah.c	2005-09-26 10:49:48.000000000 -0400
@@ -47,6 +47,7 @@
 #include <sys/syslog.h>
 #include <sys/kernel.h>
 #include <sys/sysctl.h>
+#include <sys/thread2.h>
 
 #include <net/if.h>
 
diff -N -u -r src.preview/sys/netproto/ipsec/xform_esp.c src/sys/netproto/ipsec/xform_esp.c
--- src.preview/sys/netproto/ipsec/xform_esp.c	2005-06-17 15:12:23.000000000 -0400
+++ src/sys/netproto/ipsec/xform_esp.c	2005-09-26 10:49:52.000000000 -0400
@@ -47,6 +47,7 @@
 #include <sys/kernel.h>
 #include <sys/random.h>
 #include <sys/sysctl.h>
+#include <sys/thread2.h>
 
 #include <net/if.h>
 
diff -N -u -r src.preview/sys/netproto/ipsec/xform_ipcomp.c src/sys/netproto/ipsec/xform_ipcomp.c
--- src.preview/sys/netproto/ipsec/xform_ipcomp.c	2005-06-10 19:59:31.000000000 -0400
+++ src/sys/netproto/ipsec/xform_ipcomp.c	2005-09-26 10:49:57.000000000 -0400
@@ -40,6 +40,7 @@
 #include <sys/kernel.h>
 #include <sys/protosw.h>
 #include <sys/sysctl.h>
+#include <sys/thread2.h>
 
 #include <netinet/in.h>
 #include <netinet/in_systm.h>
diff -N -u -r src.preview/tools/tools/ath/80211debug.c src/tools/tools/ath/80211debug.c
--- src.preview/tools/tools/ath/80211debug.c	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/80211debug.c	2005-09-27 15:29:13.000000000 -0400
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 80211debug [-i interface] flags
+ * (default interface is wlan).
+ */
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <string.h>
+
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+
+const char *progname;
+
+#define	IEEE80211_MSG_DEBUG	0x40000000	/* IFF_DEBUG equivalent */
+#define	IEEE80211_MSG_DUMPPKTS	0x20000000	/* IFF_LINK2 equivalant */
+#define	IEEE80211_MSG_CRYPTO	0x10000000	/* crypto work */
+#define	IEEE80211_MSG_INPUT	0x08000000	/* input handling */
+#define	IEEE80211_MSG_XRATE	0x04000000	/* rate set handling */
+#define	IEEE80211_MSG_ELEMID	0x02000000	/* element id parsing */
+#define	IEEE80211_MSG_NODE	0x01000000	/* node handling */
+#define	IEEE80211_MSG_ASSOC	0x00800000	/* association handling */
+#define	IEEE80211_MSG_AUTH	0x00400000	/* authentication handling */
+#define	IEEE80211_MSG_SCAN	0x00200000	/* scanning */
+#define	IEEE80211_MSG_OUTPUT	0x00100000	/* output handling */
+#define	IEEE80211_MSG_STATE	0x00080000	/* state machine */
+#define	IEEE80211_MSG_POWER	0x00040000	/* power save handling */
+#define	IEEE80211_MSG_DOT1X	0x00020000	/* 802.1x authenticator */
+#define	IEEE80211_MSG_DOT1XSM	0x00010000	/* 802.1x state machine */
+#define	IEEE80211_MSG_RADIUS	0x00008000	/* 802.1x radius client */
+#define	IEEE80211_MSG_RADDUMP	0x00004000	/* dump 802.1x radius packets */
+#define	IEEE80211_MSG_RADKEYS	0x00002000	/* dump 802.1x keys */
+#define	IEEE80211_MSG_WPA	0x00001000	/* WPA/RSN protocol */
+#define	IEEE80211_MSG_ACL	0x00000800	/* ACL handling */
+#define	IEEE80211_MSG_WME	0x00000400	/* WME protocol */
+#define	IEEE80211_MSG_SUPERG	0x00000200	/* Atheros SuperG protocol */
+#define	IEEE80211_MSG_DOTH	0x00000100	/* 802.11h support */
+#define	IEEE80211_MSG_INACT	0x00000080	/* inactivity handling */
+#define	IEEE80211_MSG_ROAM	0x00000040	/* sta-mode roaming */
+
+static struct {
+	const char	*name;
+	u_int		bit;
+} flags[] = {
+	{ "debug",	IEEE80211_MSG_DEBUG },
+	{ "dumppkts",	IEEE80211_MSG_DUMPPKTS },
+	{ "crypto",	IEEE80211_MSG_CRYPTO },
+	{ "input",	IEEE80211_MSG_INPUT },
+	{ "xrate",	IEEE80211_MSG_XRATE },
+	{ "elemid",	IEEE80211_MSG_ELEMID },
+	{ "node",	IEEE80211_MSG_NODE },
+	{ "assoc",	IEEE80211_MSG_ASSOC },
+	{ "auth",	IEEE80211_MSG_AUTH },
+	{ "scan",	IEEE80211_MSG_SCAN },
+	{ "output",	IEEE80211_MSG_OUTPUT },
+	{ "state",	IEEE80211_MSG_STATE },
+	{ "power",	IEEE80211_MSG_POWER },
+	{ "dotx1",	IEEE80211_MSG_DOT1X },
+	{ "dot1xsm",	IEEE80211_MSG_DOT1XSM },
+	{ "radius",	IEEE80211_MSG_RADIUS },
+	{ "raddump",	IEEE80211_MSG_RADDUMP },
+	{ "radkeys",	IEEE80211_MSG_RADKEYS },
+	{ "wpa",	IEEE80211_MSG_WPA },
+	{ "acl",	IEEE80211_MSG_ACL },
+	{ "wme",	IEEE80211_MSG_WME },
+	{ "superg",	IEEE80211_MSG_SUPERG },
+	{ "doth",	IEEE80211_MSG_DOTH },
+	{ "inact",	IEEE80211_MSG_INACT },
+	{ "roam",	IEEE80211_MSG_ROAM },
+};
+
+static u_int
+getflag(const char *name, int len)
+{
+	int i;
+
+	for (i = 0; i < N(flags); i++)
+		if (strncasecmp(flags[i].name, name, len) == 0)
+			return flags[i].bit;
+	return 0;
+}
+
+static const char *
+getflagname(u_int flag)
+{
+	int i;
+
+	for (i = 0; i < N(flags); i++)
+		if (flags[i].bit == flag)
+			return flags[i].name;
+	return "???";
+}
+
+static void
+usage(void)
+{
+	int i;
+
+	fprintf(stderr, "usage: %s [-i device] [flags]\n", progname);
+	fprintf(stderr, "where flags are:\n");
+	for (i = 0; i < N(flags); i++)
+		printf("%s\n", flags[i].name);
+	exit(-1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const char *ifname = "ath0";
+	const char *cp, *tp;
+	const char *sep;
+	int c, op, i, unit;
+	u_int32_t debug, ndebug;
+	size_t debuglen, parentlen;
+	char oid[256], parent[256];
+
+	progname = argv[0];
+	if (argc > 1) {
+		if (strcmp(argv[1], "-i") == 0) {
+			if (argc < 2)
+				errx(1, "missing interface name for -i option");
+			ifname = argv[2];
+			argc -= 2, argv += 2;
+		} else if (strcmp(argv[1], "-?") == 0)
+			usage();
+	}
+
+	for (unit = 0; unit < 10; unit++) {
+#ifdef __linux__
+		snprintf(oid, sizeof(oid), "net.wlan%d.%%parent", unit);
+#else
+		snprintf(oid, sizeof(oid), "net.wlan.%%parent", unit);
+#endif
+		parentlen = sizeof(parent);
+		if (sysctlbyname(oid, parent, &parentlen, NULL, 0) >= 0 &&
+		    strncmp(parent, ifname, parentlen) == 0)
+			break;
+	}
+	if (unit == 10)
+		errx(1, "%s: cannot locate wlan sysctl node.", ifname);
+#ifdef __linux__
+	snprintf(oid, sizeof(oid), "net.wlan%d.debug", unit);
+#else
+	snprintf(oid, sizeof(oid), "net.wlan.debug", unit);
+#endif
+	debuglen = sizeof(debug);
+	if (sysctlbyname(oid, &debug, &debuglen, NULL, 0) < 0)
+		err(1, "sysctl-get(%s)", oid);
+	ndebug = debug;
+	for (; argc > 1; argc--, argv++) {
+		cp = argv[1];
+		do {
+			u_int bit;
+
+			if (*cp == '-') {
+				cp++;
+				op = -1;
+			} else if (*cp == '+') {
+				cp++;
+				op = 1;
+			} else
+				op = 0;
+			for (tp = cp; *tp != '\0' && *tp != '+' && *tp != '-';)
+				tp++;
+			bit = getflag(cp, tp-cp);
+			if (op < 0)
+				ndebug &= ~bit;
+			else if (op > 0)
+				ndebug |= bit;
+			else {
+				if (bit == 0) {
+					if (isdigit(*cp))
+						bit = strtoul(cp, NULL, 0);
+					else
+						errx(1, "unknown flag %.*s",
+							tp-cp, cp);
+				}
+				ndebug = bit;
+			}
+		} while (*(cp = tp) != '\0');
+	}
+	if (debug != ndebug) {
+		printf("%s: 0x%x => ", oid, debug);
+		if (sysctlbyname(oid, NULL, NULL, &ndebug, sizeof(ndebug)) < 0)
+			err(1, "sysctl-set(%s)", oid);
+		printf("0x%x", ndebug);
+		debug = ndebug;
+	} else
+		printf("%s: 0x%x", oid, debug);
+	sep = "<";
+	for (i = 0; i < N(flags); i++)
+		if (debug & flags[i].bit) {
+			printf("%s%s", sep, flags[i].name);
+			sep = ",";
+		}
+	printf("%s\n", *sep != '<' ? ">" : "");
+	return 0;
+}
diff -N -u -r src.preview/tools/tools/ath/80211stats.c src/tools/tools/ath/80211stats.c
--- src.preview/tools/tools/ath/80211stats.c	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/80211stats.c	2005-09-27 15:22:11.000000000 -0400
@@ -0,0 +1,300 @@
+/*-
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 80211stats [-i interface]
+ * (default interface is ath0).
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <err.h>
+
+#include "../../../sys/netproto/802_11/ieee80211_ioctl.h"
+
+const char *progname;
+
+static void
+printstats(FILE *fd, const struct ieee80211_stats *stats)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+#define	STAT(x,fmt) \
+	if (stats->is_##x) fprintf(fd, "%u " fmt "\n", stats->is_##x)
+	STAT(rx_badversion,	"rx frame with bad version");
+	STAT(rx_tooshort,	"rx frame too short");
+	STAT(rx_wrongbss,	"rx from wrong bssid");
+	STAT(rx_dup,		"rx discard 'cuz dup");
+	STAT(rx_wrongdir,	"rx w/ wrong direction");
+	STAT(rx_mcastecho,	"rx discard 'cuz mcast echo");
+	STAT(rx_notassoc,	"rx discard 'cuz sta !assoc");
+	STAT(rx_noprivacy,	"rx w/ wep but privacy off");
+	STAT(rx_unencrypted,	"rx w/o wep and privacy on");
+	STAT(rx_wepfail,	"rx wep processing failed");
+	STAT(rx_decap,		"rx decapsulation failed");
+	STAT(rx_mgtdiscard,	"rx discard mgt frames");
+	STAT(rx_ctl,		"rx discard ctrl frames");
+	STAT(rx_beacon,		"rx beacon frames");
+	STAT(rx_rstoobig,	"rx rate set truncated");
+	STAT(rx_elem_missing,	"rx required element missing");
+	STAT(rx_elem_toobig,	"rx element too big");
+	STAT(rx_elem_toosmall,	"rx element too small");
+	STAT(rx_elem_unknown,	"rx element unknown");
+	STAT(rx_badchan,	"rx frame w/ invalid chan");
+	STAT(rx_chanmismatch,	"rx frame chan mismatch");
+	STAT(rx_nodealloc,	"nodes allocated (rx)");
+	STAT(rx_ssidmismatch,	"rx frame ssid mismatch");
+	STAT(rx_auth_unsupported,"rx w/ unsupported auth alg");
+	STAT(rx_auth_fail,	"rx sta auth failure");
+	STAT(rx_auth_countermeasures,
+		"rx sta auth failure 'cuz of TKIP countermeasures");
+	STAT(rx_assoc_bss,	"rx assoc from wrong bssid");
+	STAT(rx_assoc_notauth,	"rx assoc w/o auth");
+	STAT(rx_assoc_capmismatch,"rx assoc w/ cap mismatch");
+	STAT(rx_assoc_norate,	"rx assoc w/ no rate match");
+	STAT(rx_assoc_badwpaie,	"rx assoc w/ bad WPA IE");
+	STAT(rx_deauth,		"rx deauthentication");
+	STAT(rx_disassoc,	"rx disassociation");
+	STAT(rx_badsubtype,	"rx frame w/ unknown subtype");
+	STAT(rx_nobuf,		"rx failed for lack of sk_buffer");
+	STAT(rx_decryptcrc,	"rx decrypt failed on crc");
+	STAT(rx_ahdemo_mgt,
+		"rx discard mgmt frame received in ahdoc demo mode");
+	STAT(rx_bad_auth,	"rx bad authentication request");
+	STAT(rx_unauth,		"rx discard 'cuz port unauthorized");
+	STAT(rx_badkeyid,	"rx w/ incorrect keyid");
+	STAT(rx_ccmpreplay,	"rx seq# violation (CCMP)");
+	STAT(rx_ccmpformat,	"rx format bad (CCMP)");
+	STAT(rx_ccmpmic,	"rx MIC check failed (CCMP)");
+	STAT(rx_tkipreplay,	"rx seq# violation (TKIP)");
+	STAT(rx_tkipformat,	"rx format bad (TKIP)");
+	STAT(rx_tkipmic,	"rx MIC check failed (TKIP)");
+	STAT(rx_tkipicv,	"rx ICV check failed (TKIP)");
+	STAT(rx_badcipher,	"rx failed 'cuz bad cipher/key type");
+	STAT(rx_nocipherctx,	"rx failed 'cuz key/cipher ctx not setup");
+	STAT(rx_acl,		"rx discard 'cuz acl policy");
+	STAT(tx_nobuf,		"tx failed for lack of sk_buffer");
+	STAT(tx_nonode,		"tx failed for no node");
+	STAT(tx_unknownmgt,	"tx of unknown mgt frame");
+	STAT(tx_badcipher,	"tx failed 'cuz bad ciper/key type");
+	STAT(tx_nodefkey,	"tx failed 'cuz no defkey");
+	STAT(tx_noheadroom,	"tx failed 'cuz no space for crypto hdrs");
+	STAT(tx_fragframes,	"tx frames fragmented");
+	STAT(tx_frags,		"tx frags generated");
+	STAT(scan_active,	"active scans started");
+	STAT(scan_passive,	"passive scans started");
+	STAT(node_timeout,	"nodes timed out inactivity");
+	STAT(crypto_nomem,	"cipher context malloc failed");
+	STAT(crypto_tkip,	"tkip crypto done in s/w");
+	STAT(crypto_tkipenmic,	"tkip tx MIC done in s/w");
+	STAT(crypto_tkipdemic,	"tkip rx MIC done in s/w");
+	STAT(crypto_tkipcm,	"tkip dropped frames 'cuz of countermeasures");
+	STAT(crypto_ccmp,	"ccmp crypto done in s/w");
+	STAT(crypto_wep,	"wep crypto done in s/w");
+	STAT(crypto_setkey_cipher,"setkey failed 'cuz cipher rejected data");
+	STAT(crypto_setkey_nokey,"setkey failed 'cuz no key index");
+	STAT(crypto_delkey,	"driver key delete failed");
+	STAT(crypto_badcipher,	"setkey failed 'cuz unknown cipher");
+	STAT(crypto_nocipher,	"setkey failed 'cuz cipher module unavailable");
+	STAT(crypto_attachfail,	"setkey failed 'cuz cipher attach failed");
+	STAT(crypto_swfallback,	"crypto fell back to s/w implementation");
+	STAT(crypto_keyfail,	"setkey failed 'cuz driver key alloc failed");
+	STAT(crypto_enmicfail,	"enmic failed (may be mbuf exhaustion)");
+	STAT(ibss_capmismatch,	"ibss merge faied 'cuz capabilities mismatch");
+	STAT(ibss_norate,	"ibss merge faied 'cuz rate set mismatch");
+	STAT(ps_unassoc,	"ps-poll received for unassociated station");
+	STAT(ps_badaid,		"ps-poll received with invalid association id");
+	STAT(ps_qempty,		"ps-poll received with nothing to send");
+	STAT(ff_badhdr,		"fast frame rx'd w/ bad hdr");
+	STAT(ff_tooshort,	"fast frame rx decap error");
+	STAT(ff_split,		"fast frame rx split error");
+	STAT(ff_decap,		"fast frames decap'd");
+	STAT(ff_encap,		"fast frames encap'd for tx");
+#undef STAT
+#undef N
+}
+
+struct ifreq ifr;
+int	s;
+
+static void
+print_sta_stats(FILE *fd, const u_int8_t macaddr[IEEE80211_ADDR_LEN])
+{
+#define	STAT(x,fmt) \
+	if (ns->ns_##x) { fprintf(fd, "%s" #x " " fmt, sep, ns->ns_##x); sep = " "; }
+	struct ieee80211req ireq;
+	struct ieee80211req_sta_stats stats;
+	const struct ieee80211_nodestats *ns = &stats.is_stats;
+	const char *sep;
+
+	(void) memset(&ireq, 0, sizeof(ireq));
+	(void) strncpy(ireq.i_name, ifr.ifr_name, sizeof(ireq.i_name));
+	ireq.i_type = IEEE80211_IOC_STA_STATS;
+	ireq.i_data = &stats;
+	ireq.i_len = sizeof(stats);
+	memcpy(stats.is_u.macaddr, macaddr, IEEE80211_ADDR_LEN);
+	if (ioctl(s, SIOCG80211, &ireq) < 0)
+		err(1, "unable to get station stats for %s",
+			ether_ntoa((const struct ether_addr*) macaddr));
+
+	fprintf(fd, "%s:\n", ether_ntoa((const struct ether_addr*) macaddr));
+
+	sep = "\t";
+	STAT(rx_data, "%u");
+	STAT(rx_mgmt, "%u");
+	STAT(rx_ctrl, "%u");
+	STAT(rx_beacons, "%u");
+	STAT(rx_proberesp, "%u");
+	STAT(rx_ucast, "%u");
+	STAT(rx_mcast, "%u");
+	STAT(rx_bytes, "%llu");
+	STAT(rx_dup, "%u");
+	STAT(rx_noprivacy, "%u");
+	STAT(rx_wepfail, "%u");
+	STAT(rx_demicfail, "%u");
+	STAT(rx_decap, "%u");
+	STAT(rx_defrag, "%u");
+	STAT(rx_disassoc, "%u");
+	STAT(rx_deauth, "%u");
+	STAT(rx_decryptcrc, "%u");
+	STAT(rx_unauth, "%u");
+	STAT(rx_unencrypted, "%u");
+	fprintf(fd, "\n");
+
+	sep = "\t";
+	STAT(tx_data, "%u");
+	STAT(tx_mgmt, "%u");
+	STAT(tx_probereq, "%u");
+	STAT(tx_ucast, "%u");
+	STAT(tx_mcast, "%u");
+	STAT(tx_bytes, "%llu");
+	STAT(tx_novlantag, "%u");
+	STAT(tx_vlanmismatch, "%u");
+	fprintf(fd, "\n");
+
+	sep = "\t";
+	STAT(tx_assoc, "%u");
+	STAT(tx_assoc_fail, "%u");
+	STAT(tx_auth, "%u");
+	STAT(tx_auth_fail, "%u");
+	STAT(tx_deauth, "%u");
+	STAT(tx_deauth_code, "%llu");
+	STAT(tx_disassoc, "%u");
+	STAT(tx_disassoc_code, "%u");
+	fprintf(fd, "\n");
+
+#undef STAT
+}
+
+int
+main(int argc, char *argv[])
+{
+	int c, len;
+	struct ieee80211req_sta_info *si;
+	uint8_t buf[24*1024], *cp;
+	struct ieee80211req ireq;
+	int allnodes = 0;
+
+	progname = argv[0];
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0)
+		err(1, "socket");
+	strncpy(ifr.ifr_name, "ath0", sizeof (ifr.ifr_name));
+	while ((c = getopt(argc, argv, "ai:")) != -1)
+		switch (c) {
+		case 'a':
+			allnodes++;
+			break;
+		case 'i':
+			strncpy(ifr.ifr_name, optarg, sizeof (ifr.ifr_name));
+			break;
+		default:
+			errx(1, "usage: %s [-a] [-i device] [mac...]\n", progname);
+			/*NOTREACHED*/
+		}
+
+	if (argc == optind && !allnodes) {
+		struct ieee80211_stats stats;
+
+		/* no args, just show global stats */
+		ifr.ifr_data = (caddr_t) &stats;
+		if (ioctl(s, SIOCG80211STATS, &ifr) < 0)
+			err(1, ifr.ifr_name);
+		printstats(stdout, &stats);
+		return 0;
+	}
+	if (allnodes) {
+		/*
+		 * Retrieve station/neighbor table and print stats for each.
+		 */
+		(void) memset(&ireq, 0, sizeof(ireq));
+		(void) strncpy(ireq.i_name, ifr.ifr_name, sizeof(ireq.i_name));
+		ireq.i_type = IEEE80211_IOC_STA_INFO;
+		ireq.i_data = buf;
+		ireq.i_len = sizeof(buf);
+		if (ioctl(s, SIOCG80211, &ireq) < 0)
+			err(1, "unable to get station information");
+		len = ireq.i_len;
+		if (len >= sizeof(struct ieee80211req_sta_info)) {
+			cp = buf;
+			do {
+				si = (struct ieee80211req_sta_info *) cp;
+				print_sta_stats(stdout, si->isi_macaddr);
+				cp += si->isi_len, len -= si->isi_len;
+			} while (len >= sizeof(struct ieee80211req_sta_info));
+		}
+	} else {
+		/*
+		 * Print stats for specified stations.
+		 */
+		for (c = optind; c < argc; c++) {
+			const struct ether_addr *ea = ether_aton(argv[c]);
+			if (ea != NULL)
+				print_sta_stats(stdout, ea->octet);
+		}
+	}
+}
diff -N -u -r src.preview/tools/tools/ath/80211watch.c src/tools/tools/ath/80211watch.c
--- src.preview/tools/tools/ath/80211watch.c	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/80211watch.c	2005-09-27 15:23:58.000000000 -0400
@@ -0,0 +1,385 @@
+/*-
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Monitor 802.11 events using a routing socket.
+ * Code liberaly swiped from route(8).
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netatalk/at.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+static	void print_rtmsg(struct rt_msghdr *rtm, int msglen);
+
+int	nflag = 0;
+
+int
+main(int argc, char *argv[])
+{
+	int n, s;
+	char msg[2048];
+
+	s = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (s < 0)
+		err(EX_OSERR, "socket");
+	for(;;) {
+		n = read(s, msg, 2048);
+		print_rtmsg((struct rt_msghdr *)msg, n);
+	}
+	return 0;
+}
+
+static void
+bprintf(fp, b, s)
+	FILE *fp;
+	int b;
+	u_char *s;
+{
+	int i;
+	int gotsome = 0;
+
+	if (b == 0)
+		return;
+	while ((i = *s++) != 0) {
+		if (b & (1 << (i-1))) {
+			if (gotsome == 0)
+				i = '<';
+			else
+				i = ',';
+			(void) putc(i, fp);
+			gotsome = 1;
+			for (; (i = *s) > 32; s++)
+				(void) putc(i, fp);
+		} else
+			while (*s > 32)
+				s++;
+	}
+	if (gotsome)
+		putc('>', fp);
+}
+
+char metricnames[] =
+"\011pksent\010rttvar\7rtt\6ssthresh\5sendpipe\4recvpipe\3expire\2hopcount"
+"\1mtu";
+char routeflags[] =
+"\1UP\2GATEWAY\3HOST\4REJECT\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"
+"\011CLONING\012XRESOLVE\013LLINFO\014STATIC\015BLACKHOLE\016b016"
+"\017PROTO2\020PROTO1\021PRCLONING\022WASCLONED\023PROTO3\024CHAINDELETE"
+"\025PINNED\026LOCAL\027BROADCAST\030MULTICAST";
+char ifnetflags[] =
+"\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5PTP\6b6\7RUNNING\010NOARP"
+"\011PPROMISC\012ALLMULTI\013OACTIVE\014SIMPLEX\015LINK0\016LINK1"
+"\017LINK2\020MULTICAST";
+char addrnames[] =
+"\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR\010BRD";
+
+const char *
+routename(sa)
+	struct sockaddr *sa;
+{
+	char *cp;
+	static char line[MAXHOSTNAMELEN + 1];
+	struct hostent *hp;
+	static char domain[MAXHOSTNAMELEN + 1];
+	static int first = 1, n;
+
+	if (first) {
+		first = 0;
+		if (gethostname(domain, MAXHOSTNAMELEN) == 0 &&
+		    (cp = strchr(domain, '.'))) {
+			domain[MAXHOSTNAMELEN] = '\0';
+			(void) strcpy(domain, cp + 1);
+		} else
+			domain[0] = 0;
+	}
+
+	if (sa->sa_len == 0)
+		strcpy(line, "default");
+	else switch (sa->sa_family) {
+
+	case AF_INET:
+	    {	struct in_addr in;
+		in = ((struct sockaddr_in *)sa)->sin_addr;
+
+		cp = 0;
+		if (in.s_addr == INADDR_ANY || sa->sa_len < 4)
+			cp = "default";
+		if (cp == 0 && !nflag) {
+			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
+				AF_INET);
+			if (hp) {
+				if ((cp = strchr(hp->h_name, '.')) &&
+				    !strcmp(cp + 1, domain))
+					*cp = 0;
+				cp = hp->h_name;
+			}
+		}
+		if (cp) {
+			strncpy(line, cp, sizeof(line) - 1);
+			line[sizeof(line) - 1] = '\0';
+		} else
+			(void) sprintf(line, "%s", inet_ntoa(in));
+		break;
+	    }
+
+#ifdef INET6
+	case AF_INET6:
+	{
+		struct sockaddr_in6 sin6; /* use static var for safety */
+		int niflags = 0;
+#ifdef NI_WITHSCOPEID
+		niflags = NI_WITHSCOPEID;
+#endif
+
+		memset(&sin6, 0, sizeof(sin6));
+		memcpy(&sin6, sa, sa->sa_len);
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_family = AF_INET6;
+#ifdef __KAME__
+		if (sa->sa_len == sizeof(struct sockaddr_in6) &&
+		    (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
+		     IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr)) &&
+		    sin6.sin6_scope_id == 0) {
+			sin6.sin6_scope_id =
+			    ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+			sin6.sin6_addr.s6_addr[2] = 0;
+			sin6.sin6_addr.s6_addr[3] = 0;
+		}
+#endif
+		if (nflag)
+			niflags |= NI_NUMERICHOST;
+		if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+		    line, sizeof(line), NULL, 0, niflags) != 0)
+			strncpy(line, "invalid", sizeof(line));
+
+		return(line);
+	}
+#endif
+
+	case AF_LINK:
+		return (link_ntoa((struct sockaddr_dl *)sa));
+
+	default:
+	    {	u_short *s = (u_short *)sa;
+		u_short *slim = s + ((sa->sa_len + 1) >> 1);
+		char *cp = line + sprintf(line, "(%d)", sa->sa_family);
+		char *cpe = line + sizeof(line);
+
+		while (++s < slim && cp < cpe) /* start with sa->sa_data */
+			if ((n = snprintf(cp, cpe - cp, " %x", *s)) > 0)
+				cp += n;
+			else
+				*cp = '\0';
+		break;
+	    }
+	}
+	return (line);
+}
+
+
+static void
+pmsg_addrs(char *cp, int addrs)
+{
+	struct sockaddr *sa;
+	int i;
+
+	if (addrs == 0) {
+		(void) putchar('\n');
+		return;
+	}
+	printf("\nsockaddrs: ");
+	bprintf(stdout, addrs, addrnames);
+	putchar('\n');
+	for (i = 1; i; i <<= 1)
+		if (i & addrs) {
+			sa = (struct sockaddr *)cp;
+			printf(" %s", routename(sa));
+			cp += SA_SIZE(sa);
+		}
+	putchar('\n');
+	fflush(stdout);
+}
+
+static const char *
+ether_sprintf(const uint8_t mac[6])
+{
+	static char buf[32];
+
+	snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
+		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+	return buf;
+}
+
+static void
+print_rtmsg(struct rt_msghdr *rtm, int msglen)
+{
+	struct if_msghdr *ifm;
+	struct if_announcemsghdr *ifan;
+	char *state;
+	time_t now = time(NULL);
+	char *cnow = ctime(&now);
+
+	if (rtm->rtm_version != RTM_VERSION) {
+		(void) printf("routing message version %d not understood\n",
+		    rtm->rtm_version);
+		return;
+	}
+	switch (rtm->rtm_type) {
+	case RTM_IFINFO:
+		ifm = (struct if_msghdr *)rtm;
+		printf("%.19s RTM_IFINFO: if# %d, ",
+			cnow, ifm->ifm_index);
+#if notyet
+		switch (ifm->ifm_data.ifi_link_state) {
+		case LINK_STATE_DOWN:
+			state = "down";
+			break;
+		case LINK_STATE_UP:
+			state = "up";
+			break;
+		default:
+			state = "unknown";
+			break;
+		}
+		printf("link: %s, flags:", state);
+#endif
+		bprintf(stdout, ifm->ifm_flags, ifnetflags);
+		pmsg_addrs((char *)(ifm + 1), ifm->ifm_addrs);
+		break;
+	case RTM_IFANNOUNCE:
+		ifan = (struct if_announcemsghdr *)rtm;
+		printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
+			cnow, ifan->ifan_index);
+		switch (ifan->ifan_what) {
+		case IFAN_ARRIVAL:
+			printf("arrival");
+			break;
+		case IFAN_DEPARTURE:
+			printf("departure");
+			break;
+		default:
+			printf("#%d", ifan->ifan_what);
+			break;
+		}
+		printf("\n");
+		break;
+	case RTM_IEEE80211:
+#define	V(type)	((struct type *)(&ifan[1]))
+		ifan = (struct if_announcemsghdr *)rtm;
+		printf("%.19s RTM_IEEE80211: ", cnow);
+		switch (ifan->ifan_what) {
+		case RTM_IEEE80211_ASSOC:
+			printf("associate with %s",
+			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
+			break;
+		case RTM_IEEE80211_REASSOC:
+			printf("reassociate with %s",
+			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
+			break;
+		case RTM_IEEE80211_DISASSOC:
+			printf("disassociate");
+			break;
+		case RTM_IEEE80211_JOIN:
+		case RTM_IEEE80211_REJOIN:
+			printf("%s station %sjoin",
+			    ifan->ifan_what == RTM_IEEE80211_REJOIN ? "re" : "",
+			    ether_sprintf(V(ieee80211_join_event)->iev_addr));
+			break;
+		case RTM_IEEE80211_LEAVE:
+			printf("%s station leave",
+			    ether_sprintf(V(ieee80211_leave_event)->iev_addr));
+			break;
+		case RTM_IEEE80211_SCAN:
+			printf("scan complete");
+			break;
+		case RTM_IEEE80211_REPLAY:
+			printf("replay failure: src %s "
+			    , ether_sprintf(V(ieee80211_replay_event)->iev_src)
+			);
+			printf("dst %s cipher %u keyix %u keyrsc %llu rsc %llu"
+			    , ether_sprintf(V(ieee80211_replay_event)->iev_dst)
+			    , V(ieee80211_replay_event)->iev_cipher
+			    , V(ieee80211_replay_event)->iev_keyix
+			    , V(ieee80211_replay_event)->iev_keyrsc
+			    , V(ieee80211_replay_event)->iev_rsc
+			);
+			break;
+		case RTM_IEEE80211_MICHAEL:
+			printf("michael failure: src %s "
+			    , ether_sprintf(V(ieee80211_michael_event)->iev_src)
+			);
+			printf("dst %s cipher %u keyix %u"
+			    , ether_sprintf(V(ieee80211_michael_event)->iev_dst)
+			    , V(ieee80211_michael_event)->iev_cipher
+			    , V(ieee80211_michael_event)->iev_keyix
+			);
+			break;
+		default:
+			printf("if# %d, what: #%d",
+				ifan->ifan_index, ifan->ifan_what);
+			break;
+		}
+		printf("\n");
+		break;
+#undef V
+	}
+}
diff -N -u -r src.preview/tools/tools/ath/Makefile src/tools/tools/ath/Makefile
--- src.preview/tools/tools/ath/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/Makefile	2005-09-27 15:21:19.000000000 -0400
@@ -0,0 +1,51 @@
+#	$FreeBSD$
+#
+# Copyright (c) 2002, 2003	Sam Leffler, Errno Consulting
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+#
+BINDIR=	/usr/local/bin
+
+ALL=	athstats 80211stats 80211watch athdebug 80211debug
+
+all:	${ALL}
+
+athstats: athstats.c
+	${CC} -o athstats athstats.c
+athdebug: athdebug.c
+	${CC} -o athdebug athdebug.c
+80211stats: 80211stats.c
+	${CC} -o 80211stats 80211stats.c
+80211watch: 80211watch.c
+	${CC} -o 80211watch 80211watch.c
+80211debug: 80211debug.c
+	${CC} -o 80211debug 80211debug.c
+install: ${ALL}
+	install athstats ${DESTDIR}${BINDIR}
+	install 80211stats ${DESTDIR}${BINDIR}
+	install 80211watch ${DESTDIR}${BINDIR}
+	install 80211debug ${DESTDIR}${BINDIR}
+	install athdebug ${DESTDIR}${BINDIR}
+
+clean:
+	rm -f ${ALL} core a.out
diff -N -u -r src.preview/tools/tools/ath/athctrl.sh src/tools/tools/ath/athctrl.sh
--- src.preview/tools/tools/ath/athctrl.sh	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/athctrl.sh	2005-09-27 15:21:19.000000000 -0400
@@ -0,0 +1,42 @@
+#! /bin/sh
+#
+# Set the IFS parameters for an interface configured for
+# point-to-point use at a specific distance.  Based on a
+# program by Gunter Burchardt.
+#
+# $FreeBSD$
+#
+DEV=ath0
+d=0
+
+usage()
+{
+	echo "Usage: $0 [-i athX] [-d meters]"
+	exit 2
+}
+
+args=`getopt d:i: $*`
+test $? -ne 0 && usage
+
+set -- $args
+for i; do
+	case "$i" in
+	-i)	DEV="$2"; shift; shift;;
+	-d)	d="$2"; shift; shift;;
+	--)	shift; break;
+	esac
+done
+
+test $d -eq 0 && usage
+
+slottime=`expr 9 + \( $d / 300 \)`
+if expr \( $d % 300 \) != 0 >/dev/null 2>&1; then
+	slottime=`expr $slottime + 1`
+fi
+timeout=`expr $slottime \* 2 + 3`
+
+printf "Setup IFS parameters on interface ${DEV} for %i meter p-2-p link\n" $d
+ATHN=`echo $DEV | sed 's/ath//'`
+echo sysctl dev.ath.$ATHN.slottime=$slottime
+echo sysctl dev.ath.$ATHN.acktimeout=$timeout
+echo sysctl dev.ath.$ATHN.ctstimeout=$timeout
diff -N -u -r src.preview/tools/tools/ath/athdebug.c src/tools/tools/ath/athdebug.c
--- src.preview/tools/tools/ath/athdebug.c	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/athdebug.c	2005-09-27 15:30:15.000000000 -0400
@@ -0,0 +1,217 @@
+/*-
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ * $FreeBSD$
+ */
+
+/*
+ * athdebug [-i interface] flags
+ * (default interface is ath0).
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#define	N(a)	(sizeof(a)/sizeof(a[0]))
+
+const char *progname;
+
+enum {
+	ATH_DEBUG_XMIT		= 0x00000001,	/* basic xmit operation */
+	ATH_DEBUG_XMIT_DESC	= 0x00000002,	/* xmit descriptors */
+	ATH_DEBUG_RECV		= 0x00000004,	/* basic recv operation */
+	ATH_DEBUG_RECV_DESC	= 0x00000008,	/* recv descriptors */
+	ATH_DEBUG_RATE		= 0x00000010,	/* rate control */
+	ATH_DEBUG_RESET		= 0x00000020,	/* reset processing */
+	ATH_DEBUG_MODE		= 0x00000040,	/* mode init/setup */
+	ATH_DEBUG_BEACON 	= 0x00000080,	/* beacon handling */
+	ATH_DEBUG_WATCHDOG 	= 0x00000100,	/* watchdog timeout */
+	ATH_DEBUG_INTR		= 0x00001000,	/* ISR */
+	ATH_DEBUG_TX_PROC	= 0x00002000,	/* tx ISR proc */
+	ATH_DEBUG_RX_PROC	= 0x00004000,	/* rx ISR proc */
+	ATH_DEBUG_BEACON_PROC	= 0x00008000,	/* beacon ISR proc */
+	ATH_DEBUG_CALIBRATE	= 0x00010000,	/* periodic calibration */
+	ATH_DEBUG_KEYCACHE	= 0x00020000,	/* key cache management */
+	ATH_DEBUG_STATE		= 0x00040000,	/* 802.11 state transitions */
+	ATH_DEBUG_NODE		= 0x00080000,	/* node management */
+	ATH_DEBUG_FATAL		= 0x80000000,	/* fatal errors */
+	ATH_DEBUG_ANY		= 0xffffffff
+};
+
+static struct {
+	const char	*name;
+	u_int		bit;
+} flags[] = {
+	{ "xmit",	ATH_DEBUG_XMIT },
+	{ "xmit_desc",	ATH_DEBUG_XMIT_DESC },
+	{ "recv",	ATH_DEBUG_RECV },
+	{ "recv_desc",	ATH_DEBUG_RECV_DESC },
+	{ "rate",	ATH_DEBUG_RATE },
+	{ "reset",	ATH_DEBUG_RESET },
+	{ "mode",	ATH_DEBUG_MODE },
+	{ "beacon",	ATH_DEBUG_BEACON },
+	{ "watchdog",	ATH_DEBUG_WATCHDOG },
+	{ "intr",	ATH_DEBUG_INTR },
+	{ "xmit_proc",	ATH_DEBUG_TX_PROC },
+	{ "recv_proc",	ATH_DEBUG_RX_PROC },
+	{ "beacon_proc",ATH_DEBUG_BEACON_PROC },
+	{ "calibrate",	ATH_DEBUG_CALIBRATE },
+	{ "keycache",	ATH_DEBUG_KEYCACHE },
+	{ "state",	ATH_DEBUG_STATE },
+	{ "node",	ATH_DEBUG_NODE },
+	{ "fatal",	ATH_DEBUG_FATAL },
+};
+
+static u_int
+getflag(const char *name, int len)
+{
+	int i;
+
+	for (i = 0; i < N(flags); i++)
+		if (strncasecmp(flags[i].name, name, len) == 0)
+			return flags[i].bit;
+	return 0;
+}
+
+static const char *
+getflagname(u_int flag)
+{
+	int i;
+
+	for (i = 0; i < N(flags); i++)
+		if (flags[i].bit == flag)
+			return flags[i].name;
+	return "???";
+}
+
+static void
+usage(void)
+{
+	int i;
+
+	fprintf(stderr, "usage: %s [-i device] [flags]\n", progname);
+	fprintf(stderr, "where flags are:\n");
+	for (i = 0; i < N(flags); i++)
+		printf("%s\n", flags[i].name);
+	exit(-1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const char *ifname = "ath0";
+	const char *cp, *tp;
+	const char *sep;
+	int c, op, i;
+	u_int32_t debug, ndebug;
+	size_t debuglen;
+	char oid[256];
+
+	progname = argv[0];
+	if (argc > 1) {
+		if (strcmp(argv[1], "-i") == 0) {
+			if (argc < 2)
+				errx(1, "missing interface name for -i option");
+			ifname = argv[2];
+			if (strncmp(ifname, "ath", 3) != 0)
+				errx(2, "huh, this is for ath devices?");
+			argc -= 2, argv += 2;
+		} else if (strcmp(argv[1], "-?") == 0)
+			usage();
+	}
+
+#ifdef __linux__
+	snprintf(oid, sizeof(oid), "dev.%s.debug", ifname);
+#else
+	snprintf(oid, sizeof(oid), "hw.ath%s.debug", ifname+3);
+#endif
+	debuglen = sizeof(debug);
+	if (sysctlbyname(oid, &debug, &debuglen, NULL, 0) < 0)
+		err(1, "sysctl-get(%s)", oid);
+	ndebug = debug;
+	for (; argc > 1; argc--, argv++) {
+		cp = argv[1];
+		do {
+			u_int bit;
+
+			if (*cp == '-') {
+				cp++;
+				op = -1;
+			} else if (*cp == '+') {
+				cp++;
+				op = 1;
+			} else
+				op = 0;
+			for (tp = cp; *tp != '\0' && *tp != '+' && *tp != '-';)
+				tp++;
+			bit = getflag(cp, tp-cp);
+			if (op < 0)
+				ndebug &= ~bit;
+			else if (op > 0)
+				ndebug |= bit;
+			else {
+				if (bit == 0) {
+					if (isdigit(*cp))
+						bit = strtoul(cp, NULL, 0);
+					else
+						errx(1, "unknown flag %.*s",
+							tp-cp, cp);
+				}
+				ndebug = bit;
+			}
+		} while (*(cp = tp) != '\0');
+	}
+	if (debug != ndebug) {
+		printf("%s: 0x%x => ", oid, debug);
+		if (sysctlbyname(oid, NULL, NULL, &ndebug, sizeof(ndebug)) < 0)
+			err(1, "sysctl-set(%s)", oid);
+		printf("0x%x", ndebug);
+		debug = ndebug;
+	} else
+		printf("%s: 0x%x", oid, debug);
+	sep = "<";
+	for (i = 0; i < N(flags); i++)
+		if (debug & flags[i].bit) {
+			printf("%s%s", sep, flags[i].name);
+			sep = ",";
+		}
+	printf("%s\n", *sep != '<' ? ">" : "");
+	return 0;
+}
diff -N -u -r src.preview/tools/tools/ath/athstats.c src/tools/tools/ath/athstats.c
--- src.preview/tools/tools/ath/athstats.c	1969-12-31 19:00:00.000000000 -0500
+++ src/tools/tools/ath/athstats.c	2005-09-27 15:22:55.000000000 -0400
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer,
+ *    without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ *    redistribution must be conditioned upon including a substantially
+ *    similar Disclaimer requirement for further binary redistribution.
+ * 3. Neither the names of the above-listed copyright holders nor the names
+ *    of any contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Simple Atheros-specific tool to inspect and monitor network traffic
+ * statistics.
+ *	athstats [-i interface] [interval]
+ * (default interface is ath0).  If interval is specified a rolling output
+ * a la netstat -i is displayed every interval seconds.
+ *
+ * To build: cc -o athstats athstats.c -lkvm
+ */
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/if_var.h>
+
+#include <stdio.h>
+#include <signal.h>
+
+#include "../../../sys/contrib/dev/ath/ah_desc.h"
+#include "../../../sys/netproto/802_11/ieee80211_ioctl.h"
+#include "../../../sys/netproto/802_11/ieee80211_radiotap.h"
+#include "../../../sys/dev/netif/ath/if_athioctl.h"
+
+static const struct {
+	u_int		phyerr;
+	const char*	desc;
+} phyerrdescriptions[] = {
+	{ HAL_PHYERR_UNDERRUN,		"transmit underrun" },
+	{ HAL_PHYERR_TIMING,		"timing error" },
+	{ HAL_PHYERR_PARITY,		"illegal parity" },
+	{ HAL_PHYERR_RATE,		"illegal rate" },
+	{ HAL_PHYERR_LENGTH,		"illegal length" },
+	{ HAL_PHYERR_RADAR,		"radar detect" },
+	{ HAL_PHYERR_SERVICE,		"illegal service" },
+	{ HAL_PHYERR_TOR,		"transmit override receive" },
+	{ HAL_PHYERR_OFDM_TIMING,	"OFDM timing" },
+	{ HAL_PHYERR_OFDM_SIGNAL_PARITY,"OFDM illegal parity" },
+	{ HAL_PHYERR_OFDM_RATE_ILLEGAL,	"OFDM illegal rate" },
+	{ HAL_PHYERR_OFDM_POWER_DROP,	"OFDM power drop" },
+	{ HAL_PHYERR_OFDM_SERVICE,	"OFDM illegal service" },
+	{ HAL_PHYERR_OFDM_RESTART,	"OFDM restart" },
+	{ HAL_PHYERR_CCK_TIMING,	"CCK timing" },
+	{ HAL_PHYERR_CCK_HEADER_CRC,	"CCK header crc" },
+	{ HAL_PHYERR_CCK_RATE_ILLEGAL,	"CCK illegal rate" },
+	{ HAL_PHYERR_CCK_SERVICE,	"CCK illegal service" },
+	{ HAL_PHYERR_CCK_RESTART,	"CCK restart" },
+};
+
+static void
+printstats(FILE *fd, const struct ath_stats *stats)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+#define	STAT(x,fmt) \
+	if (stats->ast_##x) fprintf(fd, "%u " fmt "\n", stats->ast_##x)
+	int i, j;
+
+	STAT(watchdog, "watchdog timeouts");
+	STAT(hardware, "hardware error interrupts");
+	STAT(bmiss, "beacon miss interrupts");
+	STAT(bstuck, "stuck beacon conditions");
+	STAT(rxorn, "recv overrun interrupts");
+	STAT(rxeol, "recv eol interrupts");
+	STAT(txurn, "txmit underrun interrupts");
+	STAT(mib, "mib overflow interrupts");
+	STAT(intrcoal, "interrupts coalesced");
+	STAT(tx_mgmt, "tx management frames");
+	STAT(tx_discard, "tx frames discarded prior to association");
+	STAT(tx_qstop, "tx stopped 'cuz no xmit buffer");
+	STAT(tx_encap, "tx encapsulation failed");
+	STAT(tx_nonode, "tx failed 'cuz no node");
+	STAT(tx_nombuf, "tx failed 'cuz no mbuf");
+	STAT(tx_nomcl, "tx failed 'cuz no cluster");
+	STAT(tx_linear, "tx linearized to cluster");
+	STAT(tx_nodata, "tx discarded empty frame");
+	STAT(tx_busdma, "tx failed for dma resrcs");
+	STAT(tx_xretries, "tx failed 'cuz too many retries");
+	STAT(tx_fifoerr, "tx failed 'cuz FIFO underrun");
+	STAT(tx_filtered, "tx failed 'cuz xmit filtered");
+	STAT(tx_shortretry, "short on-chip tx retries");
+	STAT(tx_longretry, "long on-chip tx retries");
+	STAT(tx_badrate, "tx failed 'cuz bogus xmit rate");
+	STAT(tx_noack, "tx frames with no ack marked");
+	STAT(tx_rts, "tx frames with rts enabled");
+	STAT(tx_cts, "tx frames with cts enabled");
+	STAT(tx_shortpre, "tx frames with short preamble");
+	STAT(tx_altrate, "tx frames with an alternate rate");
+	STAT(tx_protect, "tx frames with 11g protection");
+	STAT(tx_ctsburst, "tx frames with 11g protection and bursting");
+	STAT(tx_ctsext, "tx frames with 11g cts protection extended for bursting");
+	STAT(rx_nombuf,	"rx setup failed 'cuz no mbuf");
+	STAT(rx_busdma,	"rx setup failed for dma resrcs");
+	STAT(rx_orn, "rx failed 'cuz of desc overrun");
+	STAT(rx_crcerr, "rx failed 'cuz of bad CRC");
+	STAT(rx_fifoerr, "rx failed 'cuz of FIFO overrun");
+	STAT(rx_badcrypt, "rx failed 'cuz decryption");
+	STAT(rx_badmic, "rx failed 'cuz MIC failure");
+	STAT(rx_tooshort, "rx failed 'cuz frame too short");
+	STAT(rx_toobig, "rx failed 'cuz frame too large");
+	STAT(rx_mgt, "rx management frames");
+	STAT(rx_ctl, "rx control frames");
+	STAT(rx_phyerr, "rx failed 'cuz of PHY err");
+	if (stats->ast_rx_phyerr != 0) {
+		for (i = 0; i < 32; i++) {
+			if (stats->ast_rx_phy[i] == 0)
+				continue;
+			for (j = 0; j < N(phyerrdescriptions); j++)
+				if (phyerrdescriptions[j].phyerr == i)
+					break;
+			if (j == N(phyerrdescriptions))
+				fprintf(fd,
+					"    %u (unknown phy error code %u)\n",
+					stats->ast_rx_phy[i], i);
+			else
+				fprintf(fd, "    %u %s\n",
+					stats->ast_rx_phy[i],
+					phyerrdescriptions[j].desc);
+		}
+	}
+	STAT(be_nombuf,	"beacon setup failed 'cuz no mbuf");
+	STAT(be_xmit,	"beacons transmitted");
+	STAT(per_cal, "periodic calibrations");
+	STAT(per_calfail, "periodic calibration failures");
+	STAT(per_rfgain, "rfgain value change");
+	STAT(rate_calls, "rate control checks");
+	STAT(rate_raise, "rate control raised xmit rate");
+	STAT(rate_drop, "rate control dropped xmit rate");
+	if (stats->ast_tx_rssi)
+		fprintf(fd, "rssi of last ack: %u\n", stats->ast_tx_rssi);
+	if (stats->ast_rx_rssi)
+		fprintf(fd, "avg recv rssi: %u\n", stats->ast_rx_rssi);
+	STAT(ant_defswitch, "switched default/rx antenna");
+	STAT(ant_txswitch, "tx used alternate antenna");
+	fprintf(fd, "Antenna profile:\n");
+	for (i = 0; i < 8; i++)
+		if (stats->ast_ant_rx[i] || stats->ast_ant_tx[i])
+			fprintf(fd, "[%u] tx %8u rx %8u\n", i,
+				stats->ast_ant_tx[i], stats->ast_ant_rx[i]);
+#undef STAT
+#undef N
+}
+
+static u_int
+getifrate(int s, const char* ifname)
+{
+#define	N(a)	(sizeof(a) / sizeof(a[0]))
+	static const int rates[] = {
+		0,		/* IFM_AUTO */
+		0,		/* IFM_MANUAL */
+		0,		/* IFM_NONE */
+		1,		/* IFM_IEEE80211_FH1 */
+		2,		/* IFM_IEEE80211_FH2 */
+		1,		/* IFM_IEEE80211_DS1 */
+		2,		/* IFM_IEEE80211_DS2 */
+		5,		/* IFM_IEEE80211_DS5 */
+		11,		/* IFM_IEEE80211_DS11 */
+		22,		/* IFM_IEEE80211_DS22 */
+		6,		/* IFM_IEEE80211_OFDM6 */
+		9,		/* IFM_IEEE80211_OFDM9 */
+		12,		/* IFM_IEEE80211_OFDM12 */
+		18,		/* IFM_IEEE80211_OFDM18 */
+		24,		/* IFM_IEEE80211_OFDM24 */
+		36,		/* IFM_IEEE80211_OFDM36 */
+		48,		/* IFM_IEEE80211_OFDM48 */
+		54,		/* IFM_IEEE80211_OFDM54 */
+		72,		/* IFM_IEEE80211_OFDM72 */
+	};
+	struct ifmediareq ifmr;
+	int *media_list, i;
+
+	(void) memset(&ifmr, 0, sizeof(ifmr));
+	(void) strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
+		return 0;
+	return IFM_SUBTYPE(ifmr.ifm_active) < N(rates) ?
+		rates[IFM_SUBTYPE(ifmr.ifm_active)] : 0;
+#undef N
+}
+
+static int signalled;
+
+static void
+catchalarm(int signo __unused)
+{
+	signalled = 1;
+}
+
+int
+main(int argc, char *argv[])
+{
+	int s;
+	struct ifreq ifr;
+
+	s = socket(AF_INET, SOCK_DGRAM, 0);
+	if (s < 0)
+		err(1, "socket");
+	if (argc > 1 && strcmp(argv[1], "-i") == 0) {
+		if (argc < 2) {
+			fprintf(stderr, "%s: missing interface name for -i\n",
+				argv[0]);
+			exit(-1);
+		}
+		strncpy(ifr.ifr_name, argv[2], sizeof (ifr.ifr_name));
+		argc -= 2, argv += 2;
+	} else
+		strncpy(ifr.ifr_name, "ath0", sizeof (ifr.ifr_name));
+	if (argc > 1) {
+		u_long interval = strtoul(argv[1], NULL, 0);
+		int line, omask;
+		u_int rate = getifrate(s, ifr.ifr_name);
+		struct ath_stats cur, total;
+
+		if (interval < 1)
+			interval = 1;
+		signal(SIGALRM, catchalarm);
+		signalled = 0;
+		alarm(interval);
+	banner:
+		printf("%8s %8s %7s %7s %7s %6s %6s %5s %7s %4s %4s"
+			, "input"
+			, "output"
+			, "altrate"
+			, "short"
+			, "long"
+			, "xretry"
+			, "crcerr"
+			, "crypt"
+			, "phyerr"
+			, "rssi"
+			, "rate"
+		);
+		putchar('\n');
+		fflush(stdout);
+		line = 0;
+	loop:
+		if (line != 0) {
+			ifr.ifr_data = (caddr_t) &cur;
+			if (ioctl(s, SIOCGATHSTATS, &ifr) < 0)
+				err(1, ifr.ifr_name);
+			rate = getifrate(s, ifr.ifr_name);
+			printf("%8u %8u %7u %7u %7u %6u %6u %5u %7u %4u %3uM\n"
+				, cur.ast_rx_packets - total.ast_rx_packets
+				, cur.ast_tx_packets - total.ast_tx_packets
+				, cur.ast_tx_altrate - total.ast_tx_altrate
+				, cur.ast_tx_shortretry - total.ast_tx_shortretry
+				, cur.ast_tx_longretry - total.ast_tx_longretry
+				, cur.ast_tx_xretries - total.ast_tx_xretries
+				, cur.ast_rx_crcerr - total.ast_rx_crcerr
+				, cur.ast_rx_badcrypt - total.ast_rx_badcrypt
+				, cur.ast_rx_phyerr - total.ast_rx_phyerr
+				, cur.ast_rx_rssi
+				, rate
+			);
+			total = cur;
+		} else {
+			ifr.ifr_data = (caddr_t) &total;
+			if (ioctl(s, SIOCGATHSTATS, &ifr) < 0)
+				err(1, ifr.ifr_name);
+			rate = getifrate(s, ifr.ifr_name);
+			printf("%8u %8u %7u %7u %7u %6u %6u %5u %7u %4u %3uM\n"
+				, total.ast_rx_packets
+				, total.ast_tx_packets
+				, total.ast_tx_altrate
+				, total.ast_tx_shortretry
+				, total.ast_tx_longretry
+				, total.ast_tx_xretries
+				, total.ast_rx_crcerr
+				, total.ast_rx_badcrypt
+				, total.ast_rx_phyerr
+				, total.ast_rx_rssi
+				, rate
+			);
+		}
+		fflush(stdout);
+		omask = sigblock(sigmask(SIGALRM));
+		if (!signalled)
+			sigpause(0);
+		sigsetmask(omask);
+		signalled = 0;
+		alarm(interval);
+		line++;
+		if (line == 21)		/* XXX tty line count */
+			goto banner;
+		else
+			goto loop;
+		/*NOTREACHED*/
+	} else {
+		struct ath_stats stats;
+
+		ifr.ifr_data = (caddr_t) &stats;
+		if (ioctl(s, SIOCGATHSTATS, &ifr) < 0)
+			err(1, ifr.ifr_name);
+		printstats(stdout, &stats);
+	}
+	return 0;
+}




More information about the Submit mailing list