changeset 3737:ddc3d2cb268a

PSARC 2006/705 pcwl and pcan 802.11b Wireless Drivers 6209928 drivers for Wavelan, Prism II, and Aironet 802.11b devices
author hx147065
date Wed, 28 Feb 2007 19:21:46 -0800
parents aa9dbf463acb
children 087c66d22b01
files usr/src/pkgdefs/Makefile usr/src/pkgdefs/SUNWckr/prototype_sparc usr/src/pkgdefs/SUNWpcan/Makefile usr/src/pkgdefs/SUNWpcan/pkginfo.tmpl usr/src/pkgdefs/SUNWpcan/postinstall usr/src/pkgdefs/SUNWpcan/postremove usr/src/pkgdefs/SUNWpcan/prototype_com usr/src/pkgdefs/SUNWpcan/prototype_i386 usr/src/pkgdefs/SUNWpcan/prototype_sparc usr/src/pkgdefs/SUNWpcwl/Makefile usr/src/pkgdefs/SUNWpcwl/pkginfo.tmpl usr/src/pkgdefs/SUNWpcwl/postinstall usr/src/pkgdefs/SUNWpcwl/postremove usr/src/pkgdefs/SUNWpcwl/prototype_com usr/src/pkgdefs/SUNWpcwl/prototype_i386 usr/src/pkgdefs/SUNWpcwl/prototype_sparc usr/src/uts/common/Makefile.files usr/src/uts/common/Makefile.rules usr/src/uts/common/io/pcan/pcan.c usr/src/uts/common/io/pcan/pcan.h usr/src/uts/common/io/pcwl/pcwl.c usr/src/uts/common/io/pcwl/pcwl.h usr/src/uts/intel/Makefile.intel.shared usr/src/uts/intel/pcan/Makefile usr/src/uts/intel/pcwl/Makefile usr/src/uts/sparc/Makefile.sparc.shared usr/src/uts/sparc/mac_wifi/Makefile usr/src/uts/sparc/pcan/Makefile usr/src/uts/sparc/pcwl/Makefile
diffstat 29 files changed, 11528 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/pkgdefs/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -272,6 +272,7 @@
 	SUNWypu \
 	SUNWpamsc \
 	SUNWpapi \
+	SUNWpcan \
 	SUNWpcelx \
 	SUNWpcmci  \
 	SUNWpcmcu  \
@@ -279,6 +280,7 @@
 	SUNWpcr	\
 	SUNWpcser \
 	SUNWpcu	\
+	SUNWpcwl \
 	SUNWpd    \
 	SUNWphx \
 	SUNWpolkit \
--- a/usr/src/pkgdefs/SUNWckr/prototype_sparc	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/pkgdefs/SUNWckr/prototype_sparc	Wed Feb 28 19:21:46 2007 -0800
@@ -158,6 +158,7 @@
 f none kernel/ipp/sparcv9/ipgpc 755 root sys
 d none kernel/mac/sparcv9 755 root sys
 f none kernel/mac/sparcv9/mac_ether 755 root sys
+f none kernel/mac/sparcv9/mac_wifi 755 root sys
 d none kernel/misc/sparcv9 755 root sys
 f none kernel/misc/sparcv9/busra 755 root sys
 f none kernel/misc/sparcv9/cardbus 755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES) postinstall postremove
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/pkginfo.tmpl	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+PKG=SUNWpcan
+NAME=Cisco-Aironet 802.11b driver
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="Cisco-Aironet 802.11b driver"
+CLASSES="none"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/postinstall	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,133 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+# Function: check_add_drv()
+#
+# This function will check if the module has an entry in etc/name_to_major
+# If not simply calls add_drv with the arguments given. If there is
+# such an entry in name_to_major file, it adds entries in driver_aliases
+# driver_classes and minor_perm if necessary.
+# The syntax of this function is the same as add_drv. 
+
+check_add_drv()
+{
+	if [ "$BASEDIR" = "" ]
+	then
+		BASEDIR=/  
+	fi
+	alias=""
+	class=""
+	ADD_ALIAS=0
+	ADD_CLASS=0
+	ADD_MINOR=0
+	OPTIND=1
+	IS_NET_DRIVER=0
+
+	cmd="add_drv"
+
+	NO_CMD=
+	while getopts i:b:m:c:N  opt
+	do
+		case $opt in
+			N )	NO_CMD=1;;
+			i )	ADD_ALIAS=1	
+				alias=$OPTARG
+				cmd=$cmd" -i '$alias'"
+				;;
+			m )	ADD_MINOR=1
+				minor=$OPTARG
+				cmd=$cmd" -m '$minor'"
+				;;
+			c)	ADD_CLASS=1
+				class=$OPTARG
+				cmd=$cmd" -c $class"
+				;;
+			b)	if [ "${OPTARG}" != "/" ]; then	
+					BASEDIR=$OPTARG
+					cmd=$cmd" -b $BASEDIR"
+				fi
+				;;
+			\?) 	echo "check_add_drv can not handle this option"
+				return
+				;;
+			esac
+	done 
+	shift `/usr/bin/expr $OPTIND - 1`
+	
+	drvname=$1
+
+	cmd=$cmd" "$drvname
+
+	drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'`
+
+	/usr/bin/grep "^$drvname[ 	]" $BASEDIR/etc/name_to_major >  /dev/null 2>&1
+
+	if [ "$NO_CMD" = "" -a $? -ne 0 ] 
+	then
+		eval $cmd
+	else	
+		# entry already in name_to_major, add alias, class, minorperm
+		# if necessary
+		if [ $ADD_ALIAS = 1 ]	
+		then
+			for i in $alias
+			do
+				/usr/bin/egrep "^$drvname[ 	]+$i" $BASEDIR/etc/driver_aliases>/dev/null 2>&1
+				if [ $? -ne 0 ]
+				then
+					echo "$drvname $i" >> $BASEDIR/etc/driver_aliases	
+				fi
+			done
+		fi
+
+		if [ $ADD_CLASS = 1 ]
+		then
+			/usr/bin/egrep "^$drvname[ 	]+$class( |	|$)" $BASEDIR/etc/driver_classes > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				echo "$drvname\t$class" >> $BASEDIR/etc/driver_classes
+			fi
+		fi
+
+		if [ $ADD_MINOR = 1 ]
+		then
+			/usr/bin/grep "^$drvname:" $BASEDIR/etc/minor_perm > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				minorentry="$drvname:$minor"
+				echo $minorentry >> $BASEDIR/etc/minor_perm
+			fi
+		fi
+
+	fi
+
+
+}
+
+check_add_drv -i '"pccard15f,a" "pccard15f,7" "pci14b9,5000" "pci14b9,a504"' -b "$BASEDIR" pcan 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/postremove	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,39 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+BD=${BASEDIR:-/}
+if grep "\<pcan\>" $BD/etc/name_to_major > /dev/null 2>&1
+then
+        rem_drv -b ${BD} pcan
+	if [ $? -ne 0 ]
+	then
+		exit 1
+	fi
+fi
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/prototype_com	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,49 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+i pkginfo
+i copyright
+i depend
+i postinstall
+i postremove
+#
+# source locations relative to the prototype file
+#
+d none kernel 0755 root sys
+d none kernel/drv 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/prototype_i386	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,49 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+# Wireless Lan driver for and Agere/Prism-II chipset
+f none kernel/drv/pcan 0755 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/pcan 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcan/prototype_sparc	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,48 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are sparc specific here
+# Wireless Lan driver for Agere/Prism-II chipset
+d none kernel/drv/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/pcan 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+DATAFILES += depend
+
+.KEEP_STATE:
+
+all: $(FILES) postinstall postremove
+install: all pkg
+
+include ../Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/pkginfo.tmpl	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,46 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+PKG=SUNWpcwl
+NAME=Lucent/PRISM-II 802.11b driver
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGVERS="1.0"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY=system
+VENDOR="Sun Microsystems, Inc."
+DESC="Lucent/PRISM-II 802.11b driver"
+CLASSES="none"
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+BASEDIR=/
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="true"
+SUNW_PKG_THISZONE="false"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/postinstall	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,133 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+# Function: check_add_drv()
+#
+# This function will check if the module has an entry in etc/name_to_major
+# If not simply calls add_drv with the arguments given. If there is
+# such an entry in name_to_major file, it adds entries in driver_aliases
+# driver_classes and minor_perm if necessary.
+# The syntax of this function is the same as add_drv. 
+
+check_add_drv()
+{
+	if [ "$BASEDIR" = "" ]
+	then
+		BASEDIR=/  
+	fi
+	alias=""
+	class=""
+	ADD_ALIAS=0
+	ADD_CLASS=0
+	ADD_MINOR=0
+	OPTIND=1
+	IS_NET_DRIVER=0
+
+	cmd="add_drv"
+
+	NO_CMD=
+	while getopts i:b:m:c:N  opt
+	do
+		case $opt in
+			N )	NO_CMD=1;;
+			i )	ADD_ALIAS=1	
+				alias=$OPTARG
+				cmd=$cmd" -i '$alias'"
+				;;
+			m )	ADD_MINOR=1
+				minor=$OPTARG
+				cmd=$cmd" -m '$minor'"
+				;;
+			c)	ADD_CLASS=1
+				class=$OPTARG
+				cmd=$cmd" -c $class"
+				;;
+			b)	if [ "${OPTARG}" != "/" ]; then	
+					BASEDIR=$OPTARG
+					cmd=$cmd" -b $BASEDIR"
+				fi
+				;;
+			\?) 	echo "check_add_drv can not handle this option"
+				return
+				;;
+			esac
+	done 
+	shift `/usr/bin/expr $OPTIND - 1`
+	
+	drvname=$1
+
+	cmd=$cmd" "$drvname
+
+	drvname=`echo $drvname | /usr/bin/sed 's;.*/;;g'`
+
+	/usr/bin/grep "^$drvname[ 	]" $BASEDIR/etc/name_to_major >  /dev/null 2>&1
+
+	if [ "$NO_CMD" = "" -a $? -ne 0 ] 
+	then
+		eval $cmd
+	else	
+		# entry already in name_to_major, add alias, class, minorperm
+		# if necessary
+		if [ $ADD_ALIAS = 1 ]	
+		then
+			for i in $alias
+			do
+				/usr/bin/egrep "^$drvname[ 	]+$i" $BASEDIR/etc/driver_aliases>/dev/null 2>&1
+				if [ $? -ne 0 ]
+				then
+					echo "$drvname $i" >> $BASEDIR/etc/driver_aliases	
+				fi
+			done
+		fi
+
+		if [ $ADD_CLASS = 1 ]
+		then
+			/usr/bin/egrep "^$drvname[ 	]+$class( |	|$)" $BASEDIR/etc/driver_classes > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				echo "$drvname\t$class" >> $BASEDIR/etc/driver_classes
+			fi
+		fi
+
+		if [ $ADD_MINOR = 1 ]
+		then
+			/usr/bin/grep "^$drvname:" $BASEDIR/etc/minor_perm > /dev/null 2>&1
+			if [ $? -ne 0 ]
+			then 
+				minorentry="$drvname:$minor"
+				echo $minorentry >> $BASEDIR/etc/minor_perm
+			fi
+		fi
+
+	fi
+
+
+}
+
+check_add_drv -i '"pccard156,2" "pccardb,7300" "pci1260,3873" "pci1260,3872" "pci1385,4105"' -b "$BASEDIR" pcwl 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/postremove	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,39 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+BD=${BASEDIR:-/}
+if grep "\<pcwl\>" $BD/etc/name_to_major > /dev/null 2>&1
+then
+        rem_drv -b ${BD} pcwl
+	if [ $? -ne 0 ]
+	then
+		exit 1
+	fi
+fi
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/prototype_com	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,49 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+i pkginfo
+i copyright
+i depend
+i postinstall
+i postremove
+#
+# source locations relative to the prototype file
+#
+d none kernel 0755 root sys
+d none kernel/drv 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/prototype_i386	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,49 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are i386 specific here
+# Wireless Lan driver for and Agere/Prism-II chipset
+f none kernel/drv/pcwl 0755 root sys
+d none kernel/drv/amd64 0755 root sys
+f none kernel/drv/amd64/pcwl 0755 root sys
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/pkgdefs/SUNWpcwl/prototype_sparc	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,48 @@
+#! /usr/bin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+#
+# This required package information file contains a list of package contents.
+# The 'pkgmk' command uses this file to identify the contents of a package
+# and their location on the development machine when building the package.
+# Can be created via a text editor or through use of the 'pkgproto' command.
+
+#!search <pathname pathname ...>        # where to find pkg objects
+#!include <filename>                    # include another 'prototype' file
+#!default <mode> <owner> <group>        # default used if not specified on entry
+#!<param>=<value>                       # puts parameter in pkg environment
+
+#
+# Include ISA independent files (prototype_com)
+#
+!include prototype_com
+#
+# List files which are sparc specific here
+# Wireless Lan driver for Agere/Prism-II chipset
+d none kernel/drv/sparcv9 0755 root sys
+f none kernel/drv/sparcv9/pcwl 0755 root sys
--- a/usr/src/uts/common/Makefile.files	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/uts/common/Makefile.files	Wed Feb 28 19:21:46 2007 -0800
@@ -1247,6 +1247,8 @@
 
 PCS_OBJS += pcs.o
 
+PCAN_OBJS += pcan.o
+
 PCATA_OBJS += pcide.o pcdisk.o pclabel.o pcata.o
 
 PCMEM_OBJS += pcmem.o
@@ -1255,6 +1257,8 @@
 
 PCSER_OBJS += pcser.o pcser_cis.o
 
+PCWL_OBJS += pcwl.o
+
 PSET_OBJS +=	pset.o
 
 PCI_I2ONEXUS_OBJS += pci_to_i2o.o
--- a/usr/src/uts/common/Makefile.rules	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/uts/common/Makefile.rules	Wed Feb 28 19:21:46 2007 -0800
@@ -599,6 +599,14 @@
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
 
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/pcan/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/pcwl/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
 $(OBJS_DIR)/%.o:		$(UTSBASE)/common/io/ppp/sppp/%.c
 	$(COMPILE.c) -o $@ $<
 	$(CTFCONVERT_O)
@@ -1337,6 +1345,12 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcmcia/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcan/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/pcwl/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/io/ppp/sppp/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcan/pcan.c	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,4050 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999
+ *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/dlpi.h>
+#include <sys/ethernet.h>
+#include <sys/strsun.h>
+#include <sys/stat.h>
+#include <sys/byteorder.h>
+#include <sys/pccard.h>
+#include <sys/pci.h>
+#include <sys/policy.h>
+#include <sys/mac.h>
+#include <sys/stream.h>
+#include <inet/common.h>
+#include <inet/nd.h>
+#include <inet/mi.h>
+
+#include "pcan.h"
+#include <sys/mac_wifi.h>
+#include <inet/wifi_ioctl.h>
+
+#ifdef	DEBUG
+#define	PCAN_DBG_BASIC		0x1
+#define	PCAN_DBG_INFO		0x2
+#define	PCAN_DBG_SEND		0x4
+#define	PCAN_DBG_RCV		0x8
+#define	PCAN_DBG_LINKINFO	0x10
+#define	PCAN_DBG_FW_VERSION	0x20
+#define	PCAN_DBG_CMD		0x40
+uint32_t pcan_debug = 0;
+#define	PCANDBG(x) \
+	if (pcan_debug & PCAN_DBG_BASIC) cmn_err x
+#else
+#define	PCANDBG(x)
+#endif
+
+static ddi_device_acc_attr_t accattr = {
+		DDI_DEVICE_ATTR_V0,
+		DDI_STRUCTURE_LE_ACC,
+		DDI_STRICTORDER_ACC,
+};
+
+static ddi_dma_attr_t control_cmd_dma_attr = {
+	DMA_ATTR_V0,		/* version of this structure */
+	0,			/* lowest usable address */
+	0xffffffffffffffffull,	/* highest usable address */
+	0xffffffffull,		/* maximum DMAable byte count */
+	4,			/* alignment in bytes */
+	0xfff,			/* burst sizes (any) */
+	1,			/* minimum transfer */
+	0xffffull,		/* maximum transfer */
+	0xffffffffffffffffull,	/* maximum segment length */
+	1,			/* maximum number of segments */
+	1,			/* granularity */
+	0,			/* flags (reserved) */
+};
+
+void *pcan_soft_state_p = NULL;
+static int pcan_device_type;
+
+mac_callbacks_t pcan_m_callbacks = {
+	MC_IOCTL,
+	pcan_gstat,
+	pcan_start,
+	pcan_stop,
+	pcan_prom,
+	pcan_sdmulti,
+	pcan_saddr,
+	pcan_tx,
+	NULL,
+	pcan_ioctl
+};
+
+static char *pcan_name_str = "pcan";
+
+DDI_DEFINE_STREAM_OPS(pcan_dev_ops, nulldev, pcan_probe, pcan_attach,
+
+    pcan_detach, nodev, NULL, D_MP, NULL);
+
+extern struct mod_ops mod_driverops;
+static struct modldrv modldrv = {
+	&mod_driverops,
+	"Cisco-Aironet 802.11b driver",
+	&pcan_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modldrv, NULL
+	};
+
+int
+_init(void)
+{
+	int stat;
+
+	/* Allocate soft state */
+	if ((stat = ddi_soft_state_init(&pcan_soft_state_p,
+	    sizeof (pcan_maci_t), 2)) != DDI_SUCCESS)
+		return (stat);
+
+	mac_init_ops(&pcan_dev_ops, "pcan");
+	stat = mod_install(&modlinkage);
+	if (stat != 0) {
+		mac_fini_ops(&pcan_dev_ops);
+		ddi_soft_state_fini(&pcan_soft_state_p);
+	}
+
+	return (stat);
+}
+
+int
+_fini(void)
+{
+	int stat;
+
+	stat = mod_remove(&modlinkage);
+	if (stat != DDI_SUCCESS)
+		return (stat);
+	mac_fini_ops(&pcan_dev_ops);
+	ddi_soft_state_fini(&pcan_soft_state_p);
+	return (stat);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+pcan_probe(dev_info_t *dip)
+{
+	int len, ret;
+	char *buf;
+	dev_info_t *pdip = ddi_get_parent(dip);
+
+	PCANDBG((CE_NOTE, "pcan probe: parent dip=0x%p-%s(%d)\n", (void *)pdip,
+	    ddi_driver_name(pdip), ddi_get_instance(pdip)));
+
+	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
+	    (caddr_t)&buf, &len);
+	if (ret != DDI_SUCCESS)
+		return (DDI_PROBE_FAILURE);
+
+	PCANDBG((CE_NOTE, "pcan probe: device_type %s\n", buf));
+	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
+		pcan_device_type = PCAN_DEVICE_PCCARD;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_FW_VERSION) {
+			cmn_err(CE_NOTE, "Cisco 802.11 pccard\n");
+		}
+#endif
+		ret = DDI_PROBE_SUCCESS;
+	} else if (strcmp(buf, "pci") == 0) {
+		pcan_device_type = PCAN_DEVICE_PCI;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_FW_VERSION) {
+			cmn_err(CE_NOTE, "Cisco 802.11 minipci card\n");
+		}
+#endif
+		ret = DDI_PROBE_SUCCESS;
+	} else {
+		cmn_err(CE_NOTE, "pcan probe: unsupported card\n");
+		ret = DDI_PROBE_FAILURE;
+	}
+
+	kmem_free(buf, len);
+	return (ret);
+}
+
+static int
+pcan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int ret;
+	int instance;
+	uint16_t stat;
+	uint32_t err;
+	pcan_maci_t *pcan_p;
+	wifi_data_t	wd = { 0 };
+	mac_register_t	*macp;
+	modify_config_t cfgmod;
+	char strbuf[256];
+
+	PCANDBG((CE_NOTE, "dip=0x%p cmd=%x\n", (void *)dip, cmd));
+	if (cmd != DDI_ATTACH)
+		goto attach_fail1;
+
+	/*
+	 * Since this driver is porting from freebsd, so just like
+	 * the original driver, the minipci card doesn't work on amd64
+	 * machine.
+	 * For sparc, since no pci card is available for the test, so this
+	 * version doesn't support sparc. If there is card available and
+	 * requirement, future version will try to support sparc.
+	 * This driver works well for minipci card on 32bit x86
+	 * machine, so keep the code to just support minipci card on 32bit
+	 * mode.
+	 */
+#if defined(sparc) || defined(__sparc)
+	if (pcan_device_type == PCAN_DEVICE_PCI) {
+		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
+		    "PCI/MiniPCI card on Sparc\n");
+		goto attach_fail1;
+	}
+#endif /* sparc */
+#if defined(__amd64)
+	if (pcan_device_type == PCAN_DEVICE_PCI) {
+		cmn_err(CE_NOTE, "pcan attach: this driver does not support "
+		    "PCI/MiniPCI card on amd64\n");
+		goto attach_fail1;
+	}
+#endif /* amd64 */
+
+	/* Allocate soft state associated with this instance. */
+	if (ddi_soft_state_zalloc(pcan_soft_state_p,
+	    ddi_get_instance(dip)) != DDI_SUCCESS) {
+		cmn_err(CE_CONT, "pcan attach: alloc softstate failed\n");
+		goto attach_fail1;
+	}
+	pcan_p = (pcan_maci_t *)ddi_get_soft_state(pcan_soft_state_p,
+	    ddi_get_instance(dip));
+
+	pcan_p->pcan_device_type = pcan_device_type;
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (ddi_regs_map_setup(dip, 0,
+		    (caddr_t *)&pcan_p->pcan_cfg_base, 0, 0,
+		    &accattr, &pcan_p->pcan_cfg_handle) != DDI_SUCCESS)
+			goto attach_fail2;
+
+		stat = ddi_get16(pcan_p->pcan_cfg_handle,
+		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM));
+		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
+		ddi_put16(pcan_p->pcan_cfg_handle,
+		    (uint16_t *)(pcan_p->pcan_cfg_base + PCI_CONF_COMM), stat);
+
+		ddi_regs_map_free(&pcan_p->pcan_cfg_handle);
+		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcan_p->pcan_bar0,
+		    0, 0, &accattr, &pcan_p->pcan_handle0) != DDI_SUCCESS)
+			goto attach_fail3;
+		if (ddi_regs_map_setup(dip, 2, (caddr_t *)&pcan_p->pcan_bar1,
+		    0, 0, &accattr, &pcan_p->pcan_handle1) != DDI_SUCCESS)
+			goto attach_fail3;
+		if (ddi_regs_map_setup(dip, 3, (caddr_t *)&pcan_p->pcan_bar2,
+		    0, 0, &accattr, &pcan_p->pcan_handle2) != DDI_SUCCESS)
+			goto attach_fail3;
+	}
+
+	pcan_p->pcan_dip		= dip;
+	pcan_p->pcan_flag		= 0;
+	pcan_p->glds_nocarrier		= 0;
+	pcan_p->glds_noxmtbuf		= 0;
+	pcan_p->glds_norcvbuf		= 0;
+	pcan_p->pcan_socket		= ddi_getprop(DDI_DEV_T_NONE, dip,
+					DDI_PROP_DONTPASS, "socket", -1);
+
+	pcan_p->pcan_reschedule_need = B_FALSE;
+	pcan_p->pcan_info_softint_pending = 0;
+	pcan_p->pcan_reset_delay = ddi_getprop(DDI_DEV_T_ANY, dip,
+	    DDI_PROP_DONTPASS, "reset-delay", 5000);
+
+	if (ddi_get_iblock_cookie(dip,
+	    0, &pcan_p->pcan_ib_cookie) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "pcan attach: get_iblk_cookie failed\n");
+		goto attach_fail3;
+	}
+
+	mutex_init(&pcan_p->pcan_glock, NULL,
+	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
+	mutex_init(&pcan_p->pcan_scanlist_lock, NULL,
+	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
+	mutex_init(&pcan_p->pcan_txring.an_tx_lock, NULL,
+	    MUTEX_DRIVER, pcan_p->pcan_ib_cookie);
+
+	if (ret = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
+	    &pcan_p->pcan_info_softint_id, &pcan_p->pcan_ib_cookie, NULL,
+	    pcan_info_softint, (caddr_t)pcan_p)) {
+		cmn_err(CE_WARN, "pcan attach: add info_softintr failed\n");
+		goto attach_fail3a;
+	}
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
+		    pcan_intr, (caddr_t)pcan_p)) {
+			cmn_err(CE_WARN, "pcan attach: add intr failed\n");
+			goto attach_fail4;
+		}
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		if (ret = pcan_register_cs(dip, pcan_p)) {
+			PCANDBG((CE_NOTE, "pcan attach: register_cs failed"
+			    " %x\n", ret));
+			goto attach_fail4;
+		}
+	} else {
+		cmn_err(CE_WARN, "pcan attach: unsupported device type\n");
+		goto attach_fail4;
+	}
+
+	mutex_enter(&pcan_p->pcan_glock);
+	pcan_reset_backend(pcan_p, pcan_p->pcan_reset_delay);
+	/* leaves IF down, intr disabled */
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (ret = pcan_init_dma(dip, pcan_p)) {
+			cmn_err(CE_WARN, "pcan init_dma: failed\n");
+			mutex_exit(&pcan_p->pcan_glock);
+			goto attach_fail5;
+		}
+	}
+	if (ret = pcan_get_cap(pcan_p)) { /* sets macaddr for gld_register */
+		cmn_err(CE_WARN, "pcan attach: get_cap failed %x\n", ret);
+		mutex_exit(&pcan_p->pcan_glock);
+		goto attach_fail6;
+	}
+
+	mutex_exit(&pcan_p->pcan_glock);
+	/*
+	 * Provide initial settings for the WiFi plugin; whenever this
+	 * information changes, we need to call mac_pdata_update()
+	 */
+	wd.wd_secalloc = WIFI_SEC_NONE;
+	wd.wd_opmode = IEEE80211_M_STA;
+
+	macp = mac_alloc(MAC_VERSION);
+	if (macp == NULL) {
+		PCANDBG((CE_NOTE, "pcan attach: "
+		    "MAC version mismatch\n"));
+		goto attach_fail6;
+	}
+
+	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
+	macp->m_driver		= pcan_p;
+	macp->m_dip		= dip;
+	macp->m_src_addr	= pcan_p->pcan_mac_addr;
+	macp->m_callbacks	= &pcan_m_callbacks;
+	macp->m_min_sdu		= 0;
+	macp->m_max_sdu		= IEEE80211_MTU;
+	macp->m_pdata		= &wd;
+	macp->m_pdata_size	= sizeof (wd);
+
+	err = mac_register(macp, &pcan_p->pcan_mh);
+	mac_free(macp);
+	if (err != 0) {
+		PCANDBG((CE_NOTE, "pcan attach: "
+		    "mac_register err\n"));
+		goto attach_fail6;
+	}
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		/* turn on CS interrupt */
+		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
+		    CONF_IRQ_CHANGE_VALID;
+		cfgmod.Vpp1 = 50;
+		cfgmod.Vpp2 = 50;
+		(void) csx_ModifyConfiguration(pcan_p->pcan_chdl, &cfgmod);
+
+		mutex_enter(&pcan_p->pcan_glock);
+		if (ret = pcan_init_nicmem(pcan_p)) {
+			cmn_err(CE_WARN, "pcan attach: init_nicmem failed %x\n",
+			    ret);
+			mutex_exit(&pcan_p->pcan_glock);
+			goto attach_fail7;
+		}
+		mutex_exit(&pcan_p->pcan_glock);
+	}
+	(void) ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
+	    "bad-rids", (caddr_t)&pcan_p->pcan_badrids,
+	    &pcan_p->pcan_badrids_len);
+
+	pcan_p->an_config.an_rxmode = AN_NORMAL_RXMODE;
+	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
+	mutex_enter(&pcan_p->pcan_glock);
+	list_create(&pcan_p->an_scan_list, sizeof (an_scan_list_t),
+	    offsetof(an_scan_list_t, an_scan_node));
+	pcan_p->an_scan_num = 0;
+	mutex_exit(&pcan_p->pcan_glock);
+	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
+	    pcan_p, drv_usectohz(1000000));
+
+	instance = ddi_get_instance(dip);
+	(void) snprintf(strbuf, sizeof (strbuf), "pcan%d", instance);
+	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
+	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
+		goto attach_fail8;
+	}
+	mutex_enter(&pcan_p->pcan_glock);
+	PCAN_DISABLE_INTR_CLEAR(pcan_p);
+	(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
+	pcan_p->pcan_flag |= PCAN_ATTACHED;
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		pcan_p->pcan_flag |= PCAN_CARD_READY;
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	return (DDI_SUCCESS);
+attach_fail8:
+	if (pcan_p->an_scanlist_timeout_id != 0) {
+		(void) untimeout(pcan_p->an_scanlist_timeout_id);
+		pcan_p->an_scanlist_timeout_id = 0;
+	}
+	list_destroy(&pcan_p->an_scan_list);
+attach_fail7:
+	(void) mac_unregister(pcan_p->pcan_mh);
+attach_fail6:
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI)
+		pcan_free_dma(pcan_p);
+attach_fail5:
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		pcan_unregister_cs(pcan_p);
+	}
+attach_fail4:
+	if (pcan_p->pcan_info_softint_id)
+		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
+attach_fail3a:
+	pcan_destroy_locks(pcan_p);
+attach_fail3:
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (pcan_p->pcan_handle0)
+			ddi_regs_map_free(&pcan_p->pcan_handle0);
+		if (pcan_p->pcan_handle1)
+			ddi_regs_map_free(&pcan_p->pcan_handle1);
+		if (pcan_p->pcan_handle2)
+			ddi_regs_map_free(&pcan_p->pcan_handle2);
+	}
+attach_fail2:
+	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
+attach_fail1:
+	return (DDI_FAILURE);
+}
+
+static int
+pcan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	pcan_maci_t *pcan_p;
+	an_scan_list_t *scan_item0;
+	int ret;
+	pcan_p = ddi_get_soft_state(pcan_soft_state_p, ddi_get_instance(dip));
+
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+	if (!(pcan_p->pcan_flag & PCAN_ATTACHED))
+		return (DDI_FAILURE);
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		mutex_enter(&pcan_p->pcan_glock);
+		pcan_stop_locked(pcan_p);
+		PCAN_DISABLE_INTR(pcan_p);
+		mutex_exit(&pcan_p->pcan_glock);
+	}
+	if (pcan_p->an_scanlist_timeout_id != 0) {
+		(void) untimeout(pcan_p->an_scanlist_timeout_id);
+		pcan_p->an_scanlist_timeout_id = 0;
+	}
+	if (pcan_p->pcan_connect_timeout_id != 0) {
+		(void) untimeout(pcan_p->pcan_connect_timeout_id);
+		pcan_p->pcan_connect_timeout_id = 0;
+	}
+	mutex_enter(&pcan_p->pcan_scanlist_lock);
+	scan_item0 = list_head(&pcan_p->an_scan_list);
+	while (scan_item0) {
+		pcan_delete_scan_item(pcan_p, scan_item0);
+		scan_item0 = list_head(&pcan_p->an_scan_list);
+	}
+	list_destroy(&pcan_p->an_scan_list);
+	mutex_exit(&pcan_p->pcan_scanlist_lock);
+
+	ret = mac_unregister(pcan_p->pcan_mh);
+	if (ret != 0)
+		return (DDI_FAILURE);
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		ddi_remove_intr(dip, 0, pcan_p->pcan_ib_cookie);
+		pcan_free_dma(pcan_p);
+		if (pcan_p->pcan_handle0)
+			ddi_regs_map_free(&pcan_p->pcan_handle0);
+		if (pcan_p->pcan_handle1)
+			ddi_regs_map_free(&pcan_p->pcan_handle1);
+		if (pcan_p->pcan_handle2)
+			ddi_regs_map_free(&pcan_p->pcan_handle2);
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		pcan_unregister_cs(pcan_p);
+	} else {
+		cmn_err(CE_WARN, "pcan detach: unsupported device type\n");
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	pcan_destroy_locks(pcan_p);
+	if (pcan_p->pcan_info_softint_id)
+		ddi_remove_softintr(pcan_p->pcan_info_softint_id);
+
+	if (pcan_p->pcan_badrids_len)
+		kmem_free(pcan_p->pcan_badrids, pcan_p->pcan_badrids_len);
+
+	ddi_soft_state_free(pcan_soft_state_p, ddi_get_instance(dip));
+	ddi_remove_minor_node(dip, NULL);
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * card services and event handlers
+ */
+
+static int
+pcan_register_cs(dev_info_t *dip, pcan_maci_t *pcan_p)
+{
+	int ret;
+	client_reg_t cr;
+	client_handle_t chdl; /* uint encoding of socket, function, client */
+	get_status_t card_status;
+	request_socket_mask_t sock_req;
+
+	bzero(&cr, sizeof (cr));
+	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
+	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
+	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME | CS_EVENT_PM_SUSPEND |
+	    CS_EVENT_CLIENT_INFO;
+	cr.event_callback_args.client_data = pcan_p;
+	cr.Version = CS_VERSION;
+	cr.event_handler = (csfunction_t *)pcan_ev_hdlr;
+	cr.dip = dip;
+	(void) strcpy(cr.driver_name, pcan_name_str);
+	if (ret = csx_RegisterClient(&chdl, &cr)) {
+		cmn_err(CE_WARN, "pcan: RegisterClient failed %x", ret);
+		goto regcs_ret;
+	}
+
+	pcan_p->pcan_chdl = chdl;
+
+	bzero(&card_status, sizeof (card_status));
+	(void) csx_GetStatus(chdl, &card_status);
+	PCANDBG((CE_NOTE, "pcan: getstat Sock=%x CState=%x SState=%x rState=%x",
+	    card_status.Socket, card_status.CardState,
+	    card_status.SocketState, card_status.raw_CardState));
+	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
+		/* card is not present, why are we attaching ? */
+		ret = CS_NO_CARD;
+		goto unreg;
+	}
+	cv_init(&pcan_p->pcan_cscv, NULL, CV_DRIVER, NULL);
+	mutex_init(&pcan_p->pcan_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
+	mutex_enter(&pcan_p->pcan_cslock);
+	if (ret = csx_MapLogSocket(chdl, &pcan_p->pcan_log_sock)) {
+		cmn_err(CE_WARN, "pcan: MapLogSocket failed %x", ret);
+		goto fail;
+	}
+	PCANDBG((CE_NOTE, "pcan: logsock: LogSock=%x PhyAdapter=%x PhySock=%x",
+	    pcan_p->pcan_log_sock.LogSocket,
+	    pcan_p->pcan_log_sock.PhyAdapter,
+	    pcan_p->pcan_log_sock.PhySocket));
+
+	/* turn on initialization events */
+	sock_req.Socket = 0;
+	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_REGISTRATION_COMPLETE;
+	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
+		cmn_err(CE_WARN, "pcan: RequestSocketMask failed %x\n", ret);
+		goto fail;
+	}
+
+	/* wait for and process card insertion events */
+	while (!(pcan_p->pcan_flag & PCAN_CARD_READY))
+		cv_wait(&pcan_p->pcan_cscv, &pcan_p->pcan_cslock);
+	mutex_exit(&pcan_p->pcan_cslock);
+
+	pcan_p->pcan_flag |= PCAN_CS_REGISTERED;
+	return (CS_SUCCESS);
+fail:
+	mutex_destroy(&pcan_p->pcan_cslock);
+	cv_destroy(&pcan_p->pcan_cscv);
+unreg:
+	(void) csx_DeregisterClient(chdl);
+regcs_ret:
+	pcan_p->pcan_flag &= ~PCAN_CS_REGISTERED;
+	return (ret);
+}
+
+static void
+pcan_unregister_cs(pcan_maci_t *pcan_p)
+{
+	int ret;
+	release_socket_mask_t mask;
+	mask.Socket = pcan_p->pcan_socket;
+
+	/*
+	 * The card service not registered means register_cs function
+	 * doesnot return TRUE. Then all the lelated resource has been
+	 * released in register_cs.
+	 */
+	if (!(pcan_p->pcan_flag | PCAN_CS_REGISTERED))
+		return;
+	(void) csx_ReleaseSocketMask(pcan_p->pcan_chdl, &mask);
+
+	if (pcan_p->pcan_flag & PCAN_CARD_READY) {
+		pcan_card_remove(pcan_p);
+		pcan_p->pcan_flag &= ~PCAN_CARD_READY;
+	}
+	mutex_destroy(&pcan_p->pcan_cslock);
+	cv_destroy(&pcan_p->pcan_cscv);
+	if (ret = csx_DeregisterClient(pcan_p->pcan_chdl))
+		cmn_err(CE_WARN, "pcan: deregister failed %x\n", ret);
+}
+static void
+pcan_destroy_locks(pcan_maci_t *pcan_p)
+{
+	mutex_destroy(&pcan_p->pcan_txring.an_tx_lock);
+	mutex_destroy(&pcan_p->pcan_scanlist_lock);
+	mutex_destroy(&pcan_p->pcan_glock);
+}
+
+static int
+pcan_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
+{
+	int ret = CS_SUCCESS;
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg->client_data;
+	client_info_t *ci_p = (client_info_t *)&arg->client_info;
+
+	mutex_enter(&pcan_p->pcan_cslock);
+	switch (event) {
+	case CS_EVENT_CARD_INSERTION:
+		ret = pcan_card_insert(pcan_p);
+		cv_broadcast(&pcan_p->pcan_cscv);
+		break;
+	case CS_EVENT_REGISTRATION_COMPLETE:
+		cv_broadcast(&pcan_p->pcan_cscv);
+		break;
+	case CS_EVENT_CARD_REMOVAL:
+		if (priority & CS_EVENT_PRI_HIGH)
+			break;
+		pcan_card_remove(pcan_p);
+		cv_broadcast(&pcan_p->pcan_cscv);
+		break;
+	case CS_EVENT_CLIENT_INFO:
+		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
+		    CS_CLIENT_INFO_SUBSVC_CS)
+			break;
+
+		ci_p->Revision = 0x0101;
+		ci_p->CSLevel = CS_VERSION;
+		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
+		(void) strcpy(ci_p->ClientName, PCAN_IDENT_STRING);
+		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
+		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
+		break;
+	default:
+		ret = CS_UNSUPPORTED_EVENT;
+		break;
+	}
+	mutex_exit(&pcan_p->pcan_cslock);
+	return (ret);
+}
+
+static int
+pcan_card_insert(pcan_maci_t *pcan_p)
+{
+	int ret, hi, lo;
+	tuple_t tuple;
+	cisparse_t cisparse;
+	io_req_t	io;
+	irq_req_t	irq;
+	config_req_t	cfg;
+	cistpl_config_t config;
+	cistpl_cftable_entry_t *tbl_p;
+	register client_handle_t chdl = pcan_p->pcan_chdl;
+
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcan: get manufacture id failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&cisparse, sizeof (cisparse));
+	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
+		cmn_err(CE_WARN, "pcan: parse manufacture id failed %x\n", ret);
+		goto insert_ret;
+	}
+	/* verify manufacture ID */
+	PCANDBG((CE_NOTE, "pcan: manufacturer_id=%x card=%x\n",
+	    cisparse.manfid.manf, cisparse.manfid.card));
+
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_FUNCID;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcan: get function id failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&cisparse, sizeof (cisparse));
+	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
+		cmn_err(CE_WARN, "pcan: parse function id failed %x\n", ret);
+		goto insert_ret;
+	}
+	/* verify function ID */
+	PCANDBG((CE_NOTE, "funcid=%x\n", cisparse.funcid.function));
+
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcan: get config failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&config, sizeof (config));
+	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
+		cmn_err(CE_WARN, "pcan: parse config failed %x\n", ret);
+		goto insert_ret;
+	}
+	PCANDBG((CE_NOTE,
+	    "pcan: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
+	    config.present, config.nr, config.hr, config.regs[0],
+	    config.base, config.last));
+
+	hi = 0;
+	lo = (int)-1;		/* really big number */
+	tbl_p = &cisparse.cftable;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
+		PCANDBG((CE_NOTE, "pcan: tuple idx=%x:\n", tbl_p->index));
+		if (ret = csx_GetNextTuple(chdl, &tuple)) {
+			cmn_err(CE_WARN, "pcan: get cftable failed %x\n", ret);
+			break;
+		}
+		bzero((caddr_t)&cisparse, sizeof (cisparse));
+		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
+			cmn_err(CE_WARN, "pcan: parse cftable failed%x\n", ret);
+			break;
+		}
+		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
+			tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
+			if (tbl_p->pd.pd_vcc.avgI > hi) {
+				hi = tbl_p->pd.pd_vcc.avgI;
+				pcan_p->pcan_config_hi = tbl_p->index;
+			}
+			if (tbl_p->pd.pd_vcc.avgI < lo) {
+				lo = tbl_p->pd.pd_vcc.avgI;
+				pcan_p->pcan_config = tbl_p->index;
+			}
+		}
+		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
+			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
+				pcan_p->pcan_vcc = tbl_p->pd.pd_vcc.nomV;
+			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
+				pcan_p->pcan_iodecode = tbl_p->io.addr_lines;
+		}
+	}
+	PCANDBG((CE_NOTE, "pcan: cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
+	    pcan_p->pcan_config_hi, pcan_p->pcan_config,
+	    pcan_p->pcan_vcc, pcan_p->pcan_iodecode));
+
+	bzero(&io, sizeof (io));
+	io.BasePort1.base = 0;
+	io.NumPorts1 = 1 << pcan_p->pcan_iodecode;
+	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	io.IOAddrLines = pcan_p->pcan_iodecode;
+	if (ret = csx_RequestIO(chdl, &io)) {
+		cmn_err(CE_WARN, "pcan: RequestIO failed %x\n", ret);
+		goto insert_ret;
+	}
+	pcan_p->pcan_port = io.BasePort1.handle;
+
+	if (ret = ddi_add_softintr(DIP(pcan_p), DDI_SOFTINT_HIGH,
+	    &pcan_p->pcan_softint_id, &pcan_p->pcan_ib_cookie, NULL,
+	    pcan_intr, (caddr_t)pcan_p)) {
+		cmn_err(CE_NOTE, "pcan: Add softintr failed\n");
+		goto insert_ret;
+	}
+	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+	irq.irq_handler = ddi_intr_hilevel(DIP(pcan_p), 0) ?
+	    (csfunction_t *)pcan_intr_hi : (csfunction_t *)pcan_intr;
+	irq.irq_handler_arg = pcan_p;
+	if (ret = csx_RequestIRQ(chdl, &irq)) {
+		cmn_err(CE_WARN, "pcan: RequestIRQ failed %x\n", ret);
+		goto un_io;
+	}
+
+	bzero(&cfg, sizeof (cfg));
+	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
+	cfg.Vcc = 50; /* pcan_vcc == 0 */
+	cfg.Vpp1 = 50;
+	cfg.Vpp2 = 50;
+	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
+	cfg.ConfigBase = config.base;
+	cfg.ConfigIndex = pcan_p->pcan_config;
+	cfg.Status = CCSR_IO_IS_8; /* no use */
+	cfg.Present = config.present;
+	pcan_p->pcan_flag |= PCAN_CARD_READY;
+	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
+		cmn_err(CE_WARN, "pcan: RequestConfiguration failed %x\n", ret);
+		goto un_irq;
+	}
+	return (CS_SUCCESS);
+un_irq:
+	(void) csx_ReleaseIRQ(chdl, &irq);
+un_io:
+	ddi_remove_softintr(pcan_p->pcan_softint_id);
+
+	(void) csx_ReleaseIO(chdl, &io);
+	pcan_p->pcan_port = 0;
+insert_ret:
+	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
+	return (ret);
+}
+
+/*
+ * assume card is already removed, don't touch the hardware
+ */
+static void
+pcan_card_remove(pcan_maci_t *pcan_p)
+{
+	int ret;
+	io_req_t io;
+	irq_req_t irq;
+
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY))
+		return;
+	if (ret = csx_ReleaseConfiguration(pcan_p->pcan_chdl, NULL))
+		cmn_err(CE_WARN, "pcan: ReleaseConfiguration failed %x\n", ret);
+
+	bzero(&irq, sizeof (irq));
+	if (ret = csx_ReleaseIRQ(pcan_p->pcan_chdl, &irq))
+		cmn_err(CE_WARN, "pcan: ReleaseIRQ failed %x\n", ret);
+
+	ddi_remove_softintr(pcan_p->pcan_softint_id);
+
+	bzero(&io, sizeof (io));
+	io.BasePort1.handle = pcan_p->pcan_port;
+	io.NumPorts1 = 16;
+	if (ret = csx_ReleaseIO(pcan_p->pcan_chdl, &io))
+		cmn_err(CE_WARN, "pcan: Release IO failed %x\n", ret);
+
+	pcan_p->pcan_port = 0;
+	pcan_p->pcan_flag &= ~PCAN_CARD_READY;
+}
+
+/*
+ * gld operation interface routines
+ */
+static int
+pcan_start(void *arg)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (PCAN_FAIL);
+	}
+	(void) pcan_loaddef(pcan_p);
+	pcan_start_locked(pcan_p);
+	mutex_exit(&pcan_p->pcan_glock);
+	return (PCAN_SUCCESS);
+}
+
+static void
+pcan_stop(void *arg)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return;
+	}
+	pcan_stop_locked(pcan_p);
+	mutex_exit(&pcan_p->pcan_glock);
+	if (pcan_p->pcan_connect_timeout_id != 0) {
+		(void) untimeout(pcan_p->pcan_connect_timeout_id);
+		pcan_p->pcan_connect_timeout_id = 0;
+	}
+}
+
+/*
+ * mac address can only be set in 'disable' state and
+ * be effective after 'enable' state.
+ */
+static int
+pcan_saddr(void *arg, const uint8_t *macaddr)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	int ret = PCAN_SUCCESS;
+	ether_copy(macaddr, pcan_p->pcan_mac_addr);
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	ether_copy(pcan_p->pcan_mac_addr, pcan_p->an_config.an_macaddr);
+	if (pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
+		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	if (pcan_config_mac(pcan_p)) {
+		cmn_err(CE_WARN, "pcan set mac addr: config_mac failed\n");
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	if (pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
+		cmn_err(CE_WARN, "pcan set mac addr: failed\n");
+		ret = PCAN_FAIL;
+	}
+done:
+	mutex_exit(&pcan_p->pcan_glock);
+	return (ret);
+}
+
+/*
+ * send a packet out for pccard
+ */
+static int
+pcan_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
+{
+	char *buf, *buf_p;
+	an_txfrm_t *frm_p;
+#ifdef PCAN_SEND_DEBUG
+	struct an_ltv_status radio_status;
+#endif /* PCAN_SEND_DEBUG */
+	uint16_t pkt_len, xmt_id, ring_idx;
+	struct ieee80211_frame *wh;
+	int i = 0;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
+		PCANDBG((CE_NOTE, "pcan: link down, dropped\n"));
+		pcan_p->glds_nocarrier++;
+		mutex_exit(&pcan_p->pcan_glock);
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	if (pullupmsg(mblk_p, -1) == 0) {
+		cmn_err(CE_NOTE, "pcan send: pullupmsg failed\n");
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
+
+	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
+	ring_idx = pcan_p->pcan_txring.an_tx_prod;
+	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) & AN_TX_RING_MASK;
+
+	/* check whether there is a xmt buffer available */
+	while ((i < AN_TX_RING_CNT) &&
+	    (pcan_p->pcan_txring.an_tx_ring[ring_idx])) {
+		ring_idx = pcan_p->pcan_txring.an_tx_prod;
+		pcan_p->pcan_txring.an_tx_prod =
+		    (ring_idx + 1) & AN_TX_RING_MASK;
+		i++;
+	}
+
+	if (i == AN_TX_RING_CNT) {
+		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+		PCANDBG((CE_NOTE, "pcan: ring full, retrying\n"));
+		mutex_enter(&pcan_p->pcan_glock);
+		pcan_p->pcan_reschedule_need = B_TRUE;
+		mutex_exit(&pcan_p->pcan_glock);
+		pcan_p->glds_noxmtbuf++;
+		return (PCAN_FAIL);
+	}
+	xmt_id = pcan_p->pcan_txring.an_tx_fids[ring_idx];
+	pcan_p->pcan_txring.an_tx_ring[ring_idx] = xmt_id;
+	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+
+	buf = kmem_zalloc(PCAN_NICMEM_SZ, KM_SLEEP); /* too big for stack */
+	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;	/* 16-bit round up */
+	frm_p = (an_txfrm_t *)buf_p;
+
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_SEND) {
+		cmn_err(CE_NOTE, "pcan send: packet from plugin");
+		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)mblk_p->b_rptr + i));
+	}
+#endif
+	pkt_len = msgdsize(mblk_p);
+	if (pkt_len > PCAN_NICMEM_SZ - sizeof (an_txfrm_t)) {
+		cmn_err(CE_WARN, "pcan send: mblk is too long");
+		kmem_free(buf, PCAN_NICMEM_SZ);
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
+	    IEEE80211_FC1_DIR_TODS) {
+		kmem_free(buf, PCAN_NICMEM_SZ);
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+
+	/* initialize xmt frame header, payload_len must be stored in LE */
+	bzero(frm_p, sizeof (an_txfrm_t) + 2);
+	frm_p->an_tx_ctl = AN_TXCTL_8023;
+
+	/*
+	 * mblk sent down from plugin includes station mode 802.11 frame and
+	 * llc, so we here need to remove them and add an ethernet header.
+	 */
+	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
+	    + 2;
+	bcopy(wh->i_addr3, buf_p + 0x38, ETHERADDRL); /* dst macaddr */
+	bcopy(wh->i_addr2, buf_p + 0x3e, ETHERADDRL); /* src macaddr */
+	*((uint16_t *)(buf_p + 0x36)) = pkt_len;
+	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
+	    - 2, buf_p + 0x44, pkt_len);
+
+	if (pkt_len & 1) {	/* round up to 16-bit boundary and pad 0 */
+		buf_p[pkt_len + 0x44] = 0;
+		pkt_len++;
+	}
+	ASSERT(pkt_len <= PCAN_NICMEM_SZ);
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_SEND) {
+		cmn_err(CE_NOTE, "pcan send: packet to hardware--pkt_len=%x",
+		    pkt_len);
+		for (i = 0; i < pkt_len + 4; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)buf_p + 0x36 + i));
+	}
+#endif
+	mutex_enter(&pcan_p->pcan_glock);
+	(void) WRCH1(pcan_p, xmt_id, 0, (uint16_t *)buf_p, 0x38); /* frm */
+	(void) WRPKT(pcan_p, xmt_id, 0x38, (uint16_t *)(buf_p + 0x38),
+		pkt_len + 12);
+	ring_idx = pcan_set_cmd(pcan_p, AN_CMD_TX, xmt_id);
+	mutex_exit(&pcan_p->pcan_glock);
+
+	PCANDBG((CE_NOTE, "pcan: pkt_len=0x44+%x=%x xmt=%x ret=%x\n",
+	    pkt_len, 0x44 + pkt_len, xmt_id, ring_idx));
+	kmem_free(buf, PCAN_NICMEM_SZ);
+#ifdef PCAN_SEND_DEBUG
+	if (pkt_len = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &radio_status)) {
+		PCANDBG((CE_NOTE, "pcan: bad radio status %x\n", pkt_len));
+	} else {
+		PCANDBG((CE_NOTE, "pcan: radio status:\n"));
+	}
+#endif /* PCAN_SEND_DEBUG */
+	if (ring_idx)
+		return (PCAN_FAIL);
+	else {
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);
+	}
+}
+
+/*
+ * send a packet out for PCI/MiniPCI card
+ */
+static int
+pcian_send(pcan_maci_t *pcan_p, mblk_t *mblk_p)
+{
+	char *buf;
+	uint16_t pkt_len = msgdsize(mblk_p), ring_idx;
+	uint32_t i;
+	struct ieee80211_frame *wh;
+	struct an_card_tx_desc an_tx_desc;
+
+	ring_idx = pcan_p->pcan_txring.an_tx_prod;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_LINKUP)) {	/* link down */
+		mutex_exit(&pcan_p->pcan_glock);
+		pcan_p->glds_nocarrier++;
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	if (pullupmsg(mblk_p, -1) == 0) {
+		cmn_err(CE_NOTE, "pcan(pci) send: pullupmsg failed\n");
+		freemsg(mblk_p);
+		return (PCAN_SUCCESS);		/* drop packet */
+	}
+	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
+
+	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
+	if ((pcan_p->pcan_flag & PCAN_CARD_SEND) &&
+	    (ring_idx == pcan_p->pcan_txring.an_tx_cons)) {
+		pcan_p->glds_noxmtbuf++;
+		pcan_p->pcan_reschedule_need = B_TRUE;
+		mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+		return (PCAN_FAIL);
+	}
+	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_SEND) {
+		cmn_err(CE_NOTE, "pcan(pci) send: packet from plugin");
+		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)mblk_p->b_rptr + i));
+	}
+#endif
+	mutex_enter(&pcan_p->pcan_glock);
+
+	buf = pcan_p->pcan_tx[ring_idx].dma_virtaddr;
+	bzero(buf, AN_TX_BUFFER_SIZE);
+
+	/*
+	 * mblk sent down from plugin includes station mode 802.11 frame and
+	 * llc, so we here need to remove them and add an ethernet header.
+	 */
+	*((uint16_t *)(buf + 8)) = htons(AN_TXCTL_8023);
+	pkt_len = pkt_len - (sizeof (*wh) + sizeof (struct ieee80211_llc))
+	    + 2;
+	bcopy(wh->i_addr3, buf + 0x38, ETHERADDRL); /* dst macaddr */
+	bcopy(wh->i_addr2, buf + 0x3e, ETHERADDRL); /* src macaddr */
+	*((uint16_t *)(buf + 0x36)) = pkt_len;
+	bcopy(mblk_p->b_rptr + sizeof (*wh) + sizeof (struct ieee80211_llc)
+	    - 2, buf + 0x44, pkt_len);
+
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_SEND) {
+		cmn_err(CE_NOTE, "pcan(pci) send: packet to hardware "
+		    "pkt_len=%x", pkt_len);
+		for (i = 0; i < pkt_len + 14; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)buf + 0x36 + i));
+	}
+#endif
+	bzero(&an_tx_desc, sizeof (an_tx_desc));
+	an_tx_desc.an_offset = 0;
+	an_tx_desc.an_eoc = (ring_idx == (AN_MAX_TX_DESC-1) ? 1 : 0);
+	an_tx_desc.an_valid = 1;
+	an_tx_desc.an_len =  0x44 + pkt_len;
+	an_tx_desc.an_phys  = pcan_p->pcan_tx[ring_idx].dma_physaddr;
+	for (i = 0; i < sizeof (an_tx_desc) / 4; i++) {
+		PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET +
+		    (ring_idx * sizeof (an_tx_desc)) + (i * 4),
+		    ((uint32_t *)&an_tx_desc)[i]);
+	}
+
+	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
+	pcan_p->pcan_txring.an_tx_prod = (ring_idx + 1) % AN_MAX_TX_DESC;
+	pcan_p->pcan_flag |= PCAN_CARD_SEND;
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
+	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+
+	freemsg(mblk_p);
+	mutex_exit(&pcan_p->pcan_glock);
+	return (PCAN_SUCCESS);
+}
+
+static mblk_t *
+pcan_tx(void *arg, mblk_t *mp)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	mblk_t *next;
+	int ret = 0;
+
+	ASSERT(mp != NULL);
+	mutex_enter(&pcan_p->pcan_glock);
+	if ((pcan_p->pcan_flag & (PCAN_CARD_LINKUP | PCAN_CARD_READY)) !=
+	    (PCAN_CARD_LINKUP | PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (mp);
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	while (mp != NULL) {
+		next =  mp->b_next;
+		mp->b_next = NULL;
+
+		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+			ret = pcian_send(pcan_p, mp);
+		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+			ret = pcan_send(pcan_p, mp);
+		}
+		if (ret) {
+			mp->b_next = next;
+			break;
+		}
+		mp = next;
+	}
+	return (mp);
+}
+
+/*
+ * this driver is porting from freebsd, the code in freebsd
+ * doesn't show how to set promiscous mode.
+ */
+/*ARGSUSED*/
+static int
+pcan_prom(void *arg, boolean_t on)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	int ret = PCAN_SUCCESS;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		ret = PCAN_FAIL;
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	return (ret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_gstat(void *arg, uint_t statitem, uint64_t *val)
+{
+	uint16_t i;
+	int ret = PCAN_SUCCESS;
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	uint64_t *cntr_p = pcan_p->pcan_cntrs_s;
+
+	PCANDBG((CE_NOTE, "pcan: gstat called\n"));
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	if (pcan_get_ltv(pcan_p, sizeof (pcan_p->an_stats),
+		AN_RID_16BITS_DELTACLR, (uint16_t *)&pcan_p->an_stats)) {
+		cmn_err(CE_WARN, "pcan kstat: get ltv(32 delta statistics)"
+		    " failed \n");
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	for (i = 0; i < ANC_STAT_CNT; i++) {
+		cntr_p[i] += *((uint16_t *)&pcan_p->an_stats + 1 + i);
+	}
+	if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
+		cmn_err(CE_WARN, "pcan kstat: read status failed \n");
+		ret = PCAN_FAIL;
+		goto done;
+	}
+
+	switch (statitem) {
+	case MAC_STAT_IFSPEED:
+		*val = 500000 * pcan_p->an_status.an_cur_tx_rate;
+		break;
+	case MAC_STAT_NOXMTBUF:
+		*val = pcan_p->glds_noxmtbuf;
+		break;
+	case MAC_STAT_NORCVBUF:
+		*val = pcan_p->glds_norcvbuf;
+		break;
+	case MAC_STAT_IERRORS:
+		*val = cntr_p[ANC_RX_OVERRUNS] +
+		    cntr_p[ANC_RX_PLCP_CSUM_ERRS] +
+		    cntr_p[ANC_RX_PLCP_FORMAT_ERRS] +
+		    cntr_p[ANC_RX_PLCP_LEN_ERRS] +
+		    cntr_p[ANC_RX_MAC_CRC_ERRS] +
+		    cntr_p[ANC_RX_WEP_ERRS];
+		break;
+	case MAC_STAT_OERRORS:
+		*val = cntr_p[ANC_TX_HOST_FAILED];
+		break;
+	case MAC_STAT_RBYTES:
+		*val = cntr_p[ANC_HOST_RX_BYTES];
+		break;
+	case MAC_STAT_IPACKETS:
+		*val = cntr_p[ANC_RX_HOST_UCASTS];
+		break;
+	case MAC_STAT_OBYTES:
+		*val = cntr_p[ANC_HOST_TX_BYTES];
+		break;
+	case MAC_STAT_OPACKETS:
+		*val = cntr_p[ANC_TX_HOST_UCASTS];
+		break;
+	case WIFI_STAT_TX_FAILED:
+		*val = cntr_p[ANC_TX_HOST_FAILED];
+		break;
+	case WIFI_STAT_TX_RETRANS:
+		*val = cntr_p[ANC_HOST_RETRIES];
+		break;
+	case WIFI_STAT_FCS_ERRORS:
+		*val = cntr_p[ANC_RX_MAC_CRC_ERRS];
+		break;
+	case WIFI_STAT_WEP_ERRORS:
+		*val = cntr_p[ANC_RX_WEP_ERRS];
+		break;
+	case WIFI_STAT_MCAST_TX:
+		*val = cntr_p[ANC_TX_HOST_MCASTS];
+		break;
+	case WIFI_STAT_MCAST_RX:
+		*val = cntr_p[ANC_RX_HOST_MCASTS];
+		break;
+	case WIFI_STAT_TX_FRAGS:
+	case WIFI_STAT_RX_FRAGS:
+		*val = 0;
+		break;
+	case WIFI_STAT_RTS_SUCCESS:
+		*val = cntr_p[ANC_TX_RTS_OK];
+		break;
+	case WIFI_STAT_RTS_FAILURE:
+		*val = cntr_p[ANC_NO_CTS];
+		break;
+	case WIFI_STAT_ACK_FAILURE:
+		*val = cntr_p[ANC_NO_ACK];
+		break;
+	case WIFI_STAT_RX_DUPS:
+		*val = cntr_p[ANC_RX_DUPS];
+		break;
+	default:
+		ret = ENOTSUP;
+	}
+
+
+done:
+	mutex_exit(&pcan_p->pcan_glock);
+	return (ret);
+}
+
+/*
+ * this driver is porting from freebsd, the code in freebsd
+ * doesn't show how to set multi address.
+ */
+/*ARGSUSED*/
+static int
+pcan_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (PCAN_FAIL);
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	return (PCAN_SUCCESS);
+}
+
+static uint_t
+pcan_info_softint(caddr_t arg)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	wifi_data_t wd = { 0 };
+	uint16_t link;
+	uint32_t link_up;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (pcan_p->pcan_info_softint_pending != 1) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	PCAN_READ(pcan_p, AN_LINKSTAT(pcan_p), link);
+	link_up = pcan_p->pcan_flag & PCAN_CARD_LINKUP;
+	if ((link == AN_LINKSTAT_ASSOCIATED) && !link_up) {
+		pcan_p->pcan_flag |= PCAN_CARD_LINKUP;
+		mutex_exit(&pcan_p->pcan_glock);
+		if (pcan_p->pcan_connect_timeout_id != 0) {
+			(void) untimeout(pcan_p->pcan_connect_timeout_id);
+			pcan_p->pcan_connect_timeout_id = 0;
+		}
+		mac_link_update(GLD3(pcan_p), LINK_STATE_UP);
+		mutex_enter(&pcan_p->pcan_glock);
+		(void) pcan_status_ltv(PCAN_READ_LTV, pcan_p,
+		    &pcan_p->an_status);
+		bcopy(pcan_p->an_status.an_cur_bssid, wd.wd_bssid, 6);
+		wd.wd_secalloc = WIFI_SEC_NONE;
+		wd.wd_opmode = IEEE80211_M_STA;
+		(void) mac_pdata_update(pcan_p->pcan_mh, &wd,
+		    sizeof (wd));
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_LINKINFO) {
+			cmn_err(CE_NOTE, "pcan: link Up, chan=%d, "
+			    "ssid=\"%s\""
+			    " (%02x:%02x:%02x:%02x:%02x:%02x)\n",
+			    pcan_p->an_status.an_channel_set,
+			    pcan_p->an_status.an_ssid,
+			    pcan_p->an_status.an_cur_bssid[0],
+			    pcan_p->an_status.an_cur_bssid[1],
+			    pcan_p->an_status.an_cur_bssid[2],
+			    pcan_p->an_status.an_cur_bssid[3],
+			    pcan_p->an_status.an_cur_bssid[4],
+			    pcan_p->an_status.an_cur_bssid[5]);
+		}
+#endif
+	}
+	if ((link != AN_LINKSTAT_ASSOCIATED) && link_up) {
+		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_LINKINFO) {
+			cmn_err(CE_NOTE, "pcan: link Down 0x%x\n", link);
+		}
+#endif
+		if (link != AN_LINKSTAT_SYNCLOST_HOSTREQ) {
+			pcan_p->pcan_connect_timeout_id =
+			    timeout(pcan_connect_timeout,
+			    pcan_p, drv_usectohz(1000));
+		}
+		mutex_exit(&pcan_p->pcan_glock);
+		mac_link_update(GLD3(pcan_p), LINK_STATE_DOWN);
+		mutex_enter(&pcan_p->pcan_glock);
+	}
+
+	pcan_p->pcan_info_softint_pending = 0;
+	mutex_exit(&pcan_p->pcan_glock);
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pcan_intr(caddr_t arg)
+{
+	uint16_t stat;
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
+	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
+
+	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	PCAN_DISABLE_INTR(pcan_p);
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), ~AN_INTRS(pcan_p));
+
+	PCANDBG((CE_NOTE, "pcan intr: stat=%x pcan_flags=%x\n", stat,
+		pcan_p->pcan_flag));
+
+	if (stat & AN_EV_AWAKE) {
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_AWAKE);
+	}
+	if (stat & AN_EV_LINKSTAT) {
+		pcan_p->pcan_info_softint_pending = 1;
+		ddi_trigger_softintr(pcan_p->pcan_info_softint_id);
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_LINKSTAT);
+	}
+	if (stat & AN_EV_RX) {
+		if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+			pcian_rcv(pcan_p);
+		} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+			pcan_rcv(pcan_p);
+		}
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_RX);
+	}
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (stat & AN_EV_TX_CPY) {
+			(void) pcan_txdone(pcan_p, stat & AN_EV_TX_CPY);
+			if (pcan_p->pcan_reschedule_need == B_TRUE) {
+				mac_tx_update(GLD3(pcan_p));
+				pcan_p->pcan_reschedule_need = B_FALSE;
+			}
+			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_CPY);
+	}
+	}
+	if (stat & AN_EV_TX) {
+		if (pcan_txdone(pcan_p, stat & AN_EV_TX) == 0) {
+			if (pcan_p->pcan_reschedule_need == B_TRUE) {
+				mac_tx_update(GLD3(pcan_p));
+				pcan_p->pcan_reschedule_need = B_FALSE;
+			}
+		}
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX);
+	}
+	if (stat & AN_EV_TX_EXC) {
+		if (pcan_txdone(pcan_p, stat & AN_EV_TX_EXC) == 0) {
+			if (pcan_p->pcan_reschedule_need == B_TRUE) {
+				mutex_exit(&pcan_p->pcan_glock);
+				mac_tx_update(GLD3(pcan_p));
+				mutex_enter(&pcan_p->pcan_glock);
+				pcan_p->pcan_reschedule_need = B_FALSE;
+			}
+		}
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_TX_EXC);
+	}
+	if (stat & AN_EV_ALLOC) {
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
+		PCANDBG((CE_NOTE, "pcan intr: nicmem alloc done\n"));
+	}
+	if (stat & AN_EV_MIC) {
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_MIC);
+	}
+	PCAN_ENABLE_INTR(pcan_p);
+	mutex_exit(&pcan_p->pcan_glock);
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pcan_intr_hi(caddr_t arg)
+{
+	uint16_t stat;
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if ((pcan_p->pcan_flag & (PCAN_CARD_READY | PCAN_CARD_INTREN)) !=
+	    (PCAN_CARD_READY | PCAN_CARD_INTREN)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
+	PCANDBG((CE_NOTE, "pcan intr(hi): stat=%x pcan_flags=%x\n", stat,
+	    pcan_p->pcan_flag));
+
+	if (!(stat & AN_INTRS(pcan_p)) || stat == AN_EV_ALL) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	/* disable interrupt without ack */
+	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), 0);
+	mutex_exit(&pcan_p->pcan_glock);
+	ddi_trigger_softintr(pcan_p->pcan_softint_id);
+	return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * retrieve data from pccard
+ */
+static void
+pcan_rcv(pcan_maci_t *pcan_p)
+{
+	uint16_t id, off, ret, data_len, pkt_stat, frm_ctl;
+	an_rxfrm_t frm;
+	struct ieee80211_llc *llc;
+
+	mblk_t *mp = allocb(PCAN_NICMEM_SZ, BPRI_MED);
+	if (!mp) {
+		cmn_err(CE_WARN, "pcan: failed to alloc rcv buf");
+		pcan_p->glds_norcvbuf++;
+		return;
+	}
+	ASSERT(mp->b_rptr == mp->b_wptr);
+
+	PCAN_READ(pcan_p, AN_RX_FID, id);
+	if (id == AN_INVALID_FID) {
+		PCANDBG((CE_NOTE, "pcan rcv: can't get rx_fid\n"));
+		pcan_p->glds_norcvbuf++;
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	if (ret = RDCH0(pcan_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
+		PCANDBG((CE_NOTE, "pcan rcv: read frm err %x\n", ret));
+		goto done;
+	}
+	off = sizeof (frm);
+	if (frm.an_rx_status) {
+		PCANDBG((CE_NOTE, "pcan rcv: err stat %x\n", frm.an_rx_status));
+		ret = frm.an_rx_status;
+		goto done;
+	}
+	PCANDBG((CE_NOTE, "pcan rcv: payload_len=%x gap_len=%x\n",
+	    frm.an_rx_payload_len, frm.an_gaplen));
+	if (frm.an_rx_payload_len > PCAN_NICMEM_SZ ||
+	    frm.an_gaplen > AN_RXGAP_MAX) {
+		PCANDBG((CE_NOTE, "pcan rcv: bad len\n"));
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	if (ret = RDCH0(pcan_p, id, off, &pkt_stat, sizeof (pkt_stat))) {
+		PCANDBG((CE_NOTE, "pcan rcv: pkt status err %x\n", ret));
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	off += sizeof (pkt_stat);
+	if (ret = RDCH0(pcan_p, id, off, &data_len, sizeof (data_len))) {
+		PCANDBG((CE_NOTE, "pcan rcv: payload len err %x\n", ret));
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	off += sizeof (data_len);
+	off += ETHERADDRL << 1;
+	PCANDBG((CE_NOTE, "pcan rcv: pkt_stat=%x payload_len=%x+c off=%x\n",
+		pkt_stat, data_len, off));
+
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_RCV) {
+		int i;
+		cmn_err(CE_NOTE, "pcan rcv: frm header\n");
+		for (i = 0; i < sizeof (frm); i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((uint8_t *)&frm + i));
+	}
+#endif
+	/*
+	 * this driver deal with WEP by itself. so plugin always thinks no wep.
+	 */
+	frm.an_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
+	frm_ctl = frm.an_frame_ctl;
+	PCAN_SWAP16((uint16_t *)&frm.an_frame_ctl,
+	    sizeof (struct ieee80211_frame));
+	/*
+	 * discard those frames which are not from the AP we connect or
+	 * without 'ap->sta' direction
+	 */
+	if (((pcan_p->an_config.an_opmode == AN_OPMODE_INFR_STATION)) &&
+	    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
+	    IEEE80211_FC1_DIR_FROMDS) ||
+	    bcmp(pcan_p->an_status.an_cur_bssid, frm.an_addr2, 6) != 0)) {
+		ret = PCAN_FAIL;
+		goto done;
+	}
+	bcopy(&frm.an_frame_ctl, mp->b_wptr,
+	    sizeof (struct ieee80211_frame));
+	mp->b_wptr += sizeof (struct ieee80211_frame);
+
+	/* the plugin need a llc here */
+	llc = (struct ieee80211_llc *)mp->b_wptr;
+	llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
+	llc->illc_control = AN_SNAP_CONTROL;
+	bzero(llc->illc_oc, sizeof (llc->illc_oc));
+	mp->b_wptr += AN_SNAPHDR_LEN;
+
+	/* read in the rest of data */
+	data_len += data_len & 1;	/* adjust to word boundary */
+	if (data_len > MBLKSIZE(mp)) {
+		cmn_err(CE_NOTE, "pcan rcv: data over length%x\n", data_len);
+		ret = PCAN_FAIL;
+		goto done;
+	}
+
+	if (ret = RDPKT(pcan_p, id, off, (uint16_t *)mp->b_wptr, data_len)) {
+		PCANDBG((CE_NOTE, "pcan rcv: err read data %x\n", ret));
+	}
+done:
+	if (ret) {
+		PCANDBG((CE_NOTE, "pcan rcv: rd data %x\n", ret));
+		freemsg(mp);
+		return;
+	}
+	mp->b_wptr += data_len;
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_RCV) {
+		int i;
+		cmn_err(CE_NOTE, "pcan rcv: len=0x%x\n", data_len);
+		for (i = 0; i < data_len + sizeof (frm); i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((uint8_t *)mp->b_rptr + i));
+	}
+#endif
+	mutex_exit(&pcan_p->pcan_glock);
+	mac_rx(GLD3(pcan_p), NULL, mp);
+	mutex_enter(&pcan_p->pcan_glock);
+}
+
+/*
+ * retrieve data from mini-pci card
+ */
+static void
+pcian_rcv(pcan_maci_t *pcan_p)
+{
+	struct an_card_rx_desc an_rx_desc;
+	char *buf;
+	uint16_t ret = 0, data_len;
+	int i, j;
+	struct ieee80211_frame *frm;
+	struct ieee80211_llc *llc;
+
+	mblk_t *mp = allocb(AN_RX_BUFFER_SIZE, BPRI_MED);
+	if (!mp) {
+		cmn_err(CE_WARN, "pcan(pci): failed to alloc rcv buf");
+		pcan_p->glds_norcvbuf++;
+		return;
+	}
+	ASSERT(mp->b_rptr == mp->b_wptr);
+
+	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
+		PCAN_AUX_GET32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
+		    ((uint32_t *)&an_rx_desc)[i]);
+	if (an_rx_desc.an_done && !an_rx_desc.an_valid) {
+		buf = pcan_p->pcan_rx[0].dma_virtaddr;
+		data_len = an_rx_desc.an_len;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_RCV) {
+			cmn_err(CE_NOTE, "pcan(pci) rcv: data_len=%x",
+			    data_len);
+			for (j = 0; j < data_len + 14; j++)
+				cmn_err(CE_NOTE, "pcan_rcv %d: %x", j,
+				    *((uint8_t *)buf + j));
+		}
+#endif
+		if (data_len > MBLKSIZE(mp)) {
+			cmn_err(CE_NOTE, "pcan(pci) rcv: data over length%x\n",
+			    data_len);
+			ret = PCAN_FAIL;
+			goto done;
+		}
+		/*
+		 * minipci card receive an ethernet frame, so assembly a 802.11
+		 * frame here manually.
+		 */
+		frm = (struct ieee80211_frame *)mp->b_wptr;
+		bzero(frm, sizeof (*frm));
+		frm->i_fc[0] |= IEEE80211_FC0_TYPE_DATA;
+		frm->i_fc[1] |= IEEE80211_FC1_DIR_FROMDS;
+		bcopy(pcan_p->an_status.an_cur_bssid, frm->i_addr2, 6);
+		bcopy(buf, frm->i_addr1, 6);
+		bcopy(buf + 6, frm->i_addr3, 6);
+		mp->b_wptr += sizeof (struct ieee80211_frame);
+
+		llc = (struct ieee80211_llc *)mp->b_wptr;
+		llc->illc_dsap = llc->illc_ssap = AN_SNAP_K1;
+		llc->illc_control = AN_SNAP_CONTROL;
+		bzero(llc->illc_oc, sizeof (llc->illc_oc));
+		mp->b_wptr += AN_SNAPHDR_LEN;
+
+		bcopy(buf + 12, mp->b_wptr, data_len);
+		mp->b_wptr += data_len;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_RCV) {
+			int i;
+			cmn_err(CE_NOTE, "pcan(pci) rcv: len=0x%x\n", data_len);
+			for (i = 0; i < data_len + sizeof (*frm)
+			    + sizeof (*llc); i++)
+				cmn_err(CE_NOTE, "%x: %x\n", i,
+				    *((uint8_t *)mp->b_rptr + i));
+		}
+#endif
+		mutex_exit(&pcan_p->pcan_glock);
+		mac_rx(GLD3(pcan_p), NULL, mp);
+		mutex_enter(&pcan_p->pcan_glock);
+	}
+done:
+	bzero(&an_rx_desc, sizeof (an_rx_desc));
+	an_rx_desc.an_valid = 1;
+	an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
+	an_rx_desc.an_done = 0;
+	an_rx_desc.an_phys = pcan_p->pcan_rx[0].dma_physaddr;
+
+	for (i = 0; i < sizeof (an_rx_desc) / 4; i++)
+		PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET + (i * 4),
+		    ((uint32_t *)&an_rx_desc)[i]);
+	if (ret) {
+		freemsg(mp);
+	}
+}
+
+/*ARGSUSED*/
+static uint32_t
+pcan_txdone(pcan_maci_t *pcan_p, uint16_t err)
+{
+	uint16_t fid, i, ring_idx;
+	uint32_t ret = 0;
+
+	PCAN_READ(pcan_p, AN_TX_CMP_FID(pcan_p), fid);
+	mutex_enter(&pcan_p->pcan_txring.an_tx_lock);
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		if (pcan_p->pcan_flag & PCAN_CARD_SEND) {
+			ring_idx = pcan_p->pcan_txring.an_tx_cons;
+			pcan_p->pcan_txring.an_tx_cons =
+			    (ring_idx + 1) % AN_MAX_TX_DESC;
+			if (pcan_p->pcan_txring.an_tx_prod ==
+			    pcan_p->pcan_txring.an_tx_cons) {
+				pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
+			}
+		}
+		ret = 0;
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		for (i = 0; i < AN_TX_RING_CNT; i++) {
+			if (fid == pcan_p->pcan_txring.an_tx_ring[i]) {
+				pcan_p->pcan_txring.an_tx_ring[i] = 0;
+				break;
+			}
+		}
+		pcan_p->pcan_txring.an_tx_cons =
+		    (pcan_p->pcan_txring.an_tx_cons + 1) & AN_TX_RING_MASK;
+		ret = (i == AN_TX_RING_CNT ? 1 : 0);
+	}
+	mutex_exit(&pcan_p->pcan_txring.an_tx_lock);
+	return (ret);
+}
+
+/*
+ * delay in which the mutex is not hold.
+ * assuming the mutex has already been hold.
+ */
+static void
+pcan_delay(pcan_maci_t *pcan_p, clock_t microsecs)
+{
+	ASSERT(mutex_owned(&pcan_p->pcan_glock));
+
+	mutex_exit(&pcan_p->pcan_glock);
+	delay(drv_usectohz(microsecs));
+	mutex_enter(&pcan_p->pcan_glock);
+}
+
+static void
+pcan_reset_backend(pcan_maci_t *pcan_p, int timeout)
+{
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
+		PCAN_DISABLE_INTR_CLEAR(pcan_p);
+		(void) pcan_set_cmd(pcan_p, AN_CMD_FW_RESTART, 0);
+		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP2, 0);
+		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
+		(void) pcan_set_cmd0(pcan_p, AN_CMD_NOOP2, 0, 0, 0);
+		PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), AN_CMD_FW_RESTART);
+		pcan_delay(pcan_p, timeout); /* wait for firmware restart */
+
+		(void) pcan_set_cmd(pcan_p, AN_CMD_NOOP, 0);
+		(void) pcan_set_cmd0(pcan_p, AN_CMD_DISABLE, 0, 0, 0);
+
+		PCAN_DISABLE_INTR_CLEAR(pcan_p);
+	}
+}
+
+/*
+ * set command without the need of ACK.
+ */
+static uint16_t
+pcan_set_cmd0(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t p0,
+    uint16_t p1, uint16_t p2)
+{
+	int i;
+	uint16_t stat, r0, r1, r2;
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		for (i = 0; i < AN_TIMEOUT; i++) {
+			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+			if (!(stat & AN_CMD_BUSY))
+				break;
+		}
+		if (i == AN_TIMEOUT) {
+			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
+			    AN_EV_CLR_STUCK_BUSY);
+			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+			drv_usecwait(10);
+		}
+		PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), p0);
+		PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), p1);
+		PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), p2);
+	}
+	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
+	for (i = 0; i < AN_TIMEOUT; i++) {
+		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
+		if (stat & AN_EV_CMD)
+			break;
+	}
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
+		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
+		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
+		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+		if (stat & AN_CMD_BUSY)
+			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p),
+			    AN_EV_CLR_STUCK_BUSY);
+		PCANDBG((CE_NOTE, "pcan set_cmd0: "
+		    "stat=%x, r0=%x, r1=%x, r2=%x\n",
+		    stat, r0, r1, r2));
+	}
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+	return (i == AN_TIMEOUT ? PCAN_TIMEDOUT_ACCESS : PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_set_cmd(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t param)
+{
+	int i;
+	uint16_t stat, r0, r1, r2;
+	uint16_t ret;
+
+	if (((cmd == AN_CMD_ENABLE) &&
+	    ((pcan_p->pcan_flag & PCAN_ENABLED) != 0)) ||
+	    ((cmd == AN_CMD_DISABLE) &&
+	    ((pcan_p->pcan_flag & PCAN_ENABLED) == 0)))
+		return (PCAN_SUCCESS);
+	for (i = 0; i < AN_TIMEOUT; i++) {
+		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+		if (!(stat & AN_CMD_BUSY)) {
+			break;
+		}
+	}
+	if (i == AN_TIMEOUT) {
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+		drv_usecwait(10);
+	}
+
+	PCAN_WRITE(pcan_p, AN_PARAM0(pcan_p), param);
+	PCAN_WRITE(pcan_p, AN_PARAM1(pcan_p), 0);
+	PCAN_WRITE(pcan_p, AN_PARAM2(pcan_p), 0);
+	PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
+
+	for (i = 0; i < AN_TIMEOUT; i++) {
+		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
+		if (stat & AN_EV_CMD) {
+			break;
+		}
+		PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+		if (stat == cmd)
+			PCAN_WRITE(pcan_p, AN_COMMAND(pcan_p), cmd);
+	}
+	if (i == AN_TIMEOUT) {
+		if (cmd == AN_CMD_FW_RESTART) {
+			PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+			return (PCAN_SUCCESS);
+		}
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_CMD) {
+			cmn_err(CE_WARN, "pcan set_cmd: %x timeout stat=%x\n",
+			    cmd, stat);
+		}
+#endif
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+		return (PCAN_TIMEDOUT_CMD);
+	}
+
+	for (i = 0; i < AN_TIMEOUT; i++) {
+		PCAN_READ(pcan_p, AN_STATUS(pcan_p), stat);
+		PCAN_READ(pcan_p, AN_RESP0(pcan_p), r0);
+		PCAN_READ(pcan_p, AN_RESP1(pcan_p), r1);
+		PCAN_READ(pcan_p, AN_RESP2(pcan_p), r2);
+		if ((stat & AN_STAT_CMD_CODE) == (cmd & AN_STAT_CMD_CODE))
+			break;
+	}
+	if (cmd == AN_CMD_FW_RESTART) {
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+		return (PCAN_SUCCESS);
+	}
+	if (i == AN_TIMEOUT) {
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_CMD) {
+			cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: timeout "
+			    "%x,%x,%x,%x\n", cmd, param, stat, r0, r1, r2);
+		}
+#endif
+		ret = PCAN_TIMEDOUT_ACCESS;
+	} else {
+		if (stat & AN_STAT_CMD_RESULT) {
+#ifdef DEBUG
+			if (pcan_debug & PCAN_DBG_CMD) {
+				cmn_err(CE_WARN, "pcan set_cmd<%x,%x>: failed "
+				    "%x,%x,%x,%x\n",
+				    cmd, param, stat, r0, r1, r2);
+			}
+#endif
+			ret = PCAN_TIMEDOUT_ACCESS;
+		} else {
+			ret = PCAN_SUCCESS;
+		}
+	}
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CMD);
+	PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+	if (stat & AN_CMD_BUSY)
+		PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_CLR_STUCK_BUSY);
+	if (ret == PCAN_SUCCESS) {
+		if (cmd == AN_CMD_ENABLE)
+			pcan_p->pcan_flag |= PCAN_ENABLED;
+		if (cmd == AN_CMD_DISABLE)
+			pcan_p->pcan_flag &= (~PCAN_ENABLED);
+	}
+	return (ret);
+}
+
+static uint16_t
+pcan_set_ch(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t channel)
+{
+	int i;
+	uint16_t stat, select, offset;
+
+	if (channel) {
+		select = AN_SEL1;
+		offset = AN_OFF1;
+	} else {
+		select = AN_SEL0;
+		offset = AN_OFF0;
+	}
+	PCAN_WRITE(pcan_p, select, type);
+	PCAN_WRITE(pcan_p, offset, off);
+	for (i = 0; i < AN_TIMEOUT; i++) {
+		PCAN_READ(pcan_p, offset, stat);
+		if (!(stat & (AN_OFF_BUSY|AN_OFF_ERR)))
+			break;
+	}
+	if (stat & (AN_OFF_BUSY|AN_OFF_ERR)) { /* time out */
+		PCANDBG((CE_WARN, "pcan: set_ch%d %x %x TO %x\n",
+		    channel, type, off, stat));
+		return (PCAN_TIMEDOUT_TARGET);
+	}
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_get_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
+{
+	uint16_t stat;
+
+	PCANDBG((CE_NOTE, "pcan: get_ltv(%p,%x,%x,%p)\n",
+		(void *)pcan_p, len, type, (void *)val_p));
+	ASSERT(!(len & 1));
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		uint32_t i;
+		struct an_card_rid_desc an_rid_desc;
+		struct an_ltv_gen *an_ltv;
+		if (!pcan_p->pcan_cmd.dma_virtaddr)
+			return (EIO);
+		an_rid_desc.an_valid = 1;
+		an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
+		an_rid_desc.an_rid = 0;
+		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
+		bzero(pcan_p->pcan_cmd.dma_virtaddr, AN_RID_BUFFER_SIZE);
+
+		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
+			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
+			    ((uint32_t *)&an_rid_desc)[i]);
+
+		if (pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
+		    AN_ACCESS_READ, type, 0, 0)) {
+			cmn_err(CE_WARN, "pcan get_ltv: set cmd error");
+			return (EIO);
+		}
+
+		an_ltv = (struct an_ltv_gen *)pcan_p->pcan_cmd.dma_virtaddr;
+#ifdef DEBUG
+		if (pcan_debug & PCAN_DBG_INFO) {
+			cmn_err(CE_NOTE, "pcan get_ltv: type=%x,"
+			    "expected len=%d," "actual len=%d",
+			    type, len, an_ltv->an_len);
+			for (i = 0; i < an_ltv->an_len; i++)
+				cmn_err(CE_NOTE, "%d: %x", i,
+				    *(((uint8_t *)an_ltv) + i));
+		}
+#endif
+		if (an_ltv->an_len != len) {
+			PCANDBG((CE_WARN, "pcan get_ltv: rid=%x expected len=%d"
+			    "actual: len=%d", type,
+			    len, an_ltv->an_len));
+			/* return (EIO); */
+		}
+		bcopy(an_ltv, val_p, len);
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		len >>= 1;	/* convert bytes to 16-bit words */
+
+		/* 1. select read mode */
+		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
+		    AN_ACCESS_READ, type))
+			return (stat);
+
+		/* 2. select Buffer Access Path (channel) 1 for PIO */
+		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
+			return (stat);
+
+		/* 3. read length */
+		PCAN_READ(pcan_p, AN_DATA1, stat);
+		*val_p++ = stat;
+		if (stat != (len << 1)) {
+			PCANDBG((CE_NOTE, "pcan get_ltv[%x]:expect %x,"
+			    "got %x\n", type, (len + 1) << 1, stat));
+			stat = (stat >> 1) - 1;
+			len = MIN(stat, len);
+		}
+		/* 4. read value */
+		for (stat = 0; stat < len - 1; stat++, val_p++) {
+			PCAN_READ_P(pcan_p, AN_DATA1, val_p, 1);
+		}
+	}
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_put_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type, uint16_t *val_p)
+{
+	uint16_t stat;
+	int i;
+
+	ASSERT(!(len & 1));
+
+	if (pcan_p->pcan_device_type == PCAN_DEVICE_PCI) {
+		struct an_card_rid_desc an_rid_desc;
+
+		for (i = 0; i < AN_TIMEOUT; i++) {
+			PCAN_READ(pcan_p, AN_COMMAND(pcan_p), stat);
+			if (!(stat & AN_CMD_BUSY)) {
+				break;
+			}
+		}
+		if (i == AN_TIMEOUT) {
+			cmn_err(CE_WARN, "pcan put_ltv: busy");
+		}
+
+		an_rid_desc.an_valid = 1;
+		an_rid_desc.an_len = len;
+		an_rid_desc.an_rid = type;
+		an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
+
+		bcopy(val_p, pcan_p->pcan_cmd.dma_virtaddr,
+		    an_rid_desc.an_len);
+
+		for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
+			PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
+			    ((uint32_t *)&an_rid_desc)[i]);
+		pcan_delay(pcan_p, 100000);
+		stat = pcan_set_cmd0(pcan_p, AN_CMD_ACCESS |
+		    AN_ACCESS_WRITE, type, 0, 0);
+		pcan_delay(pcan_p, 100000);
+		return (stat);
+	} else if (pcan_p->pcan_device_type == PCAN_DEVICE_PCCARD) {
+		/* 0. select read mode first */
+		if (stat = pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
+		    AN_ACCESS_READ, type))
+			return (stat);
+
+		/* 1. select Buffer Access Path (channel) 1 for PIO */
+		if (stat = pcan_set_ch(pcan_p, type, 0, 1))
+			return (stat);
+
+		/* 2. write length */
+		len >>= 1;		/* convert bytes to 16-bit words */
+		stat = len;
+		PCAN_WRITE(pcan_p, AN_DATA1, stat);
+
+		/* 3. write value */
+		val_p++;
+		for (stat = 0; stat < len-1; stat++, val_p++) {
+			PCAN_WRITE_P(pcan_p, AN_DATA1, val_p, 1);
+		}
+
+		/* 4. select write mode */
+		return (pcan_set_cmd(pcan_p, AN_CMD_ACCESS |
+		    AN_ACCESS_WRITE, type));
+	}
+	return (PCAN_FAIL);
+}
+
+/*ARGSUSED*/
+static uint16_t
+pcan_rdch0(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
+	int len, int order)
+{
+	ASSERT(!(len & 1));
+
+	if (pcan_set_ch(pcan_p, type, off, 0) != PCAN_SUCCESS)
+		return (PCAN_FAIL);
+	len >>= 1;
+	for (off = 0; off < len; off++, buf_p++) {
+		PCAN_READ_P(pcan_p, AN_DATA0, buf_p, order);
+	}
+	return (PCAN_SUCCESS);
+}
+
+/*ARGSUSED*/
+static uint16_t
+pcan_wrch1(pcan_maci_t *pcan_p, uint16_t type, uint16_t off, uint16_t *buf_p,
+	int len, int order)
+{
+	ASSERT(!(len & 1));
+
+	if (pcan_set_ch(pcan_p, type, off, 1) != PCAN_SUCCESS)
+		return (PCAN_FAIL);
+	len >>= 1;
+	for (off = 0; off < len; off++, buf_p++) {
+		PCAN_WRITE_P(pcan_p, AN_DATA1, buf_p, order);
+	}
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_status_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_status *status_p)
+{
+	uint16_t ret, len;
+
+	if (rw != PCAN_READ_LTV) {
+		cmn_err(CE_WARN, "pcan status_ltv: unsupported op %x", rw);
+		return (PCAN_FAIL);
+	}
+	if (ret = pcan_get_ltv(pcan_p, sizeof (*status_p), AN_RID_STATUS,
+	    (uint16_t *)status_p))
+		return (ret);
+
+	PCAN_SWAP16_BUF(status_p->an_macaddr);
+	PCAN_SWAP16_BUF(status_p->an_ssid);
+	len = min(status_p->an_ssidlen, 31);
+	status_p->an_ssid[len] = '\0';
+	PCAN_SWAP16_BUF(status_p->an_ap_name);
+	PCAN_SWAP16_BUF(status_p->an_cur_bssid);
+	PCAN_SWAP16_BUF(status_p->an_prev_bssid1);
+	PCAN_SWAP16_BUF(status_p->an_prev_bssid2);
+	PCAN_SWAP16_BUF(status_p->an_prev_bssid3);
+	PCAN_SWAP16_BUF(status_p->an_ap_ip_address);
+	PCAN_SWAP16_BUF(status_p->an_carrier);
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_cfg_ltv(int rw, pcan_maci_t *pcan_p, struct an_ltv_genconfig *cfg_p)
+{
+	uint16_t ret;
+	uint16_t rid = cfg_p == &pcan_p->an_config ?
+	    AN_RID_GENCONFIG : AN_RID_ACTUALCFG;
+
+	if (rw == PCAN_READ_LTV) {
+		if (ret = pcan_get_ltv(pcan_p, sizeof (*cfg_p), rid,
+		    (uint16_t *)cfg_p))
+			return (ret);
+		goto done;
+	}
+	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
+	PCAN_SWAP16_BUF(cfg_p->an_rates);
+	if (ret = pcan_put_ltv(pcan_p, sizeof (*cfg_p),
+	    rid, (uint16_t *)cfg_p))
+		return (ret);
+done:
+	PCAN_SWAP16_BUF(cfg_p->an_macaddr);
+	PCAN_SWAP16_BUF(cfg_p->an_rates);
+	return (ret);
+}
+
+static uint16_t
+pcan_cap_ltv(int rw, pcan_maci_t *pcan_p)
+{
+	uint16_t ret;
+
+	if (rw != PCAN_READ_LTV) {
+		cmn_err(CE_WARN, "pcan cap_ltv: unsupported op %x", rw);
+		return (PCAN_FAIL);
+	}
+	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_caps),
+		AN_RID_CAPABILITIES, (uint16_t *)&pcan_p->an_caps))
+		return (ret);
+
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oui);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_manufname);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodname);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_prodvers);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_oemaddr);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_aironetaddr);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_callid);
+	PCAN_SWAP16_BUF(pcan_p->an_caps.an_supported_rates);
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_ssid_ltv(int rw, pcan_maci_t *pcan_p)
+{
+	uint16_t ret;
+
+	if (rw == PCAN_READ_LTV) {
+		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
+		    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
+			return (ret);
+		goto done;
+	}
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
+	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_ssidlist),
+	    AN_RID_SSIDLIST, (uint16_t *)&pcan_p->an_ssidlist))
+		return (ret);
+done:
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid1);
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid2);
+	PCAN_SWAP16_BUF(pcan_p->an_ssidlist.an_ssid3);
+	return (ret);
+}
+
+static uint16_t
+pcan_aplist_ltv(int rw, pcan_maci_t *pcan_p)
+{
+	uint16_t ret;
+
+	if (rw == PCAN_READ_LTV) {
+		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_aplist),
+		    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
+			return (ret);
+		goto done;
+	}
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
+	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_aplist),
+	    AN_RID_APLIST, (uint16_t *)&pcan_p->an_aplist))
+		return (ret);
+done:
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap1);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap2);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap3);
+	PCAN_SWAP16_BUF(pcan_p->an_aplist.an_ap4);
+	return (ret);
+}
+
+static uint16_t
+pcan_scanresult_ltv(int rw, pcan_maci_t *pcan_p, uint16_t type,
+    struct an_ltv_scanresult *scanresult_p)
+{
+	uint16_t ret, len;
+
+	if (rw != PCAN_READ_LTV) {
+		cmn_err(CE_WARN, "pcan scan_ltv: readonly rid %x\n", type);
+		return (PCAN_FAIL);
+	}
+	if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_scanresult),
+	    type, (uint16_t *)scanresult_p))
+		return (ret);
+	PCAN_SWAP16_BUF(scanresult_p->an_bssid);
+	PCAN_SWAP16_BUF(scanresult_p->an_ssid);
+	len = min(scanresult_p->an_ssidlen, 31);
+	scanresult_p->an_ssid[len] = '\0';
+	PCAN_SWAP16_BUF(scanresult_p->an_rates);
+	return (PCAN_SUCCESS);
+}
+
+static uint16_t
+pcan_one_wepkey(int rw, pcan_maci_t *pcan_p, struct an_ltv_wepkey *wkp,
+    uint16_t rid)
+{
+	uint16_t ret;
+
+	if (rw == PCAN_READ_LTV) {
+		if (ret = pcan_get_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
+		    rid, (uint16_t *)wkp)) {
+			return (ret);
+		}
+		goto done;
+	}
+	PCAN_SWAP16_BUF(wkp->an_macaddr);
+	PCAN_SWAP16_BUF(wkp->an_key);
+	if (ret = pcan_put_ltv(pcan_p, sizeof (struct an_ltv_wepkey),
+	    rid, (uint16_t *)wkp))
+		return (ret);
+done:
+	PCAN_SWAP16_BUF(wkp->an_macaddr);
+	PCAN_SWAP16_BUF(wkp->an_key);
+	return (ret);
+}
+
+static uint16_t
+pcan_wepkey_ltv(int rw, pcan_maci_t *pcan_p)
+{
+	uint16_t ret, i;
+	struct an_ltv_wepkey wk;
+
+	if (rw == PCAN_READ_LTV) {
+		uint16_t rid = AN_RID_WEPKEY2;
+
+		if (ret = pcan_one_wepkey(rw, pcan_p, &wk, rid))
+			return (ret);
+		for (i = 0; i < 5; i++) {
+			if (wk.an_index < 4)
+				pcan_p->an_wepkey[wk.an_index] = wk;
+			else if (wk.an_index == 0xffff)
+				pcan_p->an_cur_wepkey = wk.an_macaddr[0];
+			rid = AN_RID_WEPKEY;
+		}
+		return (PCAN_SUCCESS);
+	}
+	for (i = 0; i < MAX_NWEPKEYS; i++) {
+		if (pcan_p->an_wepkey[i].an_index == i) {
+			if (ret = pcan_one_wepkey(rw, pcan_p,
+			    &pcan_p->an_wepkey[i], AN_RID_WEPKEY2))
+				return (ret);
+		}
+	}
+	/* Now set the default key */
+	(void) memset(&wk, 0, sizeof (wk));
+	wk.an_index = 0xffff;
+	wk.an_macaddr[0] = pcan_p->an_cur_wepkey;
+	ret = pcan_one_wepkey(rw, pcan_p, &wk, AN_RID_WEPKEY2);
+	return (ret);
+}
+
+static uint16_t
+pcan_alloc_nicmem(pcan_maci_t *pcan_p, uint16_t len, uint16_t *id_p)
+{
+	int i;
+	uint16_t stat;
+
+	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
+
+	if (stat = pcan_set_cmd(pcan_p, AN_CMD_ALLOC_MEM, len))
+		return (stat);
+	for (i = 0; !(stat & AN_EV_ALLOC) && (i < AN_TIMEOUT); i++) {
+		PCAN_READ(pcan_p, AN_EVENT_STAT(pcan_p), stat);
+	}
+	if (!(stat & AN_EV_ALLOC))
+		return (PCAN_TIMEDOUT_ALLOC);
+	PCAN_READ(pcan_p, AN_ALLOC_FID, stat);
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), AN_EV_ALLOC);
+	*id_p = stat;
+
+	/* zero fill the allocated NIC mem - sort of pcan_fill_ch0 */
+	(void) pcan_set_ch(pcan_p, stat, 0, 0);
+	for (len >>= 1, stat = 0; stat < len; stat++) {
+		PCAN_WRITE(pcan_p, AN_DATA0, 0);
+	}
+	return (PCAN_SUCCESS);
+}
+
+static void
+pcan_stop_rx_dma(pcan_maci_t *pcan_p)
+{
+	int i, j;
+	struct an_card_rx_desc  an_rx_desc;
+
+	for (i = 0; i < AN_MAX_RX_DESC; i++) {
+		bzero(&an_rx_desc, sizeof (an_rx_desc));
+		an_rx_desc.an_valid = 0;
+		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
+		an_rx_desc.an_done = 1;
+		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
+		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
+			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
+			    + (i * sizeof (an_rx_desc))
+			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
+	}
+}
+
+static int
+pcan_init_dma_desc(pcan_maci_t *pcan_p)
+{
+	int i, j;
+	struct an_card_rid_desc an_rid_desc;
+	struct an_card_rx_desc  an_rx_desc;
+	struct an_card_tx_desc  an_tx_desc;
+
+	/* Allocate DMA for rx */
+	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
+	    AN_DESCRIPTOR_RX, AN_RX_DESC_OFFSET,
+	    AN_MAX_RX_DESC) != PCAN_SUCCESS) {
+		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rx descriptor");
+		goto error;
+	}
+	for (i = 0; i < AN_MAX_RX_DESC; i++) {
+		bzero(&an_rx_desc, sizeof (an_rx_desc));
+		an_rx_desc.an_valid = 1;
+		an_rx_desc.an_len = AN_RX_BUFFER_SIZE;
+		an_rx_desc.an_done = 0;
+		an_rx_desc.an_phys = pcan_p->pcan_rx[i].dma_physaddr;
+		for (j = 0; j < sizeof (an_rx_desc) / 4; j++)
+			PCAN_AUX_PUT32(pcan_p, AN_RX_DESC_OFFSET
+			    + (i * sizeof (an_rx_desc))
+			    + (j * 4), ((uint32_t *)&an_rx_desc)[j]);
+	}
+
+
+	/* Allocate DMA for tx */
+	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
+	    AN_DESCRIPTOR_TX, AN_TX_DESC_OFFSET,
+	    AN_MAX_TX_DESC) != PCAN_SUCCESS) {
+		cmn_err(CE_WARN, "pcan init_dma: fail to alloc tx descriptor");
+		goto error;
+	}
+
+	for (i = 0; i < AN_MAX_TX_DESC; i++) {
+		an_tx_desc.an_offset = 0;
+		an_tx_desc.an_eoc = 0;
+		an_tx_desc.an_valid = 0;
+		an_tx_desc.an_len = 0;
+		an_tx_desc.an_phys = pcan_p->pcan_tx[i].dma_physaddr;
+
+		for (j = 0; j < sizeof (an_tx_desc) / 4; j++)
+			PCAN_AUX_PUT32(pcan_p, AN_TX_DESC_OFFSET
+			    + (i * sizeof (an_tx_desc))
+			    + (j * 4), ((uint32_t *)&an_tx_desc)[j]);
+	}
+
+	/* Allocate DMA for rid */
+	if (pcan_set_cmd0(pcan_p, AN_CMD_ALLOC_DESC,
+	    AN_DESCRIPTOR_HOSTRW, AN_HOST_DESC_OFFSET, 1) != PCAN_SUCCESS) {
+		cmn_err(CE_WARN, "pcan init_dma: fail to alloc rid descriptor");
+		goto error;
+	}
+	bzero(&an_rid_desc, sizeof (an_rid_desc));
+	an_rid_desc.an_valid = 1;
+	an_rid_desc.an_len = AN_RID_BUFFER_SIZE;
+	an_rid_desc.an_rid = 0;
+	an_rid_desc.an_phys = pcan_p->pcan_cmd.dma_physaddr;
+
+	for (i = 0; i < sizeof (an_rid_desc) / 4; i++)
+		PCAN_AUX_PUT32(pcan_p, AN_HOST_DESC_OFFSET + i * 4,
+		    ((uint32_t *)&an_rid_desc)[i]);
+
+	pcan_p->pcan_txring.an_tx_prod = 0;
+	pcan_p->pcan_txring.an_tx_cons = 0;
+	pcan_p->pcan_flag &= ~PCAN_CARD_SEND;
+	return (PCAN_SUCCESS);
+error:
+	return (PCAN_FAIL);
+}
+
+static int
+pcan_init_dma(dev_info_t *dip, pcan_maci_t *pcan_p)
+{
+	int i, ret = PCAN_FAIL;
+	ddi_dma_cookie_t dma_cookie;
+	size_t len;
+
+	/* Allocate DMA for rx */
+	for (i = 0; i < AN_MAX_RX_DESC; i++) {
+		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
+		    DDI_DMA_SLEEP, 0,
+		    &pcan_p->pcan_rx[i].dma_handle) != DDI_SUCCESS)
+			goto error;
+
+		if (ddi_dma_mem_alloc(pcan_p->pcan_rx[i].dma_handle,
+		    AN_RX_BUFFER_SIZE, &accattr,
+		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
+		    (caddr_t *)&pcan_p->pcan_rx[i].dma_virtaddr, &len,
+		    &pcan_p->pcan_rx[i].dma_acc_handle) != DDI_SUCCESS) {
+			goto error;
+		}
+		if (ddi_dma_addr_bind_handle(
+		    pcan_p->pcan_rx[i].dma_handle,
+		    NULL, (caddr_t)pcan_p->pcan_rx[i].dma_virtaddr,
+		    len, DDI_DMA_READ |
+		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
+		    &pcan_p->pcan_rx[i].ncookies) != DDI_DMA_MAPPED) {
+			goto error;
+		}
+		ASSERT(pcan_p->pcan_rx[i].ncookies == 1);
+		pcan_p->pcan_rx[i].dma_physaddr = dma_cookie.dmac_address;
+	}
+
+	/* Allocate DMA for tx */
+	for (i = 0; i < AN_MAX_TX_DESC; i++) {
+		if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
+		    DDI_DMA_SLEEP, 0,
+		    &pcan_p->pcan_tx[i].dma_handle) != DDI_SUCCESS)
+			goto error;
+
+		if (ddi_dma_mem_alloc(pcan_p->pcan_tx[i].dma_handle,
+		    AN_TX_BUFFER_SIZE, &accattr,
+		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0,
+		    (caddr_t *)&pcan_p->pcan_tx[i].dma_virtaddr, &len,
+		    &pcan_p->pcan_tx[i].dma_acc_handle) != DDI_SUCCESS) {
+			goto error;
+		}
+		if (ddi_dma_addr_bind_handle(
+		    pcan_p->pcan_tx[i].dma_handle,
+		    NULL, (caddr_t)pcan_p->pcan_tx[i].dma_virtaddr,
+		    len, DDI_DMA_WRITE |
+		    DDI_DMA_STREAMING, DDI_DMA_SLEEP, 0, &dma_cookie,
+		    &pcan_p->pcan_tx[i].ncookies) != DDI_DMA_MAPPED) {
+			goto error;
+		}
+		ASSERT(pcan_p->pcan_tx[i].ncookies == 1);
+		pcan_p->pcan_tx[i].dma_physaddr = dma_cookie.dmac_address;
+	}
+
+	/* Allocate DMA for rid */
+	if (ddi_dma_alloc_handle(dip, &control_cmd_dma_attr,
+	    DDI_DMA_SLEEP, 0,
+	    &pcan_p->pcan_cmd.dma_handle) != DDI_SUCCESS)
+		goto error;
+
+	if (ddi_dma_mem_alloc(pcan_p->pcan_cmd.dma_handle,
+	    AN_RID_BUFFER_SIZE, &accattr,
+	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0,
+	    (caddr_t *)&pcan_p->pcan_cmd.dma_virtaddr, &len,
+	    &pcan_p->pcan_cmd.dma_acc_handle) != DDI_SUCCESS) {
+		goto error;
+	}
+	if (ddi_dma_addr_bind_handle(
+	    pcan_p->pcan_cmd.dma_handle,
+	    NULL, (caddr_t)pcan_p->pcan_cmd.dma_virtaddr,
+	    len, DDI_DMA_RDWR |
+	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &dma_cookie,
+	    &pcan_p->pcan_cmd.ncookies) != DDI_DMA_MAPPED) {
+		goto error;
+	}
+	ASSERT(pcan_p->pcan_cmd.ncookies == 1);
+	pcan_p->pcan_cmd.dma_physaddr = dma_cookie.dmac_address;
+
+	if (ret = pcan_init_dma_desc(pcan_p)) {
+		cmn_err(CE_WARN, "pcan init_dma_desc: failed\n");
+		goto error;
+	}
+
+	return (PCAN_SUCCESS);
+error:
+	pcan_free_dma(pcan_p);
+	return (ret);
+}
+
+static void
+pcan_free_dma(pcan_maci_t *pcan_p)
+{
+	int i;
+
+	/* free RX dma */
+	pcan_stop_rx_dma(pcan_p);
+	for (i = 0; i < AN_MAX_RX_DESC; i++) {
+		if (pcan_p->pcan_rx[i].dma_handle != NULL) {
+			if (pcan_p->pcan_rx[i].ncookies) {
+				(void) ddi_dma_unbind_handle(
+				    pcan_p->pcan_rx[i].dma_handle);
+				pcan_p->pcan_rx[i].ncookies = 0;
+			}
+			ddi_dma_free_handle(
+			    &pcan_p->pcan_rx[i].dma_handle);
+			pcan_p->pcan_rx[i].dma_handle = NULL;
+		}
+		if (pcan_p->pcan_rx[i].dma_acc_handle != NULL) {
+			ddi_dma_mem_free(
+			    &pcan_p->pcan_rx[i].dma_acc_handle);
+			pcan_p->pcan_rx[i].dma_acc_handle = NULL;
+		}
+	}
+
+	/* free TX dma */
+	for (i = 0; i < AN_MAX_TX_DESC; i++) {
+		if (pcan_p->pcan_tx[i].dma_handle != NULL) {
+			if (pcan_p->pcan_tx[i].ncookies) {
+				(void) ddi_dma_unbind_handle(
+				    pcan_p->pcan_tx[i].dma_handle);
+				pcan_p->pcan_tx[i].ncookies = 0;
+			}
+			ddi_dma_free_handle(
+			    &pcan_p->pcan_tx[i].dma_handle);
+			pcan_p->pcan_tx[i].dma_handle = NULL;
+		}
+		if (pcan_p->pcan_tx[i].dma_acc_handle != NULL) {
+			ddi_dma_mem_free(
+			    &pcan_p->pcan_tx[i].dma_acc_handle);
+			pcan_p->pcan_tx[i].dma_acc_handle = NULL;
+		}
+	}
+
+	/* free cmd dma */
+	if (pcan_p->pcan_cmd.dma_handle != NULL) {
+		if (pcan_p->pcan_cmd.ncookies) {
+			(void) ddi_dma_unbind_handle(
+			    pcan_p->pcan_cmd.dma_handle);
+			pcan_p->pcan_cmd.ncookies = 0;
+		}
+		ddi_dma_free_handle(
+		    &pcan_p->pcan_cmd.dma_handle);
+		pcan_p->pcan_cmd.dma_handle = NULL;
+	}
+	if (pcan_p->pcan_cmd.dma_acc_handle != NULL) {
+		ddi_dma_mem_free(
+		    &pcan_p->pcan_cmd.dma_acc_handle);
+		pcan_p->pcan_cmd.dma_acc_handle = NULL;
+	}
+}
+
+/*
+ * get card capability (WEP, default channel), setup broadcast, mac addresses
+ */
+static uint32_t
+pcan_get_cap(pcan_maci_t *pcan_p)
+{
+	uint16_t stat;
+
+	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_config)) {
+		PCANDBG((CE_NOTE, "pcan get_cap: read cfg fail %x", stat));
+		return ((uint32_t)AN_RID_GENCONFIG << 16 | stat);
+	}
+
+	if (stat = pcan_cap_ltv(PCAN_READ_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan get_cap: read cap fail %x", stat));
+		return ((uint32_t)AN_RID_CAPABILITIES << 16 | stat);
+	}
+#ifdef DEBUG
+	if (pcan_debug & PCAN_DBG_FW_VERSION) {
+		cmn_err(CE_NOTE, "the version of the firmware in the wifi card "
+		    "'%s %s %s' is %s\n",
+		    pcan_p->an_caps.an_manufname,
+		    pcan_p->an_caps.an_prodname,
+		    pcan_p->pcan_device_type == PCAN_DEVICE_PCI ?
+		    "minipci" : "pccard",
+		    pcan_p->an_caps.an_prodvers);
+	}
+#endif
+
+	if (stat = pcan_ssid_ltv(PCAN_READ_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan get_cap: read ssid fail %x", stat));
+		return ((uint32_t)AN_RID_SSIDLIST << 16 | stat);
+	}
+
+	if (stat = pcan_aplist_ltv(PCAN_READ_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan get_cap: read aplist fail %x", stat));
+		return ((uint32_t)AN_RID_APLIST << 16 | stat);
+	}
+	if (stat = pcan_wepkey_ltv(PCAN_READ_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan get_cap: read wepkey fail %x", stat));
+		return ((uint32_t)AN_RID_WEPKEY2 << 16 | stat);
+	}
+	ether_copy(pcan_p->an_caps.an_oemaddr, pcan_p->pcan_mac_addr);
+	return (PCAN_SUCCESS);
+}
+
+static int
+pcan_config_mac(pcan_maci_t *pcan_p)
+{
+	uint16_t stat;
+
+	if (stat = pcan_ssid_ltv(PCAN_WRITE_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: write SSID failed%x\n",
+		    stat));
+		return ((int)stat);
+	}
+
+	if (stat = pcan_aplist_ltv(PCAN_WRITE_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: write APlist failed%x\n",
+		    stat));
+		return ((int)stat);
+	}
+	if (stat = pcan_wepkey_ltv(PCAN_WRITE_LTV, pcan_p)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: write wepkey failed%x\n",
+		    stat));
+		return ((int)stat);
+	}
+	if (pcan_p->pcan_usewep)
+		pcan_p->an_config.an_authtype |=
+		    AN_AUTHTYPE_ENABLEWEP | AN_AUTHTYPE_ALLOW_UNENCRYPTED;
+	PCANDBG((CE_NOTE, "pcan config_mac: usewep=%x authtype=%x opmode=%x\n",
+	    pcan_p->pcan_usewep, pcan_p->an_config.an_authtype,
+	    pcan_p->an_config.an_opmode));
+
+	pcan_p->an_config.an_assoc_timeout = 5000; /* stop assoc seq in 5 sec */
+	if (stat = pcan_cfg_ltv(PCAN_WRITE_LTV, pcan_p, &pcan_p->an_config)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: write cfg failed %x\n",
+		    stat));
+		return ((int)stat);
+	}
+
+	if (stat = pcan_cfg_ltv(PCAN_READ_LTV, pcan_p,
+	    &pcan_p->an_actual_config)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: read cfg failed%x\n",
+		    stat));
+		return ((int)stat);
+	}
+	PCANDBG((CE_NOTE, "pcan config_mac: optionmask=%x authtype=%x\n", 0,
+	    pcan_p->an_actual_config.an_authtype));
+
+	if (stat = pcan_status_ltv(PCAN_READ_LTV, pcan_p, &pcan_p->an_status)) {
+		PCANDBG((CE_NOTE, "pcan config_mac: read status failed %x\n",
+		    stat));
+		return ((int)stat);
+	}
+	return (PCAN_SUCCESS);
+}
+
+static int
+pcan_loaddef(pcan_maci_t *pcan_p)
+{
+	int i;
+
+	pcan_p->an_ssidlist.an_ssid1_len = 0;
+	bzero(pcan_p->an_ssidlist.an_ssid1,
+		sizeof (pcan_p->an_ssidlist.an_ssid1));
+	for (i = 0; i < MAX_NWEPKEYS; i++) {
+		pcan_p->an_wepkey[i].an_index = 0xffff;
+		bzero(pcan_p->an_wepkey[i].an_key,
+		    sizeof (pcan_p->an_wepkey[i].an_key));
+		pcan_p->an_wepkey[i].an_keylen = 0;
+		bzero(pcan_p->an_wepkey[i].an_macaddr,
+		    sizeof (pcan_p->an_wepkey[i].an_macaddr));
+		pcan_p->an_wepkey[i].an_macaddr[0] = 1;
+	}
+	pcan_p->an_cur_wepkey = 0;
+
+	pcan_p->pcan_usewep = 0;
+	pcan_p->an_config.an_opmode = AN_OPMODE_INFR_STATION;
+	pcan_p->an_config.an_authtype = AN_AUTHTYPE_OPEN;
+	pcan_p->an_config.an_stationary = 1;
+	pcan_p->an_config.an_max_beacon_lost_time = 0xffff;
+	i = pcan_config_mac(pcan_p);
+
+	return (i);
+}
+
+static int
+pcan_init_nicmem(pcan_maci_t *pcan_p)
+{
+	int i;
+	uint16_t ret;
+	pcan_txring_t *ring_p = &pcan_p->pcan_txring;
+
+	for (i = 0; i < AN_TX_RING_CNT; i++) {
+		uint16_t rc;
+		ret = pcan_alloc_nicmem(pcan_p, PCAN_NICMEM_SZ, &rc);
+		if (ret) {
+			cmn_err(CE_WARN, "pcan alloc NIC Tx buf[%x]: failed "
+			    "%x\n", i, ret);
+			return (DDI_FAILURE);
+		}
+		ring_p->an_tx_fids[i] = rc;
+		ring_p->an_tx_ring[i] = 0;
+		PCANDBG((CE_NOTE, "pcan: NIC tx_id[%x]=%x\n", i, rc));
+	}
+	ring_p->an_tx_prod = ring_p->an_tx_cons = 0;
+	return (PCAN_SUCCESS);
+}
+
+
+
+static void
+pcan_start_locked(pcan_maci_t *pcan_p)
+{
+	pcan_p->pcan_flag |= PCAN_CARD_INTREN;
+	PCAN_ENABLE_INTR(pcan_p);
+}
+
+static void
+pcan_stop_locked(pcan_maci_t *pcan_p)
+{
+	PCAN_DISABLE_INTR_CLEAR(pcan_p);
+	pcan_p->pcan_flag &= ~PCAN_CARD_INTREN;
+}
+
+/*
+ * for scan result
+ */
+static int
+pcan_add_scan_item(pcan_maci_t *pcan_p, struct an_ltv_scanresult s)
+{
+	an_scan_list_t *scan_item;
+
+	scan_item = kmem_zalloc(sizeof (an_scan_list_t), KM_SLEEP);
+	if (scan_item == NULL) {
+		cmn_err(CE_WARN, "pcan add_scan_item: zalloc failed\n");
+		return (PCAN_FAIL);
+	}
+	scan_item->an_val = s;
+	scan_item->an_timeout = AN_SCAN_TIMEOUT_MAX;
+	list_insert_tail(&pcan_p->an_scan_list, scan_item);
+	pcan_p->an_scan_num++;
+	return (PCAN_SUCCESS);
+}
+
+static void
+pcan_delete_scan_item(pcan_maci_t *pcan_p, an_scan_list_t *s)
+{
+	list_remove(&pcan_p->an_scan_list, s);
+	kmem_free(s, sizeof (*s));
+	pcan_p->an_scan_num--;
+}
+
+static void
+pcan_scanlist_timeout(void *arg)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	an_scan_list_t *scan_item0, *scan_item1;
+
+	mutex_enter(&pcan_p->pcan_scanlist_lock);
+	scan_item0 = list_head(&pcan_p->an_scan_list);
+	for (; scan_item0; ) {
+		PCANDBG((CE_NOTE, "pcan scanlist: ssid = %s\n",
+		    scan_item0->an_val.an_ssid));
+		PCANDBG((CE_NOTE, "pcan scanlist: timeout left: %ds",
+		    scan_item0->an_timeout));
+		scan_item1 = list_next(&pcan_p->an_scan_list, scan_item0);
+		if (scan_item0->an_timeout == 0) {
+			pcan_delete_scan_item(pcan_p, scan_item0);
+		} else {
+			scan_item0->an_timeout--;
+		}
+		scan_item0 = scan_item1;
+	}
+	mutex_exit(&pcan_p->pcan_scanlist_lock);
+	pcan_p->an_scanlist_timeout_id = timeout(pcan_scanlist_timeout,
+	    pcan_p, drv_usectohz(1000000));
+}
+
+
+/*
+ * for wificonfig and dlamd ioctl
+ */
+static int
+pcan_cfg_essid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	char *value;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_status *status_p;
+	struct an_ltv_ssidlist *ssidlist_p;
+
+	status_p = &pcan_p->an_status;
+	ssidlist_p = &pcan_p->an_ssidlist;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_essid: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    offsetof(wl_essid_t, wl_essid_essid) +
+		    status_p->an_ssidlen;
+		((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
+		    status_p->an_ssidlen;
+		bcopy(status_p->an_ssid, buf + WIFI_BUF_OFFSET +
+		    offsetof(wl_essid_t, wl_essid_essid),
+		    status_p->an_ssidlen);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		bzero(ssidlist_p, sizeof (*ssidlist_p));
+		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
+		(void) strncpy(ssidlist_p->an_ssid1, value,
+		    MIN(32, strlen(value)));
+		ssidlist_p->an_ssid1_len = strlen(value);
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++) {
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	}
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_bssid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *infp;
+	wldp_t *outfp;
+	char *buf;
+	wl_bssid_t *value;
+	int iret;
+	struct an_ltv_status *status_p;
+	struct an_ltv_aplist *aplist_p;
+
+	status_p = &pcan_p->an_status;
+	aplist_p = &pcan_p->an_aplist;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_bssid: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+
+		bcopy(status_p->an_cur_bssid, buf + WIFI_BUF_OFFSET,
+		    sizeof (wl_bssid_t));
+		outfp->wldp_result = WL_SUCCESS;
+		PCANDBG((CE_CONT,
+		    "pcan: cfg_bssid: bssid=%x %x %x %x %x %x\n",
+		    status_p->an_cur_bssid[0],
+		    status_p->an_cur_bssid[1],
+		    status_p->an_cur_bssid[2],
+		    status_p->an_cur_bssid[3],
+		    status_p->an_cur_bssid[4],
+		    status_p->an_cur_bssid[5]));
+	} else if (cmd == WLAN_SET_PARAM) {
+		value = (wl_bssid_t *)(infp->wldp_buf);
+		(void) strncpy((char *)aplist_p->an_ap1, (char *)value, 6);
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++) {
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	}
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cmd_scan(pcan_maci_t *pcan_p)
+{
+	uint16_t i = 0, j, ret = WL_SUCCESS;
+	uint8_t	bssid_t[6];
+	uint32_t check_num, enable;
+	an_scan_list_t *scan_item0;
+
+	enable = pcan_p->pcan_flag & PCAN_ENABLED;
+	if ((!enable) &&
+	    (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0))) {
+		ret = (int)WL_HW_ERROR;
+		goto exit;
+	}
+	if (ret = pcan_set_cmd(pcan_p, AN_CMD_SCAN, 0)) {
+		ret = (int)WL_HW_ERROR;
+		goto exit;
+	}
+
+	pcan_delay(pcan_p, 500000);
+	ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
+	    pcan_p, AN_RID_ESSIDLIST_FIRST, &pcan_p->an_scanresult[i]);
+	if ((ret) || pcan_p->an_scanresult[i].an_index == 0xffff) {
+		goto done;
+	}
+	do
+	{
+		i++;
+		ret =  pcan_scanresult_ltv(PCAN_READ_LTV,
+		    pcan_p, AN_RID_ESSIDLIST_NEXT, &pcan_p->an_scanresult[i]);
+	} while ((!ret) && (i < 32) &&
+	    (pcan_p->an_scanresult[i].an_index != 0xffff));
+done:
+	if ((!enable) &&
+	    (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))) {
+		ret = (int)WL_HW_ERROR;
+		goto exit;
+	}
+	/* record the scan result for future use */
+	bzero(bssid_t, sizeof (bssid_t));
+	for (j = 0; j < i; j++) {
+		/*
+		 * sometimes, those empty items are recorded by hardware,
+		 * this is wrong, just ignore those items here.
+		 */
+		if (bcmp(pcan_p->an_scanresult[j].an_bssid,
+		    bssid_t, 6) == 0) {
+			continue;
+		}
+		/*
+		 * save/update the scan item in scanlist
+		 */
+		mutex_enter(&pcan_p->pcan_scanlist_lock);
+		check_num = 0;
+		scan_item0 = list_head(&pcan_p->an_scan_list);
+		if (scan_item0 == NULL) {
+			if (pcan_add_scan_item(pcan_p,
+			    pcan_p->an_scanresult[j]) != 0) {
+				mutex_exit(&pcan_p->pcan_scanlist_lock);
+				return (WL_SUCCESS);
+			}
+		}
+		for (; scan_item0; ) {
+			if (bcmp(pcan_p->an_scanresult[j].an_bssid,
+			    scan_item0->an_val.an_bssid, 6) == 0) {
+				scan_item0->an_val = pcan_p->an_scanresult[j];
+				scan_item0->an_timeout = AN_SCAN_TIMEOUT_MAX;
+				break;
+			} else {
+				check_num++;
+			}
+			scan_item0 = list_next(&pcan_p->an_scan_list,
+			    scan_item0);
+		}
+		if (check_num == pcan_p->an_scan_num) {
+			if (pcan_add_scan_item(pcan_p,
+			    pcan_p->an_scanresult[j]) != 0) {
+				mutex_exit(&pcan_p->pcan_scanlist_lock);
+				return (WL_SUCCESS);
+			}
+		}
+		mutex_exit(&pcan_p->pcan_scanlist_lock);
+	}
+exit:
+	if (ret)
+		cmn_err(CE_WARN, "pcan: scan failed due to hareware error");
+	return (ret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_scan(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	wl_ess_conf_t *p_ess_conf;
+	wldp_t *outfp;
+	char *buf;
+	uint16_t i;
+	an_scan_list_t *scan_item;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_scanlist: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	mutex_enter(&pcan_p->pcan_scanlist_lock);
+	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
+	    pcan_p->an_scan_num;
+	outfp->wldp_length = WIFI_BUF_OFFSET +
+	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
+	    pcan_p->an_scan_num * sizeof (wl_ess_conf_t);
+
+	scan_item = list_head(&pcan_p->an_scan_list);
+	for (i = 0; i < pcan_p->an_scan_num; i++) {
+		if (!scan_item)
+			goto done;
+
+		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
+		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
+		    i * sizeof (wl_ess_conf_t));
+		bcopy(scan_item->an_val.an_ssid,
+		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
+		    mi_strlen(scan_item->an_val.an_ssid));
+		bcopy(scan_item->an_val.an_bssid,
+		    p_ess_conf->wl_ess_conf_bssid, 6);
+		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
+		    = WL_DSSS;
+		p_ess_conf->wl_ess_conf_wepenabled =
+		    (scan_item->an_val.an_cap & 0x10 ?
+		    WL_ENC_WEP : WL_NOENCRYPTION);
+		p_ess_conf->wl_ess_conf_bsstype =
+		    (scan_item->an_val.an_cap & 0x1 ?
+		    WL_BSS_BSS : WL_BSS_IBSS);
+		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
+		    scan_item->an_val.an_dschannel;
+		p_ess_conf->wl_ess_conf_sl = 15 -
+		    ((scan_item->an_val.an_rssi & 0xff) * 15 / 128);
+		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
+		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
+		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
+		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
+		scan_item = list_next(&pcan_p->an_scan_list, scan_item);
+	}
+done:
+	mutex_exit(&pcan_p->pcan_scanlist_lock);
+	outfp->wldp_result = WL_SUCCESS;
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (WL_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_linkstatus(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	wldp_t *outfp;
+	char *buf;
+	uint16_t i;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_linkstatus: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (pcan_p->pcan_flag & PCAN_CARD_LINKUP)
+		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
+	else
+		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
+	outfp->wldp_result = WL_SUCCESS;
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (WL_SUCCESS);
+}
+
+static int
+pcan_cfg_bsstype(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *cfg_p;
+
+	cfg_p = &pcan_p->an_config;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_bsstype: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (cfg_p->an_opmode == AN_OPMODE_INFR_STATION) {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_BSS;
+		} else if (cfg_p->an_opmode == AN_OPMODE_IBSS_ADHOC) {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_BSS_IBSS;
+		}
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_BSS)
+			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
+		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_IBSS)
+			cfg_p->an_opmode = AN_OPMODE_IBSS_ADHOC;
+		if (*(wl_bss_type_t *)(infp->wldp_buf) == WL_BSS_ANY)
+			cfg_p->an_opmode = AN_OPMODE_INFR_STATION;
+		cfg_p->an_assoc_timeout = 5000;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_phy(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t ret, i;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *cfg_p;
+	struct an_ltv_status *status_p;
+
+	cfg_p = &pcan_p->an_config;
+	status_p = &pcan_p->an_status;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_phy: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
+	if (cmd == WLAN_GET_PARAM) {
+		if (ret = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel =
+		    status_p->an_channel_set;
+		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)
+		    (((wl_phy_conf_t *)(infp->wldp_buf))
+		    ->wl_phy_dsss_conf.wl_dsss_channel);
+		if (ret < 1 || ret > 14) {
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		cfg_p->an_ds_channel = ret;
+		cfg_p->an_assoc_timeout = 5000;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_desiredrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	uint8_t rates = 0;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *cfg_p;
+	struct an_ltv_genconfig *actcfg_p;
+
+	cfg_p = &pcan_p->an_config;
+	actcfg_p = &pcan_p->an_actual_config;
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_rates: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		for (i = 0; i < sizeof (actcfg_p->an_rates); i++) {
+			if (actcfg_p->an_rates[i] == 0)
+				break;
+			rates = MAX(rates, actcfg_p->an_rates[i]);
+		}
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
+		    = rates;
+		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    offsetof(wl_rates_t, wl_rates_rates) + sizeof (char);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		bzero(cfg_p->an_rates, sizeof (cfg_p->an_rates));
+		for (i = 0; i < ((wl_rates_t *)(infp->wldp_buf))->wl_rates_num;
+		    i++) {
+			cfg_p->an_rates[i] = (((wl_rates_t *)
+			    (infp->wldp_buf))->wl_rates_rates)[i];
+		}
+		cfg_p->an_assoc_timeout = 5000;
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_supportrates(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_supportedrates: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
+		    = WL_RATE_1M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
+		    = WL_RATE_2M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
+		    = WL_RATE_5_5M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
+		    = WL_RATE_11M;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    offsetof(wl_rates_t, wl_rates_rates) +
+		    4 * sizeof (char);
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_powermode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *actcfg_p;
+
+	actcfg_p = &pcan_p->an_actual_config;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_powermode: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (pcan_cfg_ltv(PCAN_READ_LTV, pcan_p, actcfg_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
+		    actcfg_p->an_psave_mode;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    sizeof (wl_ps_mode_t);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_LACK_FEATURE;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+
+}
+
+static int
+pcan_cfg_authmode(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *cfg_p;
+	struct an_ltv_genconfig *actcfg_p;
+
+	cfg_p = &pcan_p->an_config;
+	actcfg_p = &pcan_p->an_actual_config;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_autymode: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
+		if (cfg_p->an_authtype & AN_AUTHTYPE_SHAREDKEY) {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_SHAREDKEY;
+		} else {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_OPENSYSTEM;
+		}
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_OPENSYSTEM) {
+			cfg_p->an_authtype |= AN_AUTHTYPE_OPEN;
+			cfg_p->an_assoc_timeout = 5000;
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_SUCCESS;
+		} else {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_LACK_FEATURE;
+		}
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.authmode=%x",
+	    actcfg_p->an_authtype));
+	PCANDBG((CE_NOTE, "pcan cfg_authmode: actual.home_product=%x",
+	    actcfg_p->an_rsvd6[2]));
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_encryption(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_genconfig *cfg_p;
+
+	cfg_p = &pcan_p->an_config;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_encryption: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
+		if (cfg_p->an_authtype & AN_AUTHTYPE_ENABLEWEP) {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_ENC_WEP;
+		} else {
+			*(wl_bss_type_t *)(outfp->wldp_buf) = WL_NOENCRYPTION;
+		}
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		if (*(wl_encryption_t *)(outfp->wldp_buf) == WL_ENC_WEP) {
+			cfg_p->an_authtype |= (AN_AUTHTYPE_ENABLEWEP |
+			    AN_AUTHTYPE_ALLOW_UNENCRYPTED);
+			pcan_p->pcan_usewep = 1;
+		}
+		if (*(wl_authmode_t *)(outfp->wldp_buf) == WL_NOENCRYPTION) {
+			cfg_p->an_authtype &= (~(AN_AUTHTYPE_ENABLEWEP |
+			    AN_AUTHTYPE_ALLOW_UNENCRYPTED));
+			pcan_p->pcan_usewep = 0;
+		}
+		cfg_p->an_assoc_timeout = 5000;
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_wepkeyid(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	struct an_ltv_wepkey wepkey;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_wepkeyid: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	if (cmd == WLAN_GET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
+		outfp->wldp_result = WL_SUCCESS;
+		*(wl_wep_key_id_t *)(outfp->wldp_buf) = pcan_p->an_cur_wepkey;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
+		if (ret > 3) {
+			kmem_free(buf, MAX_BUF_LEN);
+			return (EINVAL);
+		}
+		wepkey.an_index = 0xffff;
+		wepkey.an_macaddr[0] = ret & 0xff;
+		pcan_p->an_cur_wepkey = ret;
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_createibss(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_createibss: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
+	outfp->wldp_result = WL_LACK_FEATURE;
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_rssi(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i, val;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+	struct an_ltv_status *status_p;
+	status_p = &pcan_p->an_status;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_rssi: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (val = pcan_status_ltv(PCAN_READ_LTV, pcan_p, status_p)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		val = status_p->an_cur_signal_quality;
+		PCANDBG((CE_NOTE, "pcan cfg_rssi: sl=%x", val));
+		/*
+		 * we reflect the value to 1-15 as rssi
+		 */
+		*(wl_rssi_t *)(outfp->wldp_buf) = 15 -
+		    ((val & 0xff) * 15 / 128 + 1);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_result = WL_READONLY;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcan_cfg_radio(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_radio: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
+		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_LACK_FEATURE;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcan_cfg_wepkey(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	uint16_t i;
+	wl_wep_key_t *p_wepkey_tab;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	wldp_t	*infp;
+	struct an_ltv_wepkey *wepkey_p;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCANDBG((CE_NOTE, "pcan cfg_wep: failed to alloc "
+		    "memory(%d)\n", MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+
+	if (cmd == WLAN_GET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    sizeof (wl_wep_key_tab_t);
+		outfp->wldp_result = WL_WRITEONLY;
+	} else if (cmd == WLAN_SET_PARAM) {
+		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
+		for (i = 0; i < MAX_NWEPKEYS; i++) {
+			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
+				wepkey_p = &pcan_p->an_wepkey[i];
+				bzero(wepkey_p, sizeof (*wepkey_p));
+				wepkey_p->an_keylen =
+					p_wepkey_tab[i].wl_wep_length;
+				bcopy(p_wepkey_tab[i].wl_wep_key,
+					wepkey_p->an_key,
+					p_wepkey_tab[i].wl_wep_length);
+				wepkey_p->an_index = i;
+				wepkey_p->an_macaddr[0] = 1;
+			}
+		}
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static void
+pcan_connect_timeout(void *arg)
+{
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	uint16_t ret;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0))
+		goto done;
+	pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
+	if (ret = pcan_config_mac(pcan_p))
+		goto done;
+	ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0);
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcan: connect failed due to hareware error");
+	mutex_exit(&pcan_p->pcan_glock);
+	pcan_p->pcan_connect_timeout_id = 0;
+}
+
+static int
+pcan_getset(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd)
+{
+	int ret = WL_SUCCESS;
+	int connect = 0;
+
+	mutex_enter(&pcan_p->pcan_glock);
+	if (!(pcan_p->pcan_flag & PCAN_CARD_READY)) {
+		mutex_exit(&pcan_p->pcan_glock);
+		return (PCAN_FAIL);
+	}
+
+	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
+	case WL_ESSID:
+		ret = pcan_cfg_essid(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_essid\n"));
+		break;
+	case WL_BSSID:
+		ret = pcan_cfg_bssid(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_bssid\n"));
+		break;
+	case WL_ESS_LIST:
+		ret = pcan_cfg_scan(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_scan\n"));
+		break;
+	case WL_LINKSTATUS:
+		ret = pcan_cfg_linkstatus(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_linkstatus\n"));
+		break;
+	case WL_BSS_TYPE:
+		ret = pcan_cfg_bsstype(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_bsstype\n"));
+		break;
+	case WL_PHY_CONFIG:
+		ret = pcan_cfg_phy(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_phy\n"));
+		break;
+	case WL_DESIRED_RATES:
+		ret = pcan_cfg_desiredrates(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_disred-rates\n"));
+		break;
+	case WL_SUPPORTED_RATES:
+		ret = pcan_cfg_supportrates(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_supported-rates\n"));
+		break;
+	case WL_POWER_MODE:
+		ret = pcan_cfg_powermode(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_powermode\n"));
+		break;
+	case WL_AUTH_MODE:
+		ret = pcan_cfg_authmode(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_authmode\n"));
+		break;
+	case WL_ENCRYPTION:
+		ret = pcan_cfg_encryption(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_encryption\n"));
+		break;
+	case WL_WEP_KEY_ID:
+		ret = pcan_cfg_wepkeyid(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_wepkeyid\n"));
+		break;
+	case WL_CREATE_IBSS:
+		ret = pcan_cfg_createibss(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_create-ibss\n"));
+		break;
+	case WL_RSSI:
+		ret = pcan_cfg_rssi(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_rssi\n"));
+		break;
+	case WL_RADIO:
+		ret = pcan_cfg_radio(mp, pcan_p, cmd);
+		PCANDBG((CE_NOTE, "cfg_radio\n"));
+		break;
+	case WL_WEP_KEY_TAB:
+		ret = pcan_cfg_wepkey(mp, pcan_p, cmd);
+		connect = 1;
+		PCANDBG((CE_NOTE, "cfg_wepkey\n"));
+		break;
+	case WL_SCAN:
+		mutex_exit(&pcan_p->pcan_glock);
+		if (pcan_p->pcan_connect_timeout_id != 0) {
+			(void) untimeout(pcan_p->pcan_connect_timeout_id);
+			pcan_p->pcan_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcan_p->pcan_glock);
+		ret = pcan_cmd_scan(pcan_p);
+		/*
+		 * a trick here.
+		 * since the scan doesn't return too many items due to hardware
+		 * reason, so the current scan result is an accumulation of
+		 * several scans. For the first time or after many of the items
+		 * aged, we scan again if too few items now in the scan table.
+		 */
+		if (pcan_p->an_scan_num < AN_SCAN_AGAIN_THRESHOLD)
+			ret = pcan_cmd_scan(pcan_p);
+		break;
+	case WL_LOAD_DEFAULTS:
+		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcan_loaddef(pcan_p)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCANDBG((CE_NOTE, "loaddef\n"));
+		break;
+	case WL_DISASSOCIATE:
+		mutex_exit(&pcan_p->pcan_glock);
+		if (pcan_p->pcan_connect_timeout_id != 0) {
+			(void) untimeout(pcan_p->pcan_connect_timeout_id);
+			pcan_p->pcan_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcan_p->pcan_glock);
+		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
+		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcan_loaddef(pcan_p)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCANDBG((CE_NOTE, "disassociate\n"));
+		break;
+	case WL_REASSOCIATE:
+	case WL_ASSOCIAT:
+		mutex_exit(&pcan_p->pcan_glock);
+		if (pcan_p->pcan_connect_timeout_id != 0) {
+			(void) untimeout(pcan_p->pcan_connect_timeout_id);
+			pcan_p->pcan_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcan_p->pcan_glock);
+		if (ret = pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
+		if (ret = pcan_config_mac(pcan_p)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcan_set_cmd(pcan_p, AN_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCANDBG((CE_NOTE, "associate"));
+		break;
+
+	default:
+		break;
+	}
+	mutex_exit(&pcan_p->pcan_glock);
+	if ((cmd == WLAN_SET_PARAM) && (ret == WL_SUCCESS) && (connect)) {
+		pcan_p->pcan_flag &= ~PCAN_CARD_LINKUP;
+		(void) pcan_set_cmd(pcan_p, AN_CMD_DISABLE, 0);
+		if (pcan_p->pcan_connect_timeout_id != 0) {
+			(void) untimeout(pcan_p->pcan_connect_timeout_id);
+			pcan_p->pcan_connect_timeout_id = 0;
+		}
+		pcan_p->pcan_connect_timeout_id = timeout(pcan_connect_timeout,
+		    pcan_p, drv_usectohz(1000000));
+	}
+	return (ret);
+}
+
+static void
+pcan_wlan_ioctl(pcan_maci_t *pcan_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
+{
+
+	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
+	uint32_t len, ret;
+	mblk_t	*mp1;
+
+	/* sanity check */
+	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
+		miocnak(wq, mp, 0, EINVAL);
+		return;
+	}
+
+	/* assuming single data block */
+	if (mp1->b_cont) {
+		freemsg(mp1->b_cont);
+		mp1->b_cont = NULL;
+	}
+
+	/* we will overwrite everything */
+	mp1->b_wptr = mp1->b_rptr;
+
+	ret = pcan_getset(mp1, pcan_p, cmd);
+	len = msgdsize(mp1);
+	miocack(wq, mp, len, ret);
+}
+
+static void
+pcan_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+	struct iocblk *iocp;
+	uint32_t cmd, ret;
+	pcan_maci_t *pcan_p = (pcan_maci_t *)arg;
+	boolean_t need_privilege = B_TRUE;
+
+	iocp = (struct iocblk *)mp->b_rptr;
+	iocp->ioc_error = 0;
+	cmd = iocp->ioc_cmd;
+	switch (cmd) {
+	default:
+		miocnak(wq, mp, 0, EINVAL);
+		return;
+	case WLAN_GET_PARAM:
+		need_privilege = B_FALSE;
+		break;
+	case WLAN_SET_PARAM:
+	case WLAN_COMMAND:
+		break;
+	}
+
+	/* Check net_config privilege */
+	if (need_privilege) {
+		if (ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) {
+			miocnak(wq, mp, 0, ret);
+			return;
+		}
+	}
+
+	pcan_wlan_ioctl(pcan_p, wq, mp, cmd);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcan/pcan.h	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,1388 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999
+ *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef _SYS_PCAN_H
+#define	_SYS_PCAN_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	AN_TIMEOUT	600000
+
+/*
+ * Size of Aironet I/O space.
+ */
+#define	AN_IOSIZ		0x40
+
+#define	PCAN_DEVICE_PCI		0x100
+#define	PCAN_DEVICE_PCCARD	0x200
+
+/*
+ * Hermes register definitions and what little I know about them.
+ */
+
+/*
+ * Hermes command/status registers.
+ */
+#define	AN_COMMAND(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x00 : 0x00)
+#define	AN_PARAM0(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x04 : 0x02)
+#define	AN_PARAM1(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x08 : 0x04)
+#define	AN_PARAM2(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x0c : 0x06)
+#define	AN_STATUS(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x10 : 0x08)
+#define	AN_RESP0(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x14 : 0x0A)
+#define	AN_RESP1(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x18 : 0x0C)
+#define	AN_RESP2(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x1c : 0x0E)
+#define	AN_LINKSTAT(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x20 : 0x10)
+
+/*
+ * Command register
+ */
+#define	AN_CMD_BUSY		0x8000 /* busy bit */
+#define	AN_CMD_NO_ACK		0x0080 /* don't acknowledge command */
+#define	AN_CMD_CODE_MASK	0x003F
+#define	AN_CMD_QUAL_MASK	0x7F00
+
+/*
+ * Command codes
+ */
+#define	AN_CMD_NOOP		0x0000 /* no-op */
+#define	AN_CMD_ENABLE		0x0001 /* enable */
+#define	AN_CMD_DISABLE		0x0002 /* disable */
+#define	AN_CMD_FORCE_SYNCLOSS	0x0003 /* force loss of sync */
+#define	AN_CMD_FW_RESTART	0x0004 /* firmware resrart */
+#define	AN_CMD_HOST_SLEEP	0x0005
+#define	AN_CMD_MAGIC_PKT	0x0006
+#define	AN_CMD_READCFG		0x0008
+#define	AN_CMD_ALLOC_MEM	0x000A /* allocate NIC memory */
+#define	AN_CMD_TX		0x000B /* transmit */
+#define	AN_CMD_DEALLOC_MEM	0x000C
+#define	AN_CMD_NOOP2		0x0010
+#define	AN_CMD_ALLOC_DESC	0x0020
+#define	AN_CMD_ACCESS		0x0021
+#define	AN_CMD_ALLOC_BUF	0x0028
+#define	AN_CMD_PSP_NODES	0x0030
+#define	AN_CMD_SET_PHYREG	0x003E
+#define	AN_CMD_TX_TEST		0x003F
+#define	AN_CMD_SLEEP		0x0085
+#define	AN_CMD_SCAN		0x0103
+#define	AN_CMD_SAVECFG		0x0108
+
+/*
+ * Reclaim qualifier bit, applicable to the
+ * TX command.
+ */
+#define	AN_RECLAIM		0x0100 /* reclaim NIC memory */
+
+/*
+ * MPI 350 DMA descriptor information
+ */
+#define	AN_DESCRIPTOR_TX	0x01
+#define	AN_DESCRIPTOR_RX	0x02
+#define	AN_DESCRIPTOR_TXCMP	0x04
+#define	AN_DESCRIPTOR_HOSTWRITE 0x08
+#define	AN_DESCRIPTOR_HOSTREAD  0x10
+#define	AN_DESCRIPTOR_HOSTRW    0x20
+
+#define	AN_MAX_RX_DESC 1
+#define	AN_MAX_TX_DESC 1
+#define	AN_HOSTBUFSIZ 1840
+
+/*
+ * dma descriptor definition for miniPci card.
+ * the miniPci card only works on x86.
+ */
+struct an_card_rid_desc
+{
+	uint32_t	an_rid:16;
+	uint32_t	an_len:15;
+	uint32_t	an_valid:1;
+	uint64_t	an_phys;
+};
+
+struct an_card_rx_desc
+{
+	uint32_t	an_ctrl:15;
+	uint32_t	an_done:1;
+	uint32_t	an_len:15;
+	uint32_t	an_valid:1;
+	uint64_t	an_phys;
+};
+
+struct an_card_tx_desc
+{
+	uint32_t	an_offset:15;
+	uint32_t	an_eoc:1;
+	uint32_t	an_len:15;
+	uint32_t	an_valid:1;
+	uint64_t	an_phys;
+};
+
+#define	AN_MAX_DATALEN	4096
+#define	AN_RID_BUFFER_SIZE	AN_MAX_DATALEN
+#define	AN_RX_BUFFER_SIZE	AN_HOSTBUFSIZ
+#define	AN_TX_BUFFER_SIZE	AN_HOSTBUFSIZ
+#define	AN_HOST_DESC_OFFSET	0x800
+#define	AN_RX_DESC_OFFSET  (AN_HOST_DESC_OFFSET + \
+    sizeof (struct an_card_rid_desc))
+#define	AN_TX_DESC_OFFSET (AN_RX_DESC_OFFSET + \
+	(AN_MAX_RX_DESC * sizeof (struct an_card_rx_desc)))
+
+/*
+ * ACCESS command qualifier bits.
+ */
+#define	AN_ACCESS_READ		0x0000
+#define	AN_ACCESS_WRITE		0x0100
+
+/*
+ * PROGRAM command qualifier bits.
+ */
+#define	AN_PROGRAM_DISABLE	0x0000
+#define	AN_PROGRAM_ENABLE_RAM	0x0100
+#define	AN_PROGRAM_ENABLE_NVRAM	0x0200
+#define	AN_PROGRAM_NVRAM	0x0300
+
+/*
+ * Status register values
+ */
+#define	AN_STAT_CMD_CODE	0x003F
+#define	AN_STAT_CMD_RESULT	0x7F00
+
+/*
+ * Linkstat register
+ */
+#define	AN_LINKSTAT_ASSOCIATED		0x0400
+#define	AN_LINKSTAT_AUTHFAIL		0x0300
+#define	AN_LINKSTAT_ASSOC_FAIL		0x8400	/* (low byte is reason code) */
+#define	AN_LINKSTAT_DISASSOC		0x8200	/* (low byte is reason code) */
+#define	AN_LINKSTAT_DEAUTH		0x8100	/* (low byte is reason code) */
+#define	AN_LINKSTAT_SYNCLOST_TSF	0x8004
+#define	AN_LINKSTAT_SYNCLOST_HOSTREQ	0x8003
+#define	AN_LINKSTAT_SYNCLOST_AVGRETRY	0x8002
+#define	AN_LINKSTAT_SYNCLOST_MAXRETRY	0x8001
+#define	AN_LINKSTAT_SYNCLOST_MISSBEACON	0x8000
+
+/*
+ * Link stat low byte reason code
+ */
+#define	AN_LINKSTAT_RC_RESERVED		0 /* Reserved return code */
+#define	AN_LINKSTAT_RC_NOREASON		1 /* Unspecified reason */
+#define	AN_LINKSTAT_RC_AUTHINV		2 /* Prev auth invalid */
+#define	AN_LINKSTAT_RC_DEAUTH		3 /* Deauth due sender leaving */
+#define	AN_LINKSTAT_RC_NOACT		4 /* Disassociated due inactivity */
+#define	AN_LINKSTAT_RC_MAXLOAD		5 /* Disassociated due 2many stations */
+/*
+ * Class 2 frame received from non-Authenticated station
+ */
+#define	AN_LINKSTAT_RC_BADCLASS2	6
+/*
+ * Class 3 frame received from non-Associated station
+ */
+#define	AN_LINKSTAT_RC_BADCLASS3	7
+/*
+ * Disassociated because sending station is leaving BSS
+ */
+#define	AN_LINKSTAT_RC_STATLEAVE	8
+/*
+ * Station requesting (Re)Association not Authenticated w/responding station
+ */
+#define	AN_LINKSTAT_RC_NOAUTH		9
+
+/*
+ * memory handle management registers
+ */
+#define	AN_RX_FID		0x20
+#define	AN_ALLOC_FID		0x22
+#define	AN_TX_CMP_FID(p) \
+	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x1a : 0x24)
+
+#define	AN_INVALID_FID		0xffff  /* invalid fid value */
+
+/*
+ * Buffer Access Path (BAP) registers.
+ * These are I/O channels. I believe you can use each one for
+ * any desired purpose independently of the other. In general
+ * though, we use BAP1 for reading and writing LTV records and
+ * reading received data frames, and BAP0 for writing transmit
+ * frames. This is a convention though, not a rule.
+ */
+#define	AN_SEL0			0x18
+#define	AN_SEL1			0x1A
+#define	AN_OFF0			0x1C
+#define	AN_OFF1			0x1E
+#define	AN_DATA0		0x36
+#define	AN_DATA1		0x38
+#define	AN_BAP0			AN_DATA0
+#define	AN_BAP1			AN_DATA1
+
+#define	AN_OFF_BUSY		0x8000
+#define	AN_OFF_ERR		0x4000
+#define	AN_OFF_DONE		0x2000
+#define	AN_OFF_DATAOFF		0x0FFF
+
+/*
+ * Event registers
+ */
+#define	AN_EVENT_STAT(p) (p->pcan_device_type == PCAN_DEVICE_PCI ? 0x60 : 0x30)
+/*
+ * Interrupt enable/disable
+ */
+#define	AN_INT_EN(p) (p->pcan_device_type == PCAN_DEVICE_PCI ? 0x64 : 0x32)
+#define	AN_EVENT_ACK(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x68 : 0x34)
+
+/*
+ * Events - AN_EVENT_ACK register only
+ */
+#define	AN_EV_CLR_STUCK_BUSY	0x4000	/* clear stuck busy bit */
+#define	AN_EV_WAKEREQUEST	0x2000	/* awaken from PSP mode */
+					/* Events shared by all 3 event regs: */
+#define	AN_EV_MIC		0x1000	/* Message Integrity Check */
+#define	AN_EV_AWAKE		0x0100	/* station woke up from PSP mode */
+#define	AN_EV_LINKSTAT		0x0080	/* link status available */
+#define	AN_EV_CMD		0x0010	/* command completed */
+#define	AN_EV_ALLOC		0x0008	/* async alloc/reclaim completed */
+#define	AN_EV_TX_EXC		0x0004	/* async xmit completed with failure */
+#define	AN_EV_TX		0x0002	/* async xmit completed succesfully */
+#define	AN_EV_RX		0x0001	/* async rx completed */
+#define	AN_EV_TX_CPY		0x0400
+
+#define	AN_EV_ALL		0xffff	/* all events */
+#define	AN_INTRS(p) \
+	(p->pcan_device_type == PCAN_DEVICE_PCI ? \
+	(AN_EV_RX|AN_EV_TX|AN_EV_TX_EXC|AN_EV_ALLOC|AN_EV_LINKSTAT|AN_EV_MIC \
+	|AN_EV_TX_CPY) : \
+	(AN_EV_RX|AN_EV_TX|AN_EV_TX_EXC|AN_EV_ALLOC|AN_EV_LINKSTAT|AN_EV_MIC))
+
+/*
+ * Host software registers
+ */
+#define	AN_SW0(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x50 : 0x28)
+#define	AN_SW1(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x54 : 0x2A)
+#define	AN_SW2(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x58 : 0x2C)
+#define	AN_SW3(p)	(p->pcan_device_type == PCAN_DEVICE_PCI ? 0x5c : 0x2E)
+
+#define	AN_CNTL			0x14
+
+#define	AN_CNTL_AUX_ENA		0xC000
+#define	AN_CNTL_AUX_ENA_STAT	0xC000
+#define	AN_CNTL_AUX_DIS_STAT	0x0000
+#define	AN_CNTL_AUX_ENA_CNTL	0x8000
+#define	AN_CNTL_AUX_DIS_CNTL	0x4000
+
+#define	AN_AUX_PAGE		0x3A
+#define	AN_AUX_OFFSET		0x3C
+#define	AN_AUX_DATA		0x3E
+
+struct an_ltv_gen {
+	uint16_t	an_len;
+	uint16_t	an_type;
+	uint16_t	an_val;
+};
+
+/*
+ * General configuration information.
+ */
+#define	AN_RID_GENCONFIG	0xFF10
+struct an_ltv_genconfig {
+	uint16_t	an_len;			/* 0x00 */
+	uint16_t	an_opmode;		/* 0x02 */
+	uint16_t	an_rxmode;		/* 0x04 */
+	uint16_t	an_fragthresh;		/* 0x06 */
+	uint16_t	an_rtsthresh;		/* 0x08 */
+	uint8_t		an_macaddr[6];		/* 0x0A */
+	uint8_t		an_rates[8];		/* 0x10 */
+	uint16_t	an_shortretry_limit;	/* 0x18 */
+	uint16_t	an_longretry_limit;	/* 0x1A */
+	uint16_t	an_tx_msdu_lifetime;	/* 0x1C */
+	uint16_t	an_rx_msdu_lifetime;	/* 0x1E */
+	uint16_t	an_stationary;		/* 0x20 */
+	uint16_t	an_ordering;		/* 0x22 */
+	uint16_t	an_devtype;		/* 0x24 */
+	uint16_t	an_rsvd0[5];		/* 0x26 */
+	/*
+	 * Scanning associating.
+	 */
+	uint16_t	an_scanmode;		/* 0x30 */
+	uint16_t	an_probedelay;		/* 0x32 */
+	uint16_t	an_probe_energy_timeout; /* 0x34 */
+	uint16_t	an_probe_response_timeout; /* 0x36 */
+	uint16_t	an_beacon_listen_timeout; /* 0x38 */
+	uint16_t	an_ibss_join_net_timeout; /* 0x3A */
+	uint16_t	an_auth_timeout;	/* 0x3C */
+	uint16_t	an_authtype;		/* 0x3E */
+	uint16_t	an_assoc_timeout;	/* 0x40 */
+	uint16_t	an_specified_ap_timeout; /* 0x42 */
+	uint16_t	an_offline_scan_interval; /* 0x44 */
+	uint16_t	an_offline_scan_duration; /* 0x46 */
+	uint16_t	an_link_loss_delay;	/* 0x48 */
+	uint16_t	an_max_beacon_lost_time; /* 0x4A */
+	uint16_t	an_refresh_interval;	/* 0x4C */
+	uint16_t	an_rsvd1;		/* 0x4E */
+	/*
+	 * Power save operation
+	 */
+	uint16_t	an_psave_mode;		/* 0x50 */
+	uint16_t	an_sleep_for_dtims;	/* 0x52 */
+	uint16_t	an_listen_interval;	/* 0x54 */
+	uint16_t	an_fast_listen_interval; /* 0x56 */
+	uint16_t	an_listen_decay;	/* 0x58 */
+	uint16_t	an_fast_listen_decay;	/* 0x5A */
+	uint16_t	an_rsvd2[2];		/* 0x5C */
+	/*
+	 * Ad-hoc (or AP) operation.
+	 */
+	uint16_t	an_beacon_period;	/* 0x60 */
+	uint16_t	an_atim_duration;	/* 0x62 */
+	uint16_t	an_rsvd3;		/* 0x64 */
+	uint16_t	an_ds_channel;		/* 0x66 */
+	uint16_t	an_rsvd4;		/* 0x68 */
+	uint16_t	an_dtim_period;		/* 0x6A */
+	uint16_t	an_rsvd5[2];		/* 0x6C */
+	/*
+	 * Radio operation.
+	 */
+	uint16_t	an_radiotype;		/* 0x70 */
+	uint16_t	an_diversity;		/* 0x72 */
+	uint16_t	an_tx_power;		/* 0x74 */
+	uint16_t	an_rss_thresh;		/* 0x76 */
+	uint16_t	an_rsvd6[4];		/* 0x78 */
+	/*
+	 * Aironet extensions.
+	 */
+	uint8_t		an_nodename[16];	/* 0x80 */
+	uint16_t	an_arl_thresh;		/* 0x90 */
+	uint16_t	an_arl_decay;		/* 0x92 */
+	uint16_t	an_arl_delay;		/* 0x94 */
+	uint8_t		an_rsvd7;		/* 0x96 */
+	uint8_t		an_rsvd8;		/* 0x97 */
+	uint8_t		an_magic_packet_action;	/* 0x98 */
+	uint8_t		an_magic_packet_ctl;	/* 0x99 */
+	uint16_t	an_auto_wake;		/* 0x9A */
+	uint16_t	an_pad[20];
+};
+
+#define	AN_OPMODE_IBSS_ADHOC			0x0000
+#define	AN_OPMODE_INFR_STATION			0x0001
+#define	AN_OPMODE_AP				0x0002
+#define	AN_OPMODE_AP_REPEATER			0x0003
+#define	AN_OPMODE_UNMODIFIED_PAYLOAD		0x0100
+#define	AN_OPMODE_AIRONET_EXTENSIONS		0x0200
+#define	AN_OPMODE_AP_EXTENSIONS			0x0400
+
+#define	AN_RXMODE_BC_MC_ADDR			0x0000
+#define	AN_RXMODE_BC_ADDR			0x0001
+#define	AN_RXMODE_ADDR				0x0002
+#define	AN_RXMODE_80211_MONITOR_CURBSS		0x0003
+#define	AN_RXMODE_80211_MONITOR_ANYBSS		0x0004
+#define	AN_RXMODE_LAN_MONITOR_CURBSS		0x0005
+#define	AN_RXMODE_NO_8023_HEADER		0x0100
+#define	AN_RXMODE_USE_8023_HEADER		0x0000
+
+#define	AN_RATE_1MBPS				0x0002
+#define	AN_RATE_2MBPS				0x0004
+#define	AN_RATE_5_5MBPS				0x000B
+#define	AN_RATE_11MBPS				0x0016
+
+#define	AN_DEVTYPE_PC4500			0x0065
+#define	AN_DEVTYPE_PC4800			0x006D
+
+#define	AN_SCANMODE_ACTIVE			0x0000
+#define	AN_SCANMODE_PASSIVE			0x0001
+#define	AN_SCANMODE_AIRONET_ACTIVE		0x0002
+
+#define	AN_AUTHTYPE_NONE			0x0000
+#define	AN_AUTHTYPE_OPEN			0x0001
+#define	AN_AUTHTYPE_SHAREDKEY			0x0002
+#define	AN_AUTHTYPE_EXCLUDE_UNENCRYPTED		0x0004
+#define	AN_AUTHTYPE_ENABLEWEP			0x0100
+#define	AN_AUTHTYPE_ALLOW_UNENCRYPTED		0x0200
+
+#define	AN_PSAVE_NONE				0x0000
+#define	AN_PSAVE_CAM				0x0001
+#define	AN_PSAVE_PSP				0x0002
+#define	AN_PSAVE_PSP_CAM			0x0003
+
+#define	AN_RADIOTYPE_80211_FH			0x0001
+#define	AN_RADIOTYPE_80211_DS			0x0002
+#define	AN_RADIOTYPE_LM2000_DS			0x0004
+
+#define	AN_DIVERSITY_FACTORY_DEFAULT		0x0000
+#define	AN_DIVERSITY_ANTENNA_1_ONLY		0x0001
+#define	AN_DIVERSITY_ANTENNA_2_ONLY		0x0002
+#define	AN_DIVERSITY_ANTENNA_1_AND_2		0x0003
+
+#define	AN_TXPOWER_FACTORY_DEFAULT		0x0000
+#define	AN_TXPOWER_50MW				50
+#define	AN_TXPOWER_100MW			100
+#define	AN_TXPOWER_250MW			250
+
+/*
+ * Valid SSID list. You can specify up to three SSIDs denoting
+ * the service sets that you want to join. The first SSID always
+ * defaults to "tsunami" which is a handy way to detect the
+ * card.
+ */
+#define	AN_RID_SSIDLIST		0xFF11
+struct an_ltv_ssidlist {
+	uint16_t		an_len;
+	uint16_t		an_ssid1_len;
+	char			an_ssid1[32];
+	uint16_t		an_ssid2_len;
+	char			an_ssid2[32];
+	uint16_t		an_ssid3_len;
+	char			an_ssid3[32];
+	uint8_t			an_pad[748];
+};
+
+#define	AN_DEF_SSID_LEN		7
+#define	AN_DEF_SSID		"tsunami"
+
+/*
+ * Valid AP list.
+ */
+#define	AN_RID_APLIST		0xFF12
+struct an_ltv_aplist {
+	uint16_t	an_len;
+	uint8_t		an_ap1[6];
+	uint8_t		an_ap2[6];
+	uint8_t		an_ap3[6];
+	uint8_t		an_ap4[6];
+};
+
+/*
+ * Driver name.
+ */
+#define	AN_RID_DRVNAME		0xFF13
+struct an_ltv_drvname {
+	uint16_t	an_len;
+	uint8_t		an_drvname[16];
+};
+
+/*
+ * Frame encapsulation.
+ */
+#define	AN_RID_ENCAP		0xFF14
+struct an_rid_encap {
+	uint16_t		an_len;
+	uint16_t		an_ethertype_default;
+	uint16_t		an_action_default;
+	uint16_t		an_ethertype0;
+	uint16_t		an_action0;
+	uint16_t		an_ethertype1;
+	uint16_t		an_action1;
+	uint16_t		an_ethertype2;
+	uint16_t		an_action2;
+	uint16_t		an_ethertype3;
+	uint16_t		an_action3;
+	uint16_t		an_ethertype4;
+	uint16_t		an_action4;
+	uint16_t		an_ethertype5;
+	uint16_t		an_action5;
+	uint16_t		an_ethertype6;
+	uint16_t		an_action6;
+};
+
+#define	AN_ENCAP_ACTION_RX	0x0001
+#define	AN_ENCAP_ACTION_TX	0x0002
+
+#define	AN_RXENCAP_NONE		0x0000
+#define	AN_RXENCAP_RFC1024	0x0001
+
+#define	AN_TXENCAP_RFC1024	0x0000
+#define	AN_TXENCAP_80211	0x0002
+
+#define	AN_RID_WEPKEY		0xFF15
+#define	AN_RID_WEPKEY2		0xFF16
+struct an_ltv_wepkey {
+	uint16_t	an_len;
+	uint16_t	an_index;
+	uint8_t		an_macaddr[6];
+	uint16_t	an_keylen;	/* WEP40: 5, WEP128: 13 bytes */
+	uint8_t		an_key[16];	/* key value */
+};
+
+#define	AN_RID_CRYPT		0xFF18
+struct an_ltv_crypt {
+	uint16_t	an_operation;		/* 0: enable  1: disable */
+	uint8_t		an_optionmask[2];	/* 1: WEP40   2: WEP128 */
+	uint8_t		an_filler[8];		/* put struct 6 bytes longer */
+};
+
+/*
+ * Actual config, same structure as general config (read only).
+ */
+#define	AN_RID_ACTUALCFG	0xFF20
+
+/*
+ * Card capabilities (read only).
+ */
+#define	AN_RID_CAPABILITIES	0xFF00
+struct an_ltv_caps {
+	uint16_t	an_len;
+	uint8_t		an_oui[3];		/* 0x02 */
+	uint8_t		an_pad0;		/* 0x05 */
+	uint16_t	an_prodnum;		/* 0x06 */
+	uint8_t		an_manufname[32];	/* 0x08 */
+	uint8_t		an_prodname[16];	/* 0x28 */
+	uint8_t		an_prodvers[8];		/* 0x38 */
+	uint8_t		an_oemaddr[6];		/* 0x40 */
+	uint8_t		an_aironetaddr[6];	/* 0x46 */
+	uint16_t	an_radiotype;		/* 0x4C */
+	uint16_t	an_country;		/* 0x4E */
+	uint8_t		an_callid[6];		/* 0x50 */
+	uint8_t		an_supported_rates[8];	/* 0x56 */
+	uint8_t		an_rx_diversity;	/* 0x5E */
+	uint8_t		an_tx_diversity;	/* 0x5F */
+	uint16_t	an_tx_powerlevels[8];	/* 0x60 */
+	uint16_t	an_hwver;		/* 0x70 */
+	uint16_t	an_hwcaps;		/* 0x72 */
+	uint16_t	an_temprange;		/* 0x74 */
+	uint16_t	an_fwrev;		/* 0x76 */
+	uint16_t	an_fwsubrev;		/* 0x78 */
+	uint16_t	an_interfacerev;	/* 0x7A */
+	uint16_t	an_softcap;		/* 0x7C */
+	uint16_t	an_bootblockrev;	/* 0x7E */
+	uint16_t	an_requiredhw;		/* 0x80 */
+	uint16_t	an_pad;
+};
+
+/*
+ * Access point (read only)
+ */
+#define	AN_RID_APINFO		0xFF01
+struct an_ltv_apinfo {
+	uint16_t		an_len;
+	uint16_t		an_tim_addr;
+	uint16_t		an_airo_addr;
+};
+
+/*
+ * Radio info (read only).
+ */
+#define	AN_RID_RADIOINFO	0xFF02
+struct an_ltv_radioinfo {
+	uint16_t		an_len;
+	/*
+	 * some more fields here, waiting for freebsd code update.
+	 */
+};
+
+/*
+ * Status (read only). Note: the manual claims this RID is 108 bytes
+ * long (0x6A is the last datum, which is 2 bytes long) however when
+ * this RID is read from the NIC, it returns a length of 110. To be
+ * on the safe side, this structure is padded with an extra 16-bit
+ * word. (There is a misprint in the manual which says the macaddr
+ * field is 8 bytes long.)
+ *
+ * Also, the channel_set and current_channel fields appear to be
+ * reversed. Either that, or the hop_period field is unused.
+ */
+#define	AN_RID_STATUS		0xFF50
+struct an_ltv_status {
+	uint16_t	an_len;
+	uint8_t		an_macaddr[6];		/* 0x02 */
+	uint16_t	an_opmode;		/* 0x08 */
+	uint16_t	an_errcode;		/* 0x0A */
+	uint16_t	an_cur_signal_quality;	/* 0x0C */
+	uint16_t	an_ssidlen;		/* 0x0E */
+	uint8_t		an_ssid[32];		/* 0x10 */
+	uint8_t		an_ap_name[16];		/* 0x30 */
+	uint8_t		an_cur_bssid[6];	/* 0x40 */
+	uint8_t		an_prev_bssid1[6];	/* 0x46 */
+	uint8_t		an_prev_bssid2[6];	/* 0x4C */
+	uint8_t		an_prev_bssid3[6];	/* 0x52 */
+	uint16_t	an_beacon_period;	/* 0x58 */
+	uint16_t	an_dtim_period;		/* 0x5A */
+	uint16_t	an_atim_duration;	/* 0x5C */
+	uint16_t	an_hop_period;		/* 0x5E */
+	uint16_t	an_channel_set;		/* 0x60 */
+	uint16_t	an_cur_channel;		/* 0x62 */
+	uint16_t	an_hops_to_backbone;	/* 0x64 */
+	uint16_t	an_ap_total_load;	/* 0x66 */
+	uint16_t	an_our_generated_load;	/* 0x68 */
+	uint16_t	an_accumulated_arl;	/* 0x6A */
+	uint16_t	an_signale_quality;	/* 0x6C */
+	uint16_t	an_cur_tx_rate;		/* 0x6E */
+	uint16_t	an_ap_device;		/* 0x70 */
+	uint16_t	an_normallized_rssi;	/* 0x72 */
+	uint16_t	an_short_preamble;	/* 0x74 */
+	uint8_t		an_ap_ip_address[4];	/* 0x76 */
+	uint8_t		an_noise_pct;		/* 0x7A */
+	uint8_t		an_noise_dbm;		/* 0x7B */
+	uint8_t		an_noise_average_pct;	/* 0x7C */
+	uint8_t		an_noise_average_dbm;	/* 0x7D */
+	uint8_t		an_noise_max_pct;	/* 0x7E */
+	uint8_t		an_noise_max_dbm;	/* 0x7F */
+	uint16_t	an_load;		/* 0x80 */
+	uint8_t		an_carrier[4];		/* 0x82 */
+	uint16_t	an_assoc_status;	/* 0x86 */
+	uint16_t	an_pad;
+};
+
+#define	AN_STATUS_OPMODE_CONFIGURED		0x0001
+#define	AN_STATUS_OPMODE_MAC_ENABLED		0x0002
+#define	AN_STATUS_OPMODE_RX_ENABLED		0x0004
+#define	AN_STATUS_OPMODE_IN_SYNC		0x0010
+#define	AN_STATUS_OPMODE_ASSOCIATED		0x0020
+#define	AN_STATUS_OPMODE_ERROR			0x8000
+
+
+/*
+ * Statistics
+ */
+#define	AN_RID_16BITS_CUM	0xFF60	/* Cumulative 16-bit stats counters */
+#define	AN_RID_16BITS_DELTA	0xFF61	/* 16-bit stats (since last clear) */
+#define	AN_RID_16BITS_DELTACLR	0xFF62	/* 16-bit stats, clear on read */
+#define	AN_RID_32BITS_CUM	0xFF68	/* Cumulative 32-bit stats counters */
+#define	AN_RID_32BITS_DELTA	0xFF69	/* 32-bit stats (since last clear) */
+#define	AN_RID_32BITS_DELTACLR	0xFF6A	/* 32-bit stats, clear on read */
+
+/*
+ * Grrr. The manual says the statistics record is 384 bytes in length,
+ * but the card says the record is 404 bytes. There's some padding left
+ * at the end of this structure to account for any discrepancies.
+ */
+struct an_ltv_stats {
+	uint16_t		an_len;
+	uint16_t		an_rx_overruns;		/* 0x02 */
+	uint16_t		an_rx_plcp_csum_errs;	/* 0x04 */
+	uint16_t		an_rx_plcp_format_errs;	/* 0x06 */
+	uint16_t		an_rx_plcp_len_errs;	/* 0x08 */
+	uint16_t		an_rx_mac_crc_errs;	/* 0x0A */
+	uint16_t		an_rx_mac_crc_ok;	/* 0x0C */
+	uint16_t		an_rx_wep_errs;		/* 0x0E */
+	uint16_t		an_rx_wep_ok;		/* 0x10 */
+	uint16_t		an_retry_long;		/* 0x12 */
+	uint16_t		an_retry_short;		/* 0x14 */
+	uint16_t		an_retry_max;		/* 0x16 */
+	uint16_t		an_no_ack;		/* 0x18 */
+	uint16_t		an_no_cts;		/* 0x1A */
+	uint16_t		an_rx_ack_ok;		/* 0x1C */
+	uint16_t		an_rx_cts_ok;		/* 0x1E */
+	uint16_t		an_tx_ack_ok;		/* 0x20 */
+	uint16_t		an_tx_rts_ok;		/* 0x22 */
+	uint16_t		an_tx_cts_ok;		/* 0x24 */
+	uint16_t		an_tx_lmac_mcasts;	/* 0x26 */
+	uint16_t		an_tx_lmac_bcasts;	/* 0x28 */
+	uint16_t		an_tx_lmac_ucast_frags;	/* 0x2A */
+	uint16_t		an_tx_lmac_ucasts;	/* 0x2C */
+	uint16_t		an_tx_beacons;		/* 0x2E */
+	uint16_t		an_rx_beacons;		/* 0x30 */
+	uint16_t		an_tx_single_cols;	/* 0x32 */
+	uint16_t		an_tx_multi_cols;	/* 0x34 */
+	uint16_t		an_tx_defers_no;	/* 0x36 */
+	uint16_t		an_tx_defers_prot;	/* 0x38 */
+	uint16_t		an_tx_defers_energy;	/* 0x3A */
+	uint16_t		an_rx_dups;		/* 0x3C */
+	uint16_t		an_rx_partial;		/* 0x3E */
+	uint16_t		an_tx_too_old;		/* 0x40 */
+	uint16_t		an_rx_too_old;		/* 0x42 */
+	uint16_t		an_lostsync_max_retries; /* 0x44 */
+	uint16_t		an_lostsync_missed_beacons; /* 0x46 */
+	uint16_t		an_lostsync_arl_exceeded; /* 0x48 */
+	uint16_t		an_lostsync_deauthed;	/* 0x4A */
+	uint16_t		an_lostsync_disassociated; /* 0x4C */
+	uint16_t		an_lostsync_tsf_timing;	/* 0x4E */
+	uint16_t		an_tx_host_mcasts;	/* 0x50 */
+	uint16_t		an_tx_host_bcasts;	/* 0x52 */
+	uint16_t		an_tx_host_ucasts;	/* 0x54 */
+	uint16_t		an_tx_host_failed;	/* 0x56 */
+	uint16_t		an_rx_host_mcasts;	/* 0x58 */
+	uint16_t		an_rx_host_bcasts;	/* 0x5A */
+	uint16_t		an_rx_host_ucasts;	/* 0x5C */
+	uint16_t		an_rx_host_discarded;	/* 0x5E */
+	uint16_t		an_tx_hmac_mcasts;	/* 0x60 */
+	uint16_t		an_tx_hmac_bcasts;	/* 0x62 */
+	uint16_t		an_tx_hmac_ucasts;	/* 0x64 */
+	uint16_t		an_tx_hmac_failed;	/* 0x66 */
+	uint16_t		an_rx_hmac_mcasts;	/* 0x68 */
+	uint16_t		an_rx_hmac_bcasts;	/* 0x6A */
+	uint16_t		an_rx_hmac_ucasts;	/* 0x6C */
+	uint16_t		an_rx_hmac_discarded;	/* 0x6E */
+	uint16_t		an_tx_hmac_accepted;	/* 0x70 */
+	uint16_t		an_ssid_mismatches;	/* 0x72 */
+	uint16_t		an_ap_mismatches;	/* 0x74 */
+	uint16_t		an_rates_mismatches;	/* 0x76 */
+	uint16_t		an_auth_rejects;	/* 0x78 */
+	uint16_t		an_auth_timeouts;	/* 0x7A */
+	uint16_t		an_assoc_rejects;	/* 0x7C */
+	uint16_t		an_assoc_timeouts;	/* 0x7E */
+	uint16_t		an_reason_outside_table; /* 0x80 */
+	uint16_t		an_reason1;		/* 0x82 */
+	uint16_t		an_reason2;		/* 0x84 */
+	uint16_t		an_reason3;		/* 0x86 */
+	uint16_t		an_reason4;		/* 0x88 */
+	uint16_t		an_reason5;		/* 0x8A */
+	uint16_t		an_reason6;		/* 0x8C */
+	uint16_t		an_reason7;		/* 0x8E */
+	uint16_t		an_reason8;		/* 0x90 */
+	uint16_t		an_reason9;		/* 0x92 */
+	uint16_t		an_reason10;		/* 0x94 */
+	uint16_t		an_reason11;		/* 0x96 */
+	uint16_t		an_reason12;		/* 0x98 */
+	uint16_t		an_reason13;		/* 0x9A */
+	uint16_t		an_reason14;		/* 0x9C */
+	uint16_t		an_reason15;		/* 0x9E */
+	uint16_t		an_reason16;		/* 0xA0 */
+	uint16_t		an_reason17;		/* 0xA2 */
+	uint16_t		an_reason18;		/* 0xA4 */
+	uint16_t		an_reason19;		/* 0xA6 */
+	uint16_t		an_rx_mgmt_pkts;	/* 0xA8 */
+	uint16_t		an_tx_mgmt_pkts;	/* 0xAA */
+	uint16_t		an_rx_refresh_pkts;	/* 0xAC */
+	uint16_t		an_tx_refresh_pkts;	/* 0xAE */
+	uint16_t		an_rx_poll_pkts;	/* 0xB0 */
+	uint16_t		an_tx_poll_pkts;	/* 0xB2 */
+	uint16_t		an_host_retries;	/* 0xB4 */
+	uint16_t		an_lostsync_hostreq;	/* 0xB6 */
+	uint16_t		an_host_tx_bytes;	/* 0xB8 */
+	uint16_t		an_host_rx_bytes;	/* 0xBA */
+	uint16_t		an_uptime_usecs;	/* 0xBC */
+	uint16_t		an_uptime_secs;		/* 0xBE */
+	uint16_t		an_lostsync_better_ap;	/* 0xC0 */
+	uint16_t		an_privacy_mismatch;	/* 0xC2 */
+	uint16_t		an_jammed;		/* 0xC4 */
+	uint16_t		an_rx_disc_wep_off;	/* 0xC6 */
+	uint16_t		an_phy_ele_mismatch;	/* 0xC8 */
+	uint16_t		an_leap_success;	/* 0xCA */
+	uint16_t		an_leap_failure;	/* 0xCC */
+	uint16_t		an_leap_timeouts;	/* 0xCE */
+	uint16_t		an_leap_keylen_fail;	/* 0xD0 */
+};
+
+#define	AN_RID_ESSIDLIST_FIRST	0xFF72
+#define	AN_RID_ESSIDLIST_NEXT	0xFF73
+
+struct an_ltv_scanresult {
+	uint16_t	an_len;
+	uint16_t	an_index;
+	uint16_t	an_radiotype;
+	uint8_t		an_bssid[6];
+	uint8_t		an_zero;
+	uint8_t		an_ssidlen;
+	char		an_ssid[32];
+	uint16_t	an_rssi;
+	uint16_t	an_cap;
+	uint16_t	an_beaconinterval;
+	uint8_t		an_rates[8];
+	struct {
+		uint16_t	dwell;
+		uint8_t		hopset;
+		uint8_t		hoppattern;
+		uint8_t		hopindex;
+		uint8_t		pad;
+	} an_fh;
+	uint16_t	an_dschannel;
+	uint16_t	an_atimwindow;
+};
+
+/*
+ * seconds after which the scan item ages
+ */
+#define	AN_SCAN_TIMEOUT_MAX	30
+
+/*
+ * threshold of scan result items below which scan will run again.
+ */
+#define	AN_SCAN_AGAIN_THRESHOLD	5
+
+typedef struct an_scan_list {
+	struct an_ltv_scanresult	an_val;
+	uint32_t			an_timeout;
+	list_node_t			an_scan_node;
+} an_scan_list_t;
+
+/*
+ * Receive frame structure.
+ */
+typedef struct an_rxframe {
+	uint32_t	an_rx_time;		/* 0x00 */
+	uint16_t	an_rx_status;		/* 0x04 */
+	uint16_t	an_rx_payload_len;	/* 0x06 */
+	uint8_t		an_rsvd0;		/* 0x08 */
+	uint8_t		an_rx_signal_strength;	/* 0x09 */
+	uint8_t		an_rx_rate;		/* 0x0A */
+	uint8_t		an_rx_chan;		/* 0x0B */
+	uint8_t		an_rx_assoc_cnt;	/* 0x0C */
+	uint8_t		an_rsvd1[3];		/* 0x0D */
+	uint8_t		an_plcp_hdr[4];		/* 0x10 */
+	uint16_t	an_frame_ctl;		/* 0x14 */
+	uint16_t	an_duration;		/* 0x16 */
+	uint8_t		an_addr1[6];		/* 0x18 */
+	uint8_t		an_addr2[6];		/* 0x1E */
+	uint8_t		an_addr3[6];		/* 0x24 */
+	uint16_t	an_seq_ctl;		/* 0x2A */
+	uint8_t		an_addr4[6];		/* 0x2C */
+	uint16_t	an_gaplen;		/* 0x32 */
+} an_rxfrm_t;
+
+#define	AN_RXGAP_MAX	8
+
+/*
+ * Transmit frame structure.
+ */
+typedef struct an_txframe {
+	uint32_t	an_tx_sw;		/* 0x00 */
+	uint16_t	an_tx_status;		/* 0x04 */
+	uint16_t	an_tx_payload_len;	/* 0x06 */
+	uint16_t	an_tx_ctl;		/* 0x08 */
+	uint16_t	an_tx_assoc_id;		/* 0x0A */
+	uint16_t	an_tx_retry;		/* 0x0C */
+	uint8_t		an_tx_assoc_cnt;	/* 0x0E */
+	uint8_t		an_tx_rate;		/* 0x0F */
+	uint8_t		an_tx_max_long_retries;	/* 0x10 */
+	uint8_t		an_tx_max_short_retries; /* 0x11 */
+	uint8_t		an_rsvd0[2];		/* 0x12 */
+	uint16_t	an_frame_ctl;		/* 0x14 */
+	uint16_t	an_duration;		/* 0x16 */
+	uint8_t		an_addr1[6];		/* 0x18 */
+	uint8_t		an_addr2[6];		/* 0x1E */
+	uint8_t		an_addr3[6];		/* 0x24 */
+	uint16_t	an_seq_ctl;		/* 0x2A */
+	uint8_t		an_addr4[6];		/* 0x2C */
+	uint16_t	an_gaplen;		/* 0x32 */
+} an_txfrm_t;
+
+typedef struct an_frame {
+	union {
+		an_rxfrm_t rxfrm;
+		an_txfrm_t txfrm;
+	} frm;
+} an_frm_t;
+
+#define	AN_TXSTAT_EXCESS_RETRY	0x0002
+#define	AN_TXSTAT_LIFE_EXCEEDED	0x0004
+#define	AN_TXSTAT_AID_FAIL	0x0008
+#define	AN_TXSTAT_MAC_DISABLED	0x0010
+#define	AN_TXSTAT_ASSOC_LOST	0x0020
+
+#define	AN_TXCTL_RSVD		0x0001
+#define	AN_TXCTL_TXOK_INTR	0x0002
+#define	AN_TXCTL_TXERR_INTR	0x0004
+#define	AN_TXCTL_HEADER_TYPE	0x0008
+#define	AN_TXCTL_PAYLOAD_TYPE	0x0010
+#define	AN_TXCTL_NORELEASE	0x0020
+#define	AN_TXCTL_NORETRIES	0x0040
+#define	AN_TXCTL_CLEAR_AID	0x0080
+#define	AN_TXCTL_STRICT_ORDER	0x0100
+#define	AN_TXCTL_USE_RTS	0x0200
+
+#define	AN_HEADERTYPE_8023	0x0000
+#define	AN_HEADERTYPE_80211	0x0008
+
+#define	AN_PAYLOADTYPE_ETHER	0x0000
+#define	AN_PAYLOADTYPE_LLC	0x0010
+
+typedef enum {
+	ANC_RX_OVERRUNS,		/* 0x04 */
+	ANC_RX_PLCP_CSUM_ERRS,		/* 0x08 */
+	ANC_RX_PLCP_FORMAT_ERRS,	/* 0x0c */
+	ANC_RX_PLCP_LEN_ERRS,		/* 0x10 */
+	ANC_RX_MAC_CRC_ERRS,		/* 0x14 */
+	ANC_RX_MAC_CRC_OK,		/* 0x18 */
+	ANC_RX_WEP_ERRS,		/* 0x1c */
+	ANC_RX_WEP_OK,			/* 0x20 */
+	ANC_RETRY_LONG,			/* 0x24 */
+	ANC_RETRY_SHORT,		/* 0x28 */
+	ANC_RETRY_MAX,			/* 0x2c */
+	ANC_NO_ACK,			/* 0x30 */
+	ANC_NO_CTS,			/* 0x34 */
+	ANC_RX_ACK_OK,			/* 0x38 */
+	ANC_RX_CTS_OK,			/* 0x3c */
+	ANC_TX_ACK_OK,			/* 0x40 */
+	ANC_TX_RTS_OK,			/* 0x44 */
+	ANC_TX_CTS_OK,			/* 0x48 */
+	ANC_TX_LMAC_MCASTS,		/* 0x4c */
+	ANC_TX_LMAC_BCASTS,		/* 0x50 */
+	ANC_TX_LMAC_UCAST_FRAGS,	/* 0x54 */
+	ANC_TX_LMAC_UCASTS,		/* 0x58 */
+	ANC_TX_BEACONS,			/* 0x5c */
+	ANC_RX_BEACONS,			/* 0x60 */
+	ANC_TX_SINGLE_COLS,		/* 0x64 */
+	ANC_TX_MULTI_COLS,		/* 0x68 */
+	ANC_TX_DEFERS_NO,		/* 0x6c */
+	ANC_TX_DEFERS_PROT,		/* 0x70 */
+	ANC_TX_DEFERS_ENERGY,		/* 0x74 */
+	ANC_RX_DUPS,			/* 0x78 */
+	ANC_RX_PARTIAL,			/* 0x7c */
+	ANC_TX_TOO_OLD,			/* 0x80 */
+	ANC_RX_TOO_OLD,			/* 0x84 */
+	ANC_LOSTSYNC_MAX_RETRIES,	/* 0x88 */
+	ANC_LOSTSYNC_MISSED_BEACONS,	/* 0x8c */
+	ANC_LOSTSYNC_ARL_EXCEEDED,	/* 0x90 */
+	ANC_LOSTSYNC_DEAUTHED,		/* 0x94 */
+	ANC_LOSTSYNC_DISASSOCIATED,	/* 0x98 */
+	ANC_LOSTSYNC_TSF_TIMING,	/* 0x9c */
+	ANC_TX_HOST_MCASTS,		/* 0xa0 */
+	ANC_TX_HOST_BCASTS,		/* 0xa4 */
+	ANC_TX_HOST_UCASTS,		/* 0xa8 */
+	ANC_TX_HOST_FAILED,		/* 0xac */
+	ANC_RX_HOST_MCASTS,		/* 0xb0 */
+	ANC_RX_HOST_BCASTS,		/* 0xb4 */
+	ANC_RX_HOST_UCASTS,		/* 0xb8 */
+	ANC_RX_HOST_DISCARDED,		/* 0xbc */
+	ANC_TX_HMAC_MCASTS,		/* 0xc0 */
+	ANC_TX_HMAC_BCASTS,		/* 0xc4 */
+	ANC_TX_HMAC_UCASTS,		/* 0xc8 */
+	ANC_TX_HMAC_FAILED,		/* 0xcc */
+	ANC_RX_HMAC_MCASTS,		/* 0xd0 */
+	ANC_RX_HMAC_BCASTS,		/* 0xd4 */
+	ANC_RX_HMAC_UCASTS,		/* 0xd8 */
+	ANC_RX_HMAC_DISCARDED,		/* 0xdc */
+	ANC_TX_HMAC_ACCEPTED,		/* 0xe0 */
+	ANC_SSID_MISMATCHES,		/* 0xe4 */
+	ANC_AP_MISMATCHES,		/* 0xe8 */
+	ANC_RATES_MISMATCHES,		/* 0xec */
+	ANC_AUTH_REJECTS,		/* 0xf0 */
+	ANC_AUTH_TIMEOUTS,		/* 0xf4 */
+	ANC_ASSOC_REJECTS,		/* 0xf8 */
+	ANC_ASSOC_TIMEOUTS,		/* 0xfc */
+	ANC_REASON_OUTSIDE_TABLE,	/* 0x100 */
+	ANC_REASON1,			/* 0x104 */
+	ANC_REASON2,			/* 0x108 */
+	ANC_REASON3,			/* 0x10c */
+	ANC_REASON4,			/* 0x110 */
+	ANC_REASON5,			/* 0x114 */
+	ANC_REASON6,			/* 0x118 */
+	ANC_REASON7,			/* 0x11c */
+	ANC_REASON8,			/* 0x120 */
+	ANC_REASON9,			/* 0x124 */
+	ANC_REASON10,			/* 0x128 */
+	ANC_REASON11,			/* 0x12c */
+	ANC_REASON12,			/* 0x130 */
+	ANC_REASON13,			/* 0x134 */
+	ANC_REASON14,			/* 0x138 */
+	ANC_REASON15,			/* 0x13c */
+	ANC_REASON16,			/* 0x140 */
+	ANC_REASON17,			/* 0x144 */
+	ANC_REASON18,			/* 0x148 */
+	ANC_REASON19,			/* 0x14c */
+	ANC_RX_MGMT_PKTS,		/* 0x150 */
+	ANC_TX_MGMT_PKTS,		/* 0x154 */
+	ANC_RX_REFRESH_PKTS,		/* 0x158 */
+	ANC_TX_REFRESH_PKTS,		/* 0x15c */
+	ANC_RX_POLL_PKTS,		/* 0x160 */
+	ANC_TX_POLL_PKTS,		/* 0x164 */
+	ANC_HOST_RETRIES,		/* 0x168 */
+	ANC_LOSTSYNC_HOSTREQ,		/* 0x16c */
+	ANC_HOST_TX_BYTES,		/* 0x170 */
+	ANC_HOST_RX_BYTES,		/* 0x174 */
+	ANC_UPTIME_USECS,		/* 0x178 */
+	ANC_UPTIME_SECS,		/* 0x17c */
+	ANC_LOSTSYNC_BETTER_AP,		/* 0x180 */
+	ANC_PRIVACY_MISMATCH,		/* 0x184 */
+	ANC_JAMMED,			/* 0x188 */
+	ANC_RX_DISC_WEP_OFF,		/* 0x18c */
+	ANC_PHY_ELE_MISMATCH,		/* 0x190 */
+	ANC_LEAP_SUCCESS,		/* 0x194 */
+	ANC_LEAP_FAILURE,		/* 0x198 */
+	ANC_LEAP_TIMEOUTS,		/* 0x19c */
+	ANC_LEAP_KEYLEN_FAIL,		/* 0x1a0 */
+	ANC_STAT_CNT			/* - keep it as the last entry */
+} pcan_cntr_offset;
+
+#define	AN_TXCTL_80211	(AN_TXCTL_TXOK_INTR | AN_TXCTL_TXERR_INTR | \
+		AN_HEADERTYPE_80211 | AN_PAYLOADTYPE_LLC | AN_TXCTL_NORELEASE)
+
+#define	AN_TXCTL_8023	(AN_TXCTL_TXOK_INTR | AN_TXCTL_TXERR_INTR |\
+		AN_HEADERTYPE_8023 | AN_PAYLOADTYPE_ETHER | AN_TXCTL_NORELEASE)
+
+#define	AN_TXGAP_80211		6
+#define	AN_TXGAP_8023		0
+
+#define	AN_NORMAL_RXMODE	(AN_RXMODE_BC_MC_ADDR | \
+					AN_RXMODE_USE_8023_HEADER)
+#define	AN_MONITOR_RXMODE	(AN_RXMODE_LAN_MONITOR_CURBSS | \
+					AN_RXMODE_USE_8023_HEADER)
+struct an_802_3_hdr {
+	uint16_t		an_8023_status;
+	uint16_t		an_8023_payload_len;
+	uint8_t			an_8023_dst_addr[6];
+	uint8_t			an_8023_src_addr[6];
+	uint16_t		an_8023_dat[3];	/* SNAP header */
+	uint16_t		an_8023_type;
+};
+
+typedef struct an_snap_hdr {
+	uint16_t		an_snap_dat[3];	/* SNAP header */
+	uint16_t		an_snap_type;
+} pcan_snaphdr_t;
+
+#define	AN_TX_RING_CNT		4
+#define	AN_TX_RING_MASK		(4 - 1)
+#define	AN_INC(x, y)		(x) = (x + 1) % (y)
+
+typedef struct an_tx_ring_data {
+	uint16_t		an_tx_fids[AN_TX_RING_CNT];
+	uint16_t		an_tx_ring[AN_TX_RING_CNT];
+	int			an_tx_prod;
+	int			an_tx_cons;
+	kmutex_t		an_tx_lock;	/* for send only */
+} pcan_txring_t;
+
+#define	AN_802_3_OFFSET		0x2E
+#define	AN_802_11_OFFSET	0x44
+#define	AN_802_11_OFFSET_RAW	0x3C
+
+#define	AN_STAT_BADCRC		0x0001
+#define	AN_STAT_UNDECRYPTABLE	0x0002
+#define	AN_STAT_ERRSTAT		0x0003
+#define	AN_STAT_MAC_PORT	0x0700
+#define	AN_STAT_1042		0x2000	/* RFC1042 encoded */
+#define	AN_STAT_TUNNEL		0x4000	/* Bridge-tunnel encoded */
+#define	AN_STAT_WMP_MSG		0x6000	/* WaveLAN-II management protocol */
+#define	AN_RXSTAT_MSG_TYPE	0xE000
+
+#define	AN_ENC_TX_802_3		0x00
+#define	AN_ENC_TX_802_11	0x11
+#define	AN_ENC_TX_E_II		0x0E
+
+#define	AN_ENC_TX_1042		0x00
+#define	AN_ENC_TX_TUNNEL	0xF8
+
+#define	AN_TXCNTL_MACPORT	0x00FF
+#define	AN_TXCNTL_STRUCTTYPE	0xFF00
+
+/*
+ * SNAP (sub-network access protocol) constants for transmission
+ * of IP datagrams over IEEE 802 networks, taken from RFC1042.
+ * We need these for the LLC/SNAP header fields in the TX/RX frame
+ * structure.
+ */
+#define	AN_SNAP_K1		0xaa	/* assigned global SAP for SNAP */
+#define	AN_SNAP_K2		0x00
+#define	AN_SNAP_CONTROL		0x03	/* unnumbered information format */
+#define	AN_SNAP_WORD0		(AN_SNAP_K1 | (AN_SNAP_K1 << 8))
+#define	AN_SNAP_WORD1		(AN_SNAP_K2 | (AN_SNAP_CONTROL << 8))
+#define	AN_SNAPHDR_LEN		0x6
+
+#define	AN_FTYPE_DATA		0x8
+#define	ETH_HDRLEN		(sizeof (struct ether_header))	/* 14 bytes */
+#define	MLEN(mp)		((mp)->b_wptr - (mp)->b_rptr)
+
+typedef struct pcan_dma_info {
+	ddi_dma_handle_t	dma_handle;
+	ddi_acc_handle_t	dma_acc_handle;
+	uint32_t		dma_physaddr;
+	caddr_t			dma_virtaddr;
+	uint_t			ncookies;
+} pcan_dma_info_t;
+
+#define	PCAN_DMA_SYNC(hdl, len, flag) ((void) ddi_dma_sync(hdl, 0, len, (flag)))
+
+/*
+ * The macinfo is really used as the softstate structure.
+ *
+ * pcan_mh	 - mac_handle_t structure
+ * pcan_cslock	 - lock for card services request. Used with pcan_cscv
+ * pcan_cscv	 - condition variable to wait for card events
+ * pcan_chdl	 - client handle, an uint32_t bit mask encoding for socket,
+ *			function, and client info.
+ *			See cs_priv.h MAKE_CLIENT_HANDLE.
+ * pcan_log_sock - holds the logical to physical translation for this card.
+ *			Specifically has physical adapter and socket #.
+ *			Socket # is the same as part of the pcan_chdl encoding.
+ *			Physical adapter # is from card service socket impl.
+ */
+typedef struct pcan_macinfo {
+	mac_handle_t		pcan_mh;
+	dev_info_t		*pcan_dip;
+
+	kmutex_t		pcan_cslock;	/* for card services */
+	kcondvar_t		pcan_cscv;	/* for card services */
+	client_handle_t		pcan_chdl;	/* s,f,c encoding, cs_priv.h */
+	map_log_socket_t	pcan_log_sock;	/* logical/phys socket map */
+	int			pcan_socket;	/* socket number */
+	int			pcan_config_hi;	/* cfttbl index */
+	int			pcan_config;	/* default config index */
+	int			pcan_vcc;	/* vcc level */
+	int			pcan_iodecode;	/* # of address lines */
+	int			pcan_usewep;
+	int			pcan_reset_delay;
+
+	caddr_t			pcan_cfg_base;
+	ddi_acc_handle_t	pcan_cfg_handle;
+	caddr_t			pcan_bar0;
+	ddi_acc_handle_t	pcan_handle0;
+	caddr_t			pcan_bar1;
+	ddi_acc_handle_t	pcan_handle1;
+	caddr_t			pcan_bar2;
+	ddi_acc_handle_t	pcan_handle2;
+	int			pcan_device_type; /* pci or pcmcia card */
+
+	uint8_t 		pcan_mac_addr[ETHERADDRL];
+	uint32_t		pcan_flag;
+	uint32_t		pcan_reschedule_need;
+	uint32_t		glds_nocarrier;
+	uint32_t		glds_noxmtbuf;
+	uint32_t		glds_norcvbuf;
+	uint32_t		glds_intr;
+
+	pcan_dma_info_t	pcan_cmd;
+	pcan_dma_info_t	pcan_rx[AN_MAX_RX_DESC];
+	pcan_dma_info_t	pcan_tx[AN_MAX_TX_DESC];
+
+	kmutex_t		pcan_glock;	/* generic lock */
+	kmutex_t		pcan_scanlist_lock;	/* scanlist lock */
+	pcan_txring_t		pcan_txring;
+
+	struct an_ltv_ssidlist	an_ssidlist;
+	struct an_ltv_aplist	an_aplist;
+	struct an_ltv_caps	an_caps;
+	struct an_ltv_crypt	an_crypt;
+	struct an_ltv_wepkey	an_wepkey[4];
+	struct an_ltv_scanresult an_scanresult[32];
+	uint16_t		an_cur_wepkey;
+	uint16_t		an_scan_num;
+	timeout_id_t		an_scanlist_timeout_id;
+	list_t			an_scan_list;
+	struct an_ltv_status	an_status;
+	struct an_ltv_genconfig	an_config;
+	struct an_ltv_genconfig	an_actual_config;
+	struct an_ltv_stats	an_stats;
+	uint64_t pcan_cntrs_s[ANC_STAT_CNT];
+
+	ddi_acc_handle_t	pcan_port;
+	ddi_iblock_cookie_t	pcan_ib_cookie;
+	ddi_softintr_t		pcan_softint_id;
+
+	ddi_softintr_t		pcan_info_softint_id;
+	uint32_t		pcan_info_softint_pending;
+
+	timeout_id_t		pcan_connect_timeout_id;
+	timeout_id_t		pcan_linkdown_timeout_id;
+	int			pcan_badrids_len;
+	prop_1275_cell_t	*pcan_badrids;
+} pcan_maci_t;
+
+#define	PCAN_IDENT_STRING	modldrv.drv_linkinfo
+
+#define	HDL(pcan_p)		((pcan_p)->pcan_port)
+#define	GLD3(pcan_p)		((pcan_p)->pcan_mh)
+#define	DIP(pcan_p)		((pcan_p)->pcan_dip)
+
+#define	PCAN_CARD_INTREN	0x1
+#define	PCAN_CARD_LINKUP	0x2
+#define	PCAN_ATTACHED		0x4
+#define	PCAN_CS_REGISTERED	0x8
+#define	PCAN_ENABLED		0x10
+#define	PCAN_CARD_SEND		0x20
+#define	PCAN_CARD_READY		0x40
+#define	PCAN_CARD_FAILED	0x80
+
+#define	PCAN_STATE_IDLE		0x1
+
+#define	PCAN_NICMEM_SZ		(2048) /* 80211MTU set as 1500, so 2k here */
+
+static int	pcan_probe(dev_info_t *dip);
+static int	pcan_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int	pcan_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static int	pcan_register_cs(dev_info_t *dip, pcan_maci_t *pcan_p);
+static void	pcan_unregister_cs(pcan_maci_t *pcan_p);
+static void	pcan_destroy_locks(pcan_maci_t *pcan_p);
+static void	pcan_reset_backend(pcan_maci_t *pcan_p, int timeout);
+static uint32_t	pcan_get_cap(pcan_maci_t *pcan_p);
+static int	pcan_card_insert(pcan_maci_t *pcan_p);
+static int	pcan_ev_hdlr(event_t ev, int pri, event_callback_args_t *arg);
+static void	pcan_card_remove(pcan_maci_t *pcan_p);
+static int	pcan_init_nicmem(pcan_maci_t *pcan_p);
+
+/*
+ * high level device access primitives, glock must held before calling
+ */
+static uint16_t	pcan_set_cmd0(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t p0,
+    uint16_t p1, uint16_t p2);
+static uint16_t	pcan_set_cmd(pcan_maci_t *pcan_p, uint16_t cmd, uint16_t param);
+static uint16_t pcan_set_ch(pcan_maci_t *, uint16_t, uint16_t, uint16_t);
+static int pcan_init_dma_desc(pcan_maci_t *pcan_p);
+static int pcan_init_dma(dev_info_t *dip, pcan_maci_t *pcan_p);
+static void pcan_free_dma(pcan_maci_t *pcan_p);
+static uint16_t pcan_put_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type,
+    uint16_t *val_p);
+static uint16_t	pcan_get_ltv(pcan_maci_t *pcan_p, uint16_t len, uint16_t type,
+    uint16_t *val_p);
+#define	PCAN_READ_LTV	0
+#define	PCAN_WRITE_LTV	1
+static uint16_t pcan_status_ltv(int rw, pcan_maci_t *pcan_p,
+    struct an_ltv_status *status_p);
+static uint16_t pcan_cfg_ltv(int rw, pcan_maci_t *pcan_p,
+    struct an_ltv_genconfig *cfg_p);
+static uint16_t pcan_cap_ltv(int rw, pcan_maci_t *pcan_p);
+static uint16_t pcan_ssid_ltv(int rw, pcan_maci_t *pcan_p);
+static uint16_t pcan_aplist_ltv(int rw, pcan_maci_t *pcan_p);
+static uint16_t pcan_scanresult_ltv(int rw, pcan_maci_t *pcan_p, uint16_t type,
+    struct an_ltv_scanresult *scanresult_p);
+static uint16_t pcan_wepkey_ltv(int rw, pcan_maci_t *pcan_p);
+static uint16_t pcan_rdch0(pcan_maci_t *pcan_p, uint16_t type, uint16_t off,
+    uint16_t *buf_p, int len, int order);
+static uint16_t pcan_wrch1(pcan_maci_t *pcan_p, uint16_t type, uint16_t off,
+    uint16_t *buf_p, int len, int order);
+static int	pcan_config_mac(pcan_maci_t *pcan_p);
+static void	pcan_start_locked(pcan_maci_t *pcan_p);
+static void	pcan_stop_locked(pcan_maci_t *pcan_p);
+static uint16_t	pcan_alloc_nicmem(pcan_maci_t *pcan_p, uint16_t len,
+    uint16_t *id_p);
+
+/*
+ * Required driver entry points for gld
+ */
+static int	pcan_start(void *);
+static void	pcan_stop(void *);
+static int	pcan_saddr(void *, const uint8_t *);
+static mblk_t	*pcan_tx(void *, mblk_t *);
+static int	pcan_send(pcan_maci_t *, mblk_t *);
+static int	pcian_send(pcan_maci_t *, mblk_t *);
+static int	pcan_prom(void *, boolean_t);
+static int	pcan_gstat(void *, uint_t, uint64_t *);
+static int	pcan_sdmulti(void *, boolean_t, const uint8_t *);
+static void	pcan_ioctl(void *, queue_t *, mblk_t *);
+
+static uint_t	pcan_intr(caddr_t arg);
+static uint_t	pcan_intr_hi(caddr_t arg);
+static void	pcan_rcv(pcan_maci_t *pcan_p);
+static void	pcian_rcv(pcan_maci_t *pcan_p);
+static uint_t	pcan_info_softint(caddr_t arg);
+static uint32_t	pcan_txdone(pcan_maci_t *pcan_p, uint16_t err);
+static int	pcan_getset(mblk_t *mp, pcan_maci_t *pcan_p, uint32_t cmd);
+static void	pcan_wlan_ioctl(pcan_maci_t *pcan_p, queue_t *wq,
+    mblk_t *mp, uint32_t cmd);
+static int	pcan_loaddef(pcan_maci_t *pcan_p);
+
+static void	pcan_scanlist_timeout(void *);
+static void	pcan_delete_scan_item(pcan_maci_t *, an_scan_list_t *);
+static int	pcan_add_scan_item(pcan_maci_t *, struct an_ltv_scanresult);
+static void	pcan_connect_timeout(void *arg);
+
+#define	RDCH0(h, t, o, bufp, l)		pcan_rdch0(h, t, o, bufp, l, 1)
+#define	WRCH1(h, t, o, bufp, l)		pcan_wrch1(h, t, o, bufp, l, 1)
+#define	RDPKT(h, t, o, bufp, l)		pcan_rdch0(h, t, o, bufp, l, 0)
+#define	WRPKT(h, t, o, bufp, l)		pcan_wrch1(h, t, o, bufp, l, 0)
+
+#define	PCAN_READ(p, o, v)	{ \
+	if (p->pcan_device_type == PCAN_DEVICE_PCI) { \
+		uint16_t t = ddi_get16(p->pcan_handle0, \
+		    (uint16_t *)(p->pcan_bar0 + o)); \
+		v = LE_16(t); \
+	} else { \
+		uint16_t t = csx_Get16(HDL(p), o); \
+		v = LE_16(t); \
+	}\
+}
+#define	PCAN_WRITE(p, o, v)	{ \
+	if (p->pcan_device_type == PCAN_DEVICE_PCI) { \
+		ddi_put16(p->pcan_handle0, \
+		    (uint16_t *)(p->pcan_bar0 + o), LE_16(v)); \
+	} else { \
+		csx_Put16(HDL(p), o, LE_16(v)); \
+	}\
+}
+#define	PCAN_READ_P(p, o, v, h)	{ \
+	if (p->pcan_device_type == PCAN_DEVICE_PCI) { \
+		uint16_t t = ddi_get16(p->pcan_handle0, \
+		    (uint16_t *)(p->pcan_bar0 + o)); \
+		*(v) = h ? LE_16(t) : t; \
+	} else { \
+		uint16_t t = csx_Get16(HDL(p), o); \
+		*(v) = h ? LE_16(t) : t; \
+	}\
+}
+#define	PCAN_WRITE_P(p, o, v, h)	{ \
+	if (p->pcan_device_type == PCAN_DEVICE_PCI) { \
+		ddi_put16(p->pcan_handle0, (uint16_t *)(p->pcan_bar0 + o), \
+		    h ? LE_16(*(v)) : (*(v))); \
+	} else {\
+		csx_Put16(HDL(p), o, h ? LE_16(*(v)) : (*(v))); \
+	}\
+}
+
+#ifdef _BIG_ENDIAN
+#define	PCAN_SWAP16(buf_p, len) { \
+	uint16_t pcan_swap_len = len; \
+	for (pcan_swap_len = (pcan_swap_len + 1) >> 1; pcan_swap_len; ) { \
+		uint16_t val; \
+		pcan_swap_len--; \
+		val = *((uint16_t *)(buf_p) + pcan_swap_len); \
+		*((uint16_t *)(buf_p) + pcan_swap_len) = LE_16(val); \
+	} \
+}
+#define	PCAN_SWAP16_BUF(buf_p) PCAN_SWAP16(buf_p, sizeof (buf_p))
+#else /* _BIG_ENDIAN */
+#define	PCAN_SWAP16(buf_p, len)
+#define	PCAN_SWAP16_BUF(buf_p)
+#endif /* _BIG_ENDIAN */
+
+#define	PCAN_ENABLE_INTR(pcan_p)	{\
+	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), AN_INTRS(pcan_p));\
+}
+#define	PCAN_DISABLE_INTR(pcan_p)	{ \
+	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), 0); \
+}
+#define	PCAN_DISABLE_INTR_CLEAR(pcan_p)	{ \
+	PCAN_WRITE(pcan_p, AN_INT_EN(pcan_p), 0); \
+	PCAN_WRITE(pcan_p, AN_EVENT_ACK(pcan_p), 0xffff);\
+}
+
+#define	PCAN_AUX_PUT32(p, o, v)\
+	ddi_put32(p->pcan_handle2, (uint32_t *)(p->pcan_bar2 + o), v)
+#define	PCAN_AUX_GET32(p, o, v) {\
+	v = ddi_get32(p->pcan_handle2, (uint32_t *)(p->pcan_bar2 + o));\
+}
+
+/*
+ * 16-bit driver private status code
+ */
+#define	PCAN_SUCCESS		0
+#define	PCAN_FAIL		1
+#define	PCAN_TIMEDOUT_CMD	0x10
+#define	PCAN_TIMEDOUT_ACCESS	0x11
+#define	PCAN_TIMEDOUT_TARGET	0x12
+#define	PCAN_BADLEN		0x13
+#define	PCAN_BADTYPE		0x14
+#define	PCAN_TIMEDOUT_ALLOC	0x15
+
+#define	PCAN_STATUS_MAX		0xffff
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_PCAN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcwl/pcwl.c	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,3798 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999
+ *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/dlpi.h>
+#include <sys/ethernet.h>
+#include <sys/strsun.h>
+#include <sys/stat.h>
+#include <sys/byteorder.h>
+#include <sys/pccard.h>
+#include <sys/pci.h>
+#include <sys/policy.h>
+#include <sys/mac.h>
+#include <sys/stream.h>
+#include <inet/common.h>
+#include <inet/nd.h>
+#include <inet/mi.h>
+
+#include "pcwl.h"
+#include <sys/mac_wifi.h>
+#include <inet/wifi_ioctl.h>
+
+#ifdef DEBUG
+#define	PCWL_DBG_BASIC		0x1
+#define	PCWL_DBG_INFO		0x2
+#define	PCWL_DBG_SEND		0x4
+#define	PCWL_DBG_RCV		0x8
+#define	PCWL_DBG_LINKINFO	0x10
+uint32_t pcwl_debug = 0;
+#define	PCWLDBG(x) \
+	if (pcwl_debug & PCWL_DBG_BASIC) cmn_err x
+#else
+#define	PCWLDBG(x)
+#endif
+
+/* for pci card */
+static ddi_device_acc_attr_t accattr = {
+		DDI_DEVICE_ATTR_V0,
+		DDI_NEVERSWAP_ACC,
+		DDI_STRICTORDER_ACC,
+		DDI_DEFAULT_ACC
+};
+
+void *pcwl_soft_state_p = NULL;
+static int pcwl_device_type;
+
+mac_callbacks_t pcwl_m_callbacks = {
+	MC_IOCTL,
+	pcwl_gstat,
+	pcwl_start,
+	pcwl_stop,
+	pcwl_prom,
+	pcwl_sdmulti,
+	pcwl_saddr,
+	pcwl_tx,
+	NULL,
+	pcwl_ioctl
+};
+
+static char *pcwl_name_str = "pcwl";
+
+DDI_DEFINE_STREAM_OPS(pcwl_dev_ops, nulldev, pcwl_probe, pcwl_attach,
+    pcwl_detach, nodev, NULL, D_MP, NULL);
+
+extern struct mod_ops mod_driverops;
+static struct modldrv modldrv = {
+	&mod_driverops,
+	"Lucent/PRISM-II 802.11b driver",
+	&pcwl_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modldrv, NULL
+	};
+
+int
+_init(void)
+{
+	int stat;
+
+	/* Allocate soft state */
+	if ((stat = ddi_soft_state_init(&pcwl_soft_state_p,
+	    sizeof (pcwl_maci_t), N_PCWL)) != DDI_SUCCESS)
+		return (stat);
+
+	mac_init_ops(&pcwl_dev_ops, "pcwl");
+	wl_frame_default.wl_dat[0] = htons(WL_SNAP_WORD0);
+	wl_frame_default.wl_dat[1] = htons(WL_SNAP_WORD1);
+	stat = mod_install(&modlinkage);
+	if (stat != DDI_SUCCESS) {
+		mac_fini_ops(&pcwl_dev_ops);
+		ddi_soft_state_fini(&pcwl_soft_state_p);
+	}
+	return (stat);
+}
+
+int
+_fini(void)
+{
+	int stat;
+
+	if ((stat = mod_remove(&modlinkage)) != DDI_SUCCESS)
+		return (stat);
+	mac_fini_ops(&pcwl_dev_ops);
+	ddi_soft_state_fini(&pcwl_soft_state_p);
+
+	return (stat);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+pcwl_probe(dev_info_t *dip)
+{
+	int len, ret;
+	char *buf;
+	dev_info_t *pdip = ddi_get_parent(dip);
+
+	ret = ddi_getlongprop(DDI_DEV_T_ANY, pdip, 0, "device_type",
+	    (caddr_t)&buf, &len);
+	if (ret != DDI_SUCCESS)
+		return (DDI_PROBE_FAILURE);
+
+	PCWLDBG((CE_NOTE, "pcwl probe: device_type %s\n", buf));
+	if ((strcmp(buf, "pccard") == 0) || (strcmp(buf, "pcmcia") == 0)) {
+		pcwl_device_type = PCWL_DEVICE_PCCARD;
+		ret = DDI_PROBE_SUCCESS;
+	} else if (strcmp(buf, "pci") == 0) {
+		pcwl_device_type = PCWL_DEVICE_PCI;
+		ret = DDI_PROBE_SUCCESS;
+	} else {
+		ret = DDI_PROBE_FAILURE;
+	}
+	kmem_free(buf, len);
+	return (ret);
+}
+
+static int
+pcwl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int ret, i;
+	int instance;
+	uint16_t stat;
+	uint32_t err;
+	pcwl_maci_t *pcwl_p;
+	wifi_data_t	wd = { 0 };
+	mac_register_t	*macp;
+	modify_config_t cfgmod;
+	char strbuf[256];
+
+	PCWLDBG((CE_NOTE, "pcwl attach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
+	if (cmd != DDI_ATTACH)
+		goto attach_fail1;
+	/*
+	 * Allocate soft state associated with this instance.
+	 */
+	if (ddi_soft_state_zalloc(pcwl_soft_state_p,
+	    ddi_get_instance(dip)) != DDI_SUCCESS) {
+		cmn_err(CE_CONT, "pcwl attach: alloc softstate failed\n");
+		goto attach_fail1;
+	}
+	pcwl_p = (pcwl_maci_t *)ddi_get_soft_state(pcwl_soft_state_p,
+	    ddi_get_instance(dip));
+	pcwl_p->pcwl_device_type = pcwl_device_type;
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		if (ddi_regs_map_setup(dip, 0,
+		    (caddr_t *)&pcwl_p->pcwl_cfg_base, 0, 0,
+		    &accattr, &pcwl_p->pcwl_cfg_handle) != DDI_SUCCESS) {
+			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
+			    " failed\n");
+			goto attach_fail2;
+		}
+
+		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
+		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
+		stat |= (PCI_COMM_IO | PCI_COMM_MAE);
+		ddi_put16(pcwl_p->pcwl_cfg_handle,
+		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM), stat);
+		stat = ddi_get16(pcwl_p->pcwl_cfg_handle,
+		    (uint16_t *)(pcwl_p->pcwl_cfg_base + PCI_CONF_COMM));
+		if ((stat & (PCI_COMM_IO | PCI_COMM_MAE)) !=
+		    (PCI_COMM_IO | PCI_COMM_MAE)) {
+			cmn_err(CE_WARN, "pcwl(pci) attach: pci command"
+			    " reg enable failed\n");
+			goto attach_fail2a;
+		}
+
+
+		if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcwl_p->pcwl_bar,
+		    0, 0, &accattr, (ddi_acc_handle_t *)&pcwl_p->pcwl_handle)
+		    != DDI_SUCCESS) {
+			cmn_err(CE_WARN, "pcwl(pci) attach: pci_regs_map_setup"
+			    " failed\n");
+			goto attach_fail2a;
+		}
+		PCWLDBG((CE_NOTE, "pcwl(pci): regs_map_setup,bar=%p\n",
+		    (void *)pcwl_p->pcwl_bar));
+
+		/*
+		 * tricky! copy from freebsd code.
+		 */
+		PCWL_WRITE(pcwl_p, 0x26, 0x80);
+		drv_usecwait(500000);
+		PCWL_WRITE(pcwl_p, 0x26, 0x0);
+		drv_usecwait(500000);
+
+		for (i = 0; i < WL_TIMEOUT; i++) {
+			PCWL_READ(pcwl_p, 0x0, stat);
+			if (stat & WL_CMD_BUSY)
+				drv_usecwait(10);
+			else
+				break;
+		}
+		if (i == WL_TIMEOUT) {
+			cmn_err(CE_WARN, "pcwl(pci) attach: hardware init"
+			    " failed\n");
+			goto attach_fail3;
+		}
+
+		/*
+		 * magic number verification.
+		 * tricky! copy from freebsd code.
+		 */
+		PCWL_WRITE(pcwl_p, 0x28, 0x4a2d);
+		PCWL_READ(pcwl_p, 0x28, stat);
+		PCWLDBG((CE_NOTE, "pcwl(pci):magic number = %x\n", stat));
+		if (stat != 0x4a2d) {
+			cmn_err(CE_WARN, "pcwl(pci) attach: magic verify"
+			    " failed\n");
+			goto attach_fail3;
+		}
+	}
+	pcwl_p->pcwl_dip	= dip;
+	pcwl_p->pcwl_flag	= 0;
+	pcwl_p->pcwl_socket	= ddi_getprop(DDI_DEV_T_NONE, dip,
+	    DDI_PROP_DONTPASS, "socket", -1);
+	pcwl_p->pcwl_reschedule_need = B_FALSE;
+
+	if (ddi_get_iblock_cookie(dip,
+	    0, &pcwl_p->pcwl_ib_cookie) != DDI_SUCCESS) {
+		cmn_err(CE_WARN, "pcwl attach: get_iblk_cookie failed\n");
+		goto attach_fail3;
+	}
+
+	mutex_init(&pcwl_p->pcwl_glock, NULL, MUTEX_DRIVER,
+	    pcwl_p->pcwl_ib_cookie);
+	mutex_init(&pcwl_p->pcwl_scanlist_lock, NULL, MUTEX_DRIVER,
+	    pcwl_p->pcwl_ib_cookie);
+	mutex_init(&pcwl_p->pcwl_txring.wl_tx_lock, NULL, MUTEX_DRIVER,
+	    pcwl_p->pcwl_ib_cookie);
+
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		if (ret = ddi_add_intr(dip, 0, NULL, NULL,
+		    pcwl_intr, (caddr_t)pcwl_p)) {
+			cmn_err(CE_NOTE, "pcwl(pci) attach: add intr failed\n");
+			goto attach_fail3a;
+		}
+	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
+		if (ret = pcwl_register_cs(dip, pcwl_p)) {
+			cmn_err(CE_WARN, "pcwl attach(pccard): "
+			    "register_cs err %x\n", ret);
+			goto attach_fail3a;
+		}
+	} else {
+		cmn_err(CE_WARN, "pcwl attach: unsupported device type\n");
+		goto attach_fail3a;
+	}
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (ret = pcwl_reset_backend(pcwl_p)) {
+		cmn_err(CE_WARN, "pcwl attach: reset_backend failed %x\n", ret);
+		mutex_exit(&pcwl_p->pcwl_glock);
+		goto attach_fail4;
+	}
+	if (ret = pcwl_get_cap(pcwl_p)) { /* sets macaddr for mac_register */
+		cmn_err(CE_WARN, "pcwl attach: get_cap failed %x\n", ret);
+		mutex_exit(&pcwl_p->pcwl_glock);
+		goto attach_fail4;
+	}
+	mutex_exit(&pcwl_p->pcwl_glock);
+	/*
+	 * Provide initial settings for the WiFi plugin; whenever this
+	 * information changes, we need to call mac_pdata_update()
+	 */
+	wd.wd_secalloc = WIFI_SEC_NONE;
+	wd.wd_opmode = IEEE80211_M_STA;
+
+	macp = mac_alloc(MAC_VERSION);
+	if (macp == NULL) {
+		PCWLDBG((CE_NOTE, "pcwl attach: "
+		    "MAC version mismatch\n"));
+		goto attach_fail4;
+	}
+
+	macp->m_type_ident	= MAC_PLUGIN_IDENT_WIFI;
+	macp->m_driver		= pcwl_p;
+	macp->m_dip		= dip;
+	macp->m_src_addr	= pcwl_p->pcwl_mac_addr;
+	macp->m_callbacks	= &pcwl_m_callbacks;
+	macp->m_min_sdu		= 0;
+	macp->m_max_sdu		= IEEE80211_MTU;
+	macp->m_pdata		= &wd;
+	macp->m_pdata_size	= sizeof (wd);
+
+	err = mac_register(macp, &pcwl_p->pcwl_mh);
+	mac_free(macp);
+	if (err != 0) {
+		PCWLDBG((CE_NOTE, "pcwl attach: "
+		    "mac_register err\n"));
+		goto attach_fail4;
+	}
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
+		/*
+		 * turn on CS interrupt
+		 */
+		cfgmod.Attributes = CONF_ENABLE_IRQ_STEERING |
+		    CONF_IRQ_CHANGE_VALID;
+		cfgmod.Vpp1 = 0;
+		cfgmod.Vpp2 = 0;
+		(void) csx_ModifyConfiguration(pcwl_p->pcwl_chdl, &cfgmod);
+
+	}
+	if (ret = pcwl_init_nicmem(pcwl_p)) {
+		cmn_err(CE_WARN, "pcwl(pccard) attach: pcwl_init_nicmem"
+		    " failed %x\n", ret);
+		mutex_exit(&pcwl_p->pcwl_glock);
+		goto attach_fail5;
+	}
+	pcwl_chip_type(pcwl_p);
+	if (ret = pcwl_loaddef_rf(pcwl_p)) {
+		cmn_err(CE_WARN, "pcwl attach: config_rf failed%x\n", ret);
+		mutex_exit(&pcwl_p->pcwl_glock);
+		goto attach_fail5;
+	}
+	(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
+	pcwl_stop_locked(pcwl_p);	/* leaves interface down */
+	list_create(&pcwl_p->pcwl_scan_list, sizeof (wl_scan_list_t),
+	    offsetof(wl_scan_list_t, wl_scan_node));
+	pcwl_p->pcwl_scan_num = 0;
+	mutex_exit(&pcwl_p->pcwl_glock);
+	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
+	    pcwl_p, drv_usectohz(1000000));
+	instance = ddi_get_instance(dip);
+	(void) snprintf(strbuf, sizeof (strbuf), "pcwl%d", instance);
+	if (ddi_create_minor_node(dip, strbuf, S_IFCHR,
+	    instance + 1, DDI_NT_NET_WIFI, 0) != DDI_SUCCESS) {
+		goto attach_fail6;
+	}
+	pcwl_p->pcwl_flag |= PCWL_ATTACHED;
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		pcwl_p->pcwl_flag |= PCWL_CARD_READY;
+	}
+	return (DDI_SUCCESS);
+attach_fail6:
+	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
+		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
+		pcwl_p->pcwl_scanlist_timeout_id = 0;
+	}
+	list_destroy(&pcwl_p->pcwl_scan_list);
+attach_fail5:
+	(void) mac_unregister(pcwl_p->pcwl_mh);
+attach_fail4:
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
+	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
+		pcwl_unregister_cs(pcwl_p);
+	}
+attach_fail3a:
+	pcwl_destroy_locks(pcwl_p);
+attach_fail3:
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
+		ddi_regs_map_free(&pcwl_p->pcwl_handle);
+attach_fail2a:
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI)
+		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
+attach_fail2:
+	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
+attach_fail1:
+	return (DDI_FAILURE);
+}
+
+static int
+pcwl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	pcwl_maci_t *pcwl_p;
+	wl_scan_list_t *scan_item0;
+	int ret;
+	pcwl_p = ddi_get_soft_state(pcwl_soft_state_p, ddi_get_instance(dip));
+
+	PCWLDBG((CE_NOTE, "pcwl detach: dip=0x%p cmd=%x\n", (void *)dip, cmd));
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+	if (!(pcwl_p->pcwl_flag & PCWL_ATTACHED))
+		return (DDI_FAILURE);
+
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		mutex_enter(&pcwl_p->pcwl_glock);
+		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
+		PCWL_DISABLE_INTR(pcwl_p);
+		mutex_exit(&pcwl_p->pcwl_glock);
+	}
+	if (pcwl_p->pcwl_scanlist_timeout_id != 0) {
+		(void) untimeout(pcwl_p->pcwl_scanlist_timeout_id);
+		pcwl_p->pcwl_scanlist_timeout_id = 0;
+	}
+	if (pcwl_p->pcwl_connect_timeout_id != 0) {
+		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+		pcwl_p->pcwl_connect_timeout_id = 0;
+	}
+	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
+	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
+	while (scan_item0) {
+		pcwl_delete_scan_item(pcwl_p, scan_item0);
+		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
+	}
+	list_destroy(&pcwl_p->pcwl_scan_list);
+	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+	ret = mac_unregister(pcwl_p->pcwl_mh);
+	if (ret != 0)
+		return (DDI_FAILURE);
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCI) {
+		ddi_remove_intr(dip, 0, pcwl_p->pcwl_ib_cookie);
+		ddi_regs_map_free(&pcwl_p->pcwl_handle);
+		ddi_regs_map_free(&pcwl_p->pcwl_cfg_handle);
+	} else if (pcwl_p->pcwl_device_type == PCWL_DEVICE_PCCARD) {
+		pcwl_unregister_cs(pcwl_p);
+	}
+	mutex_exit(&pcwl_p->pcwl_glock);
+	pcwl_destroy_locks(pcwl_p);
+	ddi_remove_minor_node(dip, NULL);
+	ddi_soft_state_free(pcwl_soft_state_p, ddi_get_instance(dip));
+	return (DDI_SUCCESS);
+}
+
+/*
+ * card services and event handlers
+ */
+static int
+pcwl_register_cs(dev_info_t *dip, pcwl_maci_t *pcwl_p)
+{
+	int ret;
+	client_reg_t cr;
+	client_handle_t chdl; /* uint encoding of socket, function, client */
+	get_status_t card_status;
+	request_socket_mask_t sock_req;
+
+	bzero(&cr, sizeof (cr));
+	cr.Attributes	= INFO_IO_CLIENT|INFO_CARD_EXCL|INFO_CARD_SHARE;
+	cr.EventMask	= CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_REGISTRATION_COMPLETE | CS_EVENT_CARD_REMOVAL_LOWP |
+	    CS_EVENT_CARD_READY | CS_EVENT_PM_RESUME |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_CLIENT_INFO;
+	cr.event_callback_args.client_data = pcwl_p;
+	cr.Version = CS_VERSION;
+	cr.event_handler = (csfunction_t *)pcwl_ev_hdlr;
+	cr.dip = dip;
+	(void) strcpy(cr.driver_name, pcwl_name_str);
+	if (ret = csx_RegisterClient(&chdl, &cr)) {
+		cmn_err(CE_WARN, "pcwl: RegisterClient failed %x\n", ret);
+		goto regcs_ret;
+	}
+	pcwl_p->pcwl_chdl = chdl;
+
+	bzero(&card_status, sizeof (card_status));
+	(void) csx_GetStatus(chdl, &card_status);
+	PCWLDBG((CE_NOTE,
+	    "pcwl: register_cs: Sock=%x CState=%x SState=%x rState=%x\n",
+	    card_status.Socket, card_status.CardState,
+	    card_status.SocketState, card_status.raw_CardState));
+	if (!(card_status.CardState & CS_STATUS_CARD_INSERTED)) {
+		/* card is not present, why are we attaching ? */
+		ret = CS_NO_CARD;
+		goto regcs_unreg;
+	}
+	cv_init(&pcwl_p->pcwl_cscv, NULL, CV_DRIVER, NULL);
+	mutex_init(&pcwl_p->pcwl_cslock, NULL, MUTEX_DRIVER, *cr.iblk_cookie);
+	mutex_enter(&pcwl_p->pcwl_cslock);
+	if (ret = csx_MapLogSocket(chdl, &pcwl_p->pcwl_log_sock)) {
+		cmn_err(CE_WARN, "pcwl: MapLogSocket failed %x\n", ret);
+		goto regcs_fail;
+	}
+	PCWLDBG((CE_NOTE,
+	    "pcwl: register_cs: LogSock=%x PhyAdapter=%x PhySock=%x\n",
+	    pcwl_p->pcwl_log_sock.LogSocket,
+	    pcwl_p->pcwl_log_sock.PhyAdapter,
+	    pcwl_p->pcwl_log_sock.PhySocket));
+	/* turn on initialization events */
+	sock_req.Socket = 0;
+	sock_req.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_REGISTRATION_COMPLETE;
+	if (ret = csx_RequestSocketMask(chdl, &sock_req)) {
+		cmn_err(CE_WARN, "pcwl: RequestSocketMask failed %x\n", ret);
+		goto regcs_fail;
+	}
+	/* wait for and process card insertion events */
+	while (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
+		cv_wait(&pcwl_p->pcwl_cscv, &pcwl_p->pcwl_cslock);
+	mutex_exit(&pcwl_p->pcwl_cslock);
+
+	pcwl_p->pcwl_flag |= PCWL_CS_REGISTERED;
+	return (PCWL_SUCCESS);
+regcs_fail:
+	mutex_destroy(&pcwl_p->pcwl_cslock);
+	cv_destroy(&pcwl_p->pcwl_cscv);
+regcs_unreg:
+	(void) csx_DeregisterClient(chdl);
+regcs_ret:
+	pcwl_p->pcwl_flag &= ~PCWL_CS_REGISTERED;
+	return (ret);
+}
+
+static void
+pcwl_unregister_cs(pcwl_maci_t *pcwl_p)
+{
+	int ret;
+	release_socket_mask_t mask;
+	mask.Socket = pcwl_p->pcwl_socket;
+
+	/*
+	 * The card service not registered means register_cs function
+	 * doesnot return TRUE. Then all the lelated resource has been
+	 * released in register_cs.
+	 */
+	if (!(pcwl_p->pcwl_flag | PCWL_CS_REGISTERED))
+		return;
+
+	if (ret = csx_ReleaseSocketMask(pcwl_p->pcwl_chdl, &mask))
+		cmn_err(CE_WARN, "pcwl: ReleaseSocket mask failed %x\n", ret);
+
+	if (pcwl_p->pcwl_flag & PCWL_CARD_READY) {
+		pcwl_card_remove(pcwl_p);
+		pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
+	}
+	mutex_destroy(&pcwl_p->pcwl_cslock);
+	cv_destroy(&pcwl_p->pcwl_cscv);
+	if (ret = csx_DeregisterClient(pcwl_p->pcwl_chdl))
+		cmn_err(CE_WARN, "pcwl: Deregister failed %x\n", ret);
+}
+
+static void
+pcwl_destroy_locks(pcwl_maci_t *pcwl_p)
+{
+	mutex_destroy(&pcwl_p->pcwl_txring.wl_tx_lock);
+	mutex_destroy(&pcwl_p->pcwl_scanlist_lock);
+	mutex_destroy(&pcwl_p->pcwl_glock);
+}
+
+static int
+pcwl_ev_hdlr(event_t event, int priority, event_callback_args_t *arg)
+{
+	int ret = CS_SUCCESS;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg->client_data;
+	client_info_t *ci_p = (client_info_t *)&arg->client_info;
+
+	mutex_enter(&pcwl_p->pcwl_cslock);
+	switch (event) {
+	case CS_EVENT_CARD_INSERTION:
+		ret = pcwl_card_insert(pcwl_p);
+		cv_broadcast(&pcwl_p->pcwl_cscv);
+		break;
+	case CS_EVENT_REGISTRATION_COMPLETE:
+		cv_broadcast(&pcwl_p->pcwl_cscv);
+		break;
+	case CS_EVENT_CARD_REMOVAL:
+		if (priority & CS_EVENT_PRI_HIGH)
+			break;
+		pcwl_card_remove(pcwl_p);
+		cv_broadcast(&pcwl_p->pcwl_cscv);
+		break;
+	case CS_EVENT_CLIENT_INFO:
+		if (GET_CLIENT_INFO_SUBSVC(ci_p->Attributes) !=
+		    CS_CLIENT_INFO_SUBSVC_CS)
+			break;
+
+		ci_p->Revision = 0x0101;
+		ci_p->CSLevel = CS_VERSION;
+		ci_p->RevDate = CS_CLIENT_INFO_MAKE_DATE(9, 12, 14);
+		(void) strcpy(ci_p->ClientName, PCWL_IDENT_STRING);
+		(void) strcpy(ci_p->VendorName, CS_SUN_VENDOR_DESCRIPTION);
+		ci_p->Attributes |= CS_CLIENT_INFO_VALID;
+		break;
+	default:
+		ret = CS_UNSUPPORTED_EVENT;
+		break;
+	}
+	mutex_exit(&pcwl_p->pcwl_cslock);
+	return (ret);
+}
+
+static int
+pcwl_card_insert(pcwl_maci_t *pcwl_p)
+{
+	int ret, hi, lo;
+	tuple_t tuple;
+	cisparse_t cisparse;
+	io_req_t	io;
+	irq_req_t	irq;
+	config_req_t	cfg;
+	cistpl_config_t config;
+	cistpl_cftable_entry_t *tbl_p;
+	register client_handle_t chdl = pcwl_p->pcwl_chdl;
+
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcwl: get manufacture id failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&cisparse, sizeof (cisparse));
+	if (ret = csx_Parse_CISTPL_MANFID(chdl, &tuple, &cisparse.manfid)) {
+		cmn_err(CE_WARN, "pcwl: parse manufacture id failed %x\n", ret);
+		goto insert_ret;
+	}
+
+	/*
+	 * verify manufacture ID
+	 */
+	PCWLDBG((CE_NOTE, "pcwl insert: manufacturer_id=%x card=%x\n",
+	    cisparse.manfid.manf, cisparse.manfid.card));
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_FUNCID;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcwl: get function id failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&cisparse, sizeof (cisparse));
+	if (ret = csx_Parse_CISTPL_FUNCID(chdl, &tuple, &cisparse.funcid)) {
+		cmn_err(CE_WARN, "pcwl: parse function id failed %x\n", ret);
+		goto insert_ret;
+	}
+
+	/*
+	 * verify function ID
+	 */
+	PCWLDBG((CE_NOTE, "insert:fun_id=%x\n", cisparse.funcid.function));
+	bzero(&tuple, sizeof (tuple));
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	if (ret = csx_GetFirstTuple(chdl, &tuple)) {
+		cmn_err(CE_WARN, "pcwl: get config failed %x\n", ret);
+		goto insert_ret;
+	}
+	bzero(&config, sizeof (config));
+	if (ret = csx_Parse_CISTPL_CONFIG(chdl, &tuple, &config)) {
+		cmn_err(CE_WARN, "pcwl: parse config failed %x\n", ret);
+		goto insert_ret;
+	}
+	PCWLDBG((CE_NOTE,
+	    "pcwl: config present=%x nr=%x hr=%x regs[0]=%x base=%x last=%x\n",
+	    config.present, config.nr, config.hr, config.regs[0],
+	    config.base, config.last));
+	hi = 0;
+	lo = (int)-1;		/* really big number */
+	tbl_p = &cisparse.cftable;
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	for (tbl_p->index = 0; tbl_p->index <= config.hr; ) {
+		PCWLDBG((CE_NOTE, "pcwl insert:tuple idx=%x:\n", tbl_p->index));
+		if (ret = csx_GetNextTuple(chdl, &tuple)) {
+			cmn_err(CE_WARN, "pcwl: get cftable failed %x\n",
+			    ret);
+			break;
+		}
+		bzero((caddr_t)&cisparse, sizeof (cisparse));
+		if (ret = csx_Parse_CISTPL_CFTABLE_ENTRY(chdl, &tuple, tbl_p)) {
+			cmn_err(CE_WARN, "pcwl: parse cftable failed %x\n",
+			    ret);
+			break;
+		}
+		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_PWR &&
+			tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC) {
+			if (tbl_p->pd.pd_vcc.avgI > hi) {
+				hi = tbl_p->pd.pd_vcc.avgI;
+				pcwl_p->pcwl_config_hi = tbl_p->index;
+			}
+			if (tbl_p->pd.pd_vcc.avgI < lo) {
+				lo = tbl_p->pd.pd_vcc.avgI;
+				pcwl_p->pcwl_config = tbl_p->index;
+			}
+		}
+		if (tbl_p->flags & CISTPL_CFTABLE_TPCE_DEFAULT) {
+			if (tbl_p->pd.flags & CISTPL_CFTABLE_TPCE_FS_PWR_VCC)
+				pcwl_p->pcwl_vcc = tbl_p->pd.pd_vcc.nomV;
+			if (tbl_p->flags & CISTPL_CFTABLE_TPCE_FS_IO)
+				pcwl_p->pcwl_iodecode = tbl_p->io.addr_lines;
+		}
+	}
+	PCWLDBG((CE_NOTE, "pcwl: insert:cfg_hi=%x cfg=%x vcc=%x iodecode=%x\n",
+	    pcwl_p->pcwl_config_hi, pcwl_p->pcwl_config,
+	    pcwl_p->pcwl_vcc, pcwl_p->pcwl_iodecode));
+	bzero(&io, sizeof (io));
+	io.BasePort1.base = 0;
+	io.NumPorts1 = 1 << pcwl_p->pcwl_iodecode;
+	io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	io.IOAddrLines = pcwl_p->pcwl_iodecode;
+	if (ret = csx_RequestIO(chdl, &io)) {
+		cmn_err(CE_WARN, "pcwl: RequestIO failed %x\n", ret);
+		goto insert_ret;
+	}
+	pcwl_p->pcwl_port = io.BasePort1.handle;
+	if (ret = ddi_add_softintr(DIP(pcwl_p), DDI_SOFTINT_HIGH,
+	    &pcwl_p->pcwl_softint_id, &pcwl_p->pcwl_ib_cookie, NULL,
+	    pcwl_intr, (caddr_t)pcwl_p)) {
+		cmn_err(CE_NOTE, "pcwl(pccard): add softintr failed\n");
+		goto insert_ret;
+	}
+	irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+	irq.irq_handler = ddi_intr_hilevel(DIP(pcwl_p), 0) ?
+	    (csfunction_t *)pcwl_intr_hi : (csfunction_t *)pcwl_intr;
+	irq.irq_handler_arg = pcwl_p;
+	if (ret = csx_RequestIRQ(pcwl_p->pcwl_chdl, &irq)) {
+		cmn_err(CE_WARN, "pcwl: RequestIRQ failed %x\n", ret);
+		goto un_io;
+	}
+	bzero(&cfg, sizeof (cfg));
+	cfg.Attributes = 0; /* not ready for CONF_ENABLE_IRQ_STEERING yet */
+	cfg.Vcc = 50;
+	cfg.IntType = SOCKET_INTERFACE_MEMORY_AND_IO;
+	cfg.ConfigBase = config.base;
+	cfg.ConfigIndex = pcwl_p->pcwl_config;
+	cfg.Status = CCSR_IO_IS_8;
+	cfg.Present = config.present;
+	pcwl_p->pcwl_flag |= PCWL_CARD_READY;
+	if (ret = csx_RequestConfiguration(chdl, &cfg)) {
+		cmn_err(CE_WARN, "pcwl: RequestConfiguration failed %x\n", ret);
+		goto un_irq;
+	}
+	return (CS_SUCCESS);
+un_irq:
+	(void) csx_ReleaseIRQ(chdl, &irq);
+un_io:
+	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
+	(void) csx_ReleaseIO(chdl, &io);
+	pcwl_p->pcwl_port = 0;
+insert_ret:
+	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
+	return (ret);
+
+}
+
+/*
+ * assume card is already removed, don't touch the hardware
+ */
+static void
+pcwl_card_remove(pcwl_maci_t *pcwl_p)
+{
+	int ret;
+	io_req_t io;
+	irq_req_t irq;
+
+	/*
+	 * The card not ready means Insert function doesnot return TRUE.
+	 * then the IO and IRQ has been released in Insert
+	 */
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY))
+		return;
+	if (ret = csx_ReleaseConfiguration(pcwl_p->pcwl_chdl, NULL))
+		cmn_err(CE_WARN, "pcwl: ReleaseConfiguration failed %x\n", ret);
+
+	bzero(&irq, sizeof (irq));
+	if (ret = csx_ReleaseIRQ(pcwl_p->pcwl_chdl, &irq))
+		cmn_err(CE_WARN, "pcwl: ReleaseIRQ failed %x\n", ret);
+
+	ddi_remove_softintr(pcwl_p->pcwl_softint_id);
+
+	bzero(&io, sizeof (io));
+	io.BasePort1.handle = pcwl_p->pcwl_port;
+	io.NumPorts1 = 16;
+	if (ret = csx_ReleaseIO(pcwl_p->pcwl_chdl, &io))
+		cmn_err(CE_WARN, "pcwl: ReleaseIO failed %x\n", ret);
+
+	pcwl_p->pcwl_port = 0;
+	pcwl_p->pcwl_flag &= ~PCWL_CARD_READY;
+}
+
+/*
+ * mac operation interface routines
+ */
+static int
+pcwl_start(void *arg)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (PCWL_FAIL);
+	}
+	pcwl_start_locked(pcwl_p);
+	mutex_exit(&pcwl_p->pcwl_glock);
+	return (PCWL_SUCCESS);
+}
+
+static void
+pcwl_stop(void *arg)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	PCWLDBG((CE_NOTE, "pcwl_stop called\n"));
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return;
+	}
+
+	pcwl_stop_locked(pcwl_p);
+	mutex_exit(&pcwl_p->pcwl_glock);
+	if (pcwl_p->pcwl_connect_timeout_id != 0) {
+		(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+		pcwl_p->pcwl_connect_timeout_id = 0;
+	}
+}
+
+static int
+pcwl_saddr(void *arg, const uint8_t *macaddr)
+{
+	int ret = PCWL_SUCCESS;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	ether_copy(macaddr, pcwl_p->pcwl_mac_addr);
+	if (pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	if (pcwl_saddr_locked(pcwl_p)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	if (pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+		ret = PCWL_FAIL;
+	}
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcwl set_mac_addr: failed\n");
+	mutex_exit(&pcwl_p->pcwl_glock);
+	return (ret);
+}
+
+static int
+pcwl_send(pcwl_maci_t *pcwl_p, mblk_t *mblk_p)
+{
+	int i = 0;
+	char *buf, *buf_p;
+	wl_frame_t *frm_p;
+	uint16_t pkt_len, ret;
+	uint16_t xmt_id, ring_idx;
+	struct ieee80211_frame *wh;
+	struct ieee80211_llc *llc;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_LINKUP)) !=
+	    (PCWL_CARD_READY | PCWL_CARD_LINKUP)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		freemsg(mblk_p);
+		return (PCWL_SUCCESS);		/* drop packet */
+	}
+	mutex_exit(&pcwl_p->pcwl_glock);
+
+	if (pullupmsg(mblk_p, -1) == 0) {
+		freemsg(mblk_p);
+		return (PCWL_SUCCESS);		/* drop packet */
+	}
+	wh = (struct ieee80211_frame *)mblk_p->b_rptr;
+	llc = (struct ieee80211_llc *)&wh[1];
+
+	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
+	ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
+	pcwl_p->pcwl_txring.wl_tx_prod = (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
+
+	/*
+	 * check whether there is a xmt buffer available
+	 */
+	while ((i < WL_XMT_BUF_NUM) &&
+	    (pcwl_p->pcwl_txring.wl_tx_ring[ring_idx])) {
+		ring_idx = pcwl_p->pcwl_txring.wl_tx_prod;
+		pcwl_p->pcwl_txring.wl_tx_prod =
+		    (ring_idx + 1) & (WL_XMT_BUF_NUM - 1);
+		i++;
+	}
+	if (i == WL_XMT_BUF_NUM) {
+		mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
+		mutex_enter(&pcwl_p->pcwl_glock);
+		pcwl_p->pcwl_reschedule_need = B_TRUE;
+		mutex_exit(&pcwl_p->pcwl_glock);
+		pcwl_p->pcwl_noxmtbuf++;
+		return (PCWL_FAIL);
+	}
+	xmt_id = pcwl_p->pcwl_txring.wl_tx_fids[ring_idx];
+	pcwl_p->pcwl_txring.wl_tx_ring[ring_idx] = xmt_id;
+	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
+
+	buf = kmem_zalloc(PCWL_NICMEM_SZ, KM_SLEEP);
+	buf_p = (ulong_t)buf & 1 ? buf + 1 : buf;
+	frm_p = (wl_frame_t *)buf_p;
+#ifdef DEBUG
+	if (pcwl_debug & PCWL_DBG_SEND) {
+		cmn_err(CE_NOTE, "pcwl send: packet");
+		for (i = 0; i < mblk_p->b_wptr - mblk_p->b_rptr; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)mblk_p->b_rptr + i));
+	}
+#endif
+	pkt_len = msgdsize(mblk_p);
+	if (pkt_len > (PCWL_NICMEM_SZ - sizeof (wl_frame_t))) {
+		cmn_err(CE_WARN, "pcwl: send mblk is too long");
+		kmem_free(buf, PCWL_NICMEM_SZ);
+		freemsg(mblk_p);
+		return (PCWL_SUCCESS);		/* drop packet */
+	}
+	if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
+	    IEEE80211_FC1_DIR_TODS) {
+		kmem_free(buf, PCWL_NICMEM_SZ);
+		freemsg(mblk_p);
+		return (PCWL_SUCCESS);		/* drop packet */
+	}
+	bzero(frm_p, WL_802_11_HDRLEN);
+
+	frm_p->wl_tx_ctl = WL_TXCNTL_SET;
+	bcopy(wh->i_addr3, frm_p->wl_dst_addr, ETHERADDRL); /* dst macaddr */
+	bcopy(wh->i_addr2, frm_p->wl_src_addr, ETHERADDRL); /* src macaddr */
+	frm_p->wl_len = htons(pkt_len  - sizeof (*wh));
+	bcopy(llc, &frm_p->wl_dat[0], pkt_len - sizeof (*wh));
+	pkt_len = pkt_len - (sizeof (*wh) + sizeof (*llc)) +
+	    WL_802_11_HDRLEN;
+	PCWLDBG((CE_NOTE, "send: DIX frmsz=%x pkt_len=%x\n",
+	    WL_802_11_HDRLEN, pkt_len));
+
+	if (pkt_len & 1)	/* round up to 16-bit boundary and pad 0 */
+		buf_p[pkt_len++] = 0;
+
+	ASSERT(pkt_len <= PCWL_NICMEM_SZ);
+#ifdef DEBUG
+	if (pcwl_debug & PCWL_DBG_SEND) {
+		cmn_err(CE_NOTE, "pkt_len = %x\n", pkt_len);
+		for (i = 0; i < pkt_len; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((unsigned char *)buf + i));
+	}
+#endif
+	mutex_enter(&pcwl_p->pcwl_glock);
+	ret = (WRCH1(pcwl_p, xmt_id, 0, (uint16_t *)buf_p, 0x2e) ||
+	    WRPKT(pcwl_p, xmt_id, 0x2e, (uint16_t *)(buf_p + 0x2e),
+	    pkt_len - 0x2e));
+	if (ret) {
+		goto done;
+	}
+	PCWLDBG((CE_NOTE, "send: xmt_id=%x send=%x\n", xmt_id, pkt_len));
+	(void) pcwl_set_cmd(pcwl_p, WL_CMD_TX | WL_RECLAIM, xmt_id);
+
+done:
+	mutex_exit(&pcwl_p->pcwl_glock);
+	kmem_free(buf, PCWL_NICMEM_SZ);
+	freemsg(mblk_p);
+	return (PCWL_SUCCESS);
+}
+
+static mblk_t *
+pcwl_tx(void *arg, mblk_t *mp)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	mblk_t *next;
+
+	ASSERT(mp != NULL);
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if ((pcwl_p->pcwl_flag & (PCWL_CARD_LINKUP | PCWL_CARD_READY)) !=
+	    (PCWL_CARD_LINKUP | PCWL_CARD_READY)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (mp);
+	}
+	mutex_exit(&pcwl_p->pcwl_glock);
+	while (mp != NULL) {
+		next =  mp->b_next;
+		mp->b_next = NULL;
+
+		if (pcwl_send(pcwl_p, mp)) {
+			mp->b_next = next;
+			break;
+		}
+		mp = next;
+	}
+	return (mp);
+}
+
+static int
+pcwl_prom(void *arg, boolean_t on)
+{
+	int ret = PCWL_SUCCESS;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+
+	PCWLDBG((CE_NOTE, "pcwl_prom called %x\n", on));
+
+	if (on)
+		pcwl_p->pcwl_rf.rf_promiscuous = 1;
+	else
+		pcwl_p->pcwl_rf.rf_promiscuous = 0;
+	if (ret = pcwl_fil_ltv(pcwl_p, 2, WL_RID_PROMISC,
+	    pcwl_p->pcwl_rf.rf_promiscuous)) {
+		ret = PCWL_FAIL;
+	}
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcwl promisc: failed\n");
+	mutex_exit(&pcwl_p->pcwl_glock);
+	return (ret);
+}
+
+static int
+pcwl_gstat(void *arg, uint_t statitem, uint64_t *val)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	int ret = PCWL_SUCCESS;
+	uint64_t *cntr_p = pcwl_p->pcwl_cntrs_s;
+	uint16_t rate = 0;
+	uint64_t speed;
+
+	PCWLDBG((CE_NOTE, "pcwl_gstat called\n"));
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+
+	if (pcwl_get_ltv(pcwl_p, 2, WL_RID_CUR_TX_RATE, &rate)) {
+		cmn_err(CE_WARN, "pcwl kstat: get speed failed\n");
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	switch (pcwl_p->pcwl_chip_type) {
+	case PCWL_CHIP_PRISMII:
+		switch (rate) {
+		case WL_SPEED_1Mbps_P2:		rate = 2;	break;
+		case WL_SPEED_2Mbps_P2:		rate = 4;	break;
+		case WL_SPEED_55Mbps_P2:	rate = 11;	break;
+		case WL_SPEED_11Mbps_P2:	rate = 22;	break;
+		default:			rate = 0;	break;
+		}
+		speed = rate * 500000;
+		break;
+	case PCWL_CHIP_LUCENT:
+	default:
+		speed = rate * 1000000;
+		if (rate == 6)
+			speed = 5500000;
+		break;
+	}
+
+	switch (statitem) {
+	case MAC_STAT_IFSPEED:
+		*val = speed;
+		break;
+	case MAC_STAT_NOXMTBUF:
+		*val = pcwl_p->pcwl_noxmtbuf;
+		break;
+	case MAC_STAT_NORCVBUF:
+		*val = cntr_p[WLC_RX_DISCARDS_NOBUF];
+		break;
+	case MAC_STAT_IERRORS:
+		*val = 0;
+		break;
+	case MAC_STAT_OERRORS:
+		*val = cntr_p[WLC_TX_DISCARDS] +
+		    cntr_p[WLC_TX_DISCARDS_WRONG_SA];
+		break;
+	case MAC_STAT_RBYTES:
+		*val = cntr_p[WLC_RX_UNICAST_OCTETS];
+		break;
+	case MAC_STAT_IPACKETS:
+		*val = cntr_p[WLC_RX_UNICAST_FRAMES];
+		break;
+	case MAC_STAT_OBYTES:
+		*val = cntr_p[WLC_TX_UNICAST_OCTETS];
+		break;
+	case MAC_STAT_OPACKETS:
+		*val = cntr_p[WLC_TX_UNICAST_FRAMES];
+		break;
+	case WIFI_STAT_TX_FAILED:
+		*val = cntr_p[WLC_TX_RETRY_LIMIT] +
+		    cntr_p[WLC_TX_DEFERRED_XMITS];
+		break;
+	case WIFI_STAT_TX_RETRANS:
+		*val = cntr_p[WLC_TX_SINGLE_RETRIES] +
+		    cntr_p[WLC_TX_MULTI_RETRIES];
+		break;
+	case WIFI_STAT_FCS_ERRORS:
+		*val = cntr_p[WLC_RX_FCS_ERRORS];
+		break;
+	case WIFI_STAT_WEP_ERRORS:
+		*val = cntr_p[WLC_RX_WEP_CANT_DECRYPT];
+		break;
+	case WIFI_STAT_MCAST_TX:
+		*val = cntr_p[WLC_TX_MULTICAST_FRAMES];
+		break;
+	case WIFI_STAT_MCAST_RX:
+		*val = cntr_p[WLC_RX_MULTICAST_FRAMES];
+		break;
+	case WIFI_STAT_TX_FRAGS:
+		*val = cntr_p[WLC_TX_FRAGMENTS];
+		break;
+	case WIFI_STAT_RX_FRAGS:
+		*val =	cntr_p[WLC_RX_FRAGMENTS] +
+		    cntr_p[WLC_RX_MSG_IN_MSG_FRAGS] +
+		    cntr_p[WLC_RX_MSG_IN_BAD_MSG_FRAGS];
+		break;
+	case WIFI_STAT_RTS_SUCCESS:
+	case WIFI_STAT_RTS_FAILURE:
+	case WIFI_STAT_ACK_FAILURE:
+	case WIFI_STAT_RX_DUPS:
+		*val = 0;
+		break;
+	default:
+		ret = ENOTSUP;
+	}
+done:
+	mutex_exit(&pcwl_p->pcwl_glock);
+	return (ret);
+}
+
+static int
+pcwl_sdmulti(void *arg, boolean_t add, const uint8_t *eth_p)
+{
+	int ret = PCWL_SUCCESS;
+	uint16_t i;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	uint16_t *mc_p = pcwl_p->pcwl_mcast;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		ret = PCWL_FAIL;
+		goto done;
+	}
+
+	if (add) { /* enable multicast on eth_p, search for available entries */
+		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
+			if (!ether_cmp(eth_p, mc_p))
+				break;
+		}
+		if (i < 16)	/* already part of the filter */
+			goto done;
+		mc_p = pcwl_p->pcwl_mcast;	/* reset mc_p for 2nd scan */
+		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
+			PCWLDBG((CE_NOTE, "smulti: mc[%x]=%s\n", i,
+			    ether_sprintf((struct ether_addr *)mc_p)));
+			if (mc_p[0] == 0 && mc_p[1] == 0 && mc_p[2] == 0)
+				break;
+		}
+		if (i >= 16)	/* can't find a vacant entry */
+			goto done;
+		ether_copy(eth_p, mc_p);
+	} else { /* disable multicast, locate the entry and clear it */
+		for (i = 0; i < 16; i++, mc_p += (ETHERADDRL >> 1)) {
+			if (!ether_cmp(eth_p, mc_p))
+				break;
+		}
+		if (i >= 16)
+			goto done;
+		mc_p[0] = 0;
+		mc_p[1] = 0;
+		mc_p[2] = 0;
+	}
+	/*
+	 * re-blow the entire 16 entries buffer
+	 */
+	if (i = pcwl_put_ltv(pcwl_p, ETHERADDRL << 4, WL_RID_MCAST,
+	    pcwl_p->pcwl_mcast)) {
+		ret = PCWL_FAIL;
+	}
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcwl set multi addr: failed\n");
+	mutex_exit(&pcwl_p->pcwl_glock);
+	return (ret);
+}
+
+static uint_t
+pcwl_intr(caddr_t arg)
+{
+	uint16_t stat;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
+	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
+	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+
+	PCWL_WRITE(pcwl_p, WL_INT_EN, 0);
+	if (stat & WL_EV_RX) {
+		pcwl_rcv(pcwl_p);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_RX);
+	}
+	if (stat & WL_EV_TX) {
+		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
+			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
+				mutex_exit(&pcwl_p->pcwl_glock);
+				mac_tx_update(GLD3(pcwl_p));
+				mutex_enter(&pcwl_p->pcwl_glock);
+				pcwl_p->pcwl_reschedule_need = B_FALSE;
+			}
+		}
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX);
+	}
+	if (stat & WL_EV_ALLOC) {
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC | 0x1000);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, 0x1000);
+	}
+	if (stat & WL_EV_INFO) {
+		pcwl_infodone(pcwl_p);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO);
+	}
+	if (stat & WL_EV_TX_EXC) {
+		if (pcwl_txdone(pcwl_p) == PCWL_SUCCESS) {
+			if (pcwl_p->pcwl_reschedule_need == B_TRUE) {
+				mutex_exit(&pcwl_p->pcwl_glock);
+				mac_tx_update(GLD3(pcwl_p));
+				mutex_enter(&pcwl_p->pcwl_glock);
+				pcwl_p->pcwl_reschedule_need = B_FALSE;
+			}
+		}
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX_EXC);
+	}
+	if (stat & WL_EV_INFO_DROP) {
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
+		PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_INFO_DROP);
+	}
+	PCWL_ENABLE_INTR(pcwl_p);
+	mutex_exit(&pcwl_p->pcwl_glock);
+
+	return (DDI_INTR_CLAIMED);
+}
+
+static uint_t
+pcwl_intr_hi(caddr_t arg)
+{
+	uint16_t stat;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if ((pcwl_p->pcwl_flag & (PCWL_CARD_READY | PCWL_CARD_INTREN)) !=
+	    (PCWL_CARD_READY | PCWL_CARD_INTREN)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
+	if (!(stat & WL_INTRS) || stat == WL_EV_ALL) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (DDI_INTR_UNCLAIMED);
+	}
+	PCWL_WRITE(pcwl_p, WL_INT_EN, 0); /* disable interrupt without ack */
+	mutex_exit(&pcwl_p->pcwl_glock);
+	ddi_trigger_softintr(pcwl_p->pcwl_softint_id);
+	return (DDI_INTR_CLAIMED);
+}
+
+/*
+ * called at interrupt context to retrieve data from card
+ */
+static void
+pcwl_rcv(pcwl_maci_t *pcwl_p)
+{
+	uint16_t id, len, off, ret, frm_ctl;
+	wl_frame_t frm;
+	mblk_t *mp = allocb(PCWL_NICMEM_SZ, BPRI_MED);
+	if (!mp)
+		return;
+	ASSERT(mp->b_rptr == mp->b_wptr);
+
+	PCWL_READ(pcwl_p, WL_RX_FID, id);
+	PCWL_WRITE(pcwl_p, WL_RX_FID, 0);
+	if (id == WL_INVALID_FID) {
+		PCWLDBG((CE_NOTE, "pcwl rcv: get rx_fid failed\n"));
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	if (ret = RDCH0(pcwl_p, id, 0, (uint16_t *)&frm, sizeof (frm))) {
+		PCWLDBG((CE_NOTE, "pcwl rcv: read frm failed %x\n", ret));
+		goto done;
+	}
+	if (frm.wl_status & WL_STAT_ERRSTAT) {
+		PCWLDBG((CE_NOTE, "pcwl rcv: errstat %x\n", frm.wl_status));
+		ret = frm.wl_status;
+		goto done;
+	}
+	PCWLDBG((CE_NOTE, "pcwl rcv: frame type %x\n", frm.wl_status));
+#ifdef DEBUG
+	if (pcwl_debug & PCWL_DBG_RCV) {
+		int i;
+		cmn_err(CE_NOTE, "pcwl rcv: frm header\n");
+		for (i = 0; i < WL_802_11_HDRLEN; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((uint8_t *)&frm + i));
+	}
+#endif
+	len = frm.wl_dat_len;
+	/*
+	 * this driver deal with WEP by itself. so plugin always thinks no wep.
+	 */
+	frm.wl_frame_ctl &= ~(IEEE80211_FC1_WEP << 8);
+	frm_ctl = frm.wl_frame_ctl;
+	switch (frm.wl_status) {
+	case WL_STAT_1042:
+	case WL_STAT_TUNNEL:
+	case WL_STAT_WMP_MSG:
+		PCWL_SWAP16((uint16_t *)&frm.wl_frame_ctl,
+		    sizeof (struct ieee80211_frame));
+		/*
+		 * discard those frames which are not from the AP we connect or
+		 * without 'ap->sta' direction
+		 */
+		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_BSS) &&
+		    ((((frm_ctl >> 8) & IEEE80211_FC1_DIR_MASK) !=
+		    IEEE80211_FC1_DIR_FROMDS) ||
+		    bcmp(pcwl_p->pcwl_bssid, frm.wl_addr2, 6) != 0)) {
+			ret = PCWL_FAIL;
+			goto done;
+		}
+
+		bcopy(&frm.wl_frame_ctl, mp->b_wptr,
+		    sizeof (struct ieee80211_frame));
+		mp->b_wptr += sizeof (struct ieee80211_frame);
+
+		PCWL_SWAP16((uint16_t *)&frm.wl_dat[0],
+		    sizeof (struct ieee80211_llc));
+		bcopy(&frm.wl_dat[0], mp->b_wptr,
+		    sizeof (struct ieee80211_llc));
+		mp->b_wptr += sizeof (struct ieee80211_llc);
+
+		len -= (2 + WL_SNAPHDR_LEN);
+		off = WL_802_11_HDRLEN;
+		break;
+	default:
+		PCWLDBG((CE_NOTE, "pcwl rcv: incorrect pkt\n"));
+		break;
+	}
+	if (len > MBLKSIZE(mp)) {
+		PCWLDBG((CE_NOTE, "pcwl rcv: oversz pkt %x\n", len));
+		ret = PCWL_FAIL;
+		goto done;
+	}
+	if (len & 1)
+		len++;
+	ret = RDPKT(pcwl_p, id, off, (uint16_t *)mp->b_wptr, len);
+done:
+	if (ret) {
+		PCWLDBG((CE_NOTE, "pcwl rcv: rd data %x\n", ret));
+		freemsg(mp);
+		return;
+	}
+	mp->b_wptr = mp->b_wptr + len;
+#ifdef DEBUG
+	if (pcwl_debug & PCWL_DBG_RCV) {
+		int i;
+		cmn_err(CE_NOTE, "pcwl rcv: len=0x%x\n", len);
+		for (i = 0; i < len+14; i++)
+			cmn_err(CE_NOTE, "%x: %x\n", i,
+			    *((uint8_t *)mp->b_rptr + i));
+	}
+#endif
+	mutex_exit(&pcwl_p->pcwl_glock);
+	mac_rx(GLD3(pcwl_p), NULL, mp);
+	mutex_enter(&pcwl_p->pcwl_glock);
+}
+
+static uint32_t
+pcwl_txdone(pcwl_maci_t *pcwl_p)
+{
+	uint16_t fid, i;
+	PCWL_READ(pcwl_p, WL_ALLOC_FID, fid);
+	PCWL_WRITE(pcwl_p, WL_ALLOC_FID, 0);
+
+	mutex_enter(&pcwl_p->pcwl_txring.wl_tx_lock);
+	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
+		if (fid == pcwl_p->pcwl_txring.wl_tx_ring[i]) {
+			pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
+			break;
+		}
+	}
+	pcwl_p->pcwl_txring.wl_tx_cons =
+	    (pcwl_p->pcwl_txring.wl_tx_cons + 1) & (WL_XMT_BUF_NUM - 1);
+	mutex_exit(&pcwl_p->pcwl_txring.wl_tx_lock);
+	if (i == WL_XMT_BUF_NUM)
+		return (PCWL_FAIL);
+	return (PCWL_SUCCESS);
+
+}
+
+static void
+pcwl_infodone(pcwl_maci_t *pcwl_p)
+{
+	uint16_t id, ret, i;
+	uint16_t linkStatus[2];
+	uint16_t linkStat;
+	wifi_data_t wd = { 0 };
+
+	PCWL_READ(pcwl_p, WL_INFO_FID, id);
+	if (id == WL_INVALID_FID) {
+		cmn_err(CE_WARN, "pcwl infodone: read info_fid failed\n");
+		return;
+	}
+	if (ret = RDCH0(pcwl_p, id, 0, linkStatus, sizeof (linkStatus))) {
+		PCWLDBG((CE_WARN, "pcwl infodone read infoFrm failed %x\n",
+		    ret));
+		return;
+	}
+	PCWLDBG((CE_NOTE, "pcwl infodone: Frame length= %x, Frame Type = %x\n",
+	    linkStatus[0], linkStatus[1]));
+
+	switch (linkStatus[1]) {
+	case WL_INFO_LINK_STAT:
+		(void) RDCH0(pcwl_p, id, sizeof (linkStatus), &linkStat,
+		    sizeof (linkStat));
+		PCWLDBG((CE_NOTE, "pcwl infodone: link status=%x\n", linkStat));
+		if (!(pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
+		    linkStat == WL_LINK_CONNECT) {
+#ifdef DEBUG
+		if (pcwl_debug & PCWL_DBG_LINKINFO)
+			cmn_err(CE_NOTE, "pcwl: Link up \n");
+#endif
+			pcwl_p->pcwl_flag |= PCWL_CARD_LINKUP;
+			mutex_exit(&pcwl_p->pcwl_glock);
+			if (pcwl_p->pcwl_connect_timeout_id != 0) {
+				(void) untimeout(pcwl_p->
+				    pcwl_connect_timeout_id);
+				pcwl_p->pcwl_connect_timeout_id = 0;
+			}
+			mutex_enter(&pcwl_p->pcwl_glock);
+			mac_link_update(GLD3(pcwl_p), LINK_STATE_UP);
+			(void) pcwl_get_ltv(pcwl_p, 6,
+			    WL_RID_BSSID, (uint16_t *)pcwl_p->pcwl_bssid);
+			PCWL_SWAP16((uint16_t *)pcwl_p->pcwl_bssid, 6);
+			pcwl_get_rssi(pcwl_p);
+			bcopy(pcwl_p->pcwl_bssid, wd.wd_bssid, 6);
+			wd.wd_secalloc = WIFI_SEC_NONE;
+			wd.wd_opmode = IEEE80211_M_STA;
+			(void) mac_pdata_update(pcwl_p->pcwl_mh, &wd,
+			    sizeof (wd));
+		}
+		if ((pcwl_p->pcwl_flag & PCWL_CARD_LINKUP) &&
+		    ((linkStat == WL_LINK_DISCONNECT) ||
+		    (linkStat == WL_LINK_AP_OOR))) {
+#ifdef DEBUG
+		if (pcwl_debug & PCWL_DBG_LINKINFO)
+			cmn_err(CE_NOTE, "pcwl: Link down \n");
+#endif
+			PCWLDBG((CE_NOTE, "pcwl infodone: link status = %d\n",
+			    linkStat));
+			pcwl_p->pcwl_flag &= ~PCWL_CARD_LINKUP;
+			if (linkStat == WL_LINK_AP_OOR)
+				pcwl_p->pcwl_connect_timeout_id =
+				    timeout(pcwl_connect_timeout,
+				    pcwl_p, drv_usectohz(1000));
+			mutex_exit(&pcwl_p->pcwl_glock);
+			mac_link_update(GLD3(pcwl_p), LINK_STATE_DOWN);
+			mutex_enter(&pcwl_p->pcwl_glock);
+		}
+		break;
+	case WL_INFO_SCAN_RESULTS:
+	case WL_INFO_HSCAN_RESULTS:
+		pcwl_ssid_scan(pcwl_p, id, linkStatus[0], linkStatus[1]);
+			break;
+	case WL_INFO_COUNTERS:
+		linkStatus[0]--;
+		if (linkStatus[0] > WLC_STAT_CNT) {
+			linkStatus[0] = MIN(linkStatus[0], WLC_STAT_CNT);
+		}
+		(void) RDCH0(pcwl_p, id, sizeof (linkStatus),
+		    pcwl_p->pcwl_cntrs_t, linkStatus[0]<<1);
+		/*
+		 * accumulate all the statistics items for kstat use.
+		 */
+		for (i = 0; i < WLC_STAT_CNT; i++)
+			pcwl_p->pcwl_cntrs_s[i] += pcwl_p->pcwl_cntrs_t[i];
+		break;
+	default:
+		break;
+	}
+}
+
+static uint16_t
+pcwl_set_cmd(pcwl_maci_t *pcwl_p, uint16_t cmd, uint16_t param)
+{
+	int i;
+	uint16_t stat;
+
+	if (((cmd == WL_CMD_ENABLE) &&
+	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) != 0)) ||
+	    ((cmd == WL_CMD_DISABLE) &&
+	    ((pcwl_p->pcwl_flag & PCWL_ENABLED) == 0)))
+		return (PCWL_SUCCESS);
+
+	for (i = 0; i < WL_TIMEOUT; i++) {
+		PCWL_READ(pcwl_p, WL_COMMAND, stat);
+		if (stat & WL_CMD_BUSY) {
+			drv_usecwait(1);
+		} else {
+			break;
+		}
+	}
+	if (i == WL_TIMEOUT) {
+		cmn_err(CE_WARN, "pcwl: setcmd %x, %x timeout %x due to "
+		    "busy bit\n", cmd, param, stat);
+		return (PCWL_TIMEDOUT_CMD);
+	}
+
+	PCWL_WRITE(pcwl_p, WL_PARAM0, param);
+	PCWL_WRITE(pcwl_p, WL_PARAM1, 0);
+	PCWL_WRITE(pcwl_p, WL_PARAM2, 0);
+	PCWL_WRITE(pcwl_p, WL_COMMAND, cmd);
+	if (cmd == WL_CMD_INI)
+		drv_usecwait(100000); /* wait .1 sec */
+
+	for (i = 0; i < WL_TIMEOUT; i++) {
+		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
+		if (!(stat & WL_EV_CMD)) {
+			drv_usecwait(1);
+		} else {
+			break;
+		}
+	}
+	if (i == WL_TIMEOUT) {
+		cmn_err(CE_WARN, "pcwl: setcmd %x,%x timeout %x\n",
+		    cmd, param, stat);
+		if (stat & (WL_EV_ALLOC | WL_EV_RX))
+			PCWL_WRITE(pcwl_p, WL_EVENT_ACK, stat);
+		return (PCWL_TIMEDOUT_CMD);
+	}
+	PCWL_READ(pcwl_p, WL_STATUS, stat);
+	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_CMD);
+	if (stat & WL_STAT_CMD_RESULT) { /* err in feedback status */
+		cmn_err(CE_WARN, "pcwl: set_cmd %x,%x failed %x\n",
+		    cmd, param, stat);
+		return (PCWL_FAILURE_CMD);
+	}
+	if (cmd == WL_CMD_ENABLE)
+		pcwl_p->pcwl_flag |= PCWL_ENABLED;
+	if (cmd == WL_CMD_DISABLE)
+		pcwl_p->pcwl_flag &= (~PCWL_ENABLED);
+	return (PCWL_SUCCESS);
+}
+
+static uint16_t
+pcwl_set_ch(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t channel)
+{
+	int i;
+	uint16_t stat, select, offset;
+
+	if (channel) {
+		select = WL_SEL1;
+		offset = WL_OFF1;
+	} else {
+		select = WL_SEL0;
+		offset = WL_OFF0;
+	}
+	PCWL_WRITE(pcwl_p, select, type);
+	PCWL_WRITE(pcwl_p, offset, off);
+	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
+		PCWL_READ(pcwl_p, offset, stat);
+		if (!(stat & (WL_OFF_BUSY|WL_OFF_ERR)))
+			break;
+		else {
+			drv_usecwait(1);
+		}
+	}
+	if (i == WL_TIMEOUT) {
+		cmn_err(CE_WARN, "set_ch%d %x,%x failed %x\n",
+		    channel, type, off, stat);
+		return (PCWL_TIMEDOUT_TARGET);
+	}
+	return (PCWL_SUCCESS);
+}
+
+static uint16_t
+pcwl_get_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
+{
+	uint16_t stat;
+
+	ASSERT(!(len & 1));
+	len >>= 1;	/* convert bytes to 16-bit words */
+
+	/*
+	 * 1. select read mode
+	 */
+	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS | WL_ACCESS_READ, type))
+		return (stat);
+
+	/*
+	 * 2. select Buffer Access Path (channel) 1 for PIO
+	 */
+	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
+		return (stat);
+
+	/*
+	 * 3. read length
+	 */
+	PCWL_READ(pcwl_p, WL_DATA1, stat);
+	if (stat != (len + 1)) {
+		PCWLDBG((CE_NOTE, "get_ltv 0x%x expected 0x%x+1, got 0x%x\n",
+		    type, (len + 1) << 1, stat));
+		stat = (stat >> 1) - 1;
+		len = MIN(stat, len);
+	}
+
+	/*
+	 * 4. read type
+	 */
+	PCWL_READ(pcwl_p, WL_DATA1, stat);
+	if (stat != type)
+		return (PCWL_BADTYPE);
+
+	/*
+	 * 5. read value
+	 */
+	for (stat = 0; stat < len; stat++, val_p++) {
+		PCWL_READ_P(pcwl_p, WL_DATA1, val_p, 1);
+	}
+	return (PCWL_SUCCESS);
+}
+
+static uint16_t
+pcwl_fil_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t val)
+{
+	uint16_t stat;
+
+	ASSERT(!(len & 1));
+
+	/*
+	 * 1. select Buffer Access Path (channel) 1 for PIO
+	 */
+	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
+		return (stat);
+
+	/*
+	 * 2. write length
+	 */
+	len >>= 1;		/* convert bytes to 16-bit words */
+	stat = len + 1;		/* 1 extra word */
+	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
+
+	/*
+	 * 3. write type
+	 */
+	PCWL_WRITE(pcwl_p, WL_DATA1, type);
+
+	/*
+	 * 4. fill value
+	 */
+	for (stat = 0; stat < len; stat++) {
+		PCWL_WRITE(pcwl_p, WL_DATA1, val);
+	}
+
+	/*
+	 * 5. select write mode
+	 */
+	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
+}
+
+static uint16_t
+pcwl_put_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type, uint16_t *val_p)
+{
+	uint16_t stat;
+
+	ASSERT(!(len & 1));
+
+	/*
+	 * 1. select Buffer Access Path (channel) 1 for PIO
+	 */
+	if (stat = pcwl_set_ch(pcwl_p, type, 0, 1))
+		return (stat);
+
+	/*
+	 * 2. write length
+	 */
+	len >>= 1;		/* convert bytes to 16-bit words */
+	stat = len + 1;		/* 1 extra word */
+	PCWL_WRITE(pcwl_p, WL_DATA1, stat);
+
+	/*
+	 * 3. write type
+	 */
+	PCWL_WRITE(pcwl_p, WL_DATA1, type);
+
+	/*
+	 * 4. write value
+	 */
+	for (stat = 0; stat < len; stat++, val_p++) {
+		PCWL_WRITE_P(pcwl_p, WL_DATA1, val_p, 1);
+	}
+
+	/*
+	 * 5. select write mode
+	 */
+	return (pcwl_set_cmd(pcwl_p, WL_CMD_ACCESS|WL_ACCESS_WRITE, type));
+}
+
+#define	PCWL_COMPSTR_LEN	34
+static uint16_t
+pcwl_put_str(pcwl_maci_t *pcwl_p, uint16_t type, char *str_p)
+{
+	uint16_t buf[PCWL_COMPSTR_LEN / 2];
+	uint8_t str_len = strlen(str_p);
+
+	bzero(buf, PCWL_COMPSTR_LEN);
+	buf[0] = str_len;
+	bcopy(str_p, (caddr_t)(buf + 1), str_len);
+	PCWLDBG((CE_NOTE, "put_str: buf[0]=%x buf=%s\n",
+	    buf[0], (caddr_t)(buf + 1)));
+	PCWL_SWAP16(buf + 1, PCWL_COMPSTR_LEN - 2);
+	return (pcwl_put_ltv(pcwl_p, PCWL_COMPSTR_LEN, type, buf));
+}
+
+/*ARGSUSED*/
+static uint16_t
+pcwl_rdch0(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
+	int len, int order)
+{
+	uint16_t o;
+	ASSERT(!(len & 1));
+	/*
+	 * It seems that for PrismII chip, frequently overlap use of path0
+	 * and path1 may hang the hardware. So for PrismII chip, just use
+	 * path1. Test proves this workaround is OK.
+	 */
+	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
+		if (type = pcwl_set_ch(pcwl_p, type, off, 1))
+			return (type);
+		o = WL_DATA1;
+	} else {
+		if (type = pcwl_set_ch(pcwl_p, type, off, 0))
+			return (type);
+		o = WL_DATA0;
+	}
+	len >>= 1;
+	for (off = 0; off < len; off++, buf_p++) {
+		PCWL_READ_P(pcwl_p, o, buf_p, order);
+	}
+	return (PCWL_SUCCESS);
+}
+
+/*ARGSUSED*/
+static uint16_t
+pcwl_wrch1(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off, uint16_t *buf_p,
+	int len, int order)
+{
+	ASSERT(!(len & 1));
+	if (type = pcwl_set_ch(pcwl_p, type, off, 1))
+		return (type);
+	len >>= 1;
+	for (off = 0; off < len; off++, buf_p++) {
+		PCWL_WRITE_P(pcwl_p, WL_DATA1, buf_p, order);
+	}
+	return (PCWL_SUCCESS);
+}
+
+static uint16_t
+pcwl_alloc_nicmem(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t *id_p)
+{
+	int i;
+	uint16_t stat;
+
+	len = ((len + 1) >> 1) << 1;	/* round up to 16-bit boundary */
+
+	if (stat = pcwl_set_cmd(pcwl_p, WL_CMD_ALLOC_MEM, len))
+		return (stat);
+	for (stat = 0, i = 0; i < WL_TIMEOUT; i++) {
+		PCWL_READ(pcwl_p, WL_EVENT_STAT, stat);
+		if (stat & WL_EV_ALLOC)
+			break;
+		else
+			drv_usecwait(1);
+	}
+	if (i == WL_TIMEOUT)
+		return (PCWL_TIMEDOUT_ALLOC);
+	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_ALLOC);
+	PCWL_READ(pcwl_p, WL_ALLOC_FID, stat);
+	*id_p = stat;
+
+	/*
+	 * zero fill the allocated NIC mem - sort of pcwl_fill_ch
+	 */
+	(void) pcwl_set_ch(pcwl_p, stat, 0, 1);
+
+	for (len >>= 1, stat = 0; stat < len; stat++) {
+		PCWL_WRITE(pcwl_p, WL_DATA1, 0);
+	}
+	return (PCWL_SUCCESS);
+}
+
+static int
+pcwl_add_scan_item(pcwl_maci_t *pcwl_p, wl_scan_result_t s)
+{
+	wl_scan_list_t *scan_item;
+
+	scan_item = kmem_zalloc(sizeof (wl_scan_list_t), KM_SLEEP);
+	if (scan_item == NULL) {
+		cmn_err(CE_WARN, "pcwl add_scan_item: zalloc failed\n");
+		return (PCWL_FAIL);
+	}
+	scan_item->wl_val = s;
+	scan_item->wl_timeout = WL_SCAN_TIMEOUT_MAX;
+	list_insert_tail(&pcwl_p->pcwl_scan_list, scan_item);
+	pcwl_p->pcwl_scan_num++;
+	return (PCWL_SUCCESS);
+}
+
+static void
+pcwl_delete_scan_item(pcwl_maci_t *pcwl_p, wl_scan_list_t *s)
+{
+	list_remove(&pcwl_p->pcwl_scan_list, s);
+	kmem_free(s, sizeof (*s));
+	pcwl_p->pcwl_scan_num--;
+}
+
+static void
+pcwl_scanlist_timeout(void *arg)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	wl_scan_list_t *scan_item0, *scan_item1;
+
+	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
+	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
+	for (; scan_item0; ) {
+		PCWLDBG((CE_NOTE, "ssid = %s\n",
+		    scan_item0->wl_val.wl_srt_ssid));
+		PCWLDBG((CE_NOTE, "timeout left: %ds",
+		    scan_item0->wl_timeout));
+		scan_item1 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
+		if (scan_item0->wl_timeout == 0) {
+			pcwl_delete_scan_item(pcwl_p, scan_item0);
+		} else {
+			scan_item0->wl_timeout--;
+		}
+		scan_item0 = scan_item1;
+	}
+	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+	pcwl_p->pcwl_scanlist_timeout_id = timeout(pcwl_scanlist_timeout,
+	    pcwl_p, drv_usectohz(1000000));
+}
+
+static void
+pcwl_get_rssi(pcwl_maci_t *pcwl_p)
+{
+	wl_scan_list_t *scan_item0;
+	uint16_t cq[3];
+
+	bzero(cq, sizeof (cq));
+	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
+	scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
+	for (; scan_item0; ) {
+		if (bcmp(scan_item0->wl_val.wl_srt_bssid,
+		    pcwl_p->pcwl_bssid, 6) == 0) {
+			pcwl_p->pcwl_rssi = scan_item0->wl_val.wl_srt_sl;
+		}
+		scan_item0 = list_next(&pcwl_p->pcwl_scan_list, scan_item0);
+	}
+	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+	if (!pcwl_p->pcwl_rssi) {
+		(void) pcwl_get_ltv(pcwl_p, 6, WL_RID_COMMQUAL, cq);
+		pcwl_p->pcwl_rssi = cq[1];
+	}
+}
+
+/*
+ * Note:
+ * PrismII chipset has 2 extra space for the reason why scan is initiated
+ */
+static void
+pcwl_ssid_scan(pcwl_maci_t *pcwl_p, uint16_t fid, uint16_t flen, uint16_t stype)
+{
+	uint16_t stat;
+	uint16_t ssidNum, i;
+	uint16_t off, szbuf;
+	uint16_t tmp[2];
+	wl_scan_list_t *scan_item0;
+	uint32_t check_num;
+	uint8_t	bssid_t[6];
+
+	wl_scan_result_t sctbl;
+
+	off = sizeof (uint16_t) * 2;
+	switch (pcwl_p->pcwl_chip_type) {
+	case PCWL_CHIP_PRISMII:
+		(void) RDCH0(pcwl_p, fid, off, tmp, 4);
+		off += 4;
+		szbuf = (stype == WL_INFO_SCAN_RESULTS ? 31 : 32);
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: PRISM chip\n"));
+		break;
+	case PCWL_CHIP_LUCENT:
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan LUCENT chip\n"));
+	default:
+		szbuf = 25;
+	}
+
+	flen = flen + 1 - (off >> 1);
+	ssidNum = flen/szbuf;
+	ssidNum = min(WL_SRT_MAX_NUM, ssidNum);
+
+	PCWLDBG((CE_NOTE, "pcwl: ssid_scan frame length = %d\n", flen));
+
+	PCWLDBG((CE_NOTE, "pcwl ssid_scan: %d ssid(s) available", ssidNum));
+
+	bzero(bssid_t, sizeof (bssid_t));
+	for (i = 0; i < ssidNum; i++) {
+		(void) RDCH0(pcwl_p, fid, off, (uint16_t *)&sctbl, 2*szbuf);
+
+#ifdef DEBUG
+		if (pcwl_debug & PCWL_DBG_INFO) {
+			int j;
+			for (j = 0; j < sizeof (sctbl); j++)
+				cmn_err(CE_NOTE, "%d: %x\n", j,
+				    *((uint8_t *)&sctbl + j));
+		}
+#endif
+
+		off += (szbuf << 1);
+		stat = min(sctbl.wl_srt_ssidlen, 31);
+		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_bssid), 6);
+		PCWL_SWAP16((uint16_t *)(sctbl.wl_srt_ssid), stat);
+		sctbl.wl_srt_ssid[stat] = '\0';
+		sctbl.wl_srt_sl &= 0x7f;
+
+		/*
+		 * sometimes, those empty items are recorded by hardware,
+		 * this is wrong, just ignore those items here.
+		 */
+		if (bcmp(sctbl.wl_srt_bssid,
+		    bssid_t, 6) == 0) {
+			continue;
+		}
+		if (bcmp(sctbl.wl_srt_bssid,
+		    pcwl_p->pcwl_bssid, 6) == 0) {
+			pcwl_p->pcwl_rssi = sctbl.wl_srt_sl;
+		}
+		/*
+		 * save/update the scan item in scanlist
+		 */
+		mutex_enter(&pcwl_p->pcwl_scanlist_lock);
+		check_num = 0;
+		scan_item0 = list_head(&pcwl_p->pcwl_scan_list);
+		if (scan_item0 == NULL) {
+			if (pcwl_add_scan_item(pcwl_p, sctbl)
+			    != 0) {
+				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+				return;
+			}
+		}
+		for (; scan_item0; ) {
+			if (bcmp(sctbl.wl_srt_bssid,
+			    scan_item0->wl_val.wl_srt_bssid, 6) == 0) {
+				scan_item0->wl_val = sctbl;
+				scan_item0->wl_timeout = WL_SCAN_TIMEOUT_MAX;
+				break;
+			} else {
+				check_num++;
+			}
+			scan_item0 = list_next(&pcwl_p->pcwl_scan_list,
+			    scan_item0);
+		}
+		if (check_num == pcwl_p->pcwl_scan_num) {
+			if (pcwl_add_scan_item(pcwl_p, sctbl)
+			    != 0) {
+				mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+				return;
+			}
+		}
+		mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: ssid%d = %s\n", i+1,
+		    sctbl.wl_srt_ssid));
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: channel = %d\n",
+		    sctbl.wl_srt_chid));
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: signal level= %d\n",
+		    sctbl.wl_srt_sl));
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: noise level = %d\n",
+		    sctbl.wl_srt_anl));
+		PCWLDBG((CE_NOTE, "pcwl ssid_scan: bssid%d ="
+		    " %x %x %x %x %x %x\n\n", i+1,
+		    sctbl.wl_srt_bssid[0],
+		    sctbl.wl_srt_bssid[1],
+		    sctbl.wl_srt_bssid[2],
+		    sctbl.wl_srt_bssid[3],
+		    sctbl.wl_srt_bssid[4],
+		    sctbl.wl_srt_bssid[5]));
+	}
+
+}
+
+/*
+ * delay in which the mutex is not hold.
+ * assuming the mutex has already been hold.
+ */
+static void
+pcwl_delay(pcwl_maci_t *pcwl_p, clock_t microsecs)
+{
+	ASSERT(mutex_owned(&pcwl_p->pcwl_glock));
+
+	mutex_exit(&pcwl_p->pcwl_glock);
+	delay(drv_usectohz(microsecs));
+	mutex_enter(&pcwl_p->pcwl_glock);
+}
+
+static int
+pcwl_reset_backend(pcwl_maci_t *pcwl_p)
+{
+	uint16_t ret = 0;
+
+	if (ret =  pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
+		return ((int)ret);
+	}
+
+	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
+
+	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INI, 0)) {
+		return ((int)ret);
+	}
+	pcwl_delay(pcwl_p, 100000); /* wait .1 sec */
+
+	PCWL_DISABLE_INTR(pcwl_p);
+	return (PCWL_SUCCESS);
+}
+
+
+/*
+ * get card capability (WEP, default channel), setup broadcast, mac addresses
+ */
+static int
+pcwl_get_cap(pcwl_maci_t *pcwl_p)
+{
+	uint16_t stat, ch_no;
+	uint16_t buf[ETHERADDRL >> 1];
+
+	bzero(buf, ETHERADDRL);
+	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_OWN_CHNL, &ch_no)) {
+		cmn_err(CE_CONT, "pcwl get_cap: get def channel failed"
+		    " %x\n", stat);
+		return ((int)stat);
+	}
+	if (stat = pcwl_get_ltv(pcwl_p, 2, WL_RID_WEP_AVAIL,
+	    &pcwl_p->pcwl_has_wep)) {
+		cmn_err(CE_CONT, "pcwl get_cap: get WEP capability failed"
+		    " %x\n", stat);
+		return ((int)stat);
+	}
+	if (stat = pcwl_get_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf)) {
+		cmn_err(CE_CONT, "pcwl get_cap: get macaddr failed"
+		    " %x\n", stat);
+		return ((int)stat);
+	}
+
+	/*
+	 * don't assume m_xxx members are 16-bit aligned
+	 */
+	PCWL_SWAP16(buf, ETHERADDRL);
+	ether_copy(buf, pcwl_p->pcwl_mac_addr);
+	return (PCWL_SUCCESS);
+}
+
+static int
+pcwl_init_nicmem(pcwl_maci_t *pcwl_p)
+{
+	uint16_t ret, i;
+	uint16_t rc;
+
+	for (i = 0; i < WL_XMT_BUF_NUM; i++) {
+		ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &rc);
+		if (ret) {
+			cmn_err(CE_WARN,
+			    "pcwl: alloc NIC Tx buf failed %x\n", ret);
+			return (PCWL_FAIL);
+		}
+		pcwl_p->pcwl_txring.wl_tx_fids[i] = rc;
+		pcwl_p->pcwl_txring.wl_tx_ring[i] = 0;
+		PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem_id[%d]=%x\n", i, rc));
+	}
+	pcwl_p->pcwl_txring.wl_tx_prod = pcwl_p->pcwl_txring.wl_tx_cons = 0;
+
+	ret = pcwl_alloc_nicmem(pcwl_p, PCWL_NICMEM_SZ, &pcwl_p->pcwl_mgmt_id);
+	if (ret) {
+		cmn_err(CE_WARN, "pcwl: alloc NIC Mgmt buf failed %x\n", ret);
+		return (PCWL_FAIL);
+	}
+	PCWLDBG((CE_NOTE, "pcwl: alloc_nicmem mgmt_id=%x\n",
+	    pcwl_p->pcwl_mgmt_id));
+	return (PCWL_SUCCESS);
+}
+
+static int
+pcwl_loaddef_rf(pcwl_maci_t *pcwl_p)
+{
+	pcwl_p->pcwl_rf.rf_max_datalen = WL_DEFAULT_DATALEN;
+	pcwl_p->pcwl_rf.rf_create_ibss = WL_DEFAULT_CREATE_IBSS;
+	pcwl_p->pcwl_rf.rf_porttype = WL_BSS_BSS;
+	pcwl_p->pcwl_rf.rf_rts_thresh = WL_DEFAULT_RTS_THRESH;
+	pcwl_p->pcwl_rf.rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
+	pcwl_p->pcwl_rf.rf_pm_enabled = WL_DEFAULT_PM_ENABLED;
+	pcwl_p->pcwl_rf.rf_own_chnl = WL_DEFAULT_CHAN;
+	(void) strcpy(pcwl_p->pcwl_rf.rf_own_ssid, "");
+	(void) strcpy(pcwl_p->pcwl_rf.rf_desired_ssid, "");
+	(void) strcpy(pcwl_p->pcwl_rf.rf_nodename, "");
+	pcwl_p->pcwl_rf.rf_encryption = WL_NOENCRYPTION;
+	pcwl_p->pcwl_rf.rf_authtype = WL_OPENSYSTEM;
+	pcwl_p->pcwl_rf.rf_tx_crypt_key = WL_DEFAULT_TX_CRYPT_KEY;
+	bzero((pcwl_p->pcwl_rf.rf_ckeys), sizeof (rf_ckey_t) * 4);
+
+	pcwl_p->pcwl_rf.rf_promiscuous = 0;
+
+	return (pcwl_config_rf(pcwl_p));
+}
+
+static int
+pcwl_config_rf(pcwl_maci_t *pcwl_p)
+{
+	pcwl_rf_t *rf_p = &pcwl_p->pcwl_rf;
+	uint16_t create_ibss, porttype;
+
+	/*
+	 * Lucent card:
+	 * 0 Join ESS or IBSS; 1 Join ESS or join/create IBSS
+	 * PrismII card:
+	 * 3 Join ESS or IBSS(do not create IBSS);
+	 * 1 Join ESS or join/create IBSS
+	 */
+	create_ibss = rf_p->rf_create_ibss;
+	if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
+		if (rf_p->rf_create_ibss == 0)
+			create_ibss = 3;
+	}
+	/*
+	 * Lucent card:
+	 * 1 BSS; 3 pseudo IBSS(only for test,not the 802.11 IBSS)
+	 * so porttype register should always be set to 1
+	 * PrismII card:
+	 * 0 IBSS; 1 BSS; 2 WDS; 3 pseudo IBSS; 6 hostAP
+	 */
+	switch (pcwl_p->pcwl_chip_type) {
+	case PCWL_CHIP_PRISMII:
+		if (rf_p->rf_porttype == WL_BSS_BSS)
+			porttype = 1;
+		else if (rf_p->rf_porttype == WL_BSS_IBSS)
+			porttype = 0;
+		else
+			porttype = 0;
+		break;
+	case PCWL_CHIP_LUCENT:
+	default:
+		porttype = 1;
+	}
+
+
+	FIL_LTV(pcwl_p, PCWL_MCBUF_LEN, WL_RID_MCAST, 0);
+	FIL_LTV(pcwl_p, 2,	WL_RID_PROMISC,		0);
+	FIL_LTV(pcwl_p, 2,	WL_RID_TICK_TIME,	0);
+
+	FIL_LTV(pcwl_p, 2, WL_RID_MAX_DATALEN, rf_p->rf_max_datalen);
+	FIL_LTV(pcwl_p, 2, WL_RID_CREATE_IBSS, create_ibss);
+	FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, porttype);
+	FIL_LTV(pcwl_p, 2, WL_RID_RTS_THRESH, rf_p->rf_rts_thresh);
+	FIL_LTV(pcwl_p, 2, WL_RID_TX_RATE, rf_p->rf_tx_rate);
+	FIL_LTV(pcwl_p, 2, WL_RID_SYSTEM_SCALE, rf_p->rf_system_scale);
+	FIL_LTV(pcwl_p, 2, WL_RID_PM_ENABLED, rf_p->rf_pm_enabled);
+	FIL_LTV(pcwl_p, 2, WL_RID_MAX_SLEEP, rf_p->rf_max_sleep);
+	FIL_LTV(pcwl_p, 2, WL_RID_OWN_CHNL, rf_p->rf_own_chnl);
+
+	PUT_STR(pcwl_p, WL_RID_OWN_SSID, rf_p->rf_own_ssid);
+	PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, rf_p->rf_desired_ssid);
+	PUT_STR(pcwl_p, WL_RID_NODENAME, rf_p->rf_nodename);
+
+	if (!pcwl_p->pcwl_has_wep)
+		goto done;
+
+	switch (pcwl_p->pcwl_chip_type) {
+	case PCWL_CHIP_PRISMII: {
+		int i;
+
+		for (i = 0; i < 4; i++) {
+			int k_len = strlen((char *)rf_p->rf_ckeys[i].ckey_dat);
+			if (k_len == 0)
+				continue;
+			k_len = k_len > 5 ? 14 : 6;
+			PUT_LTV(pcwl_p, k_len, WL_RID_CRYPT_KEY0_P2 + i,
+			    (uint16_t *)&rf_p->rf_ckeys[i].ckey_dat);
+		}
+		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY_P2,
+		    rf_p->rf_tx_crypt_key);
+		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_P2,
+		    rf_p->rf_authtype);
+		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION_P2,
+		    rf_p->rf_encryption);
+		if (pcwl_p->pcwl_rf.rf_promiscuous)
+			FIL_LTV(pcwl_p, 2, WL_RID_PROMISC, 1);
+		}
+		break;
+	case PCWL_CHIP_LUCENT:
+	default:
+		FIL_LTV(pcwl_p, 2, WL_RID_ENCRYPTION,
+		    rf_p->rf_encryption);
+		FIL_LTV(pcwl_p, 2, WL_RID_AUTHTYPE_L,
+		    rf_p->rf_authtype);
+		FIL_LTV(pcwl_p, 2, WL_RID_TX_CRYPT_KEY,
+		    rf_p->rf_tx_crypt_key);
+		PUT_LTV(pcwl_p, sizeof (rf_p->rf_ckeys),
+		    WL_RID_DEFLT_CRYPT_KEYS,
+		    (uint16_t *)rf_p->rf_ckeys);
+		break;
+	}
+done:
+	return (PCWL_SUCCESS);
+}
+
+static void
+pcwl_start_locked(pcwl_maci_t *pcwl_p)
+{
+	pcwl_p->pcwl_flag |= PCWL_CARD_INTREN;
+	PCWL_ENABLE_INTR(pcwl_p);
+}
+
+static void
+pcwl_stop_locked(pcwl_maci_t *pcwl_p)
+{
+	PCWL_DISABLE_INTR(pcwl_p);
+	pcwl_p->pcwl_flag &= (~PCWL_CARD_INTREN);
+	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
+	    WL_EV_ALLOC|WL_EV_INFO|WL_EV_INFO_DROP);
+	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, WL_EV_TX|WL_EV_RX|WL_EV_TX_EXC|
+	    WL_EV_ALLOC| WL_EV_INFO|WL_EV_INFO_DROP);
+}
+
+/*ARGSUSED*/
+static int
+pcwl_saddr_locked(pcwl_maci_t *pcwl_p)
+{
+	int ret;
+	uint16_t buf[ETHERADDRL >> 1];
+
+	ether_copy(pcwl_p->pcwl_mac_addr, buf);
+	PCWL_SWAP16(buf, ETHERADDRL);
+	ret = pcwl_put_ltv(pcwl_p, ETHERADDRL, WL_RID_MAC_NODE, buf);
+	if (ret) {
+		cmn_err(CE_WARN, "pcwl set_mac_addr: failed %x\n", ret);
+		return (PCWL_FAIL);
+	}
+	return (PCWL_SUCCESS);
+}
+
+static void
+pcwl_chip_type(pcwl_maci_t *pcwl_p)
+{
+	pcwl_ltv_ver_t ver;
+	pcwl_ltv_fwver_t f;
+
+	bzero(&ver, sizeof (ver));
+	(void) pcwl_get_ltv(pcwl_p, sizeof (ver),
+	    WL_RID_CARD_ID, (uint16_t *)&ver);
+	PCWLDBG((CE_NOTE, "card id:%04x-%04x-%04x-%04x\n",
+	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
+	if ((ver.wl_compid & 0xf000) != 0x8000)
+		return;	/* lucent */
+
+	pcwl_p->pcwl_chip_type = PCWL_CHIP_PRISMII;
+	(void) pcwl_get_ltv(pcwl_p, sizeof (ver), WL_RID_COMP_IDENT,
+	    (uint16_t *)&ver);
+	PCWLDBG((CE_NOTE, "PRISM-II ver:%04x-%04x-%04x-%04x\n",
+	    ver.wl_compid, ver.wl_variant, ver.wl_major, ver.wl_minor));
+
+	bzero(&f, sizeof (f));
+	(void) pcwl_get_ltv(pcwl_p, sizeof (f), WL_RID_FWVER, (uint16_t *)&f);
+	PCWL_SWAP16((uint16_t *)&f, sizeof (f));
+	PCWLDBG((CE_NOTE, "Firmware Pri:%s 2,3:%s\n",
+	    (char *)f.pri, (char *)f.st));
+}
+
+/*
+ * for wificonfig and dladm ioctl
+ */
+
+static int
+pcwl_cfg_essid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	char ssid[36];
+	uint16_t ret, i;
+	uint16_t val;
+	pcwl_rf_t *rf_p;
+	char *value;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	bzero(ssid, sizeof (ssid));
+	if (cmd == WLAN_GET_PARAM) {
+		ret =  pcwl_get_ltv(pcwl_p, 2,
+		    WL_RID_PORTSTATUS, &val);
+		if (ret) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			PCWLDBG((CE_WARN, "cfg_essid_get_error\n"));
+			goto done;
+		}
+		PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
+
+		if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
+			outfp->wldp_length = WIFI_BUF_OFFSET +
+			    offsetof(wl_essid_t, wl_essid_essid) +
+			    mi_strlen(rf_p->rf_desired_ssid);
+			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
+			    mi_strlen(rf_p->rf_desired_ssid);
+			bcopy(rf_p->rf_desired_ssid, buf + WIFI_BUF_OFFSET +
+			    offsetof(wl_essid_t, wl_essid_essid),
+			    mi_strlen(rf_p->rf_desired_ssid));
+		} else if (val == WL_PORT_TO_IBSS ||
+		    val == WL_PORT_TO_BSS ||
+		    val == WL_PORT_OOR) {
+			(void) pcwl_get_ltv((pcwl_p), 34,
+			    WL_RID_SSID, (uint16_t *)ssid);
+			PCWL_SWAP16((uint16_t *)(ssid+2), *(uint16_t *)ssid);
+			ssid[*(uint16_t *)ssid + 2] = '\0';
+			outfp->wldp_length = WIFI_BUF_OFFSET +
+			    offsetof(wl_essid_t, wl_essid_essid) +
+			    mi_strlen(ssid+2);
+			((wl_essid_t *)(outfp->wldp_buf))->wl_essid_length =
+			    mi_strlen(ssid+2);
+			bcopy(ssid + 2, buf + WIFI_BUF_OFFSET +
+			    offsetof(wl_essid_t, wl_essid_essid),
+			    mi_strlen(ssid+2));
+		} else {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+		}
+		outfp->wldp_result = WL_SUCCESS;
+		PCWLDBG((CE_CONT, "outfp->length=%d\n", outfp->wldp_length));
+		PCWLDBG((CE_CONT, "pcwl: get desired essid=%s\n",
+		    rf_p->rf_desired_ssid));
+	} else if (cmd == WLAN_SET_PARAM) {
+		value = ((wl_essid_t *)(infp->wldp_buf))->wl_essid_essid;
+		(void) strncpy(rf_p->rf_desired_ssid, value,
+		    MIN(32, strlen(value)));
+		rf_p->rf_desired_ssid[strlen(value)] = '\0';
+		(void) strncpy(rf_p->rf_own_ssid, value,
+		    MIN(32, strlen(value)));
+		rf_p->rf_own_ssid[strlen(value)] = '\0';
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+		PCWLDBG((CE_CONT, "pcwl: set: desired essid=%s\n",
+		    rf_p->rf_desired_ssid));
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_bssid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t ret, i;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+	uint8_t bssid[6];
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bssid_t);
+	if (cmd == WLAN_GET_PARAM) {
+		if (ret = pcwl_get_ltv(pcwl_p, 2,
+		    WL_RID_PORTSTATUS, &ret)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		PCWLDBG((CE_NOTE, "PortStatus = %d\n", ret));
+		if (ret == WL_PORT_DISABLED || ret == WL_PORT_INITIAL) {
+			bzero(buf + WIFI_BUF_OFFSET,
+			    sizeof (wl_bssid_t));
+		} else if (ret == WL_PORT_TO_IBSS ||
+			ret == WL_PORT_TO_BSS || ret == WL_PORT_OOR) {
+			(void) pcwl_get_ltv(pcwl_p, 6,
+			    WL_RID_BSSID, (uint16_t *)bssid);
+			PCWL_SWAP16((uint16_t *)bssid, 6);
+			bcopy(bssid, buf + WIFI_BUF_OFFSET,
+			    sizeof (wl_bssid_t));
+		}
+		outfp->wldp_result = WL_SUCCESS;
+
+		PCWLDBG((CE_CONT, "pcwl_getset: bssid=%x %x %x %x %x %x\n",
+		    bssid[0], bssid[1], bssid[2],
+		    bssid[3], bssid[4], bssid[5]));
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_result = WL_READONLY;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcwl_cmd_scan(pcwl_maci_t *pcwl_p)
+{
+	uint16_t vall[18], ret = WL_SUCCESS;
+	pcwl_rf_t *rf_p;
+	uint32_t enable, i;
+	size_t	len;
+
+	rf_p = &pcwl_p->pcwl_rf;
+
+	/*
+	 * The logic of this funtion is really tricky.
+	 * Firstly, the chip can only scan in BSS mode, so necessary
+	 * backup and restore is required before and after the scan
+	 * command.
+	 * Secondly, for Lucent chip, Alrealy associated with an AP
+	 * can only scan the APes on the fixed channel, so we must
+	 * set the desired_ssid as "" before scan and restore after.
+	 * Thirdly, scan cmd is effective only when the card is enabled
+	 * and any 'set' operation(such as set bsstype, ssid)must disable
+	 * the card first and then enable the card after the 'set'
+	 */
+	enable = pcwl_p->pcwl_flag & PCWL_ENABLED;
+	len = strlen(rf_p->rf_desired_ssid);
+
+	if (pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) {
+		if ((enable) &&
+		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
+			ret = (int)WL_HW_ERROR;
+			goto done;
+		}
+		FIL_LTV(pcwl_p, 2, WL_RID_PORTTYPE, WL_BSS_BSS);
+	}
+
+	if ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0)) {
+		if ((enable) &&
+		    (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
+			ret = (int)WL_HW_ERROR;
+			goto done;
+		}
+		PUT_STR(pcwl_p, WL_RID_DESIRED_SSID, "");
+	}
+
+	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+		ret = (int)WL_HW_ERROR;
+		goto done;
+	}
+	pcwl_delay(pcwl_p, 1000000);
+
+	switch (pcwl_p->pcwl_chip_type) {
+	case PCWL_CHIP_PRISMII:
+		bzero(vall, sizeof (vall));
+		vall[0] = 0x3fff; /* channel mask */
+		vall[1] = 0x1; /* tx rate */
+		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
+			PUT_LTV(pcwl_p, sizeof (vall),
+			    WL_RID_HSCAN_REQUEST, vall);
+			pcwl_delay(pcwl_p, 1000000);
+			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
+				break;
+		}
+		PCWLDBG((CE_NOTE, "PRISM chip\n"));
+		break;
+
+	case PCWL_CHIP_LUCENT:
+		PCWLDBG((CE_NOTE, "LUCENT chip\n"));
+	default:
+		for (i = 0; i < WL_MAX_SCAN_TIMES; i++) {
+			if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_INQUIRE,
+			    WL_INFO_SCAN_RESULTS)) {
+				ret = (int)WL_HW_ERROR;
+				goto done;
+			}
+			pcwl_delay(pcwl_p, 500000);
+			if (pcwl_p->pcwl_scan_num >= WL_SCAN_AGAIN_THRESHOLD)
+				break;
+		}
+		break;
+	}
+	if ((pcwl_p->pcwl_rf.rf_porttype != WL_BSS_BSS) ||
+	    ((pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT) && (len != 0))) {
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			goto done;
+		}
+		if (ret = pcwl_config_rf(pcwl_p)) {
+			ret = (int)WL_HW_ERROR;
+			goto done;
+		}
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			goto done;
+		}
+
+		pcwl_delay(pcwl_p, 1000000);
+	}
+
+	if ((!enable) && (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0))) {
+		ret = (int)WL_HW_ERROR;
+	}
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcwl: scan failed due to hareware error");
+	return (ret);
+
+}
+
+/*ARGSUSED*/
+static int
+pcwl_cfg_scan(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	wl_ess_conf_t *p_ess_conf;
+	wldp_t *outfp;
+	char *buf;
+	uint16_t i;
+	wl_scan_list_t *scan_item;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	mutex_enter(&pcwl_p->pcwl_scanlist_lock);
+	((wl_ess_list_t *)(outfp->wldp_buf))->wl_ess_list_num =
+	    pcwl_p->pcwl_scan_num;
+	outfp->wldp_length = WIFI_BUF_OFFSET +
+	    offsetof(wl_ess_list_t, wl_ess_list_ess) +
+	    pcwl_p->pcwl_scan_num * sizeof (wl_ess_conf_t);
+
+	scan_item = list_head(&pcwl_p->pcwl_scan_list);
+	for (i = 0; i < pcwl_p->pcwl_scan_num; i++) {
+		if (!scan_item)
+			goto done;
+		p_ess_conf = (wl_ess_conf_t *)(buf + WIFI_BUF_OFFSET +
+		    offsetof(wl_ess_list_t, wl_ess_list_ess) +
+		    i * sizeof (wl_ess_conf_t));
+		bcopy(scan_item->wl_val.wl_srt_ssid,
+		    p_ess_conf->wl_ess_conf_essid.wl_essid_essid,
+		    mi_strlen(scan_item->wl_val.wl_srt_ssid));
+		bcopy(scan_item->wl_val.wl_srt_bssid,
+		    p_ess_conf->wl_ess_conf_bssid, 6);
+		(p_ess_conf->wl_phy_conf).wl_phy_dsss_conf.wl_dsss_subtype
+		    = WL_DSSS;
+		p_ess_conf->wl_ess_conf_wepenabled =
+		    (scan_item->wl_val.wl_srt_cap & 0x10 ?
+		    WL_ENC_WEP : WL_NOENCRYPTION);
+		p_ess_conf->wl_ess_conf_bsstype =
+		    (scan_item->wl_val.wl_srt_cap & 0x1 ?
+		    WL_BSS_BSS : WL_BSS_IBSS);
+		p_ess_conf->wl_phy_conf.wl_phy_dsss_conf.wl_dsss_channel =
+		    scan_item->wl_val.wl_srt_chid;
+		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
+			p_ess_conf->wl_ess_conf_sl =
+			    min(scan_item->wl_val.wl_srt_sl * 15 / 85 + 1,
+			    15);
+		} else {
+			if (scan_item->wl_val.wl_srt_sl <= 27)
+				p_ess_conf->wl_ess_conf_sl = 1;
+			else if (scan_item->wl_val.wl_srt_sl > 154)
+				p_ess_conf->wl_ess_conf_sl = 15;
+			else
+				p_ess_conf->wl_ess_conf_sl = min(15,
+				    ((scan_item->wl_val.wl_srt_sl - 27)
+				    * 15 / 127));
+		}
+		p_ess_conf->wl_supported_rates[0] = WL_RATE_1M;
+		p_ess_conf->wl_supported_rates[1] = WL_RATE_2M;
+		p_ess_conf->wl_supported_rates[2] = WL_RATE_5_5M;
+		p_ess_conf->wl_supported_rates[3] = WL_RATE_11M;
+		scan_item = list_next(&pcwl_p->pcwl_scan_list, scan_item);
+	}
+done:
+	mutex_exit(&pcwl_p->pcwl_scanlist_lock);
+	outfp->wldp_result = WL_SUCCESS;
+
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (WL_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+pcwl_cfg_linkstatus(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	wldp_t *outfp;
+	char *buf;
+	uint16_t i, ret, val;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	ret =  pcwl_get_ltv(pcwl_p, 2,
+	    WL_RID_PORTSTATUS, &val);
+	if (ret) {
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_HW_ERROR;
+		PCWLDBG((CE_WARN, "cfg_linkstatus_get_error\n"));
+		goto done;
+	}
+	PCWLDBG((CE_NOTE, "PortStatus = %d\n", val));
+	if (val == WL_PORT_DISABLED || val == WL_PORT_INITIAL) {
+		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_NOTCONNECTED;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    sizeof (wl_linkstatus_t);
+	} else if (val == WL_PORT_TO_IBSS ||
+	    val == WL_PORT_TO_BSS || val == WL_PORT_OOR) {
+		*(wl_linkstatus_t *)(outfp->wldp_buf) = WL_CONNECTED;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    sizeof (wl_linkstatus_t);
+	} else {
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+	}
+	outfp->wldp_result = WL_SUCCESS;
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_bsstype(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t ret, i;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_bss_type_t);
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_bss_type_t *)(outfp->wldp_buf) = rf_p->rf_porttype;
+		PCWLDBG((CE_CONT, "pcwl_getset: porttype=%d\n",
+		    rf_p->rf_porttype));
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_bss_type_t *)(infp->wldp_buf));
+		if ((ret != WL_BSS_BSS) &&
+		    (ret != WL_BSS_IBSS) &&
+		    (ret != WL_BSS_ANY)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_porttype = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_phy(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t ret, i;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_dsss_t);
+	if (cmd == WLAN_GET_PARAM) {
+		if (ret = pcwl_get_ltv(pcwl_p, 2,
+		    WL_RID_CURRENT_CHNL, &ret)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_channel = ret;
+		PCWLDBG((CE_CONT, "pcwl_getset: channel=%d\n", ret));
+		((wl_dsss_t *)(outfp->wldp_buf))->wl_dsss_subtype = WL_DSSS;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)
+		    (((wl_phy_conf_t *)(infp->wldp_buf))
+		    ->wl_phy_dsss_conf.wl_dsss_channel);
+		if (ret < 1 || ret > 14) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_own_chnl = ret;
+		PCWLDBG((CE_CONT, "pcwl: set channel=%d\n", rf_p->rf_own_chnl));
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+
+}
+
+static int
+pcwl_cfg_desiredrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t rate;
+	uint16_t i;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+	char rates[4];
+	char maxrate;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (i = pcwl_get_ltv(pcwl_p, 2, WL_RID_TX_RATE, &rate)) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_HW_ERROR;
+			goto done;
+		}
+
+		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
+			((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 1;
+			outfp->wldp_length = WIFI_BUF_OFFSET +
+			    offsetof(wl_rates_t, wl_rates_rates) +
+			    1 * sizeof (char);
+			switch (rate) {
+			case WL_SPEED_1Mbps_P2:
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_1M;
+				break;
+			case WL_SPEED_2Mbps_P2:
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_2M;
+				break;
+			case WL_SPEED_55Mbps_P2:
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_5_5M;
+				break;
+			case WL_SPEED_11Mbps_P2:
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_11M;
+				break;
+			default:
+				outfp->wldp_length = WIFI_BUF_OFFSET;
+				outfp->wldp_result = WL_HW_ERROR;
+				goto done;
+			}
+		} else {
+			switch (rate) {
+			case WL_L_TX_RATE_FIX_1M:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 1;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_1M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    1 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_FIX_2M:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 1;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_2M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    1 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_AUTO_H:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 4;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_1M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[1] = WL_RATE_2M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[2] = WL_RATE_5_5M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[3] = WL_RATE_11M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    4 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_FIX_5M:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 1;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_5_5M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    1 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_FIX_11M:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 1;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_11M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    1 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_AUTO_L:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 2;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_1M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[1] = WL_RATE_2M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    2 * sizeof (char);
+				break;
+			case WL_L_TX_RATE_AUTO_M:
+				((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_num = 3;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[0] = WL_RATE_1M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[1] = WL_RATE_2M;
+				(((wl_rates_t *)(outfp->wldp_buf))->
+				    wl_rates_rates)[2] = WL_RATE_5_5M;
+				outfp->wldp_length = WIFI_BUF_OFFSET +
+				    offsetof(wl_rates_t, wl_rates_rates) +
+				    3 * sizeof (char);
+				break;
+			default:
+				outfp->wldp_length = WIFI_BUF_OFFSET;
+				outfp->wldp_result = WL_HW_ERROR;
+				goto done;
+			}
+		}
+		PCWLDBG((CE_CONT, "pcwl: get rate=%d\n", rate));
+		    outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		bzero(rates, sizeof (rates));
+		for (i = 0; i < 4; i++) {
+			rates[i] = (((wl_rates_t *)
+			    (infp->wldp_buf))->wl_rates_rates)[i];
+			PCWLDBG((CE_CONT, "pcwl: set tx_rate[%d]=%d\n",
+			    i, rates[i]));
+		}
+		PCWLDBG((CE_CONT, "pcwl: set rate_num=%d\n",
+		    ((wl_rates_t *)(infp->wldp_buf))
+		    ->wl_rates_num));
+		switch (((wl_rates_t *)
+		    (infp->wldp_buf))->wl_rates_num) {
+		case 1:
+			switch (rates[0]) {
+			case WL_RATE_1M:
+				rf_p->rf_tx_rate = WL_TX_RATE_FIX_1M(pcwl_p);
+				break;
+			case WL_RATE_2M:
+				rf_p->rf_tx_rate = WL_TX_RATE_FIX_2M(pcwl_p);
+				break;
+			case WL_RATE_11M:
+				rf_p->rf_tx_rate = WL_TX_RATE_FIX_11M(pcwl_p);
+				break;
+			case WL_RATE_5_5M:
+				rf_p->rf_tx_rate = WL_TX_RATE_FIX_5M(pcwl_p);
+				break;
+			default:
+				outfp->wldp_length = WIFI_BUF_OFFSET;
+				outfp->wldp_result = WL_NOTSUPPORTED;
+				goto done;
+			}
+			break;
+		case 2:
+			maxrate = (rates[0] > rates[1] ?
+			    rates[0] : rates[1]);
+			switch (maxrate) {
+			case WL_RATE_2M:
+				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_L(pcwl_p);
+				break;
+			case WL_RATE_11M:
+				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
+				break;
+			case WL_RATE_5_5M:
+				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
+				break;
+			default:
+				outfp->wldp_length = WIFI_BUF_OFFSET;
+				outfp->wldp_result = WL_NOTSUPPORTED;
+				goto done;
+			}
+			break;
+		case 3:
+			maxrate = (rates[0] > rates[1] ?
+			    rates[0] : rates[1]);
+			maxrate = (rates[2] > maxrate ?
+			    rates[2] : maxrate);
+			switch (maxrate) {
+			case WL_RATE_11M:
+				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
+				break;
+			case WL_RATE_5_5M:
+				rf_p->rf_tx_rate = WL_TX_RATE_AUTO_M(pcwl_p);
+				break;
+			default:
+				outfp->wldp_length = WIFI_BUF_OFFSET;
+				outfp->wldp_result = WL_NOTSUPPORTED;
+				goto done;
+			}
+			break;
+		case 4:
+			rf_p->rf_tx_rate = WL_TX_RATE_AUTO_H(pcwl_p);
+			break;
+		default:
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_LACK_FEATURE;
+			goto done;
+		}
+		PCWLDBG((CE_CONT, "pcwl: set tx_rate=%d\n", rf_p->rf_tx_rate));
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcwl_cfg_supportrates(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i;
+	wldp_t *outfp;
+	char *buf;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		((wl_rates_t *)(outfp->wldp_buf))->wl_rates_num = 4;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[0]
+		    = WL_RATE_1M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[1]
+		    = WL_RATE_2M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[2]
+		    = WL_RATE_5_5M;
+		(((wl_rates_t *)(outfp->wldp_buf))->wl_rates_rates)[3]
+		    = WL_RATE_11M;
+		outfp->wldp_length = WIFI_BUF_OFFSET +
+		    offsetof(wl_rates_t, wl_rates_rates) +
+		    4 * sizeof (char);
+		outfp->wldp_result = WL_SUCCESS;
+		for (i = 0; i < (outfp->wldp_length); i++)
+			(void) mi_mpprintf_putc((char *)mp, buf[i]);
+		kmem_free(buf, MAX_BUF_LEN);
+		return (WL_SUCCESS);
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+}
+
+static int
+pcwl_cfg_powermode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_ps_mode_t);
+	if (cmd == WLAN_GET_PARAM) {
+		((wl_ps_mode_t *)(outfp->wldp_buf))->wl_ps_mode =
+		    rf_p->rf_pm_enabled;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(((wl_ps_mode_t *)(infp->wldp_buf))
+		    ->wl_ps_mode);
+		if (ret != WL_PM_AM && ret != WL_PM_MPS && ret != WL_PM_FAST) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_pm_enabled = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+
+}
+
+static int
+pcwl_cfg_authmode(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_authmode_t);
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_authmode_t *)(outfp->wldp_buf) = rf_p->rf_authtype;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_authmode_t *)(infp->wldp_buf));
+		if (ret != WL_OPENSYSTEM && ret != WL_SHAREDKEY) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_authtype = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_encryption(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_encryption_t);
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_encryption_t *)(outfp->wldp_buf) = rf_p->rf_encryption;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_encryption_t *)(infp->wldp_buf));
+		PCWLDBG((CE_NOTE, "set encryption: %d\n", ret));
+		if (ret != WL_NOENCRYPTION && ret != WL_ENC_WEP) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_encryption = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_wepkeyid(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_id_t);
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_wep_key_id_t *)(outfp->wldp_buf) = rf_p->rf_tx_crypt_key;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_wep_key_id_t *)(infp->wldp_buf));
+		if (ret >= MAX_NWEPKEYS) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_tx_crypt_key = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_createibss(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i, ret;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_create_ibss_t);
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_create_ibss_t *)(outfp->wldp_buf) = rf_p->rf_create_ibss;
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		ret = (uint16_t)(*(wl_create_ibss_t *)(infp->wldp_buf));
+		if (ret != 0 && ret != 1) {
+			outfp->wldp_length = WIFI_BUF_OFFSET;
+			outfp->wldp_result = WL_NOTSUPPORTED;
+			goto done;
+		}
+		rf_p->rf_create_ibss = ret;
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_rssi(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_rssi_t);
+
+	if (cmd == WLAN_GET_PARAM) {
+		if (pcwl_p->pcwl_chip_type == PCWL_CHIP_PRISMII) {
+			*(wl_rssi_t *)(outfp->wldp_buf) =
+			    min((pcwl_p->pcwl_rssi * 15 / 85 + 1), 15);
+		} else {
+		/*
+		 * According to the description of the
+		 * datasheet(Lucent card), the signal level
+		 * value is between 27 -- 154.
+		 * we reflect these value to 1-15 as rssi.
+		 */
+			if (pcwl_p->pcwl_rssi <= 27)
+				*(wl_rssi_t *)(outfp->wldp_buf) = 1;
+			else if (pcwl_p->pcwl_rssi > 154)
+				*(wl_rssi_t *)(outfp->wldp_buf) = 15;
+			else
+				*(wl_rssi_t *)(outfp->wldp_buf) =
+				    min(15, ((pcwl_p->pcwl_rssi - 27)
+				    * 15 / 127));
+		}
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_result = WL_READONLY;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+/*ARGSUSED*/
+static int
+pcwl_cfg_radio(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i;
+	int iret;
+	wldp_t *outfp;
+	char *buf;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+
+	if (cmd == WLAN_GET_PARAM) {
+		*(wl_radio_t *)(outfp->wldp_buf) = B_TRUE;
+		outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_radio_t);
+		outfp->wldp_result = WL_SUCCESS;
+	} else if (cmd == WLAN_SET_PARAM) {
+		outfp->wldp_length = WIFI_BUF_OFFSET;
+		outfp->wldp_result = WL_LACK_FEATURE;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static int
+pcwl_cfg_wepkey(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	uint16_t i;
+	wl_wep_key_t *p_wepkey_tab;
+	pcwl_rf_t *rf_p;
+	wldp_t	*infp;
+	wldp_t *outfp;
+	char *buf;
+	int iret;
+
+	buf = kmem_zalloc(MAX_BUF_LEN, KM_NOSLEEP);
+	if (buf == NULL) {
+		PCWLDBG((CE_NOTE, "can not alloc so much memory!(%d)\n",
+		    MAX_BUF_LEN));
+		return (ENOMEM);
+	}
+	outfp = (wldp_t *)buf;
+	bcopy(mp->b_rptr, buf,  sizeof (wldp_t));
+	infp = (wldp_t *)mp->b_rptr;
+	rf_p = &pcwl_p->pcwl_rf;
+	bzero((rf_p->rf_ckeys), sizeof (rf_ckey_t) * MAX_NWEPKEYS);
+
+	outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_wep_key_tab_t);
+	if (cmd == WLAN_GET_PARAM) {
+		outfp->wldp_result = WL_WRITEONLY;
+	} else if (cmd == WLAN_SET_PARAM) {
+		p_wepkey_tab = (wl_wep_key_t *)(infp->wldp_buf);
+		for (i = 0; i < MAX_NWEPKEYS; i++) {
+			if (p_wepkey_tab[i].wl_wep_operation == WL_ADD) {
+				rf_p->rf_ckeys[i].ckey_len =
+				    p_wepkey_tab[i].wl_wep_length;
+				bcopy(p_wepkey_tab[i].wl_wep_key,
+				    rf_p->rf_ckeys[i].ckey_dat,
+				    p_wepkey_tab[i].wl_wep_length);
+				PCWL_SWAP16((uint16_t *)
+				    &rf_p->rf_ckeys[i].ckey_dat,
+				    rf_p->rf_ckeys[i].ckey_len + 1);
+				PCWLDBG((CE_CONT, "%s, %d\n",
+				    rf_p->rf_ckeys[i].ckey_dat, i));
+			}
+			PCWLDBG((CE_CONT, "pcwl: rf_ckeys[%d]=%s\n", i,
+				(char *)(rf_p->rf_ckeys[i].ckey_dat)));
+		}
+		outfp->wldp_result = WL_SUCCESS;
+	} else {
+		kmem_free(buf, MAX_BUF_LEN);
+		return (EINVAL);
+	}
+done:
+	for (i = 0; i < (outfp->wldp_length); i++)
+		(void) mi_mpprintf_putc((char *)mp, buf[i]);
+	iret = (int)(outfp->wldp_result);
+	kmem_free(buf, MAX_BUF_LEN);
+	return (iret);
+}
+
+static void
+pcwl_connect_timeout(void *arg)
+{
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	uint16_t ret = 0;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	PCWL_DISABLE_INTR(pcwl_p);
+	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+		goto done;
+	}
+	if (ret = pcwl_config_rf(pcwl_p)) {
+		goto done;
+	}
+	if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+		goto done;
+	}
+	PCWL_ENABLE_INTR(pcwl_p);
+done:
+	if (ret)
+		cmn_err(CE_WARN, "pcwl: connect failed due to hareware error");
+	mutex_exit(&pcwl_p->pcwl_glock);
+	pcwl_p->pcwl_connect_timeout_id = 0;
+}
+
+static int
+pcwl_getset(mblk_t *mp, pcwl_maci_t *pcwl_p, uint32_t cmd)
+{
+	int ret = WL_SUCCESS;
+	int connect = 0;
+
+	mutex_enter(&pcwl_p->pcwl_glock);
+	if (!(pcwl_p->pcwl_flag & PCWL_CARD_READY)) {
+		mutex_exit(&pcwl_p->pcwl_glock);
+		return (PCWL_FAIL);
+	}
+	switch (((wldp_t *)mp->b_rptr)->wldp_id) {
+	case WL_ESSID:
+		ret = pcwl_cfg_essid(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_essid\n"));
+		break;
+	case WL_BSSID:
+		ret = pcwl_cfg_bssid(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_bssid\n"));
+		break;
+	case WL_ESS_LIST:
+		ret = pcwl_cfg_scan(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_scan\n"));
+		break;
+	case WL_LINKSTATUS:
+		ret = pcwl_cfg_linkstatus(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_linkstatus\n"));
+		break;
+	case WL_BSS_TYPE:
+		ret = pcwl_cfg_bsstype(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_bsstype\n"));
+		break;
+	case WL_PHY_CONFIG:
+		ret = pcwl_cfg_phy(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_phy\n"));
+		break;
+	case WL_DESIRED_RATES:
+		ret = pcwl_cfg_desiredrates(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_disred-rates\n"));
+		break;
+	case WL_SUPPORTED_RATES:
+		ret = pcwl_cfg_supportrates(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_supported-rates\n"));
+		break;
+	case WL_POWER_MODE:
+		ret = pcwl_cfg_powermode(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_powermode\n"));
+		break;
+	case WL_AUTH_MODE:
+		ret = pcwl_cfg_authmode(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_authmode\n"));
+		break;
+	case WL_ENCRYPTION:
+		ret = pcwl_cfg_encryption(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_encryption\n"));
+		break;
+	case WL_WEP_KEY_ID:
+		ret = pcwl_cfg_wepkeyid(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_wepkeyid\n"));
+		break;
+	case WL_CREATE_IBSS:
+		ret = pcwl_cfg_createibss(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_create-ibss\n"));
+		break;
+	case WL_RSSI:
+		ret = pcwl_cfg_rssi(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_rssi\n"));
+		break;
+	case WL_RADIO:
+		ret = pcwl_cfg_radio(mp, pcwl_p, cmd);
+		PCWLDBG((CE_NOTE, "cfg_radio\n"));
+		break;
+	case WL_WEP_KEY_TAB:
+		ret = pcwl_cfg_wepkey(mp, pcwl_p, cmd);
+		connect = 1;
+		PCWLDBG((CE_NOTE, "cfg_wepkey\n"));
+		break;
+	case WL_SCAN:
+		mutex_exit(&pcwl_p->pcwl_glock);
+		if (pcwl_p->pcwl_connect_timeout_id != 0) {
+			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+			pcwl_p->pcwl_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcwl_p->pcwl_glock);
+		ret = pcwl_cmd_scan(pcwl_p);
+		break;
+	case WL_LOAD_DEFAULTS:
+		mutex_exit(&pcwl_p->pcwl_glock);
+		if (pcwl_p->pcwl_connect_timeout_id != 0) {
+			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+			pcwl_p->pcwl_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcwl_p->pcwl_glock);
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcwl_loaddef_rf(pcwl_p)) {
+			ret = (int)WL_HW_ERROR;
+			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
+			break;
+		}
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		pcwl_delay(pcwl_p, 1000000);
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCWLDBG((CE_NOTE, "loaddef\n"));
+		break;
+	case WL_DISASSOCIATE:
+		mutex_exit(&pcwl_p->pcwl_glock);
+		if (pcwl_p->pcwl_connect_timeout_id != 0) {
+			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+			pcwl_p->pcwl_connect_timeout_id = 0;
+		}
+
+		mutex_enter(&pcwl_p->pcwl_glock);
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		/*
+		 * A workaround here: If the card is in ad-hoc mode, the
+		 * following scan will not work correctly, so any
+		 * 'dladm connect-wifi' which need a scan first will not
+		 * succeed. software reset the card here as a workround.
+		 */
+		if ((pcwl_p->pcwl_rf.rf_porttype == WL_BSS_IBSS) &&
+		    (pcwl_p->pcwl_chip_type == PCWL_CHIP_LUCENT)) {
+			if (ret = pcwl_reset_backend(pcwl_p)) {
+				ret = (int)WL_HW_ERROR;
+				break;
+			}
+			if (ret = pcwl_init_nicmem(pcwl_p)) {
+				ret = (int)WL_HW_ERROR;
+				break;
+			}
+			pcwl_start_locked(pcwl_p);
+		}
+		if (ret = pcwl_loaddef_rf(pcwl_p)) {
+			ret = (int)WL_HW_ERROR;
+			PCWLDBG((CE_WARN, "cfg_loaddef_err\n"));
+			break;
+		}
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		pcwl_delay(pcwl_p, 1000000);
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCWLDBG((CE_NOTE, "disassociate\n"));
+		break;
+	case WL_REASSOCIATE:
+	case WL_ASSOCIAT:
+		mutex_exit(&pcwl_p->pcwl_glock);
+		if (pcwl_p->pcwl_connect_timeout_id != 0) {
+			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+			pcwl_p->pcwl_connect_timeout_id = 0;
+		}
+		mutex_enter(&pcwl_p->pcwl_glock);
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcwl_config_rf(pcwl_p)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		if (ret = pcwl_set_cmd(pcwl_p, WL_CMD_ENABLE, 0)) {
+			ret = (int)WL_HW_ERROR;
+			break;
+		}
+		PCWLDBG((CE_NOTE, "associate"));
+		break;
+	default:
+		break;
+	}
+	mutex_exit(&pcwl_p->pcwl_glock);
+	if ((cmd == WLAN_SET_PARAM) && (connect)) {
+		(void) pcwl_set_cmd(pcwl_p, WL_CMD_DISABLE, 0);
+		if (pcwl_p->pcwl_connect_timeout_id != 0) {
+			(void) untimeout(pcwl_p->pcwl_connect_timeout_id);
+			pcwl_p->pcwl_connect_timeout_id = 0;
+		}
+		pcwl_p->pcwl_connect_timeout_id = timeout(pcwl_connect_timeout,
+		    pcwl_p, 2 * drv_usectohz(1000000));
+	}
+	return (ret);
+}
+
+static void
+pcwl_wlan_ioctl(pcwl_maci_t *pcwl_p, queue_t *wq, mblk_t *mp, uint32_t cmd)
+{
+
+	struct	iocblk	*iocp = (struct iocblk *)mp->b_rptr;
+	wldp_t 	*infp;
+	uint32_t len, ret;
+	mblk_t		*mp1;
+
+	/*
+	 * sanity check
+	 */
+	if (iocp->ioc_count == 0 || !(mp1 = mp->b_cont)) {
+		miocnak(wq, mp, 0, EINVAL);
+		return;
+	}
+
+	/*
+	 * assuming single data block
+	 */
+	if (mp1->b_cont) {
+		freemsg(mp1->b_cont);
+		mp1->b_cont = NULL;
+	}
+
+	/*
+	 * we will overwrite everything
+	 */
+	mp1->b_wptr = mp1->b_rptr;
+
+	infp = (wldp_t *)mp1->b_rptr;
+	PCWLDBG((CE_NOTE, "pcwl: wldp->length=0x%x\n", infp->wldp_length));
+	PCWLDBG((CE_NOTE, "pcwl: wldp->type =:%s\n",
+	    infp->wldp_type == NET_802_11 ? "NET_802_11" : "Unknown"));
+	PCWLDBG((CE_NOTE, "pcwl: wldp->id=0x%x\n", infp->wldp_id));
+	PCWLDBG((CE_NOTE, "pcwl: wldp->result=0x%x\n", infp->wldp_result));
+
+	ret = pcwl_getset(mp1, pcwl_p, cmd);
+	len = msgdsize(mp1);
+	PCWLDBG((CE_CONT, "pcwl: ioctl message length = %d\n", len));
+	miocack(wq, mp, len, ret);
+
+}
+
+
+static void
+pcwl_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+	struct iocblk *iocp;
+	uint32_t cmd, ret;
+	pcwl_maci_t *pcwl_p = (pcwl_maci_t *)arg;
+	boolean_t need_privilege = B_TRUE;
+
+	/*
+	 * Validate the command before bothering with the mutexen ...
+	 */
+	iocp = (struct iocblk *)mp->b_rptr;
+	iocp->ioc_error = 0;
+	cmd = iocp->ioc_cmd;
+	switch (cmd) {
+	default:
+		PCWLDBG((CE_CONT, "pcwl_ioctl: unknown cmd 0x%x", cmd));
+		miocnak(wq, mp, 0, EINVAL);
+		return;
+	case WLAN_GET_PARAM:
+		need_privilege = B_TRUE;
+		break;
+	case WLAN_SET_PARAM:
+	case WLAN_COMMAND:
+		break;
+	}
+	/*
+	 * Check net_config privilege
+	 */
+	if (need_privilege) {
+		if (ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE)) {
+			miocnak(wq, mp, 0, ret);
+			return;
+		}
+	}
+	pcwl_wlan_ioctl(pcwl_p, wq, mp, cmd);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/pcwl/pcwl.h	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,948 @@
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1997, 1998, 1999
+ *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Hardware specific driver declarations for Lucent and PrismII
+ * chipsets.
+ */
+
+#ifndef _SYS_PCWL_H
+#define	_SYS_PCWL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/list.h>
+
+/*
+ * Encryption controls. We can enable or disable encryption as
+ * well as specify up to 4 encryption keys. We can also specify
+ * which of the four keys will be used for transmit encryption.
+ */
+#define	WL_RID_ENCRYPTION	0xFC20
+#define	WL_RID_ENCRYPTION_P2	0xFC28
+#define	WL_RID_DEFLT_CRYPT_KEYS 0xFCB0
+#define	WL_RID_CRYPT_KEY0_P2	0xFC24
+#define	WL_RID_TX_CRYPT_KEY	0xFCB1
+#define	WL_RID_TX_CRYPT_KEY_P2	0xFC23
+#define	WL_RID_COMP_IDENT	0xFD20	/* version */
+#define	WL_RID_WEP_AVAIL	0xFD4F
+
+#define	WL_RID_AUTHTYPE_P2	0xFC2A	/* PRISM-II */
+#define	WL_RID_AUTHTYPE_L	0xFC21	/* 0xFC21 on Lucent */
+#define	WL_AUTHTYPE_SYS_P2	0x1
+#define	WL_AUTHTYPE_KEY_P2	0x2
+#define	WL_AUTHTYPE_ALL_P2	(WL_AUTHTYPE_SYS_P2 | WL_AUTHTYPE_KEY_P2)
+
+#define	WL_SPEED_1Mbps_P2	0x1
+#define	WL_SPEED_2Mbps_P2	0x2
+#define	WL_SPEED_55Mbps_P2	0x4
+#define	WL_SPEED_11Mbps_P2	0x8
+
+/*
+ * PrismII Tx rate
+ */
+#define	WL_P_TX_RATE_FIX_1M	WL_SPEED_1Mbps_P2
+#define	WL_P_TX_RATE_FIX_2M	WL_SPEED_2Mbps_P2
+#define	WL_P_TX_RATE_FIX_5M	WL_SPEED_55Mbps_P2
+#define	WL_P_TX_RATE_FIX_11M	WL_SPEED_11Mbps_P2
+#define	WL_P_TX_RATE_AUTO_H	\
+	(WL_SPEED_11Mbps_P2 | WL_SPEED_55Mbps_P2 | \
+	WL_SPEED_2Mbps_P2 | WL_SPEED_1Mbps_P2)
+#define	WL_P_TX_RATE_AUTO_M	\
+	(WL_SPEED_55Mbps_P2 | WL_SPEED_2Mbps_P2 | \
+	WL_SPEED_1Mbps_P2)
+#define	WL_P_TX_RATE_AUTO_L	\
+	(WL_SPEED_2Mbps_P2 | WL_SPEED_1Mbps_P2)
+
+
+#define	WL_TIMEOUT	500000
+
+/*
+ * Default port: 0 (only 0 exists on stations)
+ */
+#define	WL_DEFAULT_PORT		0
+
+/*
+ * Lucent TX rate: Default 11Mbps
+ */
+#define	WL_L_TX_RATE_FIX_1M	1
+#define	WL_L_TX_RATE_FIX_2M	2
+#define	WL_L_TX_RATE_AUTO_H	3
+#define	WL_L_TX_RATE_FIX_5M	4 /* 5.5M */
+#define	WL_L_TX_RATE_FIX_11M	5
+#define	WL_L_TX_RATE_AUTO_L	6
+#define	WL_L_TX_RATE_AUTO_M	7
+
+#define	WL_TX_RATE_FIX_1M(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_FIX_1M : WL_L_TX_RATE_FIX_1M)
+#define	WL_TX_RATE_FIX_2M(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_FIX_2M : WL_L_TX_RATE_FIX_2M)
+#define	WL_TX_RATE_AUTO_H(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_AUTO_H : WL_L_TX_RATE_AUTO_H)
+#define	WL_TX_RATE_FIX_5M(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_FIX_5M : WL_L_TX_RATE_FIX_5M)
+#define	WL_TX_RATE_FIX_11M(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_FIX_11M : WL_L_TX_RATE_FIX_11M)
+#define	WL_TX_RATE_AUTO_L(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_AUTO_L : WL_L_TX_RATE_AUTO_L)
+#define	WL_TX_RATE_AUTO_M(p) \
+	(p->pcwl_chip_type == PCWL_CHIP_PRISMII ? \
+	WL_P_TX_RATE_AUTO_M : WL_L_TX_RATE_AUTO_M)
+
+/*
+ * Default network name: empty string implies any
+ */
+#define	WL_DEFAULT_NETNAME	("")
+#define	WL_DEFAULT_NODENAME	("solaris node")
+#define	WL_DEFAULT_AP_DENSITY	1
+#define	WL_DEFAULT_RTS_THRESH	2347
+#define	WL_DEFAULT_DATALEN	2304
+#define	WL_DEFAULT_CREATE_IBSS	0
+#define	WL_DEFAULT_PM_ENABLED	0
+#define	WL_DEFAULT_MAX_SLEEP	100
+#define	WL_DEFAULT_CHAN		3
+#define	WL_DEFAULT_TX_CRYPT_KEY	0
+
+/*
+ * Size of Hermes I/O space.
+ */
+#define	WL_IOSIZ	0x40
+
+/*
+ * Hermes command/status registers.
+ */
+#define	WL_COMMAND	0x00
+#define	WL_PARAM0	0x02
+#define	WL_PARAM1	0x04
+#define	WL_PARAM2	0x06
+#define	WL_STATUS	0x08
+#define	WL_RESP0	0x0A
+#define	WL_RESP1	0x0C
+#define	WL_RESP2	0x0E
+
+/*
+ * Command register values.
+ */
+#define	WL_CMD_BUSY		0x8000 /* busy bit */
+#define	WL_CMD_INI		0x0000 /* initialize */
+#define	WL_CMD_ENABLE		0x0001 /* enable */
+#define	WL_CMD_DISABLE		0x0002 /* disable */
+#define	WL_CMD_DIAG		0x0003
+#define	WL_CMD_ALLOC_MEM	0x000A /* allocate NIC memory */
+#define	WL_CMD_TX		0x000B /* transmit */
+#define	WL_CMD_NOTIFY		0x0010
+#define	WL_CMD_INQUIRE		0x0011
+#define	WL_CMD_ACCESS		0x0021
+#define	WL_CMD_PROGRAM		0x0022
+
+#define	WL_CMD_CODE_MASK	0x003F
+
+/*
+ * Reclaim qualifier bit, applicable to the
+ * TX and INQUIRE commands.
+ */
+#define	WL_RECLAIM		0x0100 /* reclaim NIC memory */
+
+/*
+ * ACCESS command qualifier bits.
+ */
+#define	WL_ACCESS_READ		0x0000
+#define	WL_ACCESS_WRITE		0x0100
+
+/*
+ * PROGRAM command qualifier bits.
+ */
+#define	WL_PROGRAM_DISABLE	0x0000
+#define	WL_PROGRAM_ENABLE_RAM	0x0100
+#define	WL_PROGRAM_ENABLE_NVRAM	0x0200
+#define	WL_PROGRAM_NVRAM	0x0300
+
+/*
+ * Status register values
+ */
+#define	WL_STAT_CMD_CODE	0x003F
+#define	WL_STAT_DIAG_ERR	0x0100
+#define	WL_STAT_INQ_ERR		0x0500
+#define	WL_STAT_CMD_RESULT	0x7F00
+
+/*
+ * memory handle management registers
+ */
+#define	WL_INFO_FID		0x10
+#define	WL_RX_FID		0x20
+#define	WL_ALLOC_FID		0x22
+#define	WL_TX_CMP_FID		0x24
+
+#define	WL_INVALID_FID		0xffff
+
+/*
+ * Buffer Access Path (BAP) registers.
+ * These are I/O channels.  you can use each one for
+ * any desired purpose independently of the other. In general
+ * though, we use BAP1 for reading and writing LTV records and
+ * reading received data frames, and BAP0 for writing transmit
+ * frames. This is a convention though, not a rule.
+ * For PrismII chip, frequently overlap between BAP0 and BAP1
+ * may hang the hardware. this is a known bug, so just use BAP1
+ * for PrismII.
+ */
+#define	WL_SEL0			0x18
+#define	WL_SEL1			0x1A
+#define	WL_OFF0			0x1C
+#define	WL_OFF1			0x1E
+#define	WL_DATA0		0x36
+#define	WL_DATA1		0x38
+#define	WL_BAP0			WL_DATA0
+#define	WL_BAP1			WL_DATA1
+
+#define	WL_OFF_BUSY		0x8000
+#define	WL_OFF_ERR		0x4000
+#define	WL_OFF_DATAOFF		0x0FFF
+
+/*
+ * Event registers
+ */
+#define	WL_EVENT_STAT		0x30	/* Event status */
+#define	WL_INT_EN		0x32	/* Interrupt enable/disable */
+#define	WL_EVENT_ACK		0x34	/* Ack event */
+
+/*
+ * Events
+ */
+#define	WL_EV_TICK		0x8000	/* aux timer tick */
+#define	WL_EV_RES		0x4000	/* controller h/w error (time out) */
+#define	WL_EV_INFO_DROP		0x2000	/* no RAM to build unsolicited frame */
+#define	WL_EV_NO_CARD		0x0800	/* card removed (hunh?) */
+#define	WL_EV_DUIF_RX		0x0400	/* wavelan management packet received */
+#define	WL_EV_INFO		0x0080	/* async info frame */
+#define	WL_EV_CMD		0x0010	/* command completed */
+#define	WL_EV_ALLOC		0x0008	/* async alloc/reclaim completed */
+#define	WL_EV_TX_EXC		0x0004	/* async xmit completed with failure */
+#define	WL_EV_TX		0x0002	/* async xmit completed succesfully */
+#define	WL_EV_RX		0x0001	/* async rx completed */
+
+#define	WL_EV_ALL		0xffff	/* all events */
+#define	WL_INTRS	\
+	(WL_EV_RX|WL_EV_TX|WL_EV_TX_EXC|WL_EV_ALLOC|WL_EV_INFO|WL_EV_INFO_DROP)
+
+/*
+ * Host software registers
+ */
+#define	WL_SW0			0x28
+#define	WL_SW1			0x2A
+#define	WL_SW2			0x2C
+#define	WL_SW3			0x2E
+
+#define	WL_CNTL			0x14
+
+#define	WL_CNTL_AUX_ENA		0xC000
+#define	WL_CNTL_AUX_ENA_STAT	0xC000
+#define	WL_CNTL_AUX_DIS_STAT	0x0000
+#define	WL_CNTL_AUX_ENA_CNTL	0x8000
+#define	WL_CNTL_AUX_DIS_CNTL	0x4000
+
+#define	WL_AUX_PAGE		0x3A
+#define	WL_AUX_OFFSET		0x3C
+#define	WL_AUX_DATA		0x3E
+
+#define	WL_RID_DNLD_BUF		0xFD01
+
+/*
+ * Mem sizes (0xFD02).
+ */
+#define	WL_RID_MEMSZ		0xFD02
+#define	WL_RID_FWIDENT_P2	0xFD02
+
+/*
+ * NIC Identification (0xFD0B).
+ */
+#define	WL_RID_CARD_ID	0xFD0B
+
+typedef struct pcwl_ltv_ver {
+	uint16_t	wl_compid;
+	uint16_t	wl_variant;
+	uint16_t	wl_major;
+	uint16_t	wl_minor;
+} pcwl_ltv_ver_t;
+
+#define	WL_RID_FWVER	0xFFFF
+typedef struct pcwl_ltv_fwver {
+	uint16_t	pri[7];
+	uint16_t	st[7];
+} pcwl_ltv_fwver_t;
+
+#define	WI_NIC_EVB2	0x8000
+#define	WI_NIC_HWB3763	0x8001
+#define	WI_NIC_HWB3163	0x8002
+#define	WI_NIC_HWB3163B	0x8003
+#define	WI_NIC_EVB3	0x8004
+#define	WI_NIC_HWB1153	0x8007
+#define	WI_NIC_P2_SST	0x8008	/* Prism2 with SST flush */
+#define	WI_NIC_PRISM2_5	0x800C
+#define	WI_NIC_3874A	0x8013	/* Prism2.5 Mini-PCI */
+
+/*
+ * List of intended regulatory domains (0xFD11).
+ */
+#define	WL_RID_DOMAINS		0xFD11
+/*
+ * CIS struct (0xFD13).
+ */
+#define	WL_RID_CIS		0xFD13
+
+/*
+ * Current MAC port connection status
+ */
+#define	WL_RID_PORTSTATUS	0xFD40
+#define	WL_PORT_DISABLED	1
+#define	WL_PORT_INITIAL		2
+#define	WL_PORT_TO_IBSS		3
+#define	WL_PORT_TO_BSS		4
+#define	WL_PORT_OOR		5
+#define	WL_PORT_RADIO_OFF	7 /* only for miniPci */
+
+/*
+ * Current Service Set the station is connected to
+ */
+#define	WL_RID_SSID		0xFD41
+
+/*
+ * MAC address used as identifier of the BSS the station
+ * is connected to
+ */
+#define	WL_RID_BSSID		0xFD42
+
+/*
+ * Communications quality (0xFD43).
+ */
+#define	WL_RID_COMMQUAL		0xFD43
+
+/*
+ * Actual system scale thresholds (0xFD46).
+ */
+#define	WL_RID_SYSTEM_SCALE	0xFC06
+
+/*
+ * Actual current transmission rate
+ */
+#define	WL_RID_CUR_TX_RATE	0xFD44
+
+/*
+ * Connection control characteristics.
+ * 1 == Basic Service Set (BSS), a.k.a IEEE 802.11 Infrastructure
+ * 2 == Wireless Distribudion System (WDS), Access Point only
+ * 3 == Pseudo IBSS / Ad Hoc
+ */
+#define	WL_RID_PORTTYPE		0xFC00
+#define	WL_PORTTYPE_BSS		0x1
+#define	WL_PORTTYPE_WDS		0x2
+#define	WL_PORTTYPE_ADHOC	0x3
+
+/*
+ * Mac addresses.
+ */
+#define	WL_RID_MAC_NODE		0xFC01
+#define	WL_RID_MAC_WDS		0xFC08
+
+/*
+ * Station set identification (SSID).
+ */
+#define	WL_RID_DESIRED_SSID	0xFC02
+#define	WL_RID_OWN_SSID		0xFC04
+
+/*
+ * Set communications channel (radio frequency).
+ */
+#define	WL_RID_OWN_CHNL		0xFC03
+#define	WL_RID_CURRENT_CHNL	0xFDC1
+
+/*
+ * Frame data size.
+ */
+#define	WL_RID_MAX_DATALEN	0xFC07
+
+/*
+ * ESS power management enable
+ */
+#define	WL_RID_PM_ENABLED	0xFC09
+
+/*
+ * ESS max PM sleep internal
+ */
+#define	WL_RID_MAX_SLEEP	0xFC0C
+
+/*
+ * Set our station name.
+ */
+#define	WL_RID_NODENAME		0xFC0E
+
+/*
+ * Multicast addresses to be put in filter. We're
+ * allowed up to 16 addresses in the filter.
+ */
+#define	WL_RID_MCAST		0xFC80
+
+/*
+ * Create IBSS.
+ */
+#define	WL_RID_CREATE_IBSS	0xFC81
+
+#define	WL_RID_FRAG_THRESH	0xFC82
+#define	WL_RID_RTS_THRESH	0xFC83
+
+/*
+ * TX rate control
+ * 0 == Fixed 1mbps
+ * 1 == Fixed 2mbps
+ * 2 == auto fallback
+ */
+#define	WL_RID_TX_RATE		0xFC84
+
+/*
+ * promiscuous mode.
+ */
+#define	WL_RID_PROMISC		0xFC85
+
+/*
+ * scan ssid
+ */
+#define	WL_RID_SCAN_SSID	0xFCB2
+
+/*
+ * Auxiliary Timer tick interval
+ */
+#define	WL_RID_TICK_TIME	0xFCE0
+
+/*
+ * PrismII scan
+ */
+#define	WL_RID_SCAN_REQUEST	0xFCE1
+#define	WL_RID_HSCAN_REQUEST	0xFCE5
+
+/*
+ * Information frame types.
+ */
+#define	WL_INFO_NOTIFY		0xF000	/* Handover address */
+#define	WL_INFO_COUNTERS	0xF100	/* Statistics counters */
+#define	WL_INFO_SCAN_RESULTS	0xF101	/* Scan results */
+#define	WL_INFO_HSCAN_RESULTS	0xF103	/* HostScan results */
+#define	WL_INFO_LINK_STAT	0xF200	/* Link status */
+#define	WL_INFO_ASSOC_STAT	0xF201	/* Association status */
+
+/*
+ * Link status
+ */
+#define	WL_LINK_CONNECT		1
+#define	WL_LINK_DISCONNECT	2
+#define	WL_LINK_AP_CR		3 /* AP change */
+#define	WL_LINK_AP_OOR		4 /* AP out of range */
+#define	WL_LINK_AP_IR		5 /* AP in range */
+
+typedef struct wl_scan_result {
+	uint16_t		wl_srt_chid;  /* bss channel id */
+	uint16_t		wl_srt_anl;   /* noise level */
+	uint16_t		wl_srt_sl;    /* signal level */
+	uint8_t			wl_srt_bssid[6];  /* mac address of the bss */
+	uint16_t		wl_srt_bcnint; /* bss beacon interval */
+	uint16_t		wl_srt_cap;    /* bss capability */
+
+	uint16_t		wl_srt_ssidlen;  /* ssid name length */
+	char			wl_srt_ssid[32]; /* ssid */
+
+	uint16_t		wl_srt_suprates[5]; /* supported rates */
+	uint16_t		wl_srt_rate; /* actual data rate of the probe */
+	uint16_t		wl_srt_atim;
+} wl_scan_result_t;
+
+#define	WL_SRT_MAX_NUM		32 /* max number of scan result stored */
+#define	WL_SCAN_TIMEOUT_MAX	30 /* seconds after which the scan item ages */
+#define	WL_SCAN_AGAIN_THRESHOLD	5 /* threshold below which card scan again */
+#define	WL_MAX_SCAN_TIMES	2 /* max scan times per scan command */
+
+typedef struct wl_scan_list {
+	wl_scan_result_t	wl_val;
+	uint32_t		wl_timeout;
+	list_node_t		wl_scan_node;
+} wl_scan_list_t;
+
+#define	WL_FTYPE_MGMT		0x0000
+#define	WL_FTYPE_CTL		0x0004
+#define	WL_FTYPE_DATA		0x0008
+
+/*
+ * SNAP (sub-network access protocol) constants for transmission
+ * of IP datagrams over IEEE 802 networks, taken from RFC1042.
+ * We need these for the LLC/SNAP header fields in the TX/RX frame
+ * structure.
+ */
+#define	WL_SNAP_K1		0xaa	/* assigned global SAP for SNAP */
+#define	WL_SNAP_K2		0x00
+#define	WL_SNAP_CONTROL		0x03	/* unnumbered information format */
+#define	WL_SNAP_WORD0		(WL_SNAP_K1 | (WL_SNAP_K1 << 8))
+#define	WL_SNAP_WORD1		(WL_SNAP_K2 | (WL_SNAP_CONTROL << 8))
+#define	WL_SNAPHDR_LEN		0x6
+
+/*
+ * Hermes transmit/receive frame structure
+ */
+typedef struct wl_frame {
+	uint16_t		wl_status;	/* 0x00 */
+	uint16_t		wl_rsvd0;	/* 0x02 */
+	uint16_t		wl_rsvd1;	/* 0x04 */
+	uint16_t		wl_q_info;	/* 0x06 */
+	uint16_t		wl_rsvd2;	/* 0x08 */
+	uint16_t		wl_rsvd3;	/* 0x0A */
+	uint16_t		wl_tx_ctl;	/* 0x0C */
+	uint16_t		wl_frame_ctl;	/* 0x0E */
+	uint16_t		wl_id;		/* 0x10 */
+	uint8_t			wl_addr1[6];	/* 0x12 */
+	uint8_t			wl_addr2[6];	/* 0x18 */
+	uint8_t			wl_addr3[6];	/* 0x1E */
+	uint16_t		wl_seq_ctl;	/* 0x24 */
+	uint8_t			wl_addr4[6];	/* 0x26 */
+	uint16_t		wl_dat_len;	/* 0x2C */
+
+	uint8_t			wl_dst_addr[6];	/* 0x2E */
+	uint8_t			wl_src_addr[6];	/* 0x34 */
+	uint16_t		wl_len;		/* 0x3A */
+	uint16_t		wl_dat[3];	/* 0x3C */ /* SNAP header */
+	uint16_t		wl_type;	/* 0x42 */
+} wl_frame_t;
+
+static wl_frame_t wl_frame_default = {
+	0,			/* wl_status	   0x00 */
+	0,			/* wl_rsvd0	   0x02 */
+	0,			/* wl_rsvd1	   0x04 */
+	0,			/* wl_q_info	   0x06 */
+	0,			/* wl_rsvd2	   0x08 */
+	0,			/* wl_rsvd3	   0x0A */
+	0,			/* wl_tx_ctl	   0x0C */
+	WL_FTYPE_DATA,		/* wl_frame_ctl	   0x0E */
+	0,			/* wl_id	   0x10 */
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_addr1[6]	   0x12 */
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_addr2[6]	   0x18 */
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_addr3[6]	   0x1E */
+	0,			/* wl_seq_ctl	   0x24 */
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_addr4[6]	   0x26 */
+	(uint16_t)-WL_SNAPHDR_LEN, /* wl_dat_len	   0x2C */
+
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_dst_addr[6]  0x2E */
+	{ 0, 0, 0, 0, 0, 0 },	/* wl_src_addr[6]  0x34 */
+	(uint16_t)-WL_SNAPHDR_LEN, /* wl_len	   0x3A */
+	{ WL_SNAP_WORD0,
+	WL_SNAP_WORD1, 0 },	/* wl_dat[3]	   0x3C */ /* SNAP header */
+	0			/* wl_type	   0x42 */
+};
+
+#define	MLEN(mp)		((mp)->b_wptr - (mp)->b_rptr)
+#define	ETH_HDRLEN		(sizeof (struct ether_header))
+#define	WL_802_3_HDRLEN		0x2E
+#define	WL_802_11_HDRLEN	0x44
+#define	WL_802_11_RAW_HDRLEN	0x3C
+
+#define	WL_STAT_BADCRC		0x0001
+#define	WL_STAT_UNDECRYPTABLE	0x0002
+#define	WL_STAT_ERRSTAT		0x0003
+#define	WL_STAT_MAC_PORT	0x0700
+#define	WL_STAT_1042		0x2000	/* RFC1042 encoded */
+#define	WL_STAT_TUNNEL		0x4000	/* Bridge-tunnel encoded */
+#define	WL_STAT_WMP_MSG		0x6000	/* WaveLAN-II management protocol */
+#define	WL_RXSTAT_MSG_TYPE	0xE000
+
+#define	WL_ENC_TX_802_3		0x00
+#define	WL_ENC_TX_802_11	0x11
+#define	WL_ENC_TX_E_II		0x0E
+
+#define	WL_ENC_TX_1042		0x00
+#define	WL_ENC_TX_TUNNEL	0xF8
+
+#define	WL_TXCNTL_MACPORT	0x00FF
+#define	WL_TXCNTL_STRUCTTYPE	0xFF00
+#define	WL_TXCNTL_TXOK		0x2
+#define	WL_TXCNTL_TXEX		0x4
+#define	WL_TXCNTL_SET	(WL_TXCNTL_TXOK | WL_TXCNTL_TXEX)
+
+typedef struct rf_ckey {
+	uint16_t	ckey_len;
+	uint8_t		ckey_dat[14];
+} rf_ckey_t;
+
+/*
+ * Configurable parameters of the RF interface
+ * All the info here is passed to the card through PIO.
+ */
+typedef struct pcwl_rf {
+	uint16_t	rf_max_datalen;
+	uint16_t	rf_create_ibss;
+	uint16_t	rf_porttype;
+	uint16_t	rf_rts_thresh;
+	uint16_t	rf_tx_rate;
+	uint16_t	rf_system_scale;
+	uint16_t	rf_pm_enabled;
+	uint16_t	rf_max_sleep;
+	uint16_t	rf_own_chnl;
+	uint16_t	rf_port_no;
+	char		rf_own_ssid[34];
+	char		rf_desired_ssid[34];
+	char		rf_nodename[34];
+	uint16_t	rf_promiscuous;
+	uint16_t	rf_encryption;		/* use encryption? */
+	uint16_t	rf_authtype;		/* prism2 only */
+	uint16_t	rf_tx_crypt_key;
+	rf_ckey_t	rf_ckeys[4];
+} pcwl_rf_t;
+
+#define	PCWL_MCAST_ENTSHIFT	4
+#define	PCWL_MCAST_ENTRIES	(1 << PCWL_MCAST_ENTSHIFT)
+#define	PCWL_MCBUF_LEN		(ETHERADDRL << PCWL_MCAST_ENTSHIFT)
+#define	PCWL_MCBUF_WORDS	(PCWL_MCBUF_LEN >> 1)
+
+typedef enum {
+	WLC_TX_UNICAST_FRAMES,		/*  0+ */
+	WLC_TX_MULTICAST_FRAMES,	/*  1+ */
+	WLC_TX_FRAGMENTS,		/*  2+ */
+	WLC_TX_UNICAST_OCTETS,		/*  3+ */
+	WLC_TX_MULTICAST_OCTETS,	/*  4  */
+	WLC_TX_DEFERRED_XMITS,		/*  5+ */
+	WLC_TX_SINGLE_RETRIES,		/*  6+ */
+	WLC_TX_MULTI_RETRIES,		/*  7+ */
+	WLC_TX_RETRY_LIMIT,		/*  8+ */
+	WLC_TX_DISCARDS,		/*  9+ */
+	WLC_RX_UNICAST_FRAMES,		/* 10+ */
+	WLC_RX_MULTICAST_FRAMES,	/* 11+ */
+	WLC_RX_FRAGMENTS,		/* 12+ */
+	WLC_RX_UNICAST_OCTETS,		/* 13+ */
+	WLC_RX_MULTICAST_OCTETS,	/* 14  */
+	WLC_RX_FCS_ERRORS,		/* 15+ */
+	WLC_RX_DISCARDS_NOBUF,		/* 16+ */
+	WLC_TX_DISCARDS_WRONG_SA,	/* 17+ */
+	WLC_RX_WEP_CANT_DECRYPT,	/* 18+ */
+	WLC_RX_MSG_IN_MSG_FRAGS,	/* 19+ */
+	WLC_RX_MSG_IN_BAD_MSG_FRAGS,	/* 20+ */
+	WLC_STAT_CNT			/* 21 - keep it as the last entry */
+} pcwl_cntr_offset;
+
+#define	WL_XMT_BUF_NUM	8
+typedef struct	wl_tx_ring_data {
+	uint16_t		wl_tx_fids[WL_XMT_BUF_NUM];
+	uint16_t		wl_tx_ring[WL_XMT_BUF_NUM];
+	int			wl_tx_prod;
+	int			wl_tx_cons;
+	kmutex_t		wl_tx_lock;	/* for send only */
+} pcwl_txring_t;
+
+#define	PCWL_DEVICE_PCI		0
+#define	PCWL_DEVICE_PCCARD	1
+
+/*
+ * The macinfo is really used as the softstate structure.
+ *
+ * pcwl_mh	 - mac_handle_t structure
+ * pcwl_cslock	 - lock for card services request. Used with pcwl_cscv
+ * pcwl_cscv	 - condition variable to wait for card events
+ * pcwl_chdl	 - client handle, an uint32_t bit mask encoding for socket,
+ *			function, and client info.
+ *			See cs_priv.h MAKE_CLIENT_HANDLE.
+ * pcwl_log_sock - holds the logical to physical translation for this card.
+ *			Specifically has physical adapter and socket #.
+ *			Socket # is the same as part of the pcwl_chdl encoding.
+ *			Physical adapter # is from card service socket impl.
+ */
+typedef struct pcwl_macinfo {
+	mac_handle_t		pcwl_mh;
+	dev_info_t		*pcwl_dip;
+	int			pcwl_device_type; /* pci or pcmcia card */
+	kmutex_t		pcwl_cslock;	/* for card services */
+	kcondvar_t		pcwl_cscv;	/* for card services */
+	client_handle_t		pcwl_chdl;	/* s,f,c encoding, cs_priv.h */
+	map_log_socket_t	pcwl_log_sock;	/* logical/phys socket map */
+
+	int			pcwl_socket;    /* socket number */
+	int			pcwl_config_hi;	/* cfttbl index */
+	int			pcwl_config;	/* default config index */
+	int			pcwl_vcc;	/* vcc level */
+	int			pcwl_iodecode;	/* # of address lines */
+	int			pcwl_chip_type;	/* Lucent or Prism-II */
+
+	uint8_t 		pcwl_mac_addr[ETHERADDRL];
+	uint8_t 		pcwl_bssid[ETHERADDRL];
+	uint16_t		pcwl_has_wep;	/* has encryption capability */
+	uint32_t		pcwl_flag;
+	uint32_t		pcwl_reschedule_need;
+	pcwl_rf_t		pcwl_rf;	/* RF interface parameters */
+
+	uint16_t		pcwl_dmem_id;	/* nic mem id for tx buffer */
+	uint16_t		pcwl_mgmt_id;	/* nic mem id for mgmt buffer */
+	pcwl_txring_t		pcwl_txring;
+
+	uint16_t		pcwl_mcast[PCWL_MCBUF_WORDS]; /* MC filters */
+
+	kmutex_t		pcwl_scanlist_lock;	/* scanlist lock */
+	kmutex_t		pcwl_glock;	/* generic lock */
+
+	caddr_t			pcwl_bar;	/* for pci device only */
+	ddi_acc_handle_t	pcwl_handle;
+	caddr_t			pcwl_cfg_base;
+	ddi_acc_handle_t	pcwl_cfg_handle;
+
+	ddi_acc_handle_t	pcwl_port;	/* for pcmcia device only */
+
+	ddi_iblock_cookie_t	pcwl_ib_cookie;
+	ddi_softintr_t		pcwl_softint_id; /* pcwl_intr soft intr id */
+
+	uint16_t		pcwl_cntrs_t[WLC_STAT_CNT];
+	uint64_t		pcwl_cntrs_s[WLC_STAT_CNT];
+	uint64_t		pcwl_noxmtbuf;
+	timeout_id_t		pcwl_scanlist_timeout_id;
+	list_t			pcwl_scan_list;
+	uint16_t		pcwl_scan_num;
+	uint16_t		pcwl_rssi;
+	timeout_id_t		pcwl_connect_timeout_id;
+} pcwl_maci_t;
+
+#define	PCWL_IDENT_STRING	modldrv.drv_linkinfo
+#define	PCWL_CHIP_LUCENT	0
+#define	PCWL_CHIP_PRISMII	1
+#define	HDL(pcwl_p)		((pcwl_p)->pcwl_port)
+#define	GLD3(pcwl_p)		((pcwl_p)->pcwl_mh)
+#define	DIP(pcwl_p)		((pcwl_p)->pcwl_dip)
+#define	RF(pcwl_p)		(&(pcwl_p)->pcwl_rf)
+
+#define	PCWL_CARD_INTREN	0x1
+#define	PCWL_SOFTINTR		0x2	/* high level and soft intr enabled */
+#define	PCWL_CARD_LINKUP	0x4	/* link status of the STA */
+#define	PCWL_CARD_GSTAT		0x8
+#define	PCWL_ATTACHED		0x10
+#define	PCWL_CS_REGISTERED	0x20
+#define	PCWL_ENABLED		0x40
+#define	PCWL_CARD_READY		0x80
+#define	PCWL_CARD_FAILED	0x100
+#define	PCWL_CARD_INTR		0x200
+
+#define	PCWL_STATE_IDLE		0x1
+
+#define	PCWL_NICMEM_SZ		(2048) /* 80211MTU set as 1500, so 2k here */
+
+static int	pcwl_probe(dev_info_t *dip);
+static int	pcwl_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int	pcwl_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+
+static int	pcwl_register_cs(dev_info_t *dip, pcwl_maci_t *pcwl_p);
+static void	pcwl_unregister_cs(pcwl_maci_t *pcwl_p);
+static void	pcwl_destroy_locks(pcwl_maci_t *pcwl_p);
+static int	pcwl_reset_backend(pcwl_maci_t *pcwl_p);
+static int	pcwl_get_cap(pcwl_maci_t *pcwl_p);
+static int	pcwl_card_insert(pcwl_maci_t *pcwl_p);
+static int	pcwl_ev_hdlr(event_t ev, int pri, event_callback_args_t *arg);
+static void	pcwl_card_remove(pcwl_maci_t *pcwl_p);
+static int	pcwl_init_nicmem(pcwl_maci_t *pcwl_p);
+
+/*
+ * high level device access primitives, glock must held before calling
+ */
+static uint16_t	pcwl_set_cmd(pcwl_maci_t *pcwl_p, uint16_t mode, uint16_t type);
+static uint16_t pcwl_set_ch(pcwl_maci_t *, uint16_t, uint16_t, uint16_t);
+static uint16_t	pcwl_get_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type,
+			uint16_t *val_p);
+static uint16_t	pcwl_put_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type,
+			uint16_t *val_p);
+static uint16_t	pcwl_fil_ltv(pcwl_maci_t *pcwl_p, uint16_t len, uint16_t type,
+			uint16_t val);
+static uint16_t	pcwl_put_str(pcwl_maci_t *pcwl_p, uint16_t type, char *str_p);
+static uint16_t pcwl_rdch0(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off,
+			uint16_t *buf_p, int len, int order);
+static uint16_t pcwl_wrch1(pcwl_maci_t *pcwl_p, uint16_t type, uint16_t off,
+			uint16_t *buf_p, int len, int order);
+static int	pcwl_config_rf(pcwl_maci_t *pcwl_p);
+static int	pcwl_loaddef_rf(pcwl_maci_t *pcwl_p);
+static void	pcwl_start_locked(pcwl_maci_t *pcwl_p);
+static void	pcwl_stop_locked(pcwl_maci_t *pcwl_p);
+static int	pcwl_saddr_locked(pcwl_maci_t *pcwl_p);
+static uint16_t	pcwl_alloc_nicmem(pcwl_maci_t *pcwl_p, uint16_t len,
+			uint16_t *id_p);
+static void	pcwl_chip_type(pcwl_maci_t *pcwl_p);
+
+/*
+ * Required driver entry points for mac
+ */
+static int	pcwl_start(void *);
+static void	pcwl_stop(void *);
+static int	pcwl_saddr(void *, const uint8_t *);
+static mblk_t	*pcwl_tx(void *, mblk_t *);
+static int	pcwl_send(pcwl_maci_t *, mblk_t *);
+static int	pcwl_prom(void *, boolean_t);
+static int	pcwl_gstat(void *, uint_t, uint64_t *);
+static int	pcwl_sdmulti(void *, boolean_t, const uint8_t *);
+static void 	pcwl_ioctl(void *, queue_t *, mblk_t *);
+
+static uint_t	pcwl_intr(caddr_t arg);
+static uint_t	pcwl_intr_hi(caddr_t arg);
+static void	pcwl_rcv(pcwl_maci_t *pcwl_p);
+static uint32_t pcwl_txdone(pcwl_maci_t *pcwl_p);
+static void pcwl_infodone(pcwl_maci_t *pcwl_p);
+static void 	pcwl_ssid_scan(pcwl_maci_t *, uint16_t, uint16_t, uint16_t);
+
+/*
+ * prototypes of the function for wifi ioctl
+ */
+static int	pcwl_cfg_essid(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_bssid(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_scan(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_linkstatus(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_bsstype(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_phy(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_desiredrates(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_supportrates(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_powermode(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_authmode(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_encryption(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_wepkeyid(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_createibss(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_rssi(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_radio(mblk_t *, pcwl_maci_t *, uint32_t);
+static int	pcwl_cfg_wepkey(mblk_t *, pcwl_maci_t *, uint32_t);
+static void	pcwl_wlan_ioctl(pcwl_maci_t *, queue_t *, mblk_t *, uint32_t);
+static int	pcwl_getset(mblk_t *, pcwl_maci_t *, uint32_t);
+
+static void	pcwl_scanlist_timeout(void *);
+static void	pcwl_delete_scan_item(pcwl_maci_t *, wl_scan_list_t *);
+static int	pcwl_add_scan_item(pcwl_maci_t *, wl_scan_result_t);
+static void	pcwl_get_rssi(pcwl_maci_t *);
+static void 	pcwl_connect_timeout(void *arg);
+
+#define	RDCH0(h, t, o, b_p, l)	pcwl_rdch0(h, t, o, b_p, l, 1)
+#define	WRCH1(h, t, o, b_p, l)	pcwl_wrch1(h, t, o, b_p, l, 1)
+#define	RDPKT(h, t, o, b_p, l)	pcwl_rdch0(h, t, o, b_p, l, 0)
+#define	WRPKT(h, t, o, b_p, l)	pcwl_wrch1(h, t, o, b_p, l, 0)
+
+#define	FIL_LTV(pcwl_p, len, type, val)	 \
+	(void) pcwl_fil_ltv(pcwl_p, len, type, val)
+#define	PUT_LTV(pcwl_p, len, type, v_p)	 \
+	(void) pcwl_put_ltv(pcwl_p, len, type, v_p)
+#define	PUT_STR(pcwl_p, type, str_p)	\
+	(void) pcwl_put_str(pcwl_p, type, str_p)
+
+#define	PCWL_READ(p, o, v)	{ \
+	if (p->pcwl_device_type == PCWL_DEVICE_PCI) { \
+		uint16_t t = ddi_get16(p->pcwl_handle, \
+		    (uint16_t *)(p->pcwl_bar + 2*(o))); \
+		v = LE_16(t); \
+	} else { \
+		uint16_t t = csx_Get16(HDL(p), o); \
+		v = LE_16(t); \
+	}\
+}
+#define	PCWL_WRITE(p, o, v)	{ \
+	if (p->pcwl_device_type == PCWL_DEVICE_PCI) { \
+		ddi_put16(p->pcwl_handle, \
+		    (uint16_t *)(p->pcwl_bar + 2*(o)), LE_16(v)); \
+	} else { \
+		csx_Put16(HDL(p), o, LE_16(v)); \
+	}\
+}
+#define	PCWL_READ_P(p, o, v, h)	{ \
+	if (p->pcwl_device_type == PCWL_DEVICE_PCI) { \
+		uint16_t t = ddi_get16(p->pcwl_handle, \
+		    (uint16_t *)(p->pcwl_bar + 2*(o))); \
+		*(v) = h ? LE_16(t) : t; \
+	} else { \
+		uint16_t t = csx_Get16(HDL(p), o); \
+		*(v) = h ? LE_16(t) : t; \
+	}\
+}
+#define	PCWL_WRITE_P(p, o, v, h)	{ \
+	if (p->pcwl_device_type == PCWL_DEVICE_PCI) { \
+		ddi_put16(p->pcwl_handle, (uint16_t *)(p->pcwl_bar + 2*(o)), \
+		    h ? LE_16(*(v)) : (*(v))); \
+	} else {\
+		csx_Put16(HDL(p), o, h ? LE_16(*(v)) : (*(v))); \
+	}\
+}
+
+#ifdef _BIG_ENDIAN
+#define	PCWL_SWAP16(buf_p, len) { \
+	int pcwl_swap_len = len; \
+	for (pcwl_swap_len = (pcwl_swap_len + 1) >> 1; pcwl_swap_len; ) { \
+		uint16_t val; \
+		pcwl_swap_len--; \
+		val = *((uint16_t *)(buf_p) + pcwl_swap_len); \
+		*((uint16_t *)(buf_p) + pcwl_swap_len) = LE_16(val); \
+	} \
+}
+#else /* _BIG_ENDIAN */
+#define	PCWL_SWAP16(buf_p, len)
+#endif /* _BIG_ENDIAN */
+
+#define	PCWL_ENABLE_INTR(pcwl_p)	{\
+	PCWL_WRITE(pcwl_p, WL_INT_EN, WL_INTRS);\
+}
+#define	PCWL_DISABLE_INTR(pcwl_p)	{ \
+	PCWL_WRITE(pcwl_p, WL_INT_EN, 0); \
+	PCWL_WRITE(pcwl_p, WL_EVENT_ACK, 0xffff);\
+}
+
+/*
+ * 16-bit driver private status code
+ */
+#define	PCWL_SUCCESS		0x0
+#define	PCWL_FAIL		0x1
+#define	PCWL_TIMEDOUT_CMD	0x10
+#define	PCWL_TIMEDOUT_ACCESS	0x11
+#define	PCWL_TIMEDOUT_TARGET	0x12
+#define	PCWL_BADLEN		0x13
+#define	PCWL_BADTYPE		0x14
+#define	PCWL_TIMEDOUT_ALLOC	0x15
+#define	PCWL_FAILURE_CMD	0x16
+
+#define	PCWL_STATUS_MAX		0xffff
+#define	N_PCWL			2
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_PCWL_H */
--- a/usr/src/uts/intel/Makefile.intel.shared	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/uts/intel/Makefile.intel.shared	Wed Feb 28 19:21:46 2007 -0800
@@ -266,6 +266,8 @@
 DRV_KMODS	+= pcic
 DRV_KMODS	+= pcie_pci
 DRV_KMODS	+= physmem
+DRV_KMODS	+= pcan
+DRV_KMODS	+= pcwl
 DRV_KMODS	+= pm
 DRV_KMODS	+= poll
 DRV_KMODS	+= pool
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/pcan/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,107 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the pcan driver kernel module.
+#
+#	intel implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pcan
+OBJECTS		= $(PCAN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PCAN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)  $(ITUMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CLEANFILES	+= $(MODSTUBS_O)
+INC_PATH	+= -I$(UTSBASE)/common/pcmcia
+
+#
+# lint pass one enforcement
+#
+CFLAGS          += -v
+
+#
+# STREAMS API limitations force us to turn off these lint checks.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS        += -erroff=E_PTRDIFF_OVERFLOW
+
+#
+# dependency
+#
+LDFLAGS		+= -dy -Nmisc/mac -Ndrv/ip
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/intel/pcwl/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,107 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the pcwl driver kernel module.
+#
+#	intel implementation architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pcwl
+OBJECTS		= $(PCWL_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PCWL_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)  $(ITUMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CLEANFILES	+= $(MODSTUBS_O)
+INC_PATH	+= -I$(UTSBASE)/common/pcmcia
+
+#
+# lint pass one enforcement
+#
+CFLAGS          += -v
+
+#
+# STREAMS API limitations force us to turn off these lint checks.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS        += -erroff=E_PTRDIFF_OVERFLOW
+
+#
+# dependency
+#
+LDFLAGS		+= -dy -Nmisc/mac -Ndrv/ip
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
--- a/usr/src/uts/sparc/Makefile.sparc.shared	Wed Feb 28 18:47:22 2007 -0800
+++ b/usr/src/uts/sparc/Makefile.sparc.shared	Wed Feb 28 19:21:46 2007 -0800
@@ -257,6 +257,8 @@
 #
 
 DRV_KMODS	+= e1000g
+DRV_KMODS	+= pcan
+DRV_KMODS	+= pcwl
 DRV_KMODS	+= rge
 $(CLOSED_BUILD)CLOSED_DRV_KMODS	+= ixgb
 
@@ -446,6 +448,7 @@
 #	MAC-Type Plugin Modules (/kernel/mac)
 #
 MAC_KMODS	+= mac_ether
+MAC_KMODS	+= mac_wifi
 
 #
 # 'Devname' Modules (kernel/devname)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/mac_wifi/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,92 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#
+#	This makefile drives the production of the mac_wifi plugin 
+#	kernel module.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= mac_wifi
+OBJECTS		= $(MAC_WIFI_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(MAC_WIFI_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_MAC_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy -Nmisc/mac
+#
+# STREAMS API limitations force us to turn off these lint checks.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS        += -erroff=E_PTRDIFF_OVERFLOW
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/pcan/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,105 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the pcan driver kernel module.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pcan
+OBJECTS		= $(PCAN_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PCAN_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)  $(ITUMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CLEANFILES	+= $(MODSTUBS_O)
+INC_PATH	+= -I$(UTSBASE)/common/pcmcia
+
+#
+# lint pass one enforcement
+#
+CFLAGS          += -v
+
+#
+# STREAMS API limitations force us to turn off these lint checks.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS        += -erroff=E_PTRDIFF_OVERFLOW
+
+#
+# dependency
+#
+LDFLAGS		+= -dy -Nmisc/mac -Ndrv/ip
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/sparc/pcwl/Makefile	Wed Feb 28 19:21:46 2007 -0800
@@ -0,0 +1,105 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of the pcwl driver kernel module.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= pcwl
+OBJECTS		= $(PCWL_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(PCWL_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_DRV_DIR)/$(MODULE)
+
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)  $(ITUMOD)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Override defaults to build a unique, local modstubs.o.
+#
+MODSTUBS_DIR	 = $(OBJS_DIR)
+CLEANFILES	+= $(MODSTUBS_O)
+INC_PATH	+= -I$(UTSBASE)/common/pcmcia
+
+#
+# lint pass one enforcement
+#
+CFLAGS          += -v
+
+#
+# STREAMS API limitations force us to turn off these lint checks.
+#
+LINTTAGS        += -erroff=E_BAD_PTR_CAST_ALIGN
+LINTTAGS        += -erroff=E_PTRDIFF_OVERFLOW
+
+#
+# dependency
+#
+LDFLAGS		+= -dy -Nmisc/mac -Ndrv/ip
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
+