# HG changeset patch # User hx147065 # Date 1172719306 28800 # Node ID ddc3d2cb268a184284d909cb08601223089ed973 # Parent aa9dbf463acb56c50c6ca66dd66f003645c83f71 PSARC 2006/705 pcwl and pcan 802.11b Wireless Drivers 6209928 drivers for Wavelan, Prism II, and Aironet 802.11b devices diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/Makefile --- 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 \ diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWckr/prototype_sparc --- 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/Makefile --- /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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/pkginfo.tmpl --- /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" diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/postinstall --- /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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/postremove --- /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 "\" $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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/prototype_com --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/prototype_i386 --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcan/prototype_sparc --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/Makefile --- /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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/pkginfo.tmpl --- /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" diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/postinstall --- /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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/postremove --- /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 "\" $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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/prototype_com --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/prototype_i386 --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/pkgdefs/SUNWpcwl/prototype_sparc --- /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 # where to find pkg objects +#!include # include another 'prototype' file +#!default # default used if not specified on entry +#!= # 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/Makefile.files --- 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/Makefile.rules --- 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)) diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/io/pcan/pcan.c --- /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 . 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcan.h" +#include +#include + +#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); +} diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/io/pcan/pcan.h --- /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 . 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 */ diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/io/pcwl/pcwl.c --- /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 . 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcwl.h" +#include +#include + +#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); +} diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/common/io/pcwl/pcwl.h --- /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 . 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 + +/* + * 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 */ diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/intel/Makefile.intel.shared --- 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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/intel/pcan/Makefile --- /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 + diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/intel/pcwl/Makefile --- /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 + diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/sparc/Makefile.sparc.shared --- 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) diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/sparc/mac_wifi/Makefile --- /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 diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/sparc/pcan/Makefile --- /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 + diff -r aa9dbf463acb -r ddc3d2cb268a usr/src/uts/sparc/pcwl/Makefile --- /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 +