Mercurial > illumos > illumos-gate
changeset 10741:26a74e07a95b
6873319 WiFi driver for Marvell 88W8363 chipset
line wrap: on
line diff
--- a/usr/src/pkgdefs/Makefile Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/pkgdefs/Makefile Thu Oct 08 14:27:17 2009 +0800 @@ -139,6 +139,7 @@ SUNWlxu \ SUNWmegasas \ SUNWmv88sx \ + SUNWmwl \ SUNWbcmsata \ SUNWnge \ SUNWntfsprogs \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/Makefile Thu Oct 08 14:27:17 2009 +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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +include ../Makefile.com + +DATAFILES += depend +LICENSEFILES = \ + ../../uts/common/io/mwl/THIRDPARTYLICENSE \ + ../../uts/common/io/mwl/mwl_fw/LICENSE + +.KEEP_STATE: + +all: $(FILES) postinstall postremove +install: all pkg + +include ../Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/pkginfo.tmpl Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,44 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PKG="SUNWmwl" +NAME="Marvell 88W8363 IEEE802.11b/g Wireless Network Device 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="Marvell 88W8363 IEEE802.11b/g Wireless Network Device Driver" +CLASSES="none" +HOTLINE="Please contact your local service provider" +EMAIL="" +BASEDIR=/ +SUNW_PKG_ALLZONES="true" +SUNW_PKG_HOLLOW="true" +SUNW_PKG_THISZONE="false"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/postinstall Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,91 @@ +#! /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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# Driver info +DRV=mwl +DRVALIAS='"pci11ab,2a24" "pci11ab,2a0a"' + +BASEDIR=${BASEDIR:-/} + +# Function: check_add_drv() +# +# This function will check if add_drv has been executed. +# If not simply calls add_drv. Otherwise adds entries to +# driver_aliases, driver_classes and minor_perm if necessary. +# The syntax of this function is the same as add_drv. + +check_add_drv() +{ + CMD="add_drv" + + ALIAS="" + ALIASDIR="${BASEDIR}"/etc/driver_aliases + while getopts i:b: opt 2>/dev/null; do + case "$opt" in + i) CMD="${CMD} -i ${OPTARG}" + ALIAS=`echo ${OPTARG} | /usr/bin/sed -e "s/'//g"` + ;; + b) if [ "${OPTARG}" != "/" ]; then + # On a client + # modify the sytem files and touch + # /reconfigure for reconfigure reboot + CMD="${CMD} -b \"${OPTARG}\"" + fi + ;; + \?) echo "check_add_drv(): Unknown option $opt" + return + ;; + esac + done + shift `/usr/bin/expr ${OPTIND} - 1` + DRIVER=$1 + CMD="${CMD} ${DRIVER}" + + # Make sure add_drv has not been previously executed + # before attempting to add the driver + /usr/bin/egrep -s "^${DRIVER}[ ]" "$BASEDIR"/etc/name_to_major + + if [ $? -ne 0 ]; then + eval ${CMD} + if [ $? -ne 0 ]; then + echo "Failed add_drv ${DRIVER}!\n" >&2 + exit 1 + fi + else + # Add driver entry if necessary + if [ -n "${ALIAS}" ]; then + for i in ${ALIAS}; do + /usr/bin/egrep -s "^${DRIVER}[ ]+$i" ${ALIASDIR} + if [ $? -ne 0 ]; then + echo "${DRIVER} $i" >> ${ALIASDIR} + fi + done + fi + fi +} + +check_add_drv -b "${BASEDIR}" -i "'${DRVALIAS}'" ${DRV}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/postremove Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,38 @@ +#! /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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +DRV=mwl +BD=${BASEDIR:-/} +if grep -w $DRV $BD/etc/name_to_major > /dev/null 2>&1 +then + rem_drv -b ${BD} $DRV + if [ $? -ne 0 ] + then + exit 1 + fi +fi + +exit 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/prototype_com Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,45 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# SUNWmwl +# + +i pkginfo +i copyright +i depend +i postinstall +i postremove
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/pkgdefs/SUNWmwl/prototype_i386 Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,54 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This required package information file contains a list of package contents. +# The 'pkgmk' command uses this file to identify the contents of a package +# and their location on the development machine when building the package. +# Can be created via a text editor or through use of the 'pkgproto' command. + +#!search <pathname pathname ...> # where to find pkg objects +#!include <filename> # include another 'prototype' file +#!default <mode> <owner> <group> # default used if not specified on entry +#!<param>=<value> # puts parameter in pkg environment + +# +# SUNWmwl +# + +# Include ISA independent files (prototype_com) +# +!include prototype_com + +# mwl ieee80211b/g wifi driver +d none kernel 0755 root sys +d none kernel/drv 0755 root sys +f none kernel/drv/mwl 0755 root sys +d none kernel/misc 0755 root sys +f none kernel/misc/mwlfw 0755 root sys +d none kernel/drv/amd64 0755 root sys +f none kernel/drv/amd64/mwl 0755 root sys +d none kernel/misc/amd64 0755 root sys +f none kernel/misc/amd64/mwlfw 0755 root sys
--- a/usr/src/pkgdefs/common_files/i.minorperm_i386 Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/pkgdefs/common_files/i.minorperm_i386 Thu Oct 08 14:27:17 2009 +0800 @@ -117,6 +117,7 @@ clone:iwh 0600 root sys 0666 root sys /dev/iwh clone:iwi 0600 root sys 0666 root sys /dev/iwi clone:iwk 0600 root sys 0666 root sys /dev/iwk +clone:mwl 0600 root sys 0666 root sys /dev/mwl clone:pcwl 0600 root sys 0666 root sys /dev/pcwl clone:pcan 0600 root sys 0666 root sys /dev/pcan clone:ral 0600 root sys 0666 root sys /dev/ral @@ -150,6 +151,7 @@ iwh:* 0600 root sys 0666 root sys /dev/iwh* iwi:* 0600 root sys 0666 root sys /dev/iwi* iwk:* 0600 root sys 0666 root sys /dev/iwk* +mwl:* 0600 root sys 0666 root sys /dev/mwl* pcwl:* 0600 root sys 0666 root sys /dev/pcwl* pcan:* 0600 root sys 0666 root sys /dev/pcan* ral:* 0600 root sys 0666 root sys /dev/ral* @@ -310,6 +312,7 @@ clone:iwh clone:iwi clone:iwk +clone:mwl clone:pcwl clone:pcan clone:ral @@ -343,6 +346,7 @@ iwh:* iwi:* iwk:* +mwl:* pcwl:* pcan:* ral:*
--- a/usr/src/tools/opensolaris/license-list Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/tools/opensolaris/license-list Thu Oct 08 14:27:17 2009 +0800 @@ -160,6 +160,8 @@ usr/src/uts/common/io/mxfe/THIRDPARTYLICENSE usr/src/uts/common/io/ntxn/THIRDPARTYLICENSE usr/src/uts/common/io/myri10ge/THIRDPARTYLICENSE +usr/src/uts/common/io/mwl/THIRDPARTYLICENSE +usr/src/uts/common/io/mwl/mwl_fw/LICENSE usr/src/uts/common/io/pcan/THIRDPARTYLICENSE usr/src/uts/common/io/pcwl/THIRDPARTYLICENSE usr/src/uts/common/io/ppp/THIRDPARTYLICENSE
--- a/usr/src/uts/common/Makefile.files Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/uts/common/Makefile.files Thu Oct 08 14:27:17 2009 +0800 @@ -1734,6 +1734,10 @@ IWK_OBJS += iwk2.o +MWL_OBJS += mwl.o + +MWLFW_OBJS += mwlfw_mode.o + WPI_OBJS += wpi.o RAL_OBJS += rt2560.o ral_rate.o
--- a/usr/src/uts/common/Makefile.rules Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/uts/common/Makefile.rules Thu Oct 08 14:27:17 2009 +0800 @@ -880,6 +880,14 @@ $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/mwl/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/mwl/mwl_fw/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/net80211/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -2090,6 +2098,12 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mxfe/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mwl/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/mwl/mwl_fw/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/net80211/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/THIRDPARTYLICENSE Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2009 Marvell Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + * + */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/THIRDPARTYLICENSE.descrip Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,1 @@ +MARVELL 88W8363 IEEE802.11b/g WIFI DRIVER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl.c Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,4354 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2008 Marvell Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Driver for the Marvell 88W8363 Wireless LAN controller. + */ +#include <sys/stat.h> +#include <sys/dlpi.h> +#include <inet/common.h> +#include <inet/mi.h> +#include <sys/stream.h> +#include <sys/errno.h> +#include <sys/stropts.h> +#include <sys/stat.h> +#include <sys/sunddi.h> +#include <sys/strsubr.h> +#include <sys/strsun.h> +#include <sys/pci.h> +#include <sys/mac_provider.h> +#include <sys/mac_wifi.h> +#include <sys/net80211.h> +#include <inet/wifi_ioctl.h> + +#include "mwl_var.h" + +static int mwl_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd); +static int mwl_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd); +static int mwl_quiesce(dev_info_t *devinfo); + +DDI_DEFINE_STREAM_OPS(mwl_dev_ops, nulldev, nulldev, mwl_attach, mwl_detach, + nodev, NULL, D_MP, NULL, mwl_quiesce); + +static struct modldrv mwl_modldrv = { + &mod_driverops, /* Type of module. This one is a driver */ + "Marvell 88W8363 WiFi driver v1.1", /* short description */ + &mwl_dev_ops /* driver specific ops */ +}; + +static struct modlinkage modlinkage = { + MODREV_1, (void *)&mwl_modldrv, NULL +}; + +static void *mwl_soft_state_p = NULL; + +static int mwl_m_stat(void *, uint_t, uint64_t *); +static int mwl_m_start(void *); +static void mwl_m_stop(void *); +static int mwl_m_promisc(void *, boolean_t); +static int mwl_m_multicst(void *, boolean_t, const uint8_t *); +static int mwl_m_unicst(void *, const uint8_t *); +static mblk_t *mwl_m_tx(void *, mblk_t *); +static void mwl_m_ioctl(void *, queue_t *, mblk_t *); +static int mwl_m_setprop(void *arg, const char *pr_name, + mac_prop_id_t wldp_pr_num, + uint_t wldp_length, const void *wldp_buf); +static int mwl_m_getprop(void *arg, const char *pr_name, + mac_prop_id_t wldp_pr_num, uint_t pr_flags, + uint_t wldp_length, void *wldp_buf, uint_t *); + +static mac_callbacks_t mwl_m_callbacks = { + MC_IOCTL | MC_SETPROP | MC_GETPROP, + mwl_m_stat, + mwl_m_start, + mwl_m_stop, + mwl_m_promisc, + mwl_m_multicst, + mwl_m_unicst, + mwl_m_tx, + mwl_m_ioctl, + NULL, + NULL, + NULL, + mwl_m_setprop, + mwl_m_getprop +}; + +#define MWL_DBG_ATTACH (1 << 0) +#define MWL_DBG_DMA (1 << 1) +#define MWL_DBG_FW (1 << 2) +#define MWL_DBG_HW (1 << 3) +#define MWL_DBG_INTR (1 << 4) +#define MWL_DBG_RX (1 << 5) +#define MWL_DBG_TX (1 << 6) +#define MWL_DBG_CMD (1 << 7) +#define MWL_DBG_CRYPTO (1 << 8) +#define MWL_DBG_SR (1 << 9) +#define MWL_DBG_MSG (1 << 10) + +uint32_t mwl_dbg_flags = 0x0; + +#ifdef DEBUG +#define MWL_DBG \ + mwl_debug +#else +#define MWL_DBG +#endif + +/* + * PIO access attributes for registers + */ +static ddi_device_acc_attr_t mwl_reg_accattr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC, + DDI_DEFAULT_ACC +}; + +static ddi_device_acc_attr_t mwl_cmdbuf_accattr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC, + DDI_DEFAULT_ACC +}; + +/* + * DMA access attributes for descriptors and bufs: NOT to be byte swapped. + */ +static ddi_device_acc_attr_t mwl_desc_accattr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC, + DDI_DEFAULT_ACC +}; + +static ddi_device_acc_attr_t mwl_buf_accattr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC, + DDI_DEFAULT_ACC +}; + +/* + * Describes the chip's DMA engine + */ +static ddi_dma_attr_t mwl_dma_attr = { + DMA_ATTR_V0, /* dma_attr version */ + 0x0000000000000000ull, /* dma_attr_addr_lo */ + 0xFFFFFFFF, /* dma_attr_addr_hi */ + 0x00000000FFFFFFFFull, /* dma_attr_count_max */ + 0x0000000000000001ull, /* dma_attr_align */ + 0x00000FFF, /* dma_attr_burstsizes */ + 0x00000001, /* dma_attr_minxfer */ + 0x000000000000FFFFull, /* dma_attr_maxxfer */ + 0xFFFFFFFFFFFFFFFFull, /* dma_attr_seg */ + 1, /* dma_attr_sgllen */ + 0x00000001, /* dma_attr_granular */ + 0 /* dma_attr_flags */ +}; + +/* + * Supported rates for 802.11a/b/g modes (in 500Kbps unit). + */ +static const struct ieee80211_rateset mwl_rateset_11b = + { 4, { 2, 4, 11, 22 } }; + +static const struct ieee80211_rateset mwl_rateset_11g = + { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } }; + +static int mwl_alloc_dma_mem(dev_info_t *, ddi_dma_attr_t *, size_t, + ddi_device_acc_attr_t *, uint_t, uint_t, + struct dma_area *); +static void mwl_free_dma_mem(struct dma_area *); +static int mwl_alloc_cmdbuf(struct mwl_softc *); +static void mwl_free_cmdbuf(struct mwl_softc *); +static int mwl_alloc_rx_ring(struct mwl_softc *, int); +static void mwl_free_rx_ring(struct mwl_softc *); +static int mwl_alloc_tx_ring(struct mwl_softc *, struct mwl_tx_ring *, + int); +static void mwl_free_tx_ring(struct mwl_softc *, struct mwl_tx_ring *); +static int mwl_setupdma(struct mwl_softc *); +static void mwl_txq_init(struct mwl_softc *, struct mwl_tx_ring *, int); +static int mwl_tx_setup(struct mwl_softc *, int, int); +static int mwl_setup_txq(struct mwl_softc *); +static int mwl_fwload(struct mwl_softc *, void *); +static int mwl_loadsym(ddi_modhandle_t, char *, char **, size_t *); +static void mwlFwReset(struct mwl_softc *); +static void mwlPokeSdramController(struct mwl_softc *, int); +static void mwlTriggerPciCmd(struct mwl_softc *); +static int mwlWaitFor(struct mwl_softc *, uint32_t); +static int mwlSendBlock(struct mwl_softc *, int, const void *, size_t); +static int mwlSendBlock2(struct mwl_softc *, const void *, size_t); +static void mwlSendCmd(struct mwl_softc *); +static int mwlExecuteCmd(struct mwl_softc *, unsigned short); +static int mwlWaitForCmdComplete(struct mwl_softc *, uint16_t); +static void dumpresult(struct mwl_softc *, int); +static int mwlResetHalState(struct mwl_softc *); +static int mwlGetPwrCalTable(struct mwl_softc *); +static int mwlGetCalTable(struct mwl_softc *, uint8_t, uint8_t); +static int mwlGetPwrCalTable(struct mwl_softc *); +static void dumpcaldata(const char *, const uint8_t *, int); +static void get2Ghz(MWL_HAL_CHANNELINFO *, const uint8_t *, int); +static void get5Ghz(MWL_HAL_CHANNELINFO *, const uint8_t *, int); +static void setmaxtxpow(struct mwl_hal_channel *, int, int); +static uint16_t ieee2mhz(int); +static const char * + mwlcmdname(int); +static int mwl_gethwspecs(struct mwl_softc *); +static int mwl_getchannels(struct mwl_softc *); +static void getchannels(struct mwl_softc *, int, int *, + struct mwl_channel *); +static void addchannels(struct mwl_channel *, int, int *, + const MWL_HAL_CHANNELINFO *, int); +static void addht40channels(struct mwl_channel *, int, int *, + const MWL_HAL_CHANNELINFO *, int); +static const struct mwl_channel * + findchannel(const struct mwl_channel *, int, + int, int); +static void addchan(struct mwl_channel *, int, int, int, int); + +static int mwl_chan_set(struct mwl_softc *, struct mwl_channel *); +static void mwl_mapchan(MWL_HAL_CHANNEL *, const struct mwl_channel *); +static int mwl_setcurchanrates(struct mwl_softc *); +const struct ieee80211_rateset * + mwl_get_suprates(struct ieee80211com *, + const struct mwl_channel *); +static uint32_t cvtChannelFlags(const MWL_HAL_CHANNEL *); +static const struct mwl_hal_channel * + findhalchannel(const struct mwl_softc *, + const MWL_HAL_CHANNEL *); +enum ieee80211_phymode + mwl_chan2mode(const struct mwl_channel *); +static int mwl_map2regioncode(const struct mwl_regdomain *); +static int mwl_startrecv(struct mwl_softc *); +static int mwl_mode_init(struct mwl_softc *); +static void mwl_hal_intrset(struct mwl_softc *, uint32_t); +static void mwl_hal_getisr(struct mwl_softc *, uint32_t *); +static int mwl_hal_sethwdma(struct mwl_softc *, + const struct mwl_hal_txrxdma *); +static int mwl_hal_getchannelinfo(struct mwl_softc *, int, int, + const MWL_HAL_CHANNELINFO **); +static int mwl_hal_setmac_locked(struct mwl_softc *, const uint8_t *); +static int mwl_hal_keyreset(struct mwl_softc *, const MWL_HAL_KEYVAL *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int mwl_hal_keyset(struct mwl_softc *, const MWL_HAL_KEYVAL *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int mwl_hal_newstation(struct mwl_softc *, const uint8_t *, + uint16_t, uint16_t, const MWL_HAL_PEERINFO *, int, int); +static int mwl_hal_setantenna(struct mwl_softc *, MWL_HAL_ANTENNA, int); +static int mwl_hal_setradio(struct mwl_softc *, int, MWL_HAL_PREAMBLE); +static int mwl_hal_setwmm(struct mwl_softc *, int); +static int mwl_hal_setchannel(struct mwl_softc *, const MWL_HAL_CHANNEL *); +static int mwl_hal_settxpower(struct mwl_softc *, const MWL_HAL_CHANNEL *, + uint8_t); +static int mwl_hal_settxrate(struct mwl_softc *, MWL_HAL_TXRATE_HANDLING, + const MWL_HAL_TXRATE *); +static int mwl_hal_settxrate_auto(struct mwl_softc *, + const MWL_HAL_TXRATE *); +static int mwl_hal_setrateadaptmode(struct mwl_softc *, uint16_t); +static int mwl_hal_setoptimizationlevel(struct mwl_softc *, int); +static int mwl_hal_setregioncode(struct mwl_softc *, int); +static int mwl_hal_setassocid(struct mwl_softc *, const uint8_t *, + uint16_t); +static int mwl_setrates(struct ieee80211com *); +static int mwl_hal_setrtsthreshold(struct mwl_softc *, int); +static int mwl_hal_setcsmode(struct mwl_softc *, MWL_HAL_CSMODE); +static int mwl_hal_setpromisc(struct mwl_softc *, int); +static int mwl_hal_start(struct mwl_softc *); +static int mwl_hal_setinframode(struct mwl_softc *); +static int mwl_hal_stop(struct mwl_softc *); +static struct ieee80211_node * + mwl_node_alloc(struct ieee80211com *); +static void mwl_node_free(struct ieee80211_node *); +static int mwl_key_alloc(struct ieee80211com *, + const struct ieee80211_key *, + ieee80211_keyix *, ieee80211_keyix *); +static int mwl_key_delete(struct ieee80211com *, + const struct ieee80211_key *); +static int mwl_key_set(struct ieee80211com *, const struct ieee80211_key *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void mwl_setanywepkey(struct ieee80211com *, const uint8_t *); +static void mwl_setglobalkeys(struct ieee80211com *c); +static int addgroupflags(MWL_HAL_KEYVAL *, const struct ieee80211_key *); +static void mwl_hal_txstart(struct mwl_softc *, int); +static int mwl_send(ieee80211com_t *, mblk_t *, uint8_t); +static void mwl_next_scan(void *); +static MWL_HAL_PEERINFO * + mkpeerinfo(MWL_HAL_PEERINFO *, const struct ieee80211_node *); +static uint32_t get_rate_bitmap(const struct ieee80211_rateset *); +static int mwl_newstate(struct ieee80211com *, enum ieee80211_state, int); +static int cvtrssi(uint8_t); +static uint_t mwl_intr(caddr_t, caddr_t); +static uint_t mwl_softintr(caddr_t, caddr_t); +static void mwl_tx_intr(struct mwl_softc *); +static void mwl_rx_intr(struct mwl_softc *); +static int mwl_init(struct mwl_softc *); +static void mwl_stop(struct mwl_softc *); +static int mwl_resume(struct mwl_softc *); + + +#ifdef DEBUG +static void +mwl_debug(uint32_t dbg_flags, const int8_t *fmt, ...) +{ + va_list args; + + if (dbg_flags & mwl_dbg_flags) { + va_start(args, fmt); + vcmn_err(CE_CONT, fmt, args); + va_end(args); + } +} +#endif + +/* + * Allocate an DMA memory and a DMA handle for accessing it + */ +static int +mwl_alloc_dma_mem(dev_info_t *devinfo, ddi_dma_attr_t *dma_attr, + size_t memsize, ddi_device_acc_attr_t *attr_p, uint_t alloc_flags, + uint_t bind_flags, struct dma_area *dma_p) +{ + int err; + + /* + * Allocate handle + */ + err = ddi_dma_alloc_handle(devinfo, dma_attr, + DDI_DMA_SLEEP, NULL, &dma_p->dma_hdl); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): " + "failed to alloc handle\n"); + goto fail1; + } + + /* + * Allocate memory + */ + err = ddi_dma_mem_alloc(dma_p->dma_hdl, memsize, attr_p, + alloc_flags, DDI_DMA_SLEEP, NULL, &dma_p->mem_va, + &dma_p->alength, &dma_p->acc_hdl); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): " + "failed to alloc mem\n"); + goto fail2; + } + + /* + * Bind the two together + */ + err = ddi_dma_addr_bind_handle(dma_p->dma_hdl, NULL, + dma_p->mem_va, dma_p->alength, bind_flags, + DDI_DMA_SLEEP, NULL, &dma_p->cookie, &dma_p->ncookies); + if (err != DDI_DMA_MAPPED) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): " + "failed to bind handle\n"); + goto fail3; + } + + if (dma_p->ncookies != 1) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_dma_mem(): " + "failed to alloc cookies\n"); + goto fail4; + } + + dma_p->nslots = ~0U; + dma_p->size = ~0U; + dma_p->token = ~0U; + dma_p->offset = 0; + + return (DDI_SUCCESS); + +fail4: + (void) ddi_dma_unbind_handle(dma_p->dma_hdl); +fail3: + ddi_dma_mem_free(&dma_p->acc_hdl); +fail2: + ddi_dma_free_handle(&dma_p->dma_hdl); +fail1: + return (err); +} + +static void +mwl_free_dma_mem(struct dma_area *dma_p) +{ + if (dma_p->dma_hdl != NULL) { + (void) ddi_dma_unbind_handle(dma_p->dma_hdl); + if (dma_p->acc_hdl != NULL) { + ddi_dma_mem_free(&dma_p->acc_hdl); + dma_p->acc_hdl = NULL; + } + ddi_dma_free_handle(&dma_p->dma_hdl); + dma_p->ncookies = 0; + dma_p->dma_hdl = NULL; + } +} + +static int +mwl_alloc_cmdbuf(struct mwl_softc *sc) +{ + int err; + size_t size; + + size = MWL_CMDBUF_SIZE; + + err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, size, + &mwl_cmdbuf_accattr, DDI_DMA_CONSISTENT, + DDI_DMA_RDWR | DDI_DMA_CONSISTENT, + &sc->sc_cmd_dma); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_cmdbuf(): " + "failed to alloc dma mem\n"); + return (DDI_FAILURE); + } + + sc->sc_cmd_mem = (uint16_t *)sc->sc_cmd_dma.mem_va; + sc->sc_cmd_dmaaddr = sc->sc_cmd_dma.cookie.dmac_address; + + return (DDI_SUCCESS); +} + +static void +mwl_free_cmdbuf(struct mwl_softc *sc) +{ + if (sc->sc_cmd_mem != NULL) + mwl_free_dma_mem(&sc->sc_cmd_dma); +} + +static int +mwl_alloc_rx_ring(struct mwl_softc *sc, int count) +{ + struct mwl_rx_ring *ring; + struct mwl_rxdesc *ds; + struct mwl_rxbuf *bf; + int i, err, datadlen; + + ring = &sc->sc_rxring; + ring->count = count; + ring->cur = ring->next = 0; + err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, + count * sizeof (struct mwl_rxdesc), + &mwl_desc_accattr, + DDI_DMA_CONSISTENT, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, + &ring->rxdesc_dma); + if (err) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rxring(): " + "alloc tx ring failed, size %d\n", + (uint32_t)(count * sizeof (struct mwl_rxdesc))); + return (DDI_FAILURE); + } + + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rx_ring(): " + "dma len = %d\n", (uint32_t)(ring->rxdesc_dma.alength)); + ring->desc = (struct mwl_rxdesc *)ring->rxdesc_dma.mem_va; + ring->physaddr = ring->rxdesc_dma.cookie.dmac_address; + bzero(ring->desc, count * sizeof (struct mwl_rxdesc)); + + datadlen = count * sizeof (struct mwl_rxbuf); + ring->buf = (struct mwl_rxbuf *)kmem_zalloc(datadlen, KM_SLEEP); + if (ring->buf == NULL) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_rxring(): " + "could not alloc rx ring data buffer\n"); + return (DDI_FAILURE); + } + bzero(ring->buf, count * sizeof (struct mwl_rxbuf)); + + /* + * Pre-allocate Rx buffers and populate Rx ring. + */ + for (i = 0; i < count; i++) { + ds = &ring->desc[i]; + bf = &ring->buf[i]; + /* alloc DMA memory */ + (void) mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, + sc->sc_dmabuf_size, + &mwl_buf_accattr, + DDI_DMA_STREAMING, + DDI_DMA_READ | DDI_DMA_STREAMING, + &bf->rxbuf_dma); + bf->bf_mem = (uint8_t *)(bf->rxbuf_dma.mem_va); + bf->bf_baddr = bf->rxbuf_dma.cookie.dmac_address; + bf->bf_desc = ds; + bf->bf_daddr = ring->physaddr + _PTRDIFF(ds, ring->desc); + } + + (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl, + 0, + ring->rxdesc_dma.alength, + DDI_DMA_SYNC_FORDEV); + + return (0); +} + +static void +mwl_free_rx_ring(struct mwl_softc *sc) +{ + struct mwl_rx_ring *ring; + struct mwl_rxbuf *bf; + int i; + + ring = &sc->sc_rxring; + + if (ring->desc != NULL) { + mwl_free_dma_mem(&ring->rxdesc_dma); + } + + if (ring->buf != NULL) { + for (i = 0; i < ring->count; i++) { + bf = &ring->buf[i]; + mwl_free_dma_mem(&bf->rxbuf_dma); + } + kmem_free(ring->buf, + (ring->count * sizeof (struct mwl_rxbuf))); + } +} + +static int +mwl_alloc_tx_ring(struct mwl_softc *sc, struct mwl_tx_ring *ring, + int count) +{ + struct mwl_txdesc *ds; + struct mwl_txbuf *bf; + int i, err, datadlen; + + ring->count = count; + ring->queued = 0; + ring->cur = ring->next = ring->stat = 0; + err = mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, + count * sizeof (struct mwl_txdesc), &mwl_desc_accattr, + DDI_DMA_CONSISTENT, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, + &ring->txdesc_dma); + if (err) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): " + "alloc tx ring failed, size %d\n", + (uint32_t)(count * sizeof (struct mwl_txdesc))); + return (DDI_FAILURE); + } + + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): " + "dma len = %d\n", (uint32_t)(ring->txdesc_dma.alength)); + ring->desc = (struct mwl_txdesc *)ring->txdesc_dma.mem_va; + ring->physaddr = ring->txdesc_dma.cookie.dmac_address; + bzero(ring->desc, count * sizeof (struct mwl_txdesc)); + + datadlen = count * sizeof (struct mwl_txbuf); + ring->buf = kmem_zalloc(datadlen, KM_SLEEP); + if (ring->buf == NULL) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_alloc_tx_ring(): " + "could not alloc tx ring data buffer\n"); + return (DDI_FAILURE); + } + bzero(ring->buf, count * sizeof (struct mwl_txbuf)); + + for (i = 0; i < count; i++) { + ds = &ring->desc[i]; + bf = &ring->buf[i]; + /* alloc DMA memory */ + (void) mwl_alloc_dma_mem(sc->sc_dev, &mwl_dma_attr, + sc->sc_dmabuf_size, + &mwl_buf_accattr, + DDI_DMA_STREAMING, + DDI_DMA_WRITE | DDI_DMA_STREAMING, + &bf->txbuf_dma); + bf->bf_baddr = bf->txbuf_dma.cookie.dmac_address; + bf->bf_mem = (uint8_t *)(bf->txbuf_dma.mem_va); + bf->bf_daddr = ring->physaddr + _PTRDIFF(ds, ring->desc); + bf->bf_desc = ds; + } + + (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, + 0, + ring->txdesc_dma.alength, + DDI_DMA_SYNC_FORDEV); + + return (0); +} + +/* ARGSUSED */ +static void +mwl_free_tx_ring(struct mwl_softc *sc, struct mwl_tx_ring *ring) +{ + struct mwl_txbuf *bf; + int i; + + if (ring->desc != NULL) { + mwl_free_dma_mem(&ring->txdesc_dma); + } + + if (ring->buf != NULL) { + for (i = 0; i < ring->count; i++) { + bf = &ring->buf[i]; + mwl_free_dma_mem(&bf->txbuf_dma); + } + kmem_free(ring->buf, + (ring->count * sizeof (struct mwl_txbuf))); + } +} + +/* + * Inform the f/w about location of the tx/rx dma data structures + * and related state. This cmd must be done immediately after a + * mwl_hal_gethwspecs call or the f/w will lockup. + */ +static int +mwl_hal_sethwdma(struct mwl_softc *sc, const struct mwl_hal_txrxdma *dma) +{ + HostCmd_DS_SET_HW_SPEC *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_DS_SET_HW_SPEC, HostCmd_CMD_SET_HW_SPEC); + pCmd->WcbBase[0] = LE_32(dma->wcbBase[0]); + pCmd->WcbBase[1] = LE_32(dma->wcbBase[1]); + pCmd->WcbBase[2] = LE_32(dma->wcbBase[2]); + pCmd->WcbBase[3] = LE_32(dma->wcbBase[3]); + pCmd->TxWcbNumPerQueue = LE_32(dma->maxNumTxWcb); + pCmd->NumTxQueues = LE_32(dma->maxNumWCB); + pCmd->TotalRxWcb = LE_32(1); /* XXX */ + pCmd->RxPdWrPtr = LE_32(dma->rxDescRead); + /* + * pCmd->Flags = LE_32(SET_HW_SPEC_HOSTFORM_BEACON + * #ifdef MWL_HOST_PS_SUPPORT + * | SET_HW_SPEC_HOST_POWERSAVE + * #endif + * | SET_HW_SPEC_HOSTFORM_PROBERESP); + */ + pCmd->Flags = 0; + /* disable multi-bss operation for A1-A4 parts */ + if (sc->sc_revs.mh_macRev < 5) + pCmd->Flags |= LE_32(SET_HW_SPEC_DISABLEMBSS); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_HW_SPEC); + if (retval == 0) { + if (pCmd->Flags & LE_32(SET_HW_SPEC_DISABLEMBSS)) + sc->sc_hw_flags &= ~MHF_MBSS; + else + sc->sc_hw_flags |= MHF_MBSS; + } + + return (retval); +} + +/* + * Inform firmware of our tx/rx dma setup. The BAR 0 + * writes below are for compatibility with older firmware. + * For current firmware we send this information with a + * cmd block via mwl_hal_sethwdma. + */ +static int +mwl_setupdma(struct mwl_softc *sc) +{ + int i, err; + + sc->sc_hwdma.rxDescRead = sc->sc_rxring.physaddr; + mwl_mem_write4(sc, sc->sc_hwspecs.rxDescRead, sc->sc_hwdma.rxDescRead); + mwl_mem_write4(sc, sc->sc_hwspecs.rxDescWrite, sc->sc_hwdma.rxDescRead); + + for (i = 0; i < MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES; i++) { + struct mwl_tx_ring *txring = &sc->sc_txring[i]; + sc->sc_hwdma.wcbBase[i] = txring->physaddr; + mwl_mem_write4(sc, sc->sc_hwspecs.wcbBase[i], + sc->sc_hwdma.wcbBase[i]); + } + sc->sc_hwdma.maxNumTxWcb = MWL_TX_RING_COUNT; + sc->sc_hwdma.maxNumWCB = MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES; + + err = mwl_hal_sethwdma(sc, &sc->sc_hwdma); + if (err != 0) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_setupdma(): " + "unable to setup tx/rx dma; hal status %u\n", err); + /* XXX */ + } + + return (err); +} + +/* ARGSUSED */ +static void +mwl_txq_init(struct mwl_softc *sc, struct mwl_tx_ring *txring, int qnum) +{ + struct mwl_txbuf *bf; + struct mwl_txdesc *ds; + int i; + + txring->qnum = qnum; + txring->txpri = 0; /* XXX */ + + bf = txring->buf; + ds = txring->desc; + for (i = 0; i < MWL_TX_RING_COUNT - 1; i++) { + bf++; + ds->pPhysNext = bf->bf_daddr; + ds++; + } + bf = txring->buf; + ds->pPhysNext = LE_32(bf->bf_daddr); +} + +/* + * Setup a hardware data transmit queue for the specified + * access control. We record the mapping from ac's + * to h/w queues for use by mwl_tx_start. + */ +static int +mwl_tx_setup(struct mwl_softc *sc, int ac, int mvtype) +{ +#define N(a) (sizeof (a)/sizeof (a[0])) + struct mwl_tx_ring *txring; + + if (ac >= N(sc->sc_ac2q)) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_tx_setup(): " + "AC %u out of range, max %u!\n", + ac, (uint_t)N(sc->sc_ac2q)); + return (0); + } + if (mvtype >= MWL_NUM_TX_QUEUES) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_tx_setup(): " + "mvtype %u out of range, max %u!\n", + mvtype, MWL_NUM_TX_QUEUES); + return (0); + } + txring = &sc->sc_txring[mvtype]; + mwl_txq_init(sc, txring, mvtype); + sc->sc_ac2q[ac] = txring; + return (1); +#undef N +} + +static int +mwl_setup_txq(struct mwl_softc *sc) +{ + int err = 0; + + /* NB: insure BK queue is the lowest priority h/w queue */ + if (!mwl_tx_setup(sc, WME_AC_BK, MWL_WME_AC_BK)) { + MWL_DBG(MWL_DBG_DMA, "mwl: mwl_setup_txq(): " + "unable to setup xmit queue for %s traffic!\n", + mwl_wme_acnames[WME_AC_BK]); + err = EIO; + return (err); + } + if (!mwl_tx_setup(sc, WME_AC_BE, MWL_WME_AC_BE) || + !mwl_tx_setup(sc, WME_AC_VI, MWL_WME_AC_VI) || + !mwl_tx_setup(sc, WME_AC_VO, MWL_WME_AC_VO)) { + /* + * Not enough hardware tx queues to properly do WME; + * just punt and assign them all to the same h/w queue. + * We could do a better job of this if, for example, + * we allocate queues when we switch from station to + * AP mode. + */ + sc->sc_ac2q[WME_AC_BE] = sc->sc_ac2q[WME_AC_BK]; + sc->sc_ac2q[WME_AC_VI] = sc->sc_ac2q[WME_AC_BK]; + sc->sc_ac2q[WME_AC_VO] = sc->sc_ac2q[WME_AC_BK]; + } + + return (err); +} + +/* + * find mwl firmware module's "_start" "_end" symbols + * and get its size. + */ +static int +mwl_loadsym(ddi_modhandle_t modp, char *sym, char **start, size_t *len) +{ + char start_sym[64]; + char end_sym[64]; + char *p, *end; + int rv; + size_t n; + + (void) snprintf(start_sym, sizeof (start_sym), "%s_start", sym); + (void) snprintf(end_sym, sizeof (end_sym), "%s_end", sym); + + p = (char *)ddi_modsym(modp, start_sym, &rv); + if (p == NULL || rv != 0) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadsym(): " + "mod %s: symbol %s not found\n", sym, start_sym); + return (-1); + } + + end = (char *)ddi_modsym(modp, end_sym, &rv); + if (end == NULL || rv != 0) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadsym(): " + "mod %s: symbol %s not found\n", sym, end_sym); + return (-1); + } + + n = _PTRDIFF(end, p); + *start = p; + *len = n; + + return (0); +} + +static void +mwlFwReset(struct mwl_softc *sc) +{ + if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == 0xffffffff) { + MWL_DBG(MWL_DBG_FW, "mwl: mwlFWReset(): " + "device not present!\n"); + return; + } + + mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS, ISR_RESET); + sc->sc_hw_flags &= ~MHF_FWHANG; +} + +static void +mwlPokeSdramController(struct mwl_softc *sc, int SDRAMSIZE_Addr) +{ + /* Set up sdram controller for superflyv2 */ + mwl_ctl_write4(sc, 0x00006014, 0x33); + mwl_ctl_write4(sc, 0x00006018, 0xa3a2632); + mwl_ctl_write4(sc, 0x00006010, SDRAMSIZE_Addr); +} + +static void +mwlTriggerPciCmd(struct mwl_softc *sc) +{ + (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl, + 0, + sc->sc_cmd_dma.alength, + DDI_DMA_SYNC_FORDEV); + + mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, sc->sc_cmd_dmaaddr); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); + + mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0x00); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); + + mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS, + MACREG_H2ARIC_BIT_DOOR_BELL); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); +} + +static int +mwlWaitFor(struct mwl_softc *sc, uint32_t val) +{ + int i; + + for (i = 0; i < FW_MAX_NUM_CHECKS; i++) { + DELAY(FW_CHECK_USECS); + if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == val) + return (1); + } + return (0); +} + +/* + * Firmware block xmit when talking to the boot-rom. + */ +static int +mwlSendBlock(struct mwl_softc *sc, int bsize, const void *data, size_t dsize) +{ + sc->sc_cmd_mem[0] = LE_16(HostCmd_CMD_CODE_DNLD); + sc->sc_cmd_mem[1] = LE_16(bsize); + (void) memcpy(&sc->sc_cmd_mem[4], data, dsize); + mwlTriggerPciCmd(sc); + /* XXX 2000 vs 200 */ + if (mwlWaitFor(sc, MACREG_INT_CODE_CMD_FINISHED)) { + mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0); + return (1); + } + + MWL_DBG(MWL_DBG_FW, "mwl: mwlSendBlock(): " + "timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", + mwl_ctl_read4(sc, MACREG_REG_INT_CODE)); + return (0); +} + +/* + * Firmware block xmit when talking to the 1st-stage loader. + */ +static int +mwlSendBlock2(struct mwl_softc *sc, const void *data, size_t dsize) +{ + (void) memcpy(&sc->sc_cmd_mem[0], data, dsize); + mwlTriggerPciCmd(sc); + if (mwlWaitFor(sc, MACREG_INT_CODE_CMD_FINISHED)) { + mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0); + return (1); + } + + MWL_DBG(MWL_DBG_FW, "mwl: mwlSendBlock2(): " + "timeout waiting for CMD_FINISHED, INT_CODE 0x%x\n", + mwl_ctl_read4(sc, MACREG_REG_INT_CODE)); + return (0); +} + +/* ARGSUSED */ +static int +mwl_fwload(struct mwl_softc *sc, void *fwargs) +{ + char *fwname = "mwlfw"; + char *fwbootname = "mwlboot"; + char *fwbinname = "mw88W8363fw"; + char *fwboot_index, *fw_index; + uint8_t *fw, *fwboot; + ddi_modhandle_t modfw; + /* XXX get from firmware header */ + uint32_t FwReadySignature = HostCmd_SOFTAP_FWRDY_SIGNATURE; + uint32_t OpMode = HostCmd_SOFTAP_MODE; + const uint8_t *fp, *ep; + size_t fw_size, fwboot_size; + uint32_t blocksize, nbytes; + int i, rv, err, ntries; + + rv = err = 0; + fw = fwboot = NULL; + fw_index = fwboot_index = NULL; + + modfw = ddi_modopen(fwname, KRTLD_MODE_FIRST, &rv); + if (modfw == NULL) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "module %s not found\n", fwname); + err = -1; + goto bad2; + } + + err = mwl_loadsym(modfw, fwbootname, &fwboot_index, &fwboot_size); + if (err != 0) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "could not get boot firmware\n"); + err = -1; + goto bad2; + } + + err = mwl_loadsym(modfw, fwbinname, &fw_index, &fw_size); + if (err != 0) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "could not get firmware\n"); + err = -1; + goto bad2; + } + + fwboot = (uint8_t *)kmem_alloc(fwboot_size, KM_SLEEP); + if (fwboot == NULL) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadfirmware(): " + "failed to alloc boot firmware memory\n"); + err = -1; + goto bad2; + } + (void) memcpy(fwboot, fwboot_index, fwboot_size); + + fw = (uint8_t *)kmem_alloc(fw_size, KM_SLEEP); + if (fw == NULL) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_loadfirmware(): " + "failed to alloc firmware memory\n"); + err = -1; + goto bad2; + } + (void) memcpy(fw, fw_index, fw_size); + + if (modfw != NULL) + (void) ddi_modclose(modfw); + + if (fw_size < 4) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "could not load firmware image %s\n", + fwname); + err = ENXIO; + goto bad2; + } + + if (fw[0] == 0x01 && fw[1] == 0x00 && + fw[2] == 0x00 && fw[3] == 0x00) { + /* + * 2-stage load, get the boot firmware. + */ + if (fwboot == NULL) { + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "could not load firmware image %s\n", + fwbootname); + err = ENXIO; + goto bad2; + } + } else + fwboot = NULL; + + mwlFwReset(sc); + + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CLEAR_SEL, + MACREG_A2HRIC_BIT_MASK); + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE, 0x00); + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, 0x00); + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_STATUS_MASK, + MACREG_A2HRIC_BIT_MASK); + if (sc->sc_SDRAMSIZE_Addr != 0) { + /* Set up sdram controller for superflyv2 */ + mwlPokeSdramController(sc, sc->sc_SDRAMSIZE_Addr); + } + + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "load %s firmware image (%u bytes)\n", + fwname, (unsigned int)fw_size); + + if (fwboot != NULL) { + /* + * Do 2-stage load. The 1st stage loader is setup + * with the bootrom loader then we load the real + * image using a different handshake. With this + * mechanism the firmware is segmented into chunks + * that have a CRC. If a chunk is incorrect we'll + * be told to retransmit. + */ + /* XXX assumes hlpimage fits in a block */ + /* NB: zero size block indicates download is finished */ + if (!mwlSendBlock(sc, fwboot_size, fwboot, fwboot_size) || + !mwlSendBlock(sc, 0, NULL, 0)) { + err = ETIMEDOUT; + goto bad; + } + DELAY(200 * FW_CHECK_USECS); + if (sc->sc_SDRAMSIZE_Addr != 0) { + /* Set up sdram controller for superflyv2 */ + mwlPokeSdramController(sc, sc->sc_SDRAMSIZE_Addr); + } + nbytes = ntries = 0; /* NB: silence compiler */ + for (fp = fw, ep = fp + fw_size; fp < ep; ) { + mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0); + blocksize = mwl_ctl_read4(sc, MACREG_REG_SCRATCH); + if (blocksize == 0) /* download complete */ + break; + if (blocksize > 0x00000c00) { + err = EINVAL; + goto bad; + } + if ((blocksize & 0x1) == 0) { + /* block successfully downloaded, advance */ + fp += nbytes; + ntries = 0; + } else { + if (++ntries > 2) { + /* + * Guard against f/w telling us to + * retry infinitely. + */ + err = ELOOP; + goto bad; + } + /* clear NAK bit/flag */ + blocksize &= ~0x1; + } + if (blocksize > _PTRDIFF(ep, fp)) { + /* XXX this should not happen, what to do? */ + blocksize = _PTRDIFF(ep, fp); + } + nbytes = blocksize; + if (!mwlSendBlock2(sc, fp, nbytes)) { + err = ETIMEDOUT; + goto bad; + } + } + } else { + for (fp = fw, ep = fp + fw_size; fp < ep; ) { + nbytes = _PTRDIFF(ep, fp); + if (nbytes > FW_DOWNLOAD_BLOCK_SIZE) + nbytes = FW_DOWNLOAD_BLOCK_SIZE; + if (!mwlSendBlock(sc, FW_DOWNLOAD_BLOCK_SIZE, fp, + nbytes)) { + err = EIO; + goto bad; + } + fp += nbytes; + } + } + + /* + * Wait for firmware to startup; we monitor the + * INT_CODE register waiting for a signature to + * written back indicating it's ready to go. + */ + sc->sc_cmd_mem[1] = 0; + /* + * XXX WAR for mfg fw download + */ + if (OpMode != HostCmd_STA_MODE) + mwlTriggerPciCmd(sc); + for (i = 0; i < FW_MAX_NUM_CHECKS; i++) { + mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, OpMode); + DELAY(FW_CHECK_USECS); + if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == + FwReadySignature) { + mwl_ctl_write4(sc, MACREG_REG_INT_CODE, 0x00); + return (mwlResetHalState(sc)); + } + } + MWL_DBG(MWL_DBG_FW, "mwl: mwl_fwload(): " + "firmware download timeout\n"); + return (ETIMEDOUT); +bad: + mwlFwReset(sc); +bad2: + if (fw != NULL) + kmem_free(fw, fw_size); + if (fwboot != NULL) + kmem_free(fwboot, fwboot_size); + fwboot = fw = NULL; + fwboot_index = fw_index = NULL; + if (modfw != NULL) + (void) ddi_modclose(modfw); + return (err); +} + +/* + * Low level firmware cmd block handshake support. + */ +static void +mwlSendCmd(struct mwl_softc *sc) +{ + (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl, + 0, + sc->sc_cmd_dma.alength, + DDI_DMA_SYNC_FORDEV); + + mwl_ctl_write4(sc, MACREG_REG_GEN_PTR, sc->sc_cmd_dmaaddr); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); + + mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS, + MACREG_H2ARIC_BIT_DOOR_BELL); +} + +static int +mwlExecuteCmd(struct mwl_softc *sc, unsigned short cmd) +{ + if (mwl_ctl_read4(sc, MACREG_REG_INT_CODE) == 0xffffffff) { + MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): " + "device not present!\n"); + return (EIO); + } + mwlSendCmd(sc); + if (!mwlWaitForCmdComplete(sc, 0x8000 | cmd)) { + MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): " + "timeout waiting for f/w cmd %s\n", mwlcmdname(cmd)); + return (ETIMEDOUT); + } + (void) ddi_dma_sync(sc->sc_cmd_dma.dma_hdl, + 0, + sc->sc_cmd_dma.alength, + DDI_DMA_SYNC_FORDEV); + + MWL_DBG(MWL_DBG_CMD, "mwl: mwlExecuteCmd(): " + "send cmd %s\n", mwlcmdname(cmd)); + + if (mwl_dbg_flags & MWL_DBG_CMD) + dumpresult(sc, 1); + + return (0); +} + +static int +mwlWaitForCmdComplete(struct mwl_softc *sc, uint16_t cmdCode) +{ +#define MAX_WAIT_FW_COMPLETE_ITERATIONS 10000 + int i; + + for (i = 0; i < MAX_WAIT_FW_COMPLETE_ITERATIONS; i++) { + if (sc->sc_cmd_mem[0] == LE_16(cmdCode)) + return (1); + DELAY(1 * 1000); + } + return (0); +#undef MAX_WAIT_FW_COMPLETE_ITERATIONS +} + +static const char * +mwlcmdname(int cmd) +{ + static char buf[12]; +#define CMD(x) case HostCmd_CMD_##x: return #x + switch (cmd) { + CMD(CODE_DNLD); + CMD(GET_HW_SPEC); + CMD(SET_HW_SPEC); + CMD(MAC_MULTICAST_ADR); + CMD(802_11_GET_STAT); + CMD(MAC_REG_ACCESS); + CMD(BBP_REG_ACCESS); + CMD(RF_REG_ACCESS); + CMD(802_11_RADIO_CONTROL); + CMD(802_11_RF_TX_POWER); + CMD(802_11_RF_ANTENNA); + CMD(SET_BEACON); + CMD(SET_RF_CHANNEL); + CMD(SET_AID); + CMD(SET_INFRA_MODE); + CMD(SET_G_PROTECT_FLAG); + CMD(802_11_RTS_THSD); + CMD(802_11_SET_SLOT); + CMD(SET_EDCA_PARAMS); + CMD(802_11H_DETECT_RADAR); + CMD(SET_WMM_MODE); + CMD(HT_GUARD_INTERVAL); + CMD(SET_FIXED_RATE); + CMD(SET_LINKADAPT_CS_MODE); + CMD(SET_MAC_ADDR); + CMD(SET_RATE_ADAPT_MODE); + CMD(BSS_START); + CMD(SET_NEW_STN); + CMD(SET_KEEP_ALIVE); + CMD(SET_APMODE); + CMD(SET_SWITCH_CHANNEL); + CMD(UPDATE_ENCRYPTION); + CMD(BASTREAM); + CMD(SET_RIFS); + CMD(SET_N_PROTECT_FLAG); + CMD(SET_N_PROTECT_OPMODE); + CMD(SET_OPTIMIZATION_LEVEL); + CMD(GET_CALTABLE); + CMD(SET_MIMOPSHT); + CMD(GET_BEACON); + CMD(SET_REGION_CODE); + CMD(SET_POWERSAVESTATION); + CMD(SET_TIM); + CMD(GET_TIM); + CMD(GET_SEQNO); + CMD(DWDS_ENABLE); + CMD(AMPDU_RETRY_RATEDROP_MODE); + CMD(CFEND_ENABLE); + } + (void) snprintf(buf, sizeof (buf), "0x%x", cmd); + return (buf); +#undef CMD +} + +static void +dumpresult(struct mwl_softc *sc, int showresult) +{ + const FWCmdHdr *h = (const FWCmdHdr *)sc->sc_cmd_mem; + int len; + + len = LE_16(h->Length); +#ifdef MWL_MBSS_SUPPORT + MWL_DBG(MWL_DBG_CMD, "mwl: mwl_dumpresult(): " + "Cmd %s Length %d SeqNum %d MacId %d", + mwlcmdname(LE_16(h->Cmd) & ~0x8000), len, h->SeqNum, h->MacId); +#else + MWL_DBG(MWL_DBG_CMD, "mwl: mwl_dumpresult(): " + "Cmd %s Length %d SeqNum %d", + mwlcmdname(LE_16(h->Cmd) & ~0x8000), len, LE_16(h->SeqNum)); +#endif + if (showresult) { + const char *results[] = + { "OK", "ERROR", "NOT_SUPPORT", "PENDING", "BUSY", + "PARTIAL_DATA" }; + int result = LE_16(h->Result); + + if (result <= HostCmd_RESULT_PARTIAL_DATA) + MWL_DBG(MWL_DBG_CMD, "mwl: dumpresult(): " + "Result %s", results[result]); + else + MWL_DBG(MWL_DBG_CMD, "mwl: dumpresult(): " + "Result %d", result); + } +} + +static int +mwlGetCalTable(struct mwl_softc *sc, uint8_t annex, uint8_t index) +{ + HostCmd_FW_GET_CALTABLE *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_FW_GET_CALTABLE, HostCmd_CMD_GET_CALTABLE); + pCmd->annex = annex; + pCmd->index = index; + memset(pCmd->calTbl, 0, sizeof (pCmd->calTbl)); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_GET_CALTABLE); + if (retval == 0 && + pCmd->calTbl[0] != annex && annex != 0 && annex != 255) + retval = EIO; + return (retval); +} + +/* + * Construct channel info for 2.4GHz channels from cal data. + */ +static void +get2Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len) +{ + int i, j; + + j = 0; + for (i = 0; i < len; i += 4) { + struct mwl_hal_channel *hc = &ci->channels[j]; + hc->ieee = 1+j; + hc->freq = ieee2mhz(1+j); + (void) memcpy(hc->targetPowers, &table[i], 4); + setmaxtxpow(hc, 0, 4); + j++; + } + ci->nchannels = j; + ci->freqLow = ieee2mhz(1); + ci->freqHigh = ieee2mhz(j); +} + +/* + * Construct channel info for 5GHz channels from cal data. + */ +static void +get5Ghz(MWL_HAL_CHANNELINFO *ci, const uint8_t table[], int len) +{ + int i, j, f, l, h; + + l = 32000; + h = 0; + j = 0; + for (i = 0; i < len; i += 4) { + struct mwl_hal_channel *hc; + + if (table[i] == 0) + continue; + f = 5000 + 5*table[i]; + if (f < l) + l = f; + if (f > h) + h = f; + hc = &ci->channels[j]; + hc->freq = (uint16_t)f; + hc->ieee = table[i]; + (void) memcpy(hc->targetPowers, &table[i], 4); + setmaxtxpow(hc, 1, 4); /* NB: col 1 is the freq, skip */ + j++; + } + ci->nchannels = j; + ci->freqLow = (uint16_t)((l == 32000) ? 0 : l); + ci->freqHigh = (uint16_t)h; +} + +/* + * Calculate the max tx power from the channel's cal data. + */ +static void +setmaxtxpow(struct mwl_hal_channel *hc, int i, int maxix) +{ + hc->maxTxPow = hc->targetPowers[i]; + for (i++; i < maxix; i++) + if (hc->targetPowers[i] > hc->maxTxPow) + hc->maxTxPow = hc->targetPowers[i]; +} + +static uint16_t +ieee2mhz(int chan) +{ + if (chan == 14) + return (2484); + if (chan < 14) + return (2407 + chan * 5); + return (2512 + (chan - 15) * 20); +} + +static void +dumpcaldata(const char *name, const uint8_t *table, int n) +{ + int i; + MWL_DBG(MWL_DBG_HW, "\n%s:\n", name); + for (i = 0; i < n; i += 4) + MWL_DBG(MWL_DBG_HW, "[%2d] %3d %3d %3d %3d\n", + i/4, table[i+0], table[i+1], table[i+2], table[i+3]); +} + +static int +mwlGetPwrCalTable(struct mwl_softc *sc) +{ + const uint8_t *data; + MWL_HAL_CHANNELINFO *ci; + int len; + + /* NB: we hold the lock so it's ok to use cmdbuf */ + data = ((const HostCmd_FW_GET_CALTABLE *) sc->sc_cmd_mem)->calTbl; + if (mwlGetCalTable(sc, 33, 0) == 0) { + len = (data[2] | (data[3] << 8)) - 12; + if (len > PWTAGETRATETABLE20M) + len = PWTAGETRATETABLE20M; + dumpcaldata("2.4G 20M", &data[12], len); + get2Ghz(&sc->sc_20M, &data[12], len); + } + if (mwlGetCalTable(sc, 34, 0) == 0) { + len = (data[2] | (data[3] << 8)) - 12; + if (len > PWTAGETRATETABLE40M) + len = PWTAGETRATETABLE40M; + dumpcaldata("2.4G 40M", &data[12], len); + ci = &sc->sc_40M; + get2Ghz(ci, &data[12], len); + } + if (mwlGetCalTable(sc, 35, 0) == 0) { + len = (data[2] | (data[3] << 8)) - 20; + if (len > PWTAGETRATETABLE20M_5G) + len = PWTAGETRATETABLE20M_5G; + dumpcaldata("5G 20M", &data[20], len); + get5Ghz(&sc->sc_20M_5G, &data[20], len); + } + if (mwlGetCalTable(sc, 36, 0) == 0) { + len = (data[2] | (data[3] << 8)) - 20; + if (len > PWTAGETRATETABLE40M_5G) + len = PWTAGETRATETABLE40M_5G; + dumpcaldata("5G 40M", &data[20], len); + ci = &sc->sc_40M_5G; + get5Ghz(ci, &data[20], len); + } + sc->sc_hw_flags |= MHF_CALDATA; + return (0); +} + +/* + * Reset internal state after a firmware download. + */ +static int +mwlResetHalState(struct mwl_softc *sc) +{ + int err = 0; + + /* + * Fetch cal data for later use. + * XXX may want to fetch other stuff too. + */ + /* XXX check return */ + if ((sc->sc_hw_flags & MHF_CALDATA) == 0) + err = mwlGetPwrCalTable(sc); + return (err); +} + +#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_G) +#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_A) + +static void +addchan(struct mwl_channel *c, int freq, int flags, int ieee, int txpow) +{ + c->ic_freq = (uint16_t)freq; + c->ic_flags = flags; + c->ic_ieee = (uint8_t)ieee; + c->ic_minpower = 0; + c->ic_maxpower = 2*txpow; + c->ic_maxregpower = (uint8_t)txpow; +} + +static const struct mwl_channel * +findchannel(const struct mwl_channel chans[], int nchans, + int freq, int flags) +{ + const struct mwl_channel *c; + int i; + + for (i = 0; i < nchans; i++) { + c = &chans[i]; + if (c->ic_freq == freq && c->ic_flags == flags) + return (c); + } + return (NULL); +} + +static void +addht40channels(struct mwl_channel chans[], int maxchans, int *nchans, + const MWL_HAL_CHANNELINFO *ci, int flags) +{ + struct mwl_channel *c; + const struct mwl_channel *extc; + const struct mwl_hal_channel *hc; + int i; + + c = &chans[*nchans]; + + flags &= ~IEEE80211_CHAN_HT; + for (i = 0; i < ci->nchannels; i++) { + /* + * Each entry defines an HT40 channel pair; find the + * extension channel above and the insert the pair. + */ + hc = &ci->channels[i]; + extc = findchannel(chans, *nchans, hc->freq+20, + flags | IEEE80211_CHAN_HT20); + if (extc != NULL) { + if (*nchans >= maxchans) + break; + addchan(c, hc->freq, flags | IEEE80211_CHAN_HT40U, + hc->ieee, hc->maxTxPow); + c->ic_extieee = extc->ic_ieee; + c++, (*nchans)++; + if (*nchans >= maxchans) + break; + addchan(c, extc->ic_freq, flags | IEEE80211_CHAN_HT40D, + extc->ic_ieee, hc->maxTxPow); + c->ic_extieee = hc->ieee; + c++, (*nchans)++; + } + } +} + +static void +addchannels(struct mwl_channel chans[], int maxchans, int *nchans, + const MWL_HAL_CHANNELINFO *ci, int flags) +{ + struct mwl_channel *c; + int i; + + c = &chans[*nchans]; + + for (i = 0; i < ci->nchannels; i++) { + const struct mwl_hal_channel *hc; + + hc = &ci->channels[i]; + if (*nchans >= maxchans) + break; + addchan(c, hc->freq, flags, hc->ieee, hc->maxTxPow); + c++, (*nchans)++; + + if (flags == IEEE80211_CHAN_G || flags == IEEE80211_CHAN_HTG) { + /* g channel have a separate b-only entry */ + if (*nchans >= maxchans) + break; + c[0] = c[-1]; + c[-1].ic_flags = IEEE80211_CHAN_B; + c++, (*nchans)++; + } + if (flags == IEEE80211_CHAN_HTG) { + /* HT g channel have a separate g-only entry */ + if (*nchans >= maxchans) + break; + c[-1].ic_flags = IEEE80211_CHAN_G; + c[0] = c[-1]; + c[0].ic_flags &= ~IEEE80211_CHAN_HT; + c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */ + c++, (*nchans)++; + } + if (flags == IEEE80211_CHAN_HTA) { + /* HT a channel have a separate a-only entry */ + if (*nchans >= maxchans) + break; + c[-1].ic_flags = IEEE80211_CHAN_A; + c[0] = c[-1]; + c[0].ic_flags &= ~IEEE80211_CHAN_HT; + c[0].ic_flags |= IEEE80211_CHAN_HT20; /* HT20 */ + c++, (*nchans)++; + } + } +} + +static int +mwl_hal_getchannelinfo(struct mwl_softc *sc, int band, int chw, + const MWL_HAL_CHANNELINFO **ci) +{ + switch (band) { + case MWL_FREQ_BAND_2DOT4GHZ: + *ci = (chw == MWL_CH_20_MHz_WIDTH) ? &sc->sc_20M : &sc->sc_40M; + break; + case MWL_FREQ_BAND_5GHZ: + *ci = (chw == MWL_CH_20_MHz_WIDTH) ? + &sc->sc_20M_5G : &sc->sc_40M_5G; + break; + default: + return (EINVAL); + } + return (((*ci)->freqLow == (*ci)->freqHigh) ? EINVAL : 0); +} + +static void +getchannels(struct mwl_softc *sc, int maxchans, int *nchans, + struct mwl_channel chans[]) +{ + const MWL_HAL_CHANNELINFO *ci; + + /* + * Use the channel info from the hal to craft the + * channel list. Note that we pass back an unsorted + * list; the caller is required to sort it for us + * (if desired). + */ + *nchans = 0; + if (mwl_hal_getchannelinfo(sc, + MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0) + addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTG); + if (mwl_hal_getchannelinfo(sc, + MWL_FREQ_BAND_5GHZ, MWL_CH_20_MHz_WIDTH, &ci) == 0) + addchannels(chans, maxchans, nchans, ci, IEEE80211_CHAN_HTA); + if (mwl_hal_getchannelinfo(sc, + MWL_FREQ_BAND_2DOT4GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0) + addht40channels(chans, maxchans, nchans, ci, + IEEE80211_CHAN_HTG); + if (mwl_hal_getchannelinfo(sc, + MWL_FREQ_BAND_5GHZ, MWL_CH_40_MHz_WIDTH, &ci) == 0) + addht40channels(chans, maxchans, nchans, ci, + IEEE80211_CHAN_HTA); +} + +static int +mwl_getchannels(struct mwl_softc *sc) +{ + /* + * Use the channel info from the hal to craft the + * channel list for net80211. Note that we pass up + * an unsorted list; net80211 will sort it for us. + */ + memset(sc->sc_channels, 0, sizeof (sc->sc_channels)); + sc->sc_nchans = 0; + getchannels(sc, IEEE80211_CHAN_MAX, &sc->sc_nchans, sc->sc_channels); + + sc->sc_regdomain.regdomain = SKU_DEBUG; + sc->sc_regdomain.country = CTRY_DEFAULT; + sc->sc_regdomain.location = 'I'; + sc->sc_regdomain.isocc[0] = ' '; /* XXX? */ + sc->sc_regdomain.isocc[1] = ' '; + return (sc->sc_nchans == 0 ? EIO : 0); +} + +#undef IEEE80211_CHAN_HTA +#undef IEEE80211_CHAN_HTG + +/* + * Return "hw specs". Note this must be the first + * cmd MUST be done after a firmware download or the + * f/w will lockup. + * XXX move into the hal so driver doesn't need to be responsible + */ +static int +mwl_gethwspecs(struct mwl_softc *sc) +{ + struct mwl_hal_hwspec *hw; + HostCmd_DS_GET_HW_SPEC *pCmd; + int retval; + + hw = &sc->sc_hwspecs; + _CMD_SETUP(pCmd, HostCmd_DS_GET_HW_SPEC, HostCmd_CMD_GET_HW_SPEC); + memset(&pCmd->PermanentAddr[0], 0xff, IEEE80211_ADDR_LEN); + pCmd->ulFwAwakeCookie = LE_32((unsigned int)sc->sc_cmd_dmaaddr + 2048); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_GET_HW_SPEC); + if (retval == 0) { + IEEE80211_ADDR_COPY(hw->macAddr, pCmd->PermanentAddr); + hw->wcbBase[0] = LE_32(pCmd->WcbBase0) & 0x0000ffff; + hw->wcbBase[1] = LE_32(pCmd->WcbBase1[0]) & 0x0000ffff; + hw->wcbBase[2] = LE_32(pCmd->WcbBase1[1]) & 0x0000ffff; + hw->wcbBase[3] = LE_32(pCmd->WcbBase1[2]) & 0x0000ffff; + hw->rxDescRead = LE_32(pCmd->RxPdRdPtr)& 0x0000ffff; + hw->rxDescWrite = LE_32(pCmd->RxPdWrPtr)& 0x0000ffff; + hw->regionCode = LE_16(pCmd->RegionCode) & 0x00ff; + hw->fwReleaseNumber = LE_32(pCmd->FWReleaseNumber); + hw->maxNumWCB = LE_16(pCmd->NumOfWCB); + hw->maxNumMCAddr = LE_16(pCmd->NumOfMCastAddr); + hw->numAntennas = LE_16(pCmd->NumberOfAntenna); + hw->hwVersion = pCmd->Version; + hw->hostInterface = pCmd->HostIf; + + sc->sc_revs.mh_macRev = hw->hwVersion; /* XXX */ + sc->sc_revs.mh_phyRev = hw->hostInterface; /* XXX */ + } + + return (retval); +} + +static int +mwl_hal_setmac_locked(struct mwl_softc *sc, + const uint8_t addr[IEEE80211_ADDR_LEN]) +{ + HostCmd_DS_SET_MAC *pCmd; + + _VCMD_SETUP(pCmd, HostCmd_DS_SET_MAC, HostCmd_CMD_SET_MAC_ADDR); + IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr); +#ifdef MWL_MBSS_SUPPORT + /* NB: already byte swapped */ + pCmd->MacType = WL_MAC_TYPE_PRIMARY_CLIENT; +#endif + return (mwlExecuteCmd(sc, HostCmd_CMD_SET_MAC_ADDR)); +} + +static void +cvtPeerInfo(PeerInfo_t *to, const MWL_HAL_PEERINFO *from) +{ + to->LegacyRateBitMap = LE_32(from->LegacyRateBitMap); + to->HTRateBitMap = LE_32(from->HTRateBitMap); + to->CapInfo = LE_16(from->CapInfo); + to->HTCapabilitiesInfo = LE_16(from->HTCapabilitiesInfo); + to->MacHTParamInfo = from->MacHTParamInfo; + to->AddHtInfo.ControlChan = from->AddHtInfo.ControlChan; + to->AddHtInfo.AddChan = from->AddHtInfo.AddChan; + to->AddHtInfo.OpMode = LE_16(from->AddHtInfo.OpMode); + to->AddHtInfo.stbc = LE_16(from->AddHtInfo.stbc); +} + +/* XXX station id must be in [0..63] */ +static int +mwl_hal_newstation(struct mwl_softc *sc, + const uint8_t addr[IEEE80211_ADDR_LEN], uint16_t aid, uint16_t sid, + const MWL_HAL_PEERINFO *peer, int isQosSta, int wmeInfo) +{ + HostCmd_FW_SET_NEW_STN *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_FW_SET_NEW_STN, HostCmd_CMD_SET_NEW_STN); + pCmd->AID = LE_16(aid); + pCmd->StnId = LE_16(sid); + pCmd->Action = LE_16(0); /* SET */ + if (peer != NULL) { + /* NB: must fix up byte order */ + cvtPeerInfo(&pCmd->PeerInfo, peer); + } + IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], addr); + pCmd->Qosinfo = (uint8_t)wmeInfo; + pCmd->isQosSta = (isQosSta != 0); + + MWL_DBG(MWL_DBG_HW, "mwl: mwl_hal_newstation(): " + "LegacyRateBitMap %x, CapInfo %x\n", + pCmd->PeerInfo.LegacyRateBitMap, pCmd->PeerInfo.CapInfo); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_NEW_STN); + return (retval); +} + +/* + * Configure antenna use. + * Takes effect immediately. + * XXX tx antenna setting ignored + * XXX rx antenna setting should always be 3 (for now) + */ +static int +mwl_hal_setantenna(struct mwl_softc *sc, MWL_HAL_ANTENNA dirSet, int ant) +{ + HostCmd_DS_802_11_RF_ANTENNA *pCmd; + int retval; + + if (!(dirSet == WL_ANTENNATYPE_RX || dirSet == WL_ANTENNATYPE_TX)) + return (EINVAL); + + _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_ANTENNA, + HostCmd_CMD_802_11_RF_ANTENNA); + pCmd->Action = LE_16(dirSet); + if (ant == 0) /* default to all/both antennae */ + ant = 3; + pCmd->AntennaMode = LE_16(ant); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RF_ANTENNA); + return (retval); +} + +/* + * Configure radio. + * Takes effect immediately. + * XXX preamble installed after set fixed rate cmd + */ +static int +mwl_hal_setradio(struct mwl_softc *sc, int onoff, MWL_HAL_PREAMBLE preamble) +{ + HostCmd_DS_802_11_RADIO_CONTROL *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_DS_802_11_RADIO_CONTROL, + HostCmd_CMD_802_11_RADIO_CONTROL); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET); + if (onoff == 0) + pCmd->Control = 0; + else + pCmd->Control = LE_16(preamble); + pCmd->RadioOn = LE_16(onoff); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RADIO_CONTROL); + return (retval); +} + +static int +mwl_hal_setwmm(struct mwl_softc *sc, int onoff) +{ + HostCmd_FW_SetWMMMode *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_FW_SetWMMMode, + HostCmd_CMD_SET_WMM_MODE); + pCmd->Action = LE_16(onoff); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_WMM_MODE); + return (retval); +} + +/* + * Convert public channel flags definition to a + * value suitable for feeding to the firmware. + * Note this includes byte swapping. + */ +static uint32_t +cvtChannelFlags(const MWL_HAL_CHANNEL *chan) +{ + uint32_t w; + + /* + * NB: f/w only understands FREQ_BAND_5GHZ, supplying the more + * precise band info causes it to lockup (sometimes). + */ + w = (chan->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) ? + FREQ_BAND_2DOT4GHZ : FREQ_BAND_5GHZ; + switch (chan->channelFlags.ChnlWidth) { + case MWL_CH_10_MHz_WIDTH: + w |= CH_10_MHz_WIDTH; + break; + case MWL_CH_20_MHz_WIDTH: + w |= CH_20_MHz_WIDTH; + break; + case MWL_CH_40_MHz_WIDTH: + default: + w |= CH_40_MHz_WIDTH; + break; + } + switch (chan->channelFlags.ExtChnlOffset) { + case MWL_EXT_CH_NONE: + w |= EXT_CH_NONE; + break; + case MWL_EXT_CH_ABOVE_CTRL_CH: + w |= EXT_CH_ABOVE_CTRL_CH; + break; + case MWL_EXT_CH_BELOW_CTRL_CH: + w |= EXT_CH_BELOW_CTRL_CH; + break; + } + return (LE_32(w)); +} + +static int +mwl_hal_setchannel(struct mwl_softc *sc, const MWL_HAL_CHANNEL *chan) +{ + HostCmd_FW_SET_RF_CHANNEL *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_FW_SET_RF_CHANNEL, HostCmd_CMD_SET_RF_CHANNEL); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET); + pCmd->CurrentChannel = chan->channel; + pCmd->ChannelFlags = cvtChannelFlags(chan); /* NB: byte-swapped */ + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_RF_CHANNEL); + return (retval); +} + +static int +mwl_hal_settxpower(struct mwl_softc *sc, + const MWL_HAL_CHANNEL *c, uint8_t maxtxpow) +{ + HostCmd_DS_802_11_RF_TX_POWER *pCmd; + const struct mwl_hal_channel *hc; + int i = 0, retval; + + hc = findhalchannel(sc, c); + if (hc == NULL) { + /* XXX temp while testing */ + MWL_DBG(MWL_DBG_HW, "mwl: mwl_hal_settxpower(): " + "no cal data for channel %u band %u width %u ext %u\n", + c->channel, c->channelFlags.FreqBand, + c->channelFlags.ChnlWidth, c->channelFlags.ExtChnlOffset); + return (EINVAL); + } + + _CMD_SETUP(pCmd, HostCmd_DS_802_11_RF_TX_POWER, + HostCmd_CMD_802_11_RF_TX_POWER); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET_LIST); + /* NB: 5Ghz cal data have the channel # in [0]; don't truncate */ + if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) + pCmd->PowerLevelList[i++] = LE_16(hc->targetPowers[0]); + for (; i < 4; i++) { + uint16_t pow = hc->targetPowers[i]; + if (pow > maxtxpow) + pow = maxtxpow; + pCmd->PowerLevelList[i] = LE_16(pow); + } + retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RF_TX_POWER); + return (retval); +} + +#define RATEVAL(r) ((r) &~ RATE_MCS) +#define RATETYPE(r) (((r) & RATE_MCS) ? HT_RATE_TYPE : LEGACY_RATE_TYPE) + +static int +mwl_hal_settxrate(struct mwl_softc *sc, MWL_HAL_TXRATE_HANDLING handling, + const MWL_HAL_TXRATE *rate) +{ + HostCmd_FW_USE_FIXED_RATE *pCmd; + FIXED_RATE_ENTRY *fp; + int retval, i, n; + + _VCMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE, + HostCmd_CMD_SET_FIXED_RATE); + + pCmd->MulticastRate = RATEVAL(rate->McastRate); + pCmd->MultiRateTxType = RATETYPE(rate->McastRate); + /* NB: no rate type field */ + pCmd->ManagementRate = RATEVAL(rate->MgtRate); + memset(pCmd->FixedRateTable, 0, sizeof (pCmd->FixedRateTable)); + if (handling == RATE_FIXED) { + pCmd->Action = LE_32(HostCmd_ACT_GEN_SET); + pCmd->AllowRateDrop = LE_32(FIXED_RATE_WITHOUT_AUTORATE_DROP); + fp = pCmd->FixedRateTable; + fp->FixedRate = + LE_32(RATEVAL(rate->RateSeries[0].Rate)); + fp->FixRateTypeFlags.FixRateType = + LE_32(RATETYPE(rate->RateSeries[0].Rate)); + pCmd->EntryCount = LE_32(1); + } else if (handling == RATE_FIXED_DROP) { + pCmd->Action = LE_32(HostCmd_ACT_GEN_SET); + pCmd->AllowRateDrop = LE_32(FIXED_RATE_WITH_AUTO_RATE_DROP); + n = 0; + fp = pCmd->FixedRateTable; + for (i = 0; i < 4; i++) { + if (rate->RateSeries[0].TryCount == 0) + break; + fp->FixRateTypeFlags.FixRateType = + LE_32(RATETYPE(rate->RateSeries[i].Rate)); + fp->FixedRate = + LE_32(RATEVAL(rate->RateSeries[i].Rate)); + fp->FixRateTypeFlags.RetryCountValid = + LE_32(RETRY_COUNT_VALID); + fp->RetryCount = + LE_32(rate->RateSeries[i].TryCount-1); + n++; + } + pCmd->EntryCount = LE_32(n); + } else + pCmd->Action = LE_32(HostCmd_ACT_NOT_USE_FIXED_RATE); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_FIXED_RATE); + return (retval); +} + +static int +mwl_hal_settxrate_auto(struct mwl_softc *sc, const MWL_HAL_TXRATE *rate) +{ + HostCmd_FW_USE_FIXED_RATE *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_FW_USE_FIXED_RATE, + HostCmd_CMD_SET_FIXED_RATE); + + pCmd->MulticastRate = RATEVAL(rate->McastRate); + pCmd->MultiRateTxType = RATETYPE(rate->McastRate); + /* NB: no rate type field */ + pCmd->ManagementRate = RATEVAL(rate->MgtRate); + memset(pCmd->FixedRateTable, 0, sizeof (pCmd->FixedRateTable)); + pCmd->Action = LE_32(HostCmd_ACT_NOT_USE_FIXED_RATE); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_FIXED_RATE); + return (retval); +} + +#undef RATEVAL +#undef RATETYPE + +/* XXX 0 = indoor, 1 = outdoor */ +static int +mwl_hal_setrateadaptmode(struct mwl_softc *sc, uint16_t mode) +{ + HostCmd_DS_SET_RATE_ADAPT_MODE *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_DS_SET_RATE_ADAPT_MODE, + HostCmd_CMD_SET_RATE_ADAPT_MODE); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET); + pCmd->RateAdaptMode = LE_16(mode); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_RATE_ADAPT_MODE); + return (retval); +} + +static int +mwl_hal_setoptimizationlevel(struct mwl_softc *sc, int level) +{ + HostCmd_FW_SET_OPTIMIZATION_LEVEL *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_FW_SET_OPTIMIZATION_LEVEL, + HostCmd_CMD_SET_OPTIMIZATION_LEVEL); + pCmd->OptLevel = (uint8_t)level; + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_OPTIMIZATION_LEVEL); + return (retval); +} + +/* + * Set the region code that selects the radar bin'ing agorithm. + */ +static int +mwl_hal_setregioncode(struct mwl_softc *sc, int regionCode) +{ + HostCmd_SET_REGIONCODE_INFO *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_SET_REGIONCODE_INFO, + HostCmd_CMD_SET_REGION_CODE); + /* XXX map pseudo-codes to fw codes */ + switch (regionCode) { + case DOMAIN_CODE_ETSI_131: + pCmd->regionCode = LE_16(DOMAIN_CODE_ETSI); + break; + default: + pCmd->regionCode = LE_16(regionCode); + break; + } + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_REGION_CODE); + return (retval); +} + +static int +mwl_hal_setassocid(struct mwl_softc *sc, + const uint8_t bssId[IEEE80211_ADDR_LEN], uint16_t assocId) +{ + HostCmd_FW_SET_AID *pCmd = (HostCmd_FW_SET_AID *) &sc->sc_cmd_mem[0]; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_FW_SET_AID, HostCmd_CMD_SET_AID); + pCmd->AssocID = LE_16(assocId); + IEEE80211_ADDR_COPY(&pCmd->MacAddr[0], bssId); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_AID); + return (retval); +} + +/* + * Inform firmware of tx rate parameters. Called whenever + * user-settable params change and after a channel change. + */ +static int +mwl_setrates(struct ieee80211com *ic) +{ + struct mwl_softc *sc = (struct mwl_softc *)ic; + MWL_HAL_TXRATE rates; + + const struct ieee80211_rateset *rs; + rs = &ic->ic_bss->in_rates; + + /* + * Update the h/w rate map. + * NB: 0x80 for MCS is passed through unchanged + */ + memset(&rates, 0, sizeof (rates)); + /* rate used to send management frames */ + rates.MgtRate = rs->ir_rates[0] & IEEE80211_RATE_VAL; + /* rate used to send multicast frames */ + rates.McastRate = rates.MgtRate; + + return (mwl_hal_settxrate(sc, RATE_AUTO, &rates)); +} + +/* + * Set packet size threshold for implicit use of RTS. + * Takes effect immediately. + * XXX packet length > threshold =>'s RTS + */ +static int +mwl_hal_setrtsthreshold(struct mwl_softc *sc, int threshold) +{ + HostCmd_DS_802_11_RTS_THSD *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_DS_802_11_RTS_THSD, + HostCmd_CMD_802_11_RTS_THSD); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET); + pCmd->Threshold = LE_16(threshold); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_802_11_RTS_THSD); + return (retval); +} + +static int +mwl_hal_setcsmode(struct mwl_softc *sc, MWL_HAL_CSMODE csmode) +{ + HostCmd_DS_SET_LINKADAPT_CS_MODE *pCmd; + int retval; + + _CMD_SETUP(pCmd, HostCmd_DS_SET_LINKADAPT_CS_MODE, + HostCmd_CMD_SET_LINKADAPT_CS_MODE); + pCmd->Action = LE_16(HostCmd_ACT_GEN_SET); + pCmd->CSMode = LE_16(csmode); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_LINKADAPT_CS_MODE); + return (retval); +} + +static int +mwl_hal_setpromisc(struct mwl_softc *sc, int ena) +{ + uint32_t v; + + v = mwl_ctl_read4(sc, MACREG_REG_PROMISCUOUS); + mwl_ctl_write4(sc, MACREG_REG_PROMISCUOUS, ena ? v | 1 : v & ~1); + + return (0); +} + +static int +mwl_hal_start(struct mwl_softc *sc) +{ + HostCmd_DS_BSS_START *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_DS_BSS_START, HostCmd_CMD_BSS_START); + pCmd->Enable = LE_32(HostCmd_ACT_GEN_ON); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_BSS_START); + return (retval); +} + +/* + * Enable sta-mode operation (disables beacon frame xmit). + */ +static int +mwl_hal_setinframode(struct mwl_softc *sc) +{ + HostCmd_FW_SET_INFRA_MODE *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_FW_SET_INFRA_MODE, + HostCmd_CMD_SET_INFRA_MODE); + + retval = mwlExecuteCmd(sc, HostCmd_CMD_SET_INFRA_MODE); + return (retval); +} + +static int +mwl_hal_stop(struct mwl_softc *sc) +{ + HostCmd_DS_BSS_START *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_DS_BSS_START, + HostCmd_CMD_BSS_START); + pCmd->Enable = LE_32(HostCmd_ACT_GEN_OFF); + retval = mwlExecuteCmd(sc, HostCmd_CMD_BSS_START); + + return (retval); +} + +static int +mwl_hal_keyset(struct mwl_softc *sc, const MWL_HAL_KEYVAL *kv, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY, + HostCmd_CMD_UPDATE_ENCRYPTION); + if (kv->keyFlags & (KEY_FLAG_TXGROUPKEY|KEY_FLAG_RXGROUPKEY)) + pCmd->ActionType = LE_32(EncrActionTypeSetGroupKey); + else + pCmd->ActionType = LE_32(EncrActionTypeSetKey); + pCmd->KeyParam.Length = LE_16(sizeof (pCmd->KeyParam)); + pCmd->KeyParam.KeyTypeId = LE_16(kv->keyTypeId); + pCmd->KeyParam.KeyInfo = LE_32(kv->keyFlags); + pCmd->KeyParam.KeyIndex = LE_32(kv->keyIndex); + /* NB: includes TKIP MIC keys */ + (void) memcpy(&pCmd->KeyParam.Key, &kv->key, kv->keyLen); + switch (kv->keyTypeId) { + case KEY_TYPE_ID_WEP: + pCmd->KeyParam.KeyLen = LE_16(kv->keyLen); + break; + case KEY_TYPE_ID_TKIP: + pCmd->KeyParam.KeyLen = LE_16(sizeof (TKIP_TYPE_KEY)); + pCmd->KeyParam.Key.TkipKey.TkipRsc.low = + LE_16(kv->key.tkip.rsc.low); + pCmd->KeyParam.Key.TkipKey.TkipRsc.high = + LE_32(kv->key.tkip.rsc.high); + pCmd->KeyParam.Key.TkipKey.TkipTsc.low = + LE_16(kv->key.tkip.tsc.low); + pCmd->KeyParam.Key.TkipKey.TkipTsc.high = + LE_32(kv->key.tkip.tsc.high); + break; + case KEY_TYPE_ID_AES: + pCmd->KeyParam.KeyLen = LE_16(sizeof (AES_TYPE_KEY)); + break; + } +#ifdef MWL_MBSS_SUPPORT + IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac); +#else + IEEE80211_ADDR_COPY(pCmd->Macaddr, mac); +#endif + + retval = mwlExecuteCmd(sc, HostCmd_CMD_UPDATE_ENCRYPTION); + return (retval); +} + +static int +mwl_hal_keyreset(struct mwl_softc *sc, const MWL_HAL_KEYVAL *kv, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY *pCmd; + int retval; + + _VCMD_SETUP(pCmd, HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY, + HostCmd_CMD_UPDATE_ENCRYPTION); + pCmd->ActionType = LE_16(EncrActionTypeRemoveKey); + pCmd->KeyParam.Length = LE_16(sizeof (pCmd->KeyParam)); + pCmd->KeyParam.KeyTypeId = LE_16(kv->keyTypeId); + pCmd->KeyParam.KeyInfo = LE_32(kv->keyFlags); + pCmd->KeyParam.KeyIndex = LE_32(kv->keyIndex); +#ifdef MWL_MBSS_SUPPORT + IEEE80211_ADDR_COPY(pCmd->KeyParam.Macaddr, mac); +#else + IEEE80211_ADDR_COPY(pCmd->Macaddr, mac); +#endif + retval = mwlExecuteCmd(sc, HostCmd_CMD_UPDATE_ENCRYPTION); + return (retval); +} + +/* ARGSUSED */ +static struct ieee80211_node * +mwl_node_alloc(struct ieee80211com *ic) +{ + struct mwl_node *mn; + + mn = kmem_zalloc(sizeof (struct mwl_node), KM_SLEEP); + if (mn == NULL) { + /* XXX stat+msg */ + MWL_DBG(MWL_DBG_MSG, "mwl: mwl_node_alloc(): " + "alloc node failed\n"); + return (NULL); + } + return (&mn->mn_node); +} + +static void +mwl_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->in_ic; + struct mwl_node *mn = MWL_NODE(ni); + + if (mn->mn_staid != 0) { + // mwl_hal_delstation(mn->mn_hvap, vap->iv_myaddr); + // delstaid(sc, mn->mn_staid); + mn->mn_staid = 0; + } + ic->ic_node_cleanup(ni); + kmem_free(ni, sizeof (struct mwl_node)); +} + +/* + * Allocate a key cache slot for a unicast key. The + * firmware handles key allocation and every station is + * guaranteed key space so we are always successful. + */ +static int +mwl_key_alloc(struct ieee80211com *ic, const struct ieee80211_key *k, + ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix) +{ + if (k->wk_keyix != IEEE80211_KEYIX_NONE || + (k->wk_flags & IEEE80211_KEY_GROUP)) { + if (!(&ic->ic_nw_keys[0] <= k && + k < &ic->ic_nw_keys[IEEE80211_WEP_NKID])) { + /* should not happen */ + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): " + "bogus group key\n"); + return (0); + } + /* give the caller what they requested */ + *keyix = *rxkeyix = k - ic->ic_nw_keys; + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): " + "alloc GROUP key keyix %x, rxkeyix %x\n", + *keyix, *rxkeyix); + } else { + /* + * Firmware handles key allocation. + */ + *keyix = *rxkeyix = 0; + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_alloc(): " + "reset key index in key allocation\n"); + } + + return (1); +} + +/* + * Delete a key entry allocated by mwl_key_alloc. + */ +static int +mwl_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + struct mwl_softc *sc = (struct mwl_softc *)ic; + MWL_HAL_KEYVAL hk; + const uint8_t bcastaddr[IEEE80211_ADDR_LEN] = + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + memset(&hk, 0, sizeof (hk)); + hk.keyIndex = k->wk_keyix; + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_WEP: + hk.keyTypeId = KEY_TYPE_ID_WEP; + break; + case IEEE80211_CIPHER_TKIP: + hk.keyTypeId = KEY_TYPE_ID_TKIP; + break; + case IEEE80211_CIPHER_AES_CCM: + hk.keyTypeId = KEY_TYPE_ID_AES; + break; + default: + /* XXX should not happen */ + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_delete(): " + "unknown cipher %d\n", k->wk_cipher->ic_cipher); + return (0); + } + return (mwl_hal_keyreset(sc, &hk, bcastaddr) == 0); +} + +/* + * Set the key cache contents for the specified key. Key cache + * slot(s) must already have been allocated by mwl_key_alloc. + */ +/* ARGSUSED */ +static int +mwl_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ +#define GRPXMIT (IEEE80211_KEY_XMIT | IEEE80211_KEY_GROUP) +/* NB: static wep keys are marked GROUP+tx/rx; GTK will be tx or rx */ +#define IEEE80211_IS_STATICKEY(k) \ + (((k)->wk_flags & (GRPXMIT|IEEE80211_KEY_RECV)) == \ + (GRPXMIT|IEEE80211_KEY_RECV)) + struct mwl_softc *sc = (struct mwl_softc *)ic; + const struct ieee80211_cipher *cip = k->wk_cipher; + const uint8_t *macaddr; + MWL_HAL_KEYVAL hk; + + memset(&hk, 0, sizeof (hk)); + hk.keyIndex = k->wk_keyix; + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_WEP: + hk.keyTypeId = KEY_TYPE_ID_WEP; + hk.keyLen = k->wk_keylen; + if (k->wk_keyix == ic->ic_def_txkey) + hk.keyFlags = KEY_FLAG_WEP_TXKEY; + if (!IEEE80211_IS_STATICKEY(k)) { + /* NB: WEP is never used for the PTK */ + (void) addgroupflags(&hk, k); + } + break; + case IEEE80211_CIPHER_TKIP: + hk.keyTypeId = KEY_TYPE_ID_TKIP; + hk.key.tkip.tsc.high = (uint32_t)(k->wk_keytsc >> 16); + hk.key.tkip.tsc.low = (uint16_t)k->wk_keytsc; + hk.keyFlags = KEY_FLAG_TSC_VALID | KEY_FLAG_MICKEY_VALID; + hk.keyLen = k->wk_keylen + IEEE80211_MICBUF_SIZE; + if (!addgroupflags(&hk, k)) + hk.keyFlags |= KEY_FLAG_PAIRWISE; + break; + case IEEE80211_CIPHER_AES_CCM: + hk.keyTypeId = KEY_TYPE_ID_AES; + hk.keyLen = k->wk_keylen; + if (!addgroupflags(&hk, k)) + hk.keyFlags |= KEY_FLAG_PAIRWISE; + break; + default: + /* XXX should not happen */ + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_key_set(): " + "unknown cipher %d\n", + k->wk_cipher->ic_cipher); + return (0); + } + /* + * NB: tkip mic keys get copied here too; the layout + * just happens to match that in ieee80211_key. + */ + (void) memcpy(hk.key.aes, k->wk_key, hk.keyLen); + + /* + * Locate address of sta db entry for writing key; + * the convention unfortunately is somewhat different + * than how net80211, hostapd, and wpa_supplicant think. + */ + + /* + * NB: keys plumbed before the sta reaches AUTH state + * will be discarded or written to the wrong sta db + * entry because iv_bss is meaningless. This is ok + * (right now) because we handle deferred plumbing of + * WEP keys when the sta reaches AUTH state. + */ + macaddr = ic->ic_bss->in_bssid; + if (k->wk_flags & IEEE80211_KEY_XMIT) { + /* XXX plumb to local sta db too for static key wep */ + (void) mwl_hal_keyset(sc, &hk, ic->ic_macaddr); + } + return (mwl_hal_keyset(sc, &hk, macaddr) == 0); +#undef IEEE80211_IS_STATICKEY +#undef GRPXMIT +} + +/* + * Plumb any static WEP key for the station. This is + * necessary as we must propagate the key from the + * global key table of the vap to each sta db entry. + */ +static void +mwl_setanywepkey(struct ieee80211com *ic, const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + if ((ic->ic_flags & (IEEE80211_F_PRIVACY|IEEE80211_F_WPA)) == + IEEE80211_F_PRIVACY && + ic->ic_def_txkey != IEEE80211_KEYIX_NONE && + ic->ic_nw_keys[ic->ic_def_txkey].wk_keyix != IEEE80211_KEYIX_NONE) + (void) mwl_key_set(ic, &ic->ic_nw_keys[ic->ic_def_txkey], mac); +} + +static void +mwl_setglobalkeys(struct ieee80211com *ic) +{ + struct ieee80211_key *wk; + + wk = &ic->ic_nw_keys[0]; + for (; wk < &ic->ic_nw_keys[IEEE80211_WEP_NKID]; wk++) + if (wk->wk_keyix != IEEE80211_KEYIX_NONE) + (void) mwl_key_set(ic, wk, ic->ic_macaddr); +} + +static int +addgroupflags(MWL_HAL_KEYVAL *hk, const struct ieee80211_key *k) +{ + if (k->wk_flags & IEEE80211_KEY_GROUP) { + if (k->wk_flags & IEEE80211_KEY_XMIT) + hk->keyFlags |= KEY_FLAG_TXGROUPKEY; + if (k->wk_flags & IEEE80211_KEY_RECV) + hk->keyFlags |= KEY_FLAG_RXGROUPKEY; + return (1); + } else + return (0); +} + +/* + * Set/change channels. + */ +static int +mwl_chan_set(struct mwl_softc *sc, struct mwl_channel *chan) +{ + MWL_HAL_CHANNEL hchan; + int maxtxpow; + + MWL_DBG(MWL_DBG_HW, "mwl: mwl_chan_set(): " + "chan %u MHz/flags 0x%x\n", + chan->ic_freq, chan->ic_flags); + + /* + * Convert to a HAL channel description with + * the flags constrained to reflect the current + * operating mode. + */ + mwl_mapchan(&hchan, chan); + mwl_hal_intrset(sc, 0); /* disable interrupts */ + + (void) mwl_hal_setchannel(sc, &hchan); + /* + * Tx power is cap'd by the regulatory setting and + * possibly a user-set limit. We pass the min of + * these to the hal to apply them to the cal data + * for this channel. + * XXX min bound? + */ + maxtxpow = 2 * chan->ic_maxregpower; + if (maxtxpow > 100) + maxtxpow = 100; + (void) mwl_hal_settxpower(sc, &hchan, maxtxpow / 2); + /* NB: potentially change mcast/mgt rates */ + (void) mwl_setcurchanrates(sc); + + sc->sc_curchan = hchan; + mwl_hal_intrset(sc, sc->sc_imask); + + return (0); +} + +/* + * Convert net80211 channel to a HAL channel. + */ +static void +mwl_mapchan(MWL_HAL_CHANNEL *hc, const struct mwl_channel *chan) +{ + hc->channel = chan->ic_ieee; + + *(uint32_t *)&hc->channelFlags = 0; + if (((chan)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) + hc->channelFlags.FreqBand = MWL_FREQ_BAND_2DOT4GHZ; + else if (((chan)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) + hc->channelFlags.FreqBand = MWL_FREQ_BAND_5GHZ; + if (((chan)->ic_flags & IEEE80211_CHAN_HT40) != 0) { + hc->channelFlags.ChnlWidth = MWL_CH_40_MHz_WIDTH; + if (((chan)->ic_flags & IEEE80211_CHAN_HT40U) != 0) + hc->channelFlags.ExtChnlOffset = + MWL_EXT_CH_ABOVE_CTRL_CH; + else + hc->channelFlags.ExtChnlOffset = + MWL_EXT_CH_BELOW_CTRL_CH; + } else + hc->channelFlags.ChnlWidth = MWL_CH_20_MHz_WIDTH; + /* XXX 10MHz channels */ +} + +/* + * Return the phy mode for with the specified channel. + */ +enum ieee80211_phymode +mwl_chan2mode(const struct mwl_channel *chan) +{ + + if (IEEE80211_IS_CHAN_HTA(chan)) + return (IEEE80211_MODE_11NA); + else if (IEEE80211_IS_CHAN_HTG(chan)) + return (IEEE80211_MODE_11NG); + else if (IEEE80211_IS_CHAN_108G(chan)) + return (IEEE80211_MODE_TURBO_G); + else if (IEEE80211_IS_CHAN_ST(chan)) + return (IEEE80211_MODE_STURBO_A); + else if (IEEE80211_IS_CHAN_TURBO(chan)) + return (IEEE80211_MODE_TURBO_A); + else if (IEEE80211_IS_CHAN_HALF(chan)) + return (IEEE80211_MODE_HALF); + else if (IEEE80211_IS_CHAN_QUARTER(chan)) + return (IEEE80211_MODE_QUARTER); + else if (IEEE80211_IS_CHAN_A(chan)) + return (IEEE80211_MODE_11A); + else if (IEEE80211_IS_CHAN_ANYG(chan)) + return (IEEE80211_MODE_11G); + else if (IEEE80211_IS_CHAN_B(chan)) + return (IEEE80211_MODE_11B); + else if (IEEE80211_IS_CHAN_FHSS(chan)) + return (IEEE80211_MODE_FH); + + /* NB: should not get here */ + MWL_DBG(MWL_DBG_HW, "mwl: mwl_chan2mode(): " + "cannot map channel to mode; freq %u flags 0x%x\n", + chan->ic_freq, chan->ic_flags); + return (IEEE80211_MODE_11B); +} + +/* XXX inline or eliminate? */ +const struct ieee80211_rateset * +mwl_get_suprates(struct ieee80211com *ic, const struct mwl_channel *c) +{ + /* XXX does this work for 11ng basic rates? */ + return (&ic->ic_sup_rates[mwl_chan2mode(c)]); +} + +/* + * Inform firmware of tx rate parameters. + * Called after a channel change. + */ +static int +mwl_setcurchanrates(struct mwl_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct ieee80211_rateset *rs; + MWL_HAL_TXRATE rates; + + memset(&rates, 0, sizeof (rates)); + rs = mwl_get_suprates(ic, sc->sc_cur_chan); + /* rate used to send management frames */ + rates.MgtRate = rs->ir_rates[0] & IEEE80211_RATE_VAL; + /* rate used to send multicast frames */ + rates.McastRate = rates.MgtRate; + + return (mwl_hal_settxrate_auto(sc, &rates)); +} + +static const struct mwl_hal_channel * +findhalchannel(const struct mwl_softc *sc, const MWL_HAL_CHANNEL *c) +{ + const struct mwl_hal_channel *hc; + const MWL_HAL_CHANNELINFO *ci; + int chan = c->channel, i; + + if (c->channelFlags.FreqBand == MWL_FREQ_BAND_2DOT4GHZ) { + i = chan - 1; + if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) { + ci = &sc->sc_40M; + if (c->channelFlags.ExtChnlOffset == + MWL_EXT_CH_BELOW_CTRL_CH) + i -= 4; + } else + ci = &sc->sc_20M; + /* 2.4G channel table is directly indexed */ + hc = ((unsigned)i < ci->nchannels) ? &ci->channels[i] : NULL; + } else if (c->channelFlags.FreqBand == MWL_FREQ_BAND_5GHZ) { + if (c->channelFlags.ChnlWidth == MWL_CH_40_MHz_WIDTH) { + ci = &sc->sc_40M_5G; + if (c->channelFlags.ExtChnlOffset == + MWL_EXT_CH_BELOW_CTRL_CH) + chan -= 4; + } else + ci = &sc->sc_20M_5G; + /* 5GHz channel table is sparse and must be searched */ + for (i = 0; i < ci->nchannels; i++) + if (ci->channels[i].ieee == chan) + break; + hc = (i < ci->nchannels) ? &ci->channels[i] : NULL; + } else + hc = NULL; + return (hc); +} + +/* + * Map SKU+country code to region code for radar bin'ing. + */ +static int +mwl_map2regioncode(const struct mwl_regdomain *rd) +{ + switch (rd->regdomain) { + case SKU_FCC: + case SKU_FCC3: + return (DOMAIN_CODE_FCC); + case SKU_CA: + return (DOMAIN_CODE_IC); + case SKU_ETSI: + case SKU_ETSI2: + case SKU_ETSI3: + if (rd->country == CTRY_SPAIN) + return (DOMAIN_CODE_SPAIN); + if (rd->country == CTRY_FRANCE || rd->country == CTRY_FRANCE2) + return (DOMAIN_CODE_FRANCE); + /* XXX force 1.3.1 radar type */ + return (DOMAIN_CODE_ETSI_131); + case SKU_JAPAN: + return (DOMAIN_CODE_MKK); + case SKU_ROW: + return (DOMAIN_CODE_DGT); /* Taiwan */ + case SKU_APAC: + case SKU_APAC2: + case SKU_APAC3: + return (DOMAIN_CODE_AUS); /* Australia */ + } + /* XXX KOREA? */ + return (DOMAIN_CODE_FCC); /* XXX? */ +} + +/* + * Setup the rx data structures. This should only be + * done once or we may get out of sync with the firmware. + */ +static int +mwl_startrecv(struct mwl_softc *sc) +{ + struct mwl_rx_ring *ring; + struct mwl_rxdesc *ds; + struct mwl_rxbuf *bf, *prev; + + int i; + + ring = &sc->sc_rxring; + bf = ring->buf; + + prev = NULL; + for (i = 0; i < MWL_RX_RING_COUNT; i++, bf++) { + ds = bf->bf_desc; + /* + * NB: DMA buffer contents is known to be unmodified + * so there's no need to flush the data cache. + */ + + /* + * Setup descriptor. + */ + ds->QosCtrl = 0; + ds->RSSI = 0; + ds->Status = EAGLE_RXD_STATUS_IDLE; + ds->Channel = 0; + ds->PktLen = LE_16(MWL_AGGR_SIZE); + ds->SQ2 = 0; + ds->pPhysBuffData = LE_32(bf->bf_baddr); + /* NB: don't touch pPhysNext, set once */ + ds->RxControl = EAGLE_RXD_CTRL_DRIVER_OWN; + + (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl, + i * sizeof (struct mwl_rxdesc), + sizeof (struct mwl_rxdesc), + DDI_DMA_SYNC_FORDEV); + + if (prev != NULL) { + ds = prev->bf_desc; + ds->pPhysNext = LE_32(bf->bf_daddr); + } + prev = bf; + } + + if (prev != NULL) { + ds = prev->bf_desc; + ds->pPhysNext = ring->physaddr; + } + + /* set filters, etc. */ + (void) mwl_mode_init(sc); + + return (0); +} + +static int +mwl_mode_init(struct mwl_softc *sc) +{ + /* + * NB: Ignore promisc in hostap mode; it's set by the + * bridge. This is wrong but we have no way to + * identify internal requests (from the bridge) + * versus external requests such as for tcpdump. + */ + /* mwl_setmcastfilter - not support now */ + (void) mwl_hal_setpromisc(sc, 0); + + return (0); +} + +/* + * Kick the firmware to tell it there are new tx descriptors + * for processing. The driver says what h/w q has work in + * case the f/w ever gets smarter. + */ +/* ARGSUSED */ +static void +mwl_hal_txstart(struct mwl_softc *sc, int qnum) +{ + + mwl_ctl_write4(sc, MACREG_REG_H2A_INTERRUPT_EVENTS, + MACREG_H2ARIC_BIT_PPA_READY); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); +} + +static int +mwl_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) +{ + struct mwl_softc *sc = (struct mwl_softc *)ic; + struct mwl_tx_ring *ring; + struct mwl_txdesc *ds; + struct mwl_txbuf *bf; + struct ieee80211_frame *wh, *wh1; + struct ieee80211_node *ni = NULL; + + int err, off; + int mblen, pktlen, hdrlen; + mblk_t *m, *m0; + uint8_t *addr_4, *txbuf; + uint16_t *pfwlen; + + MWL_TXLOCK(sc); + + err = DDI_SUCCESS; + if (!MWL_IS_RUNNING(sc) || MWL_IS_SUSPEND(sc)) { + err = ENXIO; + goto fail1; + } + + ring = &sc->sc_txring[1]; + if (ring->queued > 15) { + MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): " + "no txbuf, %d\n", ring->queued); + sc->sc_need_sched = 1; + sc->sc_tx_nobuf++; + err = ENOMEM; + goto fail1; + } + + m = allocb(msgdsize(mp) + 32, BPRI_MED); + if (m == NULL) { + MWL_DBG(MWL_DBG_TX, "mwl: mwl_send():" + "can't alloc mblk.\n"); + err = DDI_FAILURE; + goto fail1; + } + + for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { + mblen = MBLKL(m0); + (void) bcopy(m0->b_rptr, m->b_rptr + off, mblen); + off += mblen; + } + m->b_wptr += off; + + wh = (struct ieee80211_frame *)m->b_rptr; + ni = ieee80211_find_txnode(ic, wh->i_addr1); + if (ni == NULL) { + err = DDI_FAILURE; + sc->sc_tx_err++; + goto fail2; + } + + hdrlen = sizeof (*wh); + pktlen = msgdsize(m); + + (void) ieee80211_encap(ic, m, ni); + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + const struct ieee80211_cipher *cip; + struct ieee80211_key *k; + k = ieee80211_crypto_encap(ic, m); + if (k == NULL) { + sc->sc_tx_err++; + err = DDI_FAILURE; + goto fail3; + } + + /* + * Adjust the packet length for the crypto additions + * done during encap and any other bits that the f/w + * will add later on. + */ + cip = k->wk_cipher; + pktlen += cip->ic_header + cip->ic_miclen + cip->ic_trailer; + /* packet header may have moved, reset our local pointer */ + wh = (struct ieee80211_frame *)m->b_rptr; + } + + ds = &ring->desc[ring->cur]; + bf = &ring->buf[ring->cur]; + + bf->bf_node = ieee80211_ref_node(ni); + txbuf = (uint8_t *)bf->bf_mem; + + /* + * inject FW specific fields into the 802.11 frame + * + * 2 bytes FW len (inject) + * 24 bytes 802.11 frame header + * 6 bytes addr4 (inject) + * n bytes 802.11 frame body + */ + pfwlen = (uint16_t *)txbuf; + *pfwlen = pktlen - hdrlen; + wh1 = (struct ieee80211_frame *)(txbuf + 2); + bcopy(wh, wh1, sizeof (struct ieee80211_frame)); + addr_4 = txbuf + (sizeof (struct ieee80211_frame) + sizeof (uint16_t)); + (void) memset(addr_4, 0, 6); + bcopy(m->b_rptr + sizeof (struct ieee80211_frame), txbuf + 32, *pfwlen); + pktlen += 8; + + (void) ddi_dma_sync(bf->txbuf_dma.dma_hdl, + 0, + pktlen, + DDI_DMA_SYNC_FORDEV); + + ds->QosCtrl = 0; + ds->PktLen = (uint16_t)pktlen; + ds->PktPtr = bf->bf_baddr; + ds->Status = LE_32(EAGLE_TXD_STATUS_FW_OWNED); + ds->Format = 0; + ds->pad = 0; + ds->ack_wcb_addr = 0; + ds->TxPriority = 1; + + MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): " + "tx desc Status %x, DataRate %x, TxPriority %x, QosCtrl %x, " + "PktLen %x, SapPktInfo %x, Format %x, Pad %x, ack_wcb_addr %x\n", + ds->Status, ds->DataRate, ds->TxPriority, ds->QosCtrl, ds->PktLen, + ds->SapPktInfo, ds->Format, ds->pad, ds->ack_wcb_addr); + + (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, + ring->cur * sizeof (struct mwl_txdesc), + sizeof (struct mwl_txdesc), + DDI_DMA_SYNC_FORDEV); + + MWL_DBG(MWL_DBG_TX, "mwl: mwl_send(): " + "pktlen = %u, slot = %u, queued = %x\n", + mblen, ring->cur, ring->queued); + + ring->queued++; + ring->cur = (ring->cur + 1) % MWL_TX_RING_COUNT; + + /* + * NB: We don't need to lock against tx done because + * this just prods the firmware to check the transmit + * descriptors. The firmware will also start fetching + * descriptors by itself if it notices new ones are + * present when it goes to deliver a tx done interrupt + * to the host. So if we race with tx done processing + * it's ok. Delivering the kick here rather than in + * mwl_tx_start is an optimization to avoid poking the + * firmware for each packet. + * + * NB: the queue id isn't used so 0 is ok. + */ + mwl_hal_txstart(sc, 0); + + ic->ic_stats.is_tx_frags++; + ic->ic_stats.is_tx_bytes += pktlen; + +fail3: + ieee80211_free_node(ni); +fail2: + freemsg(m); +fail1: + if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA || + err == DDI_SUCCESS) + freemsg(mp); + MWL_TXUNLOCK(sc); + return (err); +} + +/* + * This function is called periodically (every 200ms) during scanning to + * switch from one channel to another. + */ +static void +mwl_next_scan(void *arg) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + + if (ic->ic_state == IEEE80211_S_SCAN) + (void) ieee80211_next_scan(ic); + + sc->sc_scan_id = 0; +} + +/* + * Convert a legacy rate set to a firmware bitmask. + */ +static uint32_t +get_rate_bitmap(const struct ieee80211_rateset *rs) +{ + uint32_t rates; + int i; + + rates = 0; + for (i = 0; i < rs->ir_nrates; i++) + switch (rs->ir_rates[i] & IEEE80211_RATE_VAL) { + case 2: rates |= 0x001; break; + case 4: rates |= 0x002; break; + case 11: rates |= 0x004; break; + case 22: rates |= 0x008; break; + case 44: rates |= 0x010; break; + case 12: rates |= 0x020; break; + case 18: rates |= 0x040; break; + case 24: rates |= 0x080; break; + case 36: rates |= 0x100; break; + case 48: rates |= 0x200; break; + case 72: rates |= 0x400; break; + case 96: rates |= 0x800; break; + case 108: rates |= 0x1000; break; + } + return (rates); +} + +/* + * Craft station database entry for station. + * NB: use host byte order here, the hal handles byte swapping. + */ +static MWL_HAL_PEERINFO * +mkpeerinfo(MWL_HAL_PEERINFO *pi, const struct ieee80211_node *ni) +{ + memset(pi, 0, sizeof (*pi)); + pi->LegacyRateBitMap = get_rate_bitmap(&ni->in_rates); + pi->CapInfo = ni->in_capinfo; + return (pi); +} + +static int +mwl_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct mwl_softc *sc = (struct mwl_softc *)ic; + enum ieee80211_state ostate; + struct ieee80211_channel *ic_chan; + struct ieee80211_node *ni = NULL; + MWL_HAL_PEERINFO pi; + uint32_t chan; + + if (sc->sc_scan_id != 0) { + (void) untimeout(sc->sc_scan_id); + sc->sc_scan_id = 0; + } + + MWL_GLOCK(sc); + + ostate = ic->ic_state; + MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): " + "ostate %x -> nstate %x\n", + ostate, nstate); + + switch (nstate) { + case IEEE80211_S_INIT: + break; + case IEEE80211_S_SCAN: + if (ostate != IEEE80211_S_INIT) { + ic_chan = ic->ic_curchan; + chan = ieee80211_chan2ieee(ic, ic_chan); + if (chan != 0 && chan != IEEE80211_CHAN_ANY) { + sc->sc_cur_chan = + &sc->sc_channels[3 * chan - 2]; + MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): " + "chan num is %u, sc chan is %u\n", + chan, sc->sc_cur_chan->ic_ieee); + (void) mwl_chan_set(sc, sc->sc_cur_chan); + } + } + sc->sc_scan_id = timeout(mwl_next_scan, (void *)sc, + drv_usectohz(250000)); + break; + case IEEE80211_S_AUTH: + ic_chan = ic->ic_curchan; + chan = ieee80211_chan2ieee(ic, ic_chan); + sc->sc_cur_chan = &sc->sc_channels[3 * chan - 2]; + MWL_DBG(MWL_DBG_MSG, "mwl: mwl_newstate(): " + "chan num is %u, sc chan is %u\n", + chan, sc->sc_cur_chan->ic_ieee); + (void) mwl_chan_set(sc, sc->sc_cur_chan); + ni = ic->ic_bss; + (void) mwl_hal_newstation(sc, ic->ic_macaddr, 0, 0, NULL, 0, 0); + mwl_setanywepkey(ic, ni->in_macaddr); + break; + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + ni = ic->ic_bss; + (void) mwl_hal_newstation(sc, + ic->ic_macaddr, 0, 0, mkpeerinfo(&pi, ni), 0, 0); + mwl_setglobalkeys(ic); + (void) mwl_hal_setassocid(sc, + ic->ic_bss->in_bssid, ic->ic_bss->in_associd); + (void) mwl_setrates(ic); + (void) mwl_hal_setrtsthreshold(sc, ic->ic_rtsthreshold); + (void) mwl_hal_setcsmode(sc, CSMODE_AUTO_ENA); + break; + default: + break; + } + + MWL_GUNLOCK(sc); + + return (sc->sc_newstate(ic, nstate, arg)); +} + +/* + * Set the interrupt mask. + */ +static void +mwl_hal_intrset(struct mwl_softc *sc, uint32_t mask) +{ + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, 0); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); + + sc->sc_hal_imask = mask; + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_MASK, mask); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); +} + +/* + * Return the current ISR setting and clear the cause. + */ +static void +mwl_hal_getisr(struct mwl_softc *sc, uint32_t *status) +{ + uint32_t cause; + + cause = mwl_ctl_read4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE); + if (cause == 0xffffffff) { /* card removed */ + cause = 0; + } else if (cause != 0) { + /* clear cause bits */ + mwl_ctl_write4(sc, MACREG_REG_A2H_INTERRUPT_CAUSE, + cause & ~sc->sc_hal_imask); + (void) mwl_ctl_read4(sc, MACREG_REG_INT_CODE); + cause &= sc->sc_hal_imask; + } + *status = cause; +} + +static void +mwl_tx_intr(struct mwl_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mwl_tx_ring *ring; + struct mwl_txdesc *ds; + + uint32_t status; + + MWL_TXLOCK(sc); + + ring = &sc->sc_txring[1]; + + if (!(ring->queued)) { + MWL_TXUNLOCK(sc); + return; + } + + (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, + 0, + ring->txdesc_dma.alength, + DDI_DMA_SYNC_FORCPU); + + for (;;) { + ds = &ring->desc[ring->next]; + + status = LE_32(ds->Status); + + if (status & LE_32(EAGLE_TXD_STATUS_FW_OWNED)) { + break; + } + + if (status == LE_32(EAGLE_TXD_STATUS_IDLE)) { + break; + } + + MWL_DBG(MWL_DBG_TX, "mwl: mwl_tx_intr(): " + "recv tx desc status %x, datarate %x, txpriority %x, " + "QosCtrl %x, pktLen %x, SapPktInfo %x, Format %x, " + "pad %x, ack_wcb_addr %x\n", + ds->Status, ds->DataRate, ds->TxPriority, + ds->QosCtrl, ds->PktLen, ds->SapPktInfo, + ds->Format, ds->pad, ds->ack_wcb_addr); + + /* descriptor is no longer valid */ + ds->Status = LE_32(EAGLE_TXD_STATUS_IDLE); + + (void) ddi_dma_sync(ring->txdesc_dma.dma_hdl, + ring->next * sizeof (struct mwl_txdesc), + sizeof (struct mwl_txdesc), + DDI_DMA_SYNC_FORDEV); + + ring->queued--; + ring->next = (ring->next + 1) % MWL_TX_RING_COUNT; + MWL_DBG(MWL_DBG_TX, "mwl: mwl_tx_intr(): " + " tx done idx=%u, queued= %d\n", + ring->next, ring->queued); + + if (sc->sc_need_sched && + (ring->queued < MWL_TX_RING_COUNT)) { + sc->sc_need_sched = 0; + mac_tx_update(ic->ic_mach); + } + + } + + MWL_TXUNLOCK(sc); +} + +/* + * Convert hardware signal strength to rssi. The value + * provided by the device has the noise floor added in; + * we need to compensate for this but we don't have that + * so we use a fixed value. + * + * The offset of 8 is good for both 2.4 and 5GHz. The LNA + * offset is already set as part of the initial gain. This + * will give at least +/- 3dB for 2.4GHz and +/- 5dB for 5GHz. + */ +static int +cvtrssi(uint8_t ssi) +{ + int rssi = (int)ssi + 8; + /* XXX hack guess until we have a real noise floor */ + rssi = 2 * (87 - rssi); /* NB: .5 dBm units */ + return (rssi < 0 ? 0 : rssi > 127 ? 127 : rssi); +} + +static void +mwl_rx_intr(struct mwl_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct mwl_rx_ring *ring; + struct ieee80211_node *ni; + struct ieee80211_frame *wh; + + struct mwl_rxbuf *bf; + struct mwl_rxdesc *ds; + mblk_t *mp0; + + int ntodo, len, rssi; + uint8_t *data, status; + + MWL_RXLOCK(sc); + + ring = &sc->sc_rxring; + for (ntodo = MWL_RX_RING_COUNT; ntodo > 0; ntodo--) { + bf = &ring->buf[ring->cur]; + ds = bf->bf_desc; + data = bf->bf_mem; + + (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl, + ring->cur * sizeof (struct mwl_rxdesc), + sizeof (struct mwl_rxdesc), + DDI_DMA_SYNC_FORCPU); + + if (ds->RxControl != EAGLE_RXD_CTRL_DMA_OWN) + break; + + status = ds->Status; + if (status & EAGLE_RXD_STATUS_DECRYPT_ERR_MASK) { + MWL_DBG(MWL_DBG_CRYPTO, "mwl: mwl_rx_intr(): " + "rx decrypt error\n"); + sc->sc_rx_err++; + } + + /* + * Sync the data buffer. + */ + len = LE_16(ds->PktLen); + + (void) ddi_dma_sync(bf->rxbuf_dma.dma_hdl, + 0, + bf->rxbuf_dma.alength, + DDI_DMA_SYNC_FORCPU); + + if (len < 32 || len > sc->sc_dmabuf_size) { + MWL_DBG(MWL_DBG_RX, "mwl: mwl_rx_intr(): " + "packet len error %d\n", len); + sc->sc_rx_err++; + goto rxnext; + } + + mp0 = allocb(sc->sc_dmabuf_size, BPRI_MED); + if (mp0 == NULL) { + MWL_DBG(MWL_DBG_RX, "mwl: mwl_rx_intr(): " + "alloc mblk error\n"); + sc->sc_rx_nobuf++; + goto rxnext; + } + bcopy(data+ 2, mp0->b_wptr, 24); + mp0->b_wptr += 24; + bcopy(data + 32, mp0->b_wptr, len - 32); + mp0->b_wptr += (len - 32); + + wh = (struct ieee80211_frame *)mp0->b_rptr; + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_CTL) { + freemsg(mp0); + goto rxnext; + } + + /* + * The f/w strips WEP header but doesn't clear + * the WEP bit; mark the packet with M_WEP so + * net80211 will treat the data as decrypted. + * While here also clear the PWR_MGT bit since + * power save is handled by the firmware and + * passing this up will potentially cause the + * upper layer to put a station in power save + * (except when configured with MWL_HOST_PS_SUPPORT). + */ +#ifdef MWL_HOST_PS_SUPPORT + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; +#else + wh->i_fc[1] &= ~(IEEE80211_FC1_WEP | IEEE80211_FC1_PWR_MGT); +#endif + + /* calculate rssi early so we can re-use for each aggregate */ + rssi = cvtrssi(ds->RSSI); + + ni = ieee80211_find_rxnode(ic, wh); + + /* send the frame to the 802.11 layer */ + (void) ieee80211_input(ic, mp0, ni, rssi, 0); + ieee80211_free_node(ni); +rxnext: + /* + * Setup descriptor. + */ + ds->QosCtrl = 0; + ds->RSSI = 0; + ds->Status = EAGLE_RXD_STATUS_IDLE; + ds->Channel = 0; + ds->PktLen = LE_16(MWL_AGGR_SIZE); + ds->SQ2 = 0; + ds->pPhysBuffData = bf->bf_baddr; + /* NB: don't touch pPhysNext, set once */ + ds->RxControl = EAGLE_RXD_CTRL_DRIVER_OWN; + + (void) ddi_dma_sync(ring->rxdesc_dma.dma_hdl, + ring->cur * sizeof (struct mwl_rxdesc), + sizeof (struct mwl_rxdesc), + DDI_DMA_SYNC_FORDEV); + + /* NB: ignore ENOMEM so we process more descriptors */ + ring->cur = (ring->cur + 1) % MWL_RX_RING_COUNT; + } + + MWL_RXUNLOCK(sc); +} + +/*ARGSUSED*/ +static uint_t +mwl_softintr(caddr_t data, caddr_t unused) +{ + struct mwl_softc *sc = (struct mwl_softc *)data; + + /* + * Check if the soft interrupt is triggered by another + * driver at the same level. + */ + MWL_GLOCK(sc); + if (sc->sc_rx_pend) { + sc->sc_rx_pend = 0; + MWL_GUNLOCK(sc); + mwl_rx_intr(sc); + return (DDI_INTR_CLAIMED); + } + MWL_GUNLOCK(sc); + + return (DDI_INTR_UNCLAIMED); +} + +/*ARGSUSED*/ +static uint_t +mwl_intr(caddr_t arg, caddr_t unused) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + uint32_t status; + + MWL_GLOCK(sc); + + if (!MWL_IS_RUNNING(sc) || MWL_IS_SUSPEND(sc)) { + MWL_GUNLOCK(sc); + return (DDI_INTR_UNCLAIMED); + } + + /* + * Figure out the reason(s) for the interrupt. + */ + mwl_hal_getisr(sc, &status); /* NB: clears ISR too */ + if (status == 0) { + MWL_GUNLOCK(sc); + return (DDI_INTR_UNCLAIMED); + } + + if (status & MACREG_A2HRIC_BIT_RX_RDY) { + sc->sc_rx_pend = 1; + (void) ddi_intr_trigger_softint(sc->sc_softintr_hdl, NULL); + } + if (status & MACREG_A2HRIC_BIT_TX_DONE) { + mwl_tx_intr(sc); + } + if (status & MACREG_A2HRIC_BIT_BA_WATCHDOG) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "ba watchdog\n"); + } + if (status & MACREG_A2HRIC_BIT_OPC_DONE) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "opc done\n"); + } + if (status & MACREG_A2HRIC_BIT_MAC_EVENT) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "mac event\n"); + } + if (status & MACREG_A2HRIC_BIT_ICV_ERROR) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "ICV error\n"); + } + if (status & MACREG_A2HRIC_BIT_QUEUE_EMPTY) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "queue empty\n"); + } + if (status & MACREG_A2HRIC_BIT_QUEUE_FULL) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "queue full\n"); + } + if (status & MACREG_A2HRIC_BIT_RADAR_DETECT) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "radar detect\n"); + } + if (status & MACREG_A2HRIC_BIT_CHAN_SWITCH) { + MWL_DBG(MWL_DBG_INTR, "mwl: mwl_intr(): " + "chan switch\n"); + } + + MWL_GUNLOCK(sc); + + return (DDI_INTR_CLAIMED); +} + +static int +mwl_init(struct mwl_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int err = 0; + + mwl_hal_intrset(sc, 0); + + sc->sc_txantenna = 0; /* h/w default */ + sc->sc_rxantenna = 0; /* h/w default */ + + err = mwl_hal_setantenna(sc, WL_ANTENNATYPE_RX, sc->sc_rxantenna); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: mwl_init(): " + "could not set rx antenna\n"); + goto fail; + } + + err = mwl_hal_setantenna(sc, WL_ANTENNATYPE_TX, sc->sc_txantenna); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set tx antenna\n"); + goto fail; + } + + err = mwl_hal_setradio(sc, 1, WL_AUTO_PREAMBLE); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set radio\n"); + goto fail; + } + + err = mwl_hal_setwmm(sc, (ic->ic_flags & IEEE80211_F_WME) != 0); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set wme\n"); + goto fail; + } + + /* select default channel */ + ic->ic_ibss_chan = &ic->ic_sup_channels[0]; + ic->ic_curchan = ic->ic_ibss_chan; + sc->sc_cur_chan = &sc->sc_channels[1]; + + err = mwl_chan_set(sc, sc->sc_cur_chan); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set wme\n"); + goto fail; + } + + err = mwl_hal_setrateadaptmode(sc, 0); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set rate adapt mode\n"); + goto fail; + } + + err = mwl_hal_setoptimizationlevel(sc, + (ic->ic_flags & IEEE80211_F_BURST) != 0); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set optimization level\n"); + goto fail; + } + + err = mwl_hal_setregioncode(sc, mwl_map2regioncode(&sc->sc_regdomain)); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set regioncode\n"); + goto fail; + } + + err = mwl_startrecv(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set start recv logic\n"); + goto fail; + } + + /* + * Enable interrupts. + */ + sc->sc_imask = MACREG_A2HRIC_BIT_RX_RDY + | MACREG_A2HRIC_BIT_TX_DONE + | MACREG_A2HRIC_BIT_OPC_DONE + | MACREG_A2HRIC_BIT_ICV_ERROR + | MACREG_A2HRIC_BIT_RADAR_DETECT + | MACREG_A2HRIC_BIT_CHAN_SWITCH + | MACREG_A2HRIC_BIT_BA_WATCHDOG + | MACREQ_A2HRIC_BIT_TX_ACK; + + mwl_hal_intrset(sc, sc->sc_imask); + + err = mwl_hal_start(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not get hal start\n"); + goto fail; + } + + err = mwl_hal_setinframode(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: init(): " + "could not set infra mode\n"); + goto fail; + } + +fail: + return (err); +} + +static int +mwl_resume(struct mwl_softc *sc) +{ + int qid, err = 0; + + err = mwl_fwload(sc, NULL); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "failed to load fw\n"); + goto fail; + } + + err = mwl_gethwspecs(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "failed to get hw spec\n"); + goto fail; + } + + err = mwl_alloc_rx_ring(sc, MWL_RX_RING_COUNT); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "could not alloc cmd dma buffer\n"); + goto fail; + } + + for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) { + err = mwl_alloc_tx_ring(sc, + &sc->sc_txring[qid], MWL_TX_RING_COUNT); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "could not alloc tx ring %d\n", qid); + goto fail; + } + } + + err = mwl_setupdma(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "could not setup dma\n"); + goto fail; + } + + err = mwl_setup_txq(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_resume(): " + "could not setup txq\n"); + goto fail; + } + +fail: + return (err); +} + +static void +mwl_stop(struct mwl_softc *sc) +{ + int err; + + /* by pass if it's quiesced */ + if (!MWL_IS_QUIESCE(sc)) + MWL_GLOCK(sc); + + err = mwl_hal_stop(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_HW, "mwl: mwl_stop(): " + "could not stop hw\n"); + } + + /* by pass if it's quiesced */ + if (!MWL_IS_QUIESCE(sc)) + MWL_GUNLOCK(sc); +} + +static int +mwl_m_stat(void *arg, uint_t stat, uint64_t *val) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = NULL; + struct ieee80211_rateset *rs = NULL; + + MWL_GLOCK(sc); + switch (stat) { + case MAC_STAT_IFSPEED: + ni = ic->ic_bss; + rs = &ni->in_rates; + *val = ((ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) ? + (rs->ir_rates[ni->in_txrate] & IEEE80211_RATE_VAL) + : ic->ic_fixed_rate) / 2 * 1000000; + break; + case MAC_STAT_NOXMTBUF: + *val = sc->sc_tx_nobuf; + break; + case MAC_STAT_NORCVBUF: + *val = sc->sc_rx_nobuf; + break; + case MAC_STAT_IERRORS: + *val = sc->sc_rx_err; + break; + case MAC_STAT_RBYTES: + *val = ic->ic_stats.is_rx_bytes; + break; + case MAC_STAT_IPACKETS: + *val = ic->ic_stats.is_rx_frags; + break; + case MAC_STAT_OBYTES: + *val = ic->ic_stats.is_tx_bytes; + break; + case MAC_STAT_OPACKETS: + *val = ic->ic_stats.is_tx_frags; + break; + case MAC_STAT_OERRORS: + case WIFI_STAT_TX_FAILED: + *val = sc->sc_tx_err; + break; + case WIFI_STAT_TX_RETRANS: + *val = sc->sc_tx_retries; + break; + case WIFI_STAT_FCS_ERRORS: + case WIFI_STAT_WEP_ERRORS: + case WIFI_STAT_TX_FRAGS: + case WIFI_STAT_MCAST_TX: + case WIFI_STAT_RTS_SUCCESS: + case WIFI_STAT_RTS_FAILURE: + case WIFI_STAT_ACK_FAILURE: + case WIFI_STAT_RX_FRAGS: + case WIFI_STAT_MCAST_RX: + case WIFI_STAT_RX_DUPS: + MWL_GUNLOCK(sc); + return (ieee80211_stat(ic, stat, val)); + default: + MWL_GUNLOCK(sc); + return (ENOTSUP); + } + + MWL_GUNLOCK(sc); + return (0); +} + +static int +mwl_m_start(void *arg) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + int err; + + err = mwl_init(sc); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_HW, "mwl: mwl_m_start():" + "Hardware initialization failed\n"); + goto fail1; + } + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + MWL_GLOCK(sc); + sc->sc_flags |= MWL_F_RUNNING; + MWL_GUNLOCK(sc); + + return (0); +fail1: + mwl_stop(sc); + return (err); +} + +static void +mwl_m_stop(void *arg) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + + mwl_stop(sc); + + ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); + + MWL_GLOCK(sc); + sc->sc_flags &= ~MWL_F_RUNNING; + MWL_GUNLOCK(sc); +} + +/*ARGSUSED*/ +static int +mwl_m_promisc(void *arg, boolean_t on) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + int err; + + err = mwl_hal_setpromisc(sc, on); + + return (err); +} + +/*ARGSUSED*/ +static int +mwl_m_multicst(void *arg, boolean_t add, const uint8_t *mca) +{ + return (ENOTSUP); +} + +/*ARGSUSED*/ +static int +mwl_m_unicst(void *arg, const uint8_t *macaddr) +{ + return (ENOTSUP); +} + +static mblk_t * +mwl_m_tx(void *arg, mblk_t *mp) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + mblk_t *next; + + if (MWL_IS_SUSPEND(sc)) { + freemsgchain(mp); + return (NULL); + } + + /* + * No data frames go out unless we're associated; this + * should not happen as the 802.11 layer does not enable + * the xmit queue until we enter the RUN state. + */ + if (ic->ic_state != IEEE80211_S_RUN) { + MWL_DBG(MWL_DBG_TX, "mwl: mwl_m_tx(): " + "discard, state %u\n", ic->ic_state); + freemsgchain(mp); + return (NULL); + } + + while (mp != NULL) { + next = mp->b_next; + mp->b_next = NULL; + if (mwl_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != + DDI_SUCCESS) { + mp->b_next = next; + break; + } + mp = next; + } + return (mp); +} + +static void +mwl_m_ioctl(void* arg, queue_t *wq, mblk_t *mp) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + struct ieee80211com *ic = &sc->sc_ic; + int err; + + err = ieee80211_ioctl(ic, wq, mp); + if (err == ENETRESET) { + if (ic->ic_des_esslen) { + if (MWL_IS_RUNNING(sc)) { + (void) mwl_init(sc); + (void) ieee80211_new_state(ic, + IEEE80211_S_SCAN, -1); + } + } + } +} + +/* + * Call back function for get/set proporty + */ +static int +mwl_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, + uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + int err = 0; + + err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num, + pr_flags, wldp_length, wldp_buf, perm); + + return (err); +} + +static int +mwl_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, + uint_t wldp_length, const void *wldp_buf) +{ + struct mwl_softc *sc = (struct mwl_softc *)arg; + ieee80211com_t *ic = &sc->sc_ic; + int err; + + err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, + wldp_buf); + if (err == ENETRESET) { + if (ic->ic_des_esslen) { + if (MWL_IS_RUNNING(sc)) { + (void) mwl_init(sc); + (void) ieee80211_new_state(ic, + IEEE80211_S_SCAN, -1); + } + } + err = 0; + } + return (err); +} + +static int +mwl_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd) +{ + struct mwl_softc *sc; + struct ieee80211com *ic; + int i, err, qid, instance; + int intr_type, intr_count, intr_actual; + char strbuf[32]; + uint8_t csz; + uint16_t vendor_id, device_id, command; + + wifi_data_t wd = { 0 }; + mac_register_t *macp; + + switch (cmd) { + case DDI_ATTACH: + break; + case DDI_RESUME: + sc = ddi_get_soft_state(mwl_soft_state_p, + ddi_get_instance(devinfo)); + ASSERT(sc != NULL); + MWL_GLOCK(sc); + sc->sc_flags &= ~MWL_F_SUSPEND; + MWL_GUNLOCK(sc); + if (mwl_resume(sc) != 0) { + MWL_DBG(MWL_DBG_SR, "mwl: mwl_attach(): " + "failed to resume\n"); + return (DDI_FAILURE); + } + if (MWL_IS_RUNNING(sc)) { + (void) mwl_init(sc); + ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1); + } + MWL_DBG(MWL_DBG_SR, "mwl: mwl_attach(): " + "resume now\n"); + return (DDI_SUCCESS); + default: + return (DDI_FAILURE); + } + + instance = ddi_get_instance(devinfo); + if (ddi_soft_state_zalloc(mwl_soft_state_p, + ddi_get_instance(devinfo)) != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "Unable to alloc soft state\n"); + return (DDI_FAILURE); + } + + sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(devinfo)); + ic = &sc->sc_ic; + sc->sc_dev = devinfo; + + /* PCI configuration space */ + err = ddi_regs_map_setup(devinfo, 0, (caddr_t *)&sc->sc_cfg_base, 0, 0, + &mwl_reg_accattr, &sc->sc_cfg_handle); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_regs_map_setup() failed"); + goto attach_fail0; + } + csz = ddi_get8(sc->sc_cfg_handle, + (uint8_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ)); + if (!csz) + csz = 16; + sc->sc_cachelsz = csz << 2; + sc->sc_dmabuf_size = roundup(IEEE80211_MAX_LEN, sc->sc_cachelsz); + vendor_id = ddi_get16(sc->sc_cfg_handle, + (uint16_t *)(sc->sc_cfg_base + PCI_CONF_VENID)); + device_id = ddi_get16(sc->sc_cfg_handle, + (uint16_t *)(sc->sc_cfg_base + PCI_CONF_DEVID)); + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "vendor 0x%x, device id 0x%x, cache size %d\n", + vendor_id, device_id, csz); + + /* + * Enable response to memory space accesses, + * and enabe bus master. + */ + command = PCI_COMM_MAE | PCI_COMM_ME; + ddi_put16(sc->sc_cfg_handle, + (uint16_t *)((uintptr_t)(sc->sc_cfg_base) + PCI_CONF_COMM), + command); + ddi_put8(sc->sc_cfg_handle, + (uint8_t *)(sc->sc_cfg_base + PCI_CONF_LATENCY_TIMER), 0xa8); + ddi_put8(sc->sc_cfg_handle, + (uint8_t *)(sc->sc_cfg_base + PCI_CONF_ILINE), 0x10); + + /* BAR0 */ + err = ddi_regs_map_setup(devinfo, 1, + &sc->sc_mem_base, 0, 0, &mwl_reg_accattr, &sc->sc_mem_handle); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "i/o space failed"); + goto attach_fail1; + } + + /* BAR1 */ + err = ddi_regs_map_setup(devinfo, 2, + &sc->sc_io_base, 0, 0, &mwl_reg_accattr, &sc->sc_io_handle); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "memory space failed"); + goto attach_fail2; + } + + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "PCI configuration is done successfully\n"); + + /* + * Alloc cmd DMA buffer for firmware download + */ + err = mwl_alloc_cmdbuf(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "could not alloc cmd dma buffer\n"); + goto attach_fail3; + } + + sc->sc_imask = 0; + sc->sc_hw_flags = 0; + sc->sc_flags = 0; + + /* + * Some cards have SDRAM. When loading firmware we need + * to reset the SDRAM controller prior to doing this. + * When the SDRAMSIZE is non-zero we do that work in + * mwl_hal_fwload. + */ + switch (device_id) { + case 0x2a02: /* CB82 */ + case 0x2a03: /* CB85 */ + case 0x2a08: /* MC85_B1 */ + case 0x2a0b: /* CB85AP */ + case 0x2a24: + sc->sc_SDRAMSIZE_Addr = 0x40fe70b7; /* 8M SDRAM */ + break; + case 0x2a04: /* MC85 */ + sc->sc_SDRAMSIZE_Addr = 0x40fc70b7; /* 16M SDRAM */ + break; + default: + break; + } + + err = mwl_fwload(sc, NULL); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "firmware download failed\n"); + goto attach_fail4; + } + + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "firmware download successfully\n"); + + err = mwl_gethwspecs(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "failed to get hw spec\n"); + goto attach_fail4; + } + + err = mwl_getchannels(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "failed to get channels\n"); + goto attach_fail4; + } + + /* + * Alloc rx DMA buffer + */ + err = mwl_alloc_rx_ring(sc, MWL_RX_RING_COUNT); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "could not alloc cmd dma buffer\n"); + goto attach_fail5; + } + + /* + * Alloc rx DMA buffer + */ + for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) { + err = mwl_alloc_tx_ring(sc, + &sc->sc_txring[qid], MWL_TX_RING_COUNT); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "could not alloc tx ring %d\n", qid); + goto attach_fail6; + } + } + + err = mwl_setupdma(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "could not setup dma\n"); + goto attach_fail6; + } + + err = mwl_setup_txq(sc); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "could not setup txq\n"); + goto attach_fail6; + } + + IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_hwspecs.macAddr); + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "mwl MAC:%2x:%2x:%2x:%2x:%2x:%2x\n", + ic->ic_macaddr[0], + ic->ic_macaddr[1], + ic->ic_macaddr[2], + ic->ic_macaddr[3], + ic->ic_macaddr[4], + ic->ic_macaddr[5]); + + err = mwl_hal_setmac_locked(sc, ic->ic_macaddr); + if (err != 0) { /* NB: mwl_setupdma prints msg */ + MWL_DBG(MWL_DBG_ATTACH, "mwl: attach(): " + "could not set mac\n"); + goto attach_fail6; + } + + mutex_init(&sc->sc_glock, NULL, MUTEX_DRIVER, NULL); + mutex_init(&sc->sc_rxlock, NULL, MUTEX_DRIVER, NULL); + mutex_init(&sc->sc_txlock, NULL, MUTEX_DRIVER, NULL); + + + /* set supported rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = mwl_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = mwl_rateset_11g; + + /* set supported .11b and .11g channels (1 through 14) */ + for (i = 1; i <= 14; i++) { + ic->ic_sup_channels[i].ich_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_sup_channels[i].ich_flags = + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT; /* short slot time supported */ + + /* WPA/WPA2 support */ + ic->ic_caps |= IEEE80211_C_WPA; /* Support WPA/WPA2 */ + + /* Enable hardware encryption */ + ic->ic_caps |= IEEE80211_C_WEP | IEEE80211_C_TKIP | IEEE80211_C_AES_CCM; + + ic->ic_xmit = mwl_send; + + ieee80211_attach(ic); + + /* register WPA door */ + ieee80211_register_door(ic, ddi_driver_name(devinfo), + ddi_get_instance(devinfo)); + + /* override state transition machine */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = mwl_newstate; + ic->ic_node_alloc = mwl_node_alloc; + ic->ic_node_free = mwl_node_free; + ic->ic_crypto.cs_max_keyix = 0; + ic->ic_crypto.cs_key_alloc = mwl_key_alloc; + ic->ic_crypto.cs_key_delete = mwl_key_delete; + ic->ic_crypto.cs_key_set = mwl_key_set; + + ieee80211_media_init(ic); + + ic->ic_def_txkey = 0; + + err = mwl_hal_newstation(sc, ic->ic_macaddr, 0, 0, NULL, 0, 0); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: attach(): " + "could not create new station\n"); + goto attach_fail7; + } + + IEEE80211_ADDR_COPY(ic->ic_bss->in_bssid, ic->ic_macaddr); + // mwl_setglobalkeys(ic); + + err = ddi_intr_get_supported_types(devinfo, &intr_type); + if ((err != DDI_SUCCESS) || (!(intr_type & DDI_INTR_TYPE_FIXED))) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "fixed type interrupt is not supported\n"); + goto attach_fail7; + } + + err = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_FIXED, &intr_count); + if ((err != DDI_SUCCESS) || (intr_count != 1)) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "no fixed interrupts\n"); + goto attach_fail7; + } + + sc->sc_intr_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); + + err = ddi_intr_alloc(devinfo, sc->sc_intr_htable, + DDI_INTR_TYPE_FIXED, 0, intr_count, &intr_actual, 0); + if ((err != DDI_SUCCESS) || (intr_actual != 1)) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_intr_alloc() failed 0x%x\n", err); + goto attach_fail8; + } + + err = ddi_intr_get_pri(sc->sc_intr_htable[0], &sc->sc_intr_pri); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_intr_get_pri() failed 0x%x\n", err); + goto attach_fail9; + } + + err = ddi_intr_add_softint(devinfo, &sc->sc_softintr_hdl, + DDI_INTR_SOFTPRI_MAX, mwl_softintr, (caddr_t)sc); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_add_softintr() failed"); + goto attach_fail9; + } + + err = ddi_intr_add_handler(sc->sc_intr_htable[0], mwl_intr, + (caddr_t)sc, NULL); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_intr_addr_handle() failed\n"); + goto attach_fail10; + } + + err = ddi_intr_enable(sc->sc_intr_htable[0]); + if (err != DDI_SUCCESS) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "ddi_intr_enable() failed\n"); + goto attach_fail11; + } + + /* + * Provide initial settings for the WiFi plugin; whenever this + * information changes, we need to call mac_plugindata_update() + */ + wd.wd_opmode = ic->ic_opmode; + wd.wd_secalloc = WIFI_SEC_NONE; + IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); + + if ((macp = mac_alloc(MAC_VERSION)) == NULL) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "MAC version mismatch\n"); + goto attach_fail12; + } + + macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; + macp->m_driver = sc; + macp->m_dip = devinfo; + macp->m_src_addr = ic->ic_macaddr; + macp->m_callbacks = &mwl_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, &ic->ic_mach); + mac_free(macp); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "mac_register err %x\n", err); + goto attach_fail12; + } + + /* + * Create minor node of type DDI_NT_NET_WIFI + */ + (void) snprintf(strbuf, sizeof (strbuf), "%s%d", + "mwl", instance); + err = ddi_create_minor_node(devinfo, strbuf, S_IFCHR, + instance + 1, DDI_NT_NET_WIFI, 0); + if (err != 0) { + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "create minor node error\n"); + goto attach_fail13; + } + + /* + * Notify link is down now + */ + mac_link_update(ic->ic_mach, LINK_STATE_DOWN); + + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_attach(): " + "driver attach successfully\n"); + return (DDI_SUCCESS); + +attach_fail13: + (void) mac_disable(ic->ic_mach); + (void) mac_unregister(ic->ic_mach); +attach_fail12: + (void) ddi_intr_disable(sc->sc_intr_htable[0]); +attach_fail11: + (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); +attach_fail10: + (void) ddi_intr_remove_softint(sc->sc_softintr_hdl); + sc->sc_softintr_hdl = NULL; +attach_fail9: + (void) ddi_intr_free(sc->sc_intr_htable[0]); +attach_fail8: + kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); +attach_fail7: + mutex_destroy(&sc->sc_txlock); + mutex_destroy(&sc->sc_rxlock); + mutex_destroy(&sc->sc_glock); +attach_fail6: + while (--qid >= 0) + mwl_free_tx_ring(sc, &sc->sc_txring[qid]); +attach_fail5: + mwl_free_rx_ring(sc); +attach_fail4: + mwl_free_cmdbuf(sc); +attach_fail3: + ddi_regs_map_free(&sc->sc_mem_handle); +attach_fail2: + ddi_regs_map_free(&sc->sc_io_handle); +attach_fail1: + ddi_regs_map_free(&sc->sc_cfg_handle); +attach_fail0: + ddi_soft_state_free(mwl_soft_state_p, ddi_get_instance(devinfo)); + return (DDI_FAILURE); +} + +static int32_t +mwl_detach(dev_info_t *devinfo, ddi_detach_cmd_t cmd) +{ + struct mwl_softc *sc; + int qid; + + sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(devinfo)); + ASSERT(sc != NULL); + + switch (cmd) { + case DDI_DETACH: + break; + case DDI_SUSPEND: + if (MWL_IS_RUNNING(sc)) + mwl_stop(sc); + for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) + mwl_free_tx_ring(sc, &sc->sc_txring[qid]); + mwl_free_rx_ring(sc); + MWL_GLOCK(sc); + sc->sc_flags |= MWL_F_SUSPEND; + MWL_GUNLOCK(sc); + MWL_DBG(MWL_DBG_SR, "mwl: mwl_detach(): " + "suspend now\n"); + return (DDI_SUCCESS); + default: + return (DDI_FAILURE); + } + + if (mac_disable(sc->sc_ic.ic_mach) != 0) + return (DDI_FAILURE); + + /* + * Unregister from the MAC layer subsystem + */ + (void) mac_unregister(sc->sc_ic.ic_mach); + + (void) ddi_intr_remove_softint(sc->sc_softintr_hdl); + sc->sc_softintr_hdl = NULL; + (void) ddi_intr_disable(sc->sc_intr_htable[0]); + (void) ddi_intr_remove_handler(sc->sc_intr_htable[0]); + (void) ddi_intr_free(sc->sc_intr_htable[0]); + kmem_free(sc->sc_intr_htable, sizeof (ddi_intr_handle_t)); + + /* + * detach ieee80211 layer + */ + ieee80211_detach(&sc->sc_ic); + + + for (qid = 0; qid < MWL_NUM_TX_QUEUES; qid++) + mwl_free_tx_ring(sc, &sc->sc_txring[qid]); + mwl_free_rx_ring(sc); + mwl_free_cmdbuf(sc); + + mutex_destroy(&sc->sc_txlock); + mutex_destroy(&sc->sc_rxlock); + mutex_destroy(&sc->sc_glock); + + ddi_regs_map_free(&sc->sc_mem_handle); + ddi_regs_map_free(&sc->sc_io_handle); + ddi_regs_map_free(&sc->sc_cfg_handle); + + ddi_remove_minor_node(devinfo, NULL); + ddi_soft_state_free(mwl_soft_state_p, ddi_get_instance(devinfo)); + + MWL_DBG(MWL_DBG_ATTACH, "mwl: mwl_detach(): " + "detach successfully\n"); + return (DDI_SUCCESS); +} + +/* + * quiesce(9E) entry point. + * + * This function is called when the system is single-threaded at high + * PIL with preemption disabled. Therefore, this function must not be + * blocked. + * + * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. + * DDI_FAILURE indicates an error condition and should almost never happen. + */ +int +mwl_quiesce(dev_info_t *dip) +{ + struct mwl_softc *sc; + + sc = ddi_get_soft_state(mwl_soft_state_p, ddi_get_instance(dip)); + if (sc == NULL) + return (DDI_FAILURE); + +#ifdef DEBUG + mwl_dbg_flags = 0; +#endif + + /* + * No more blocking is allowed while we are in quiesce(9E) entry point + */ + sc->sc_flags |= MWL_F_QUIESCE; + + /* + * Disable all interrupts + */ + mwl_stop(sc); + return (DDI_SUCCESS); +} + +int +_init(void) +{ + int status; + + status = ddi_soft_state_init(&mwl_soft_state_p, + sizeof (struct mwl_softc), 1); + if (status != 0) + return (status); + + mac_init_ops(&mwl_dev_ops, "mwl"); + status = mod_install(&modlinkage); + if (status != 0) { + mac_fini_ops(&mwl_dev_ops); + ddi_soft_state_fini(&mwl_soft_state_p); + } + return (status); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +} + +int +_fini(void) +{ + int status; + + status = mod_remove(&modlinkage); + if (status == 0) { + mac_fini_ops(&mwl_dev_ops); + ddi_soft_state_fini(&mwl_soft_state_p); + } + return (status); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl_fw/LICENSE Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,44 @@ +# FIRMWARE LICENSE TERMS +# +# +# Copyright (c) Marvell International Ltd. +# +# All rights reserved. +# +# Redistribution. Redistribution and use in binary form, without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions must reproduce the above copyright notice and the +# following disclaimer in the documentation and/or other materials +# provided with the distribution. +# +# * Neither the name of Marvell International Ltd. nor the names of its +# suppliers may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# * No reverse engineering, decompilation, or disassembly of this software +# is permitted. +# +# Limited patent license. Marvell International Ltd. grants a world-wide, +# royalty-free, non-exclusive license under patents it now or hereafter +# owns or controls to make, have made, use, import, offer to sell and sell +# ("Utilize") this software, but solely to the extent that any such patent +# is necessary to Utilize the software alone, or in combination with an +# operating system licensed under an approved Open Source license as +# listed by the Open Source Initiative at http://opensource.org/licenses. +# The patent license shall not apply to any other combinations which +# include this software. No hardware per se is licensed hereunder. +# +# DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +# BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIPOSSIBILITY OF SUCH DAMAGE. +# +# +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl_fw/LICENSE.descrip Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,2 @@ +Marvell 88W8363 Wireless LAN controller Boot FIRMWARE +Marvell 88W8363 Wireless LAN controller FIRMWARE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl_fw/mwlfw_mode.c Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,67 @@ +/* + * 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 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * misc module wrapper for a firmware module for mwl driver + * User must use elfwrap(1) to convert raw firmware data file to + * ELF object file. Then use ld(1) to link the ELF object file and + * this module to produce a kernel loadable module. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/errno.h> +#include <sys/modctl.h> + + +extern struct mod_ops mod_miscops; +static struct modlmisc modlmisc = { + &mod_miscops, + "mwl firmware wrapper module 1.1" +}; +static struct modlinkage modlinkage = { + MODREV_1, + &modlmisc, + 0 +}; + +int +_init(void) +{ + return (mod_install(&modlinkage)); +} + +int +_fini(void) +{ + return (mod_remove(&modlinkage)); +} + +int +_info(struct modinfo *modinfop) +{ + return (mod_info(&modlinkage, modinfop)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl_reg.h Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,866 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2009 Marvell Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Definitions for the Marvell Wireless LAN controller Hardware Access Layer. + */ + +#ifndef _MWL_REG_H +#define _MWL_REG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MWL_MBSS_SUPPORT /* enable multi-bss support */ + +/* + * Host/Firmware Interface definitions. + */ + +/* + * Define total number of TX queues in the shared memory. + * This count includes the EDCA queues, Block Ack queues, and HCCA queues + * In addition to this, there could be a management packet queue some + * time in the future + */ +#define NUM_EDCA_QUEUES 4 +#define NUM_HCCA_QUEUES 0 +#define NUM_BA_QUEUES 0 +#define NUM_MGMT_QUEUES 0 +#define NUM_ACK_EVENT_QUEUE 1 +#define TOTAL_TX_QUEUES \ + (NUM_EDCA_QUEUES + \ + NUM_HCCA_QUEUES + \ + NUM_BA_QUEUES + \ + NUM_MGMT_QUEUES + \ + NUM_ACK_EVENT_QUEUE) +#define MAX_TXWCB_QUEUES TOTAL_TX_QUEUES - NUM_ACK_EVENT_QUEUE +#define MAX_RXWCB_QUEUES 1 + +/* + * Firmware download support. + */ +#define FW_DOWNLOAD_BLOCK_SIZE 256 +#define FW_CHECK_USECS (5*1000) /* 5ms */ +#define FW_MAX_NUM_CHECKS 200 + +#define MWL_ANT_INFO_SUPPORT /* per-antenna data in rx descriptor */ + +#define MACREG_REG_TSF_LOW 0xa600 /* TSF lo */ +#define MACREG_REG_TSF_HIGH 0xa604 /* TSF hi */ +#define MACREG_REG_CHIP_REV 0xa814 /* chip rev */ + +/* + * Map to 0x80000000 (Bus control) on BAR0 + */ +/* From host to ARM */ +#define MACREG_REG_H2A_INTERRUPT_EVENTS 0x00000C18 +#define MACREG_REG_H2A_INTERRUPT_CAUSE 0x00000C1C +#define MACREG_REG_H2A_INTERRUPT_MASK 0x00000C20 +#define MACREG_REG_H2A_INTERRUPT_CLEAR_SEL 0x00000C24 +#define MACREG_REG_H2A_INTERRUPT_STATUS_MASK 0x00000C28 +/* From ARM to host */ +#define MACREG_REG_A2H_INTERRUPT_EVENTS 0x00000C2C +#define MACREG_REG_A2H_INTERRUPT_CAUSE 0x00000C30 +#define MACREG_REG_A2H_INTERRUPT_MASK 0x00000C34 +#define MACREG_REG_A2H_INTERRUPT_CLEAR_SEL 0x00000C38 +#define MACREG_REG_A2H_INTERRUPT_STATUS_MASK 0x00000C3C + + +/* Map to 0x80000000 on BAR1 */ +#define MACREG_REG_GEN_PTR 0x00000C10 +#define MACREG_REG_INT_CODE 0x00000C14 +#define MACREG_REG_SCRATCH 0x00000C40 +#define MACREG_REG_FW_PRESENT 0x0000BFFC + +#define MACREG_REG_PROMISCUOUS 0xA300 +/* Bit definitio for MACREG_REG_A2H_INTERRUPT_CAUSE (A2HRIC) */ +#define MACREG_A2HRIC_BIT_TX_DONE 0x00000001 /* bit 0 */ +#define MACREG_A2HRIC_BIT_RX_RDY 0x00000002 /* bit 1 */ +#define MACREG_A2HRIC_BIT_OPC_DONE 0x00000004 /* bit 2 */ +#define MACREG_A2HRIC_BIT_MAC_EVENT 0x00000008 /* bit 3 */ +#define MACREG_A2HRIC_BIT_RX_PROBLEM 0x00000010 /* bit 4 */ + +#define MACREG_A2HRIC_BIT_RADIO_OFF 0x00000020 /* bit 5 */ +#define MACREG_A2HRIC_BIT_RADIO_ON 0x00000040 /* bit 6 */ + +#define MACREG_A2HRIC_BIT_RADAR_DETECT 0x00000080 /* bit 7 */ + +#define MACREG_A2HRIC_BIT_ICV_ERROR 0x00000100 /* bit 8 */ +#define MACREG_A2HRIC_BIT_MIC_ERROR 0x00000200 /* bit 9 */ +#define MACREG_A2HRIC_BIT_QUEUE_EMPTY 0x00004000 +#define MACREG_A2HRIC_BIT_QUEUE_FULL 0x00000800 +#define MACREG_A2HRIC_BIT_CHAN_SWITCH 0x00001000 +#define MACREG_A2HRIC_BIT_TX_WATCHDOG 0x00002000 +#define MACREG_A2HRIC_BIT_BA_WATCHDOG 0x00000400 +#define MACREQ_A2HRIC_BIT_TX_ACK 0x00008000 +#define ISR_SRC_BITS ((MACREG_A2HRIC_BIT_RX_RDY) | \ + (MACREG_A2HRIC_BIT_TX_DONE) | \ + (MACREG_A2HRIC_BIT_OPC_DONE) | \ + (MACREG_A2HRIC_BIT_MAC_EVENT) | \ + (MACREG_A2HRIC_BIT_MIC_ERROR) | \ + (MACREG_A2HRIC_BIT_ICV_ERROR) | \ + (MACREG_A2HRIC_BIT_RADAR_DETECT)| \ + (MACREG_A2HRIC_BIT_CHAN_SWITCH) | \ + (MACREG_A2HRIC_BIT_TX_WATCHDOG) | \ + (MACREG_A2HRIC_BIT_QUEUE_EMPTY) | \ + (MACREG_A2HRIC_BIT_BA_WATCHDOG) | \ + (MACREQ_A2HRIC_BIT_TX_ACK)) + +#define MACREG_A2HRIC_BIT_MASK ISR_SRC_BITS + +/* Bit definitio for MACREG_REG_H2A_INTERRUPT_CAUSE (H2ARIC) */ +#define MACREG_H2ARIC_BIT_PPA_READY 0x00000001 /* bit 0 */ +#define MACREG_H2ARIC_BIT_DOOR_BELL 0x00000002 /* bit 1 */ +#define ISR_RESET (1<<15) + +/* INT code register event definition */ +#define MACREG_INT_CODE_CMD_FINISHED 0x00000005 + +/* + * Define OpMode for SoftAP/Station mode + */ + +/* + * The following mode signature has to be written to PCI scratch register#0 + * right after successfully downloading the last block of firmware and + * before waiting for firmware ready signature + */ +#define HostCmd_STA_MODE 0x5A +#define HostCmd_SOFTAP_MODE 0xA5 + +#define HostCmd_STA_FWRDY_SIGNATURE 0xF0F1F2F4 +#define HostCmd_SOFTAP_FWRDY_SIGNATURE 0xF1F2F4A5 + +#define HostCmd_CMD_CODE_DNLD 0x0001 +#define HostCmd_CMD_GET_HW_SPEC 0x0003 +#define HostCmd_CMD_SET_HW_SPEC 0x0004 +#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 +#define HostCmd_CMD_802_11_GET_STAT 0x0014 +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +#define HostCmd_CMD_RF_REG_ACCESS 0x001b +#define HostCmd_CMD_802_11_RADIO_CONTROL 0x001c +#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e +#define HostCmd_CMD_802_11_RF_ANTENNA 0x0020 +#define HostCmd_CMD_SET_BEACON 0x0100 +#define HostCmd_CMD_SET_AID 0x010d +#define HostCmd_CMD_SET_RF_CHANNEL 0x010a +#define HostCmd_CMD_SET_INFRA_MODE 0x010e +#define HostCmd_CMD_SET_G_PROTECT_FLAG 0x010f +#define HostCmd_CMD_802_11_RTS_THSD 0x0113 +#define HostCmd_CMD_802_11_SET_SLOT 0x0114 + +#define HostCmd_CMD_802_11H_DETECT_RADAR 0x0120 +#define HostCmd_CMD_SET_WMM_MODE 0x0123 +#define HostCmd_CMD_HT_GUARD_INTERVAL 0x0124 +#define HostCmd_CMD_SET_FIXED_RATE 0x0126 +#define HostCmd_CMD_SET_LINKADAPT_CS_MODE 0x0129 +#define HostCmd_CMD_SET_MAC_ADDR 0x0202 +#define HostCmd_CMD_SET_RATE_ADAPT_MODE 0x0203 +#define HostCmd_CMD_GET_WATCHDOG_BITMAP 0x0205 + +/* SoftAP command code */ +#define HostCmd_CMD_BSS_START 0x1100 +#define HostCmd_CMD_SET_NEW_STN 0x1111 +#define HostCmd_CMD_SET_KEEP_ALIVE 0x1112 +#define HostCmd_CMD_SET_APMODE 0x1114 +#define HostCmd_CMD_SET_SWITCH_CHANNEL 0x1121 + +/* + * @HWENCR@ + * Command to update firmware encryption keys. + */ +#define HostCmd_CMD_UPDATE_ENCRYPTION 0x1122 +/* + * @11E-BA@ + * Command to create/destroy block ACK + */ +#define HostCmd_CMD_BASTREAM 0x1125 +#define HostCmd_CMD_SET_RIFS 0x1126 +#define HostCmd_CMD_SET_N_PROTECT_FLAG 0x1131 +#define HostCmd_CMD_SET_N_PROTECT_OPMODE 0x1132 +#define HostCmd_CMD_SET_OPTIMIZATION_LEVEL 0x1133 +#define HostCmd_CMD_GET_CALTABLE 0x1134 +#define HostCmd_CMD_SET_MIMOPSHT 0x1135 +#define HostCmd_CMD_GET_BEACON 0x1138 +#define HostCmd_CMD_SET_REGION_CODE 0x1139 +#define HostCmd_CMD_SET_POWERSAVESTATION 0x1140 +#define HostCmd_CMD_SET_TIM 0x1141 +#define HostCmd_CMD_GET_TIM 0x1142 +#define HostCmd_CMD_GET_SEQNO 0x1143 +#define HostCmd_CMD_DWDS_ENABLE 0x1144 +#define HostCmd_CMD_AMPDU_RETRY_RATEDROP_MODE 0x1145 +#define HostCmd_CMD_CFEND_ENABLE 0x1146 + +/* + * Define general result code for each command + */ +/* RESULT OK */ +#define HostCmd_RESULT_OK 0x0000 +/* Genenral error */ +#define HostCmd_RESULT_ERROR 0x0001 +/* Command is not valid */ +#define HostCmd_RESULT_NOT_SUPPORT 0x0002 +/* Command is pending (will be processed) */ +#define HostCmd_RESULT_PENDING 0x0003 +/* System is busy (command ignored) */ +#define HostCmd_RESULT_BUSY 0x0004 +/* Data buffer is not big enough */ +#define HostCmd_RESULT_PARTIAL_DATA 0x0005 + +#define HostCmd_CMD_SET_EDCA_PARAMS 0x0115 + +/* + * Definition of action or option for each command + */ + +/* + * Define general purpose action + */ +#define HostCmd_ACT_GEN_READ 0x0000 +#define HostCmd_ACT_GEN_WRITE 0x0001 +#define HostCmd_ACT_GEN_GET 0x0000 +#define HostCmd_ACT_GEN_SET 0x0001 +#define HostCmd_ACT_GEN_OFF 0x0000 +#define HostCmd_ACT_GEN_ON 0x0001 + +#define HostCmd_ACT_DIFF_CHANNEL 0x0002 +#define HostCmd_ACT_GEN_SET_LIST 0x0002 + +/* Define action or option for HostCmd_FW_USE_FIXED_RATE */ +#define HostCmd_ACT_USE_FIXED_RATE 0x0001 +#define HostCmd_ACT_NOT_USE_FIXED_RATE 0x0002 + +/* Define action or option for HostCmd_CMD_802_11_SET_WEP */ +#define HostCmd_ACT_ADD 0x0002 +#define HostCmd_ACT_REMOVE 0x0004 +#define HostCmd_ACT_USE_DEFAULT 0x0008 + +/* + * PUBLIC DEFINITIONS + */ +#define RATE_INDEX_MAX_ARRAY 14 +#define WOW_MAX_STATION 32 + + +#pragma pack(1) + +struct mwl_ant_info { + uint8_t rssi_a; /* RSSI for antenna A */ + uint8_t rssi_b; /* RSSI for antenna B */ + uint8_t rssi_c; /* RSSI for antenna C */ + uint8_t rsvd1; /* Reserved */ + uint8_t nf_a; /* Noise floor for antenna A */ + uint8_t nf_b; /* Noise floor for antenna B */ + uint8_t nf_c; /* Noise floor for antenna C */ + uint8_t rsvd2; /* Reserved */ + uint8_t nf; /* Noise floor */ + uint8_t rsvd3[3]; /* Reserved - To make word aligned */ +}; + +/* + * Hardware tx/rx descriptors. + * + * NB: tx descriptor size must match f/w expected size + * because f/w prefetch's the next descriptor linearly + * and doesn't chase the next pointer. + */ +struct mwl_txdesc { + uint32_t Status; +#define EAGLE_TXD_STATUS_IDLE 0x00000000 +#define EAGLE_TXD_STATUS_USED 0x00000001 +#define EAGLE_TXD_STATUS_OK 0x00000001 +#define EAGLE_TXD_STATUS_OK_RETRY 0x00000002 +#define EAGLE_TXD_STATUS_OK_MORE_RETRY 0x00000004 +#define EAGLE_TXD_STATUS_MULTICAST_TX 0x00000008 +#define EAGLE_TXD_STATUS_BROADCAST_TX 0x00000010 +#define EAGLE_TXD_STATUS_FAILED_LINK_ERROR 0x00000020 +#define EAGLE_TXD_STATUS_FAILED_EXCEED_LIMIT 0x00000040 +#define EAGLE_TXD_STATUS_FAILED_XRETRY EAGLE_TXD_STATUS_FAILED_EXCEED_LIMIT +#define EAGLE_TXD_STATUS_FAILED_AGING 0x00000080 +#define EAGLE_TXD_STATUS_FW_OWNED 0x80000000 + uint8_t DataRate; + uint8_t TxPriority; + uint16_t QosCtrl; + uint32_t PktPtr; + uint16_t PktLen; + uint8_t DestAddr[6]; + uint32_t pPhysNext; + uint32_t SapPktInfo; +#define EAGLE_TXD_MODE_BONLY 1 +#define EAGLE_TXD_MODE_GONLY 2 +#define EAGLE_TXD_MODE_BG 3 +#define EAGLE_TXD_MODE_NONLY 4 +#define EAGLE_TXD_MODE_BN 5 +#define EAGLE_TXD_MODE_GN 6 +#define EAGLE_TXD_MODE_BGN 7 +#define EAGLE_TXD_MODE_AONLY 8 +#define EAGLE_TXD_MODE_AG 10 +#define EAGLE_TXD_MODE_AN 12 + uint16_t Format; +#define EAGLE_TXD_FORMAT 0x0001 /* frame format/rate */ +#define EAGLE_TXD_FORMAT_LEGACY 0x0000 /* legacy rate frame */ +#define EAGLE_TXD_FORMAT_HT 0x0001 /* HT rate frame */ +#define EAGLE_TXD_GI 0x0002 /* guard interval */ +#define EAGLE_TXD_GI_SHORT 0x0002 /* short guard interval */ +#define EAGLE_TXD_GI_LONG 0x0000 /* long guard interval */ +#define EAGLE_TXD_CHW 0x0004 /* channel width */ +#define EAGLE_TXD_CHW_20 0x0000 /* 20MHz channel width */ +#define EAGLE_TXD_CHW_40 0x0004 /* 40MHz channel width */ +#define EAGLE_TXD_RATE 0x01f8 /* tx rate (legacy)/ MCS */ +#define EAGLE_TXD_RATE_S 3 +#define EAGLE_TXD_ADV 0x0600 /* advanced coding */ +#define EAGLE_TXD_ADV_S 9 +#define EAGLE_TXD_ADV_NONE 0x0000 +#define EAGLE_TXD_ADV_LDPC 0x0200 +#define EAGLE_TXD_ADV_RS 0x0400 +/* NB: 3 is reserved */ +#define EAGLE_TXD_ANTENNA 0x1800 /* antenna select */ +#define EAGLE_TXD_ANTENNA_S 11 +#define EAGLE_TXD_EXTCHAN 0x6000 /* extension channel */ +#define EAGLE_TXD_EXTCHAN_S 13 +#define EAGLE_TXD_EXTCHAN_HI 0x0000 /* above */ +#define EAGLE_TXD_EXTCHAN_LO 0x2000 /* below */ +#define EAGLE_TXD_PREAMBLE 0x8000 +#define EAGLE_TXD_PREAMBLE_SHORT 0x8000 /* short preamble */ +#define EAGLE_TXD_PREAMBLE_LONG 0x0000 /* long preamble */ + uint16_t pad; /* align to 4-byte boundary */ +#define EAGLE_TXD_FIXED_RATE 0x0100 /* get tx rate from Format */ +#define EAGLE_TXD_DONT_AGGR 0x0200 /* don't aggregate frame */ + uint32_t ack_wcb_addr; +}; + +struct mwl_rxdesc { + /* control element */ + uint8_t RxControl; +#define EAGLE_RXD_CTRL_DRIVER_OWN 0x00 +#define EAGLE_RXD_CTRL_OS_OWN 0x04 +#define EAGLE_RXD_CTRL_DMA_OWN 0x80 + /* received signal strengt indication */ + uint8_t RSSI; + /* status field w/ USED bit */ + uint8_t Status; +#define EAGLE_RXD_STATUS_IDLE 0x00 +#define EAGLE_RXD_STATUS_OK 0x01 +#define EAGLE_RXD_STATUS_MULTICAST_RX 0x02 +#define EAGLE_RXD_STATUS_BROADCAST_RX 0x04 +#define EAGLE_RXD_STATUS_FRAGMENT_RX 0x08 +#define EAGLE_RXD_STATUS_GENERAL_DECRYPT_ERR 0xff +#define EAGLE_RXD_STATUS_DECRYPT_ERR_MASK 0x80 +#define EAGLE_RXD_STATUS_TKIP_MIC_DECRYPT_ERR 0x02 +#define EAGLE_RXD_STATUS_WEP_ICV_DECRYPT_ERR 0x04 +#define EAGLE_RXD_STATUS_TKIP_ICV_DECRYPT_ERR 0x08 + /* channel # pkt received on */ + uint8_t Channel; + /* total length of received data */ + uint16_t PktLen; + /* not used */ + uint8_t SQ2; + /* received data rate */ + uint8_t Rate; + /* physical address of payload data */ + uint32_t pPhysBuffData; + /* physical address of next RX desc */ + uint32_t pPhysNext; + /* received QosCtrl field variable */ + uint16_t QosCtrl; + /* like name states */ + uint16_t HtSig2; +#ifdef MWL_ANT_INFO_SUPPORT + /* antenna info */ + struct mwl_ant_info ai; +#endif +}; +#pragma pack() + + + +// ============================================================================= +// HOST COMMAND DEFINITIONS +// ============================================================================= + +// +// Definition of data structure for each command +// +// Define general data structure +#pragma pack(1) +typedef struct { + uint16_t Cmd; + uint16_t Length; +#ifdef MWL_MBSS_SUPPORT + uint8_t SeqNum; + uint8_t MacId; +#else + uint16_t SeqNum; +#endif + uint16_t Result; +} FWCmdHdr; + +typedef struct { + FWCmdHdr CmdHdr; + uint8_t annex; + uint8_t index; + uint8_t len; + uint8_t Reserverd; +#define CAL_TBL_SIZE 160 + uint8_t calTbl[CAL_TBL_SIZE]; +} HostCmd_FW_GET_CALTABLE; + +typedef struct { + FWCmdHdr CmdHdr; + /* version of the HW */ + uint8_t Version; + /* host interface */ + uint8_t HostIf; + /* Max. number of WCB FW can handle */ + uint16_t NumOfWCB; + /* MaxNbr of MC addresses FW can handle */ + uint16_t NumOfMCastAddr; + /* MAC address programmed in HW */ + uint8_t PermanentAddr[6]; + uint16_t RegionCode; + /* Number of antenna used */ + uint16_t NumberOfAntenna; + /* 4 byte of FW release number */ + uint32_t FWReleaseNumber; + uint32_t WcbBase0; + uint32_t RxPdWrPtr; + uint32_t RxPdRdPtr; + uint32_t ulFwAwakeCookie; + uint32_t WcbBase1[3]; +} HostCmd_DS_GET_HW_SPEC; + +typedef struct { + FWCmdHdr CmdHdr; + /* HW revision */ + uint8_t Version; + /* Host interface */ + uint8_t HostIf; + /* Max. number of Multicast address FW can handle */ + uint16_t NumOfMCastAdr; + /* MAC address */ + uint8_t PermanentAddr[6]; + /* Region Code */ + uint16_t RegionCode; + /* 4 byte of FW release number */ + uint32_t FWReleaseNumber; + /* Firmware awake cookie */ + uint32_t ulFwAwakeCookie; + /* Device capabilities (see above) */ + uint32_t DeviceCaps; + /* Rx shared memory queue */ + uint32_t RxPdWrPtr; + /* TX queues in WcbBase array */ + uint32_t NumTxQueues; + /* TX WCB Rings */ + uint32_t WcbBase[MAX_TXWCB_QUEUES]; + uint32_t Flags; +#define SET_HW_SPEC_DISABLEMBSS 0x08 +#define SET_HW_SPEC_HOSTFORM_BEACON 0x10 +#define SET_HW_SPEC_HOSTFORM_PROBERESP 0x20 +#define SET_HW_SPEC_HOST_POWERSAVE 0x40 +#define SET_HW_SPEC_HOSTENCRDECR_MGMT 0x80 + uint32_t TxWcbNumPerQueue; + uint32_t TotalRxWcb; +}HostCmd_DS_SET_HW_SPEC; + +// used for stand alone bssid sets/clears +typedef struct { + FWCmdHdr CmdHdr; +#ifdef MWL_MBSS_SUPPORT + uint16_t MacType; +#define WL_MAC_TYPE_PRIMARY_CLIENT 0 +#define WL_MAC_TYPE_SECONDARY_CLIENT 1 +#define WL_MAC_TYPE_PRIMARY_AP 2 +#define WL_MAC_TYPE_SECONDARY_AP 3 +#endif + uint8_t MacAddr[6]; +} HostCmd_DS_SET_MAC, + HostCmd_FW_SET_BSSID, + HostCmd_FW_SET_MAC; + +typedef struct { + uint32_t LegacyRateBitMap; + uint32_t HTRateBitMap; + uint16_t CapInfo; + uint16_t HTCapabilitiesInfo; + uint8_t MacHTParamInfo; + uint8_t Rev; + struct { + uint8_t ControlChan; + uint8_t AddChan; + uint16_t OpMode; + uint16_t stbc; + } AddHtInfo; +} PeerInfo_t; + +typedef struct { + FWCmdHdr CmdHdr; + uint16_t AID; + uint8_t MacAddr[6]; + uint16_t StnId; + uint16_t Action; + uint16_t Reserved; + PeerInfo_t PeerInfo; + uint8_t Qosinfo; + uint8_t isQosSta; + uint32_t FwStaPtr; +} HostCmd_FW_SET_NEW_STN; + +/* Define data structure for HostCmd_CMD_802_11_RF_ANTENNA */ +typedef struct _HostCmd_DS_802_11_RF_ANTENNA { + FWCmdHdr CmdHdr; + uint16_t Action; + /* Number of antennas or 0xffff(diversity) */ + uint16_t AntennaMode; +} HostCmd_DS_802_11_RF_ANTENNA; + +/* Define data structure for HostCmd_CMD_802_11_RADIO_CONTROL */ +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + /* + * @bit0: 1/0, on/off + * @bit1: 1/0, long/short + * @bit2: 1/0,auto/fix + */ + uint16_t Control; + uint16_t RadioOn; +} HostCmd_DS_802_11_RADIO_CONTROL; + +/* for HostCmd_CMD_SET_WMM_MODE */ +typedef struct { + FWCmdHdr CmdHdr; + /* 0->unset, 1->set */ + uint16_t Action; +} HostCmd_FW_SetWMMMode; + +/* bits 0-5 specify frequency band */ +#define FREQ_BAND_2DOT4GHZ 0x0001 +#define FREQ_BAND_4DOT9GHZ 0x0002 /* XXX not implemented */ +#define FREQ_BAND_5GHZ 0x0004 +#define FREQ_BAND_5DOT2GHZ 0x0008 /* XXX not implemented */ +/* bits 6-10 specify channel width */ +#define CH_AUTO_WIDTH 0x0000 /* XXX not used? */ +#define CH_10_MHz_WIDTH 0x0040 +#define CH_20_MHz_WIDTH 0x0080 +#define CH_40_MHz_WIDTH 0x0100 +/* bits 11-12 specify extension channel */ +#define EXT_CH_NONE 0x0000 /* no extension channel */ +#define EXT_CH_ABOVE_CTRL_CH 0x0800 /* extension channel above */ +#define EXT_CH_AUTO 0x1000 /* XXX not used? */ +#define EXT_CH_BELOW_CTRL_CH 0x1800 /* extension channel below */ +/* bits 13-31 are reserved */ + +#define FIXED_RATE_WITH_AUTO_RATE_DROP 0 +#define FIXED_RATE_WITHOUT_AUTORATE_DROP 1 + +#define LEGACY_RATE_TYPE 0 +#define HT_RATE_TYPE 1 + +#define RETRY_COUNT_VALID 0 +#define RETRY_COUNT_INVALID 1 + +// Define data structure for HostCmd_CMD_802_11_RF_CHANNEL +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + uint8_t CurrentChannel; /* channel # */ + uint32_t ChannelFlags; /* see below */ +} HostCmd_FW_SET_RF_CHANNEL; + +#define TX_POWER_LEVEL_TOTAL 8 + +/* Define data structure for HostCmd_CMD_802_11_RF_TX_POWER */ +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + uint16_t SupportTxPowerLevel; + uint16_t CurrentTxPowerLevel; + uint16_t Reserved; + uint16_t PowerLevelList[TX_POWER_LEVEL_TOTAL]; +} HostCmd_DS_802_11_RF_TX_POWER; + +typedef struct { + /* + * lower rate after the retry count + * 0: legacy, 1: HT + */ + uint32_t FixRateType; + /* + * 0: retry count is not valid + * 1: use retry count specified + */ + uint32_t RetryCountValid; +} FIX_RATE_FLAG; + +typedef struct { + FIX_RATE_FLAG FixRateTypeFlags; + /* legacy rate(not index) or an MCS code */ + uint32_t FixedRate; + uint32_t RetryCount; +} FIXED_RATE_ENTRY; + +typedef struct { + FWCmdHdr CmdHdr; + /* + * HostCmd_ACT_GEN_GET 0x0000 + * HostCmd_ACT_GEN_SET 0x0001 + * HostCmd_ACT_NOT_USE_FIXED_RATE 0x0002 + */ + uint32_t Action; + /* use fixed rate specified but firmware can drop */ + uint32_t AllowRateDrop; + uint32_t EntryCount; + FIXED_RATE_ENTRY FixedRateTable[4]; + uint8_t MulticastRate; + uint8_t MultiRateTxType; + uint8_t ManagementRate; +} HostCmd_FW_USE_FIXED_RATE; + +/* Define data structure for HostCmd_CMD_SET_RATE_ADAPT_MODE */ +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + uint16_t RateAdaptMode; +} HostCmd_DS_SET_RATE_ADAPT_MODE; + +typedef struct { + FWCmdHdr CmdHdr; + uint8_t OptLevel; +} HostCmd_FW_SET_OPTIMIZATION_LEVEL; + +typedef struct { + FWCmdHdr CmdHdr; + uint16_t regionCode; +} HostCmd_SET_REGIONCODE_INFO; + +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; /* 0: Get. 1:Set */ + uint32_t Option; /* 0: default. 1:Aggressive */ + uint32_t Threshold; /* Range 0-200, default 8 */ +} HostCmd_FW_AMPDU_RETRY_RATEDROP_MODE; + +typedef struct { + FWCmdHdr CmdHdr; + uint32_t Enable; /* 0 -- Disable. or 1 -- Enable */ +} HostCmd_CFEND_ENABLE; + +typedef struct { + FWCmdHdr CmdHdr; + uint32_t Enable; /* FALSE: Disable or TRUE: Enable */ +} HostCmd_DS_BSS_START; + +typedef struct { + FWCmdHdr CmdHdr; +} HostCmd_FW_SET_INFRA_MODE; + +/* used for AID sets/clears */ +typedef struct { + FWCmdHdr CmdHdr; + uint16_t AssocID; + uint8_t MacAddr[6]; /* AP's Mac Address(BSSID) */ + uint32_t GProtection; + uint8_t ApRates[RATE_INDEX_MAX_ARRAY]; +} HostCmd_FW_SET_AID; + +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + uint16_t Threshold; +} HostCmd_DS_802_11_RTS_THSD; + +/* Define data structure for HostCmd_CMD_SET_LINKADAPT_CS_MODE */ +typedef struct { + FWCmdHdr CmdHdr; + uint16_t Action; + uint16_t CSMode; +} HostCmd_DS_SET_LINKADAPT_CS_MODE; + +typedef struct { + FWCmdHdr CmdHdr; + uint32_t ActionType; /* ENCR_ACTION_TYPE */ + uint32_t DataLength; /* size of the data buffer attached */ +#ifdef MWL_MBSS_SUPPORT + uint8_t macaddr[6]; +#endif + uint8_t ActionData[1]; +} HostCmd_FW_UPDATE_ENCRYPTION; + +/* + * @HWENCR@ + * Hardware Encryption related data structures and constant definitions. + * Note that all related changes are marked with the @HWENCR@ tag. + */ + +#define MAX_ENCR_KEY_LENGTH 16 /* max 128 bits - depends on type */ +#define MIC_KEY_LENGTH 8 /* size of Tx/Rx MIC key - 8 bytes */ + +#define ENCR_KEY_TYPE_ID_WEP 0x00 /* Key type is WEP */ +#define ENCR_KEY_TYPE_ID_TKIP 0x01 /* Key type is TKIP */ +#define ENCR_KEY_TYPE_ID_AES 0x02 /* Key type is AES-CCMP */ + +/* + * flags used in structure - same as driver EKF_XXX flags + */ +/* indicate key is in use */ +#define ENCR_KEY_FLAG_INUSE 0x00000001 +/* Group key for RX only */ +#define ENCR_KEY_FLAG_RXGROUPKEY 0x00000002 +/* Group key for TX */ +#define ENCR_KEY_FLAG_TXGROUPKEY 0x00000004 +/* pairwise */ +#define ENCR_KEY_FLAG_PAIRWISE 0x00000008 +/* only used for RX */ +#define ENCR_KEY_FLAG_RXONLY 0x00000010 +/* + * These flags are new additions - for hardware encryption commands only + */ +/* Key is for Authenticator */ +#define ENCR_KEY_FLAG_AUTHENTICATOR 0x00000020 +/* Sequence counters valid */ +#define ENCR_KEY_FLAG_TSC_VALID 0x00000040 +/* Tx key for WEP */ +#define ENCR_KEY_FLAG_WEP_TXKEY 0x01000000 +/* Tx/Rx MIC keys are valid */ +#define ENCR_KEY_FLAG_MICKEY_VALID 0x02000000 + +/* + * Key material definitions (for WEP, TKIP, & AES-CCMP) + */ + +/* + * WEP Key material definition + * ---------------------------- + * WEPKey --> An array of 'MAX_ENCR_KEY_LENGTH' bytes. + * Note that we do not support 152bit WEP keys + */ +typedef struct { + /* WEP key material (max 128bit) */ + uint8_t KeyMaterial[MAX_ENCR_KEY_LENGTH]; +} WEP_TYPE_KEY; + +/* + * TKIP Key material definition + * ---------------------------- + * This structure defines TKIP key material. Note that + * the TxMicKey and RxMicKey may or may not be valid. + */ +/* + * TKIP Sequence counter - 24 bits + * Incremented on each fragment MPDU + */ +typedef struct { + uint16_t low; + uint32_t high; +} ENCR_TKIPSEQCNT; + +/* + * TKIP Key material. Key type (group or pairwise key) is + * determined by flags in KEY_PARAM_SET structure + */ +typedef struct { + uint8_t KeyMaterial[MAX_ENCR_KEY_LENGTH]; + uint8_t TkipTxMicKey[MIC_KEY_LENGTH]; + uint8_t TkipRxMicKey[MIC_KEY_LENGTH]; + ENCR_TKIPSEQCNT TkipRsc; + ENCR_TKIPSEQCNT TkipTsc; +} TKIP_TYPE_KEY; + +/* + * AES-CCMP Key material definition + * -------------------------------- + * This structure defines AES-CCMP key material. + */ +typedef struct { + /* AES Key material */ + uint8_t KeyMaterial[MAX_ENCR_KEY_LENGTH]; +} AES_TYPE_KEY; + +/* + * UPDATE_ENCRYPTION command action type. + */ +typedef enum { + /* request to enable/disable HW encryption */ + EncrActionEnableHWEncryption, + /* request to set encryption key */ + EncrActionTypeSetKey, + /* request to remove one or more keys */ + EncrActionTypeRemoveKey, + EncrActionTypeSetGroupKey +} ENCR_ACTION_TYPE; + +/* + * Encryption key definition. + * -------------------------- + * This structure provides all required/essential + * information about the key being set/removed. + */ +typedef struct { + uint16_t Length; /* Total length of this structure */ + uint16_t KeyTypeId; /* Key type - WEP, TKIP or AES-CCMP */ + uint32_t KeyInfo; /* key flags */ + uint32_t KeyIndex; /* For WEP only - actual key index */ + uint16_t KeyLen; /* Size of the key */ + union { /* Key material (variable size array) */ + WEP_TYPE_KEY WepKey; + TKIP_TYPE_KEY TkipKey; + AES_TYPE_KEY AesKey; + } Key; +#ifdef MWL_MBSS_SUPPORT + uint8_t Macaddr[6]; +#endif +} KEY_PARAM_SET; + + +typedef struct { + FWCmdHdr CmdHdr; + uint32_t ActionType; /* ENCR_ACTION_TYPE */ + uint32_t DataLength; /* size of the data buffer attached */ + KEY_PARAM_SET KeyParam; +#ifndef MWL_MBSS_SUPPORT + uint8_t Macaddr[8]; +#endif +} HostCmd_FW_UPDATE_ENCRYPTION_SET_KEY; +#pragma pack() + +#ifdef __cplusplus +} +#endif + +#endif /* _MWL_REG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/mwl/mwl_var.h Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,878 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2007-2009 Marvell Semiconductor, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Definitions for the Marvell 88W8363 Wireless LAN controller. + */ + +#ifndef _MWL_VAR_H +#define _MWL_VAR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/note.h> +#include "mwl_reg.h" + +#define MWL_CMDBUF_SIZE 0x4000 /* size of f/w command buffer */ +#define MWL_RX_RING_COUNT 256 +#define MWL_TX_RING_COUNT 256 + +#ifndef MWL_AGGR_SIZE +#define MWL_AGGR_SIZE 3839 /* max tx agregation size */ +#endif +#define MWL_AGEINTERVAL 1 /* poke f/w every sec to age q's */ + +/* + * Define total number of TX queues in the shared memory. + * This count includes the EDCA queues, Block Ack queues, and HCCA queues + * In addition to this, there could be a management packet queue some + * time in the future + */ +#define MWL_NUM_EDCA_QUEUES 4 +#define MWL_NUM_HCCA_QUEUES 0 +#define MWL_NUM_BA_QUEUES 0 +#define MWL_NUM_MGMT_QUEUES 0 +#define MWL_NUM_ACK_QUEUES 0 +#define MWL_NUM_TX_QUEUES \ + (MWL_NUM_EDCA_QUEUES + MWL_NUM_HCCA_QUEUES + MWL_NUM_BA_QUEUES + \ + MWL_NUM_MGMT_QUEUES + MWL_NUM_ACK_QUEUES) +#define MWL_MAX_RXWCB_QUEUES 1 + +#define MWL_MAX_SUPPORTED_RATES 12 +#define MWL_MAX_SUPPORTED_MCS 32 + +#define PWTAGETRATETABLE20M 14 * 4 +#define PWTAGETRATETABLE40M 9 * 4 +#define PWTAGETRATETABLE20M_5G 35 * 4 +#define PWTAGETRATETABLE40M_5G 16 * 4 + +#define MHF_CALDATA 0x0001 /* cal data retrieved */ +#define MHF_FWHANG 0x0002 /* fw appears hung */ +#define MHF_MBSS 0x0004 /* mbss enabled */ + +#define IEEE80211_CHAN_STURBO 0x00002000 /* 11a static turbo channel only */ +#define IEEE80211_CHAN_HALF 0x00004000 /* Half rate channel */ +#define IEEE80211_CHAN_QUARTER 0x00008000 /* Quarter rate channel */ + +#define IEEE80211_CHAN_HT20 0x00010000 /* HT 20 channel */ +#define IEEE80211_CHAN_HT40U 0x00020000 /* HT 40 channel w/ ext above */ +#define IEEE80211_CHAN_HT40D 0x00040000 /* HT 40 channel w/ ext below */ + +#define IEEE80211_CHAN_FHSS \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK) +#define IEEE80211_CHAN_A \ + (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_B \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK) +#define IEEE80211_CHAN_PUREG \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM) +#define IEEE80211_CHAN_G \ + (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN) +#define IEEE80211_CHAN_HT40 \ + (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D) +#define IEEE80211_CHAN_HT \ + (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40) + +#define IEEE80211_CHAN_108A \ + (IEEE80211_CHAN_A | IEEE80211_CHAN_TURBO) +#define IEEE80211_CHAN_108G \ + (IEEE80211_CHAN_PUREG | IEEE80211_CHAN_TURBO) +#define IEEE80211_CHAN_ST \ + (IEEE80211_CHAN_108A | IEEE80211_CHAN_STURBO) + +#define IEEE80211_MODE_STURBO_A 7 +#define IEEE80211_MODE_11NA 8 /* 5GHz, w/ HT */ +#define IEEE80211_MODE_11NG 9 /* 2GHz, w/ HT */ +#define IEEE80211_MODE_HALF 10 /* OFDM, 1/2x clock */ +#define IEEE80211_MODE_QUARTER 11 /* OFDM, 1/4x clock */ + + +#define IEEE80211_IS_CHAN_2GHZ_F(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) +#define IEEE80211_IS_CHAN_5GHZ_F(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_5GHZ) != 0) + +#define IEEE80211_IS_CHAN_FHSS(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS) +#define IEEE80211_IS_CHAN_A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A) +#define IEEE80211_IS_CHAN_B(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B) +#define IEEE80211_IS_CHAN_PUREG(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG) +#define IEEE80211_IS_CHAN_G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G) +#define IEEE80211_IS_CHAN_ANYG(_c) \ + (IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c)) +#define IEEE80211_IS_CHAN_ST(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST) +#define IEEE80211_IS_CHAN_108A(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_108A) == IEEE80211_CHAN_108A) +#define IEEE80211_IS_CHAN_108G(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G) + +#define IEEE80211_IS_CHAN_HTA(_c) \ + (IEEE80211_IS_CHAN_5GHZ_F(_c) && \ + ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0) + +#define IEEE80211_IS_CHAN_HTG(_c) \ + (IEEE80211_IS_CHAN_2GHZ_F(_c) && \ + ((_c)->ic_flags & IEEE80211_CHAN_HT) != 0) + +#define IEEE80211_IS_CHAN_TURBO(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_TURBO) != 0) + +#define IEEE80211_IS_CHAN_HALF(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HALF) != 0) + +#define IEEE80211_IS_CHAN_QUARTER(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_QUARTER) != 0) + +/* WME stream classes */ +#define WME_AC_BE 0 /* best effort */ +#define WME_AC_BK 1 /* background */ +#define WME_AC_VI 2 /* video */ +#define WME_AC_VO 3 /* voice */ + +/* + * Transmit queue assignment. + */ +enum { + MWL_WME_AC_BK = 0, /* background access category */ + MWL_WME_AC_BE = 1, /* best effort access category */ + MWL_WME_AC_VI = 2, /* video access category */ + MWL_WME_AC_VO = 3, /* voice access category */ +}; + +const char *mwl_wme_acnames[] = { + "WME_AC_BE", + "WME_AC_BK", + "WME_AC_VI", + "WME_AC_VO", + "WME_UPSD", +}; + +/* + * Set Antenna Configuration (legacy operation). + * + * The RX antenna can be selected using the the bitmask + * ant (bit 0 = antenna 1, bit 1 = antenna 2, etc.) + * (diversity?XXX) + */ +typedef enum { + WL_ANTENNATYPE_RX = 1, + WL_ANTENNATYPE_TX = 2, +} MWL_HAL_ANTENNA; + +/* + * Set Radio Configuration. + * + * onoff != 0 turns radio on; otherwise off. + * if radio is enabled, the preamble is set too. + */ +typedef enum { + WL_LONG_PREAMBLE = 1, + WL_SHORT_PREAMBLE = 3, + WL_AUTO_PREAMBLE = 5, +} MWL_HAL_PREAMBLE; + +/* + * Transmit rate control. Rate codes with bit 0x80 set are + * interpreted as MCS codes (this limits us to 0-127). The + * transmit rate can be set to a single fixed rate or can + * be configured to start at an initial rate and drop based + * on retry counts. + */ +typedef enum { + RATE_AUTO = 0, /* rate selected by firmware */ + RATE_FIXED = 2, /* rate fixed */ + RATE_FIXED_DROP = 1, /* rate starts fixed but may drop */ +} MWL_HAL_TXRATE_HANDLING; + +typedef enum { + CSMODE_CONSERVATIVE = 0, + CSMODE_AGGRESSIVE = 1, + CSMODE_AUTO_ENA = 2, + CSMODE_AUTO_DIS = 3, +} MWL_HAL_CSMODE; + +#pragma pack(1) + +/* + * Device revision information. + */ +typedef struct { + uint16_t mh_devid; /* PCI device ID */ + uint16_t mh_subvendorid; /* PCI subvendor ID */ + uint16_t mh_macRev; /* MAC revision */ + uint16_t mh_phyRev; /* PHY revision */ +} MWL_DIAG_REVS; + +typedef struct { + uint16_t freqLow; + uint16_t freqHigh; + int nchannels; + struct mwl_hal_channel { + uint16_t freq; /* channel center */ + uint8_t ieee; /* channel number */ + int8_t maxTxPow; /* max tx power (dBm) */ + uint8_t targetPowers[4]; /* target powers (dBm) */ +#define MWL_HAL_MAXCHAN 40 + } channels[MWL_HAL_MAXCHAN]; +} MWL_HAL_CHANNELINFO; + +typedef struct { + uint32_t FreqBand : 6, +#define MWL_FREQ_BAND_2DOT4GHZ 0x1 +#define MWL_FREQ_BAND_5GHZ 0x4 + ChnlWidth: 5, +#define MWL_CH_10_MHz_WIDTH 0x1 +#define MWL_CH_20_MHz_WIDTH 0x2 +#define MWL_CH_40_MHz_WIDTH 0x4 + ExtChnlOffset: 2, +#define MWL_EXT_CH_NONE 0x0 +#define MWL_EXT_CH_ABOVE_CTRL_CH 0x1 +#define MWL_EXT_CH_BELOW_CTRL_CH 0x3 + : 19; /* reserved */ +} MWL_HAL_CHANNEL_FLAGS; + +typedef struct { + uint32_t channel; + MWL_HAL_CHANNEL_FLAGS channelFlags; +} MWL_HAL_CHANNEL; + +/* + * Channels are specified by frequency and attributes. + */ +struct mwl_channel { + uint32_t ic_flags; /* see below */ + uint16_t ic_freq; /* setting in Mhz */ + uint8_t ic_ieee; /* IEEE channel number */ + int8_t ic_maxregpower; /* maximum regulatory tx power in dBm */ + int8_t ic_maxpower; /* maximum tx power in .5 dBm */ + int8_t ic_minpower; /* minimum tx power in .5 dBm */ + uint8_t ic_state; /* dynamic state */ + uint8_t ic_extieee; /* HT40 extension channel number */ + int8_t ic_maxantgain; /* maximum antenna gain in .5 dBm */ + uint8_t ic_pad; + uint16_t ic_devdata; /* opaque device/driver data */ +}; + +/* + * Regulatory Information. + */ +struct mwl_regdomain { + uint16_t regdomain; /* SKU */ + uint16_t country; /* ISO country code */ + uint8_t location; /* I (indoor), O (outdoor), other */ + uint8_t ecm; /* Extended Channel Mode */ + char isocc[2]; /* country code string */ + short pad[2]; +}; + +/* + * Get Hardware/Firmware capabilities. + */ +struct mwl_hal_hwspec { + uint8_t hwVersion; /* version of the HW */ + uint8_t hostInterface; /* host interface */ + uint16_t maxNumWCB; /* max # of WCB FW handles */ + uint16_t maxNumMCAddr; /* max # of mcast addresse FW handles */ + uint16_t maxNumTxWcb; /* max # of tx descs per WCB */ + uint8_t macAddr[6]; /* MAC address programmed in HW */ + uint16_t regionCode; /* EEPROM region code */ + uint16_t numAntennas; /* Number of antenna used */ + uint32_t fwReleaseNumber; /* firmware release number */ + uint32_t wcbBase0; + uint32_t rxDescRead; + uint32_t rxDescWrite; + uint32_t ulFwAwakeCookie; + uint32_t wcbBase[MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES]; +}; + +/* + * Crypto Configuration. + */ +typedef struct { + uint16_t pad; + uint16_t keyTypeId; +#define KEY_TYPE_ID_WEP 0 +#define KEY_TYPE_ID_TKIP 1 +#define KEY_TYPE_ID_AES 2 /* AES-CCMP */ + uint32_t keyFlags; +#define KEY_FLAG_INUSE 0x00000001 /* indicate key is in use */ +#define KEY_FLAG_RXGROUPKEY 0x00000002 /* Group key for RX only */ +#define KEY_FLAG_TXGROUPKEY 0x00000004 /* Group key for TX */ +#define KEY_FLAG_PAIRWISE 0x00000008 /* pairwise */ +#define KEY_FLAG_RXONLY 0x00000010 /* only used for RX */ +#define KEY_FLAG_AUTHENTICATOR 0x00000020 /* Key is for Authenticator */ +#define KEY_FLAG_TSC_VALID 0x00000040 /* Sequence counters valid */ +#define KEY_FLAG_WEP_TXKEY 0x01000000 /* Tx key for WEP */ +#define KEY_FLAG_MICKEY_VALID 0x02000000 /* Tx/Rx MIC keys are valid */ + uint32_t keyIndex; /* for WEP only; actual key index */ + uint16_t keyLen; /* key size in bytes */ + union { /* key material, keyLen gives size */ + uint8_t wep[16]; /* enough for 128 bits */ + uint8_t aes[16]; + struct { + /* NB: group or pairwise key is determined by keyFlags */ + uint8_t keyMaterial[16]; + uint8_t txMic[8]; + uint8_t rxMic[8]; + struct { + uint16_t low; + uint32_t high; + } rsc; + struct { + uint16_t low; + uint32_t high; + } tsc; + } tkip; + } key; +} MWL_HAL_KEYVAL; + +/* + * Supply tx/rx dma-related settings to the firmware. + */ +struct mwl_hal_txrxdma { + uint32_t maxNumWCB; /* max # of WCB FW handles */ + uint32_t maxNumTxWcb; /* max # of tx descs per WCB */ + uint32_t rxDescRead; + uint32_t rxDescWrite; + uint32_t wcbBase[MWL_NUM_TX_QUEUES - MWL_NUM_ACK_QUEUES]; +}; + +/* + * Inform the firmware of a new association station. + * The address is the MAC address of the peer station. + * The AID is supplied sans the 0xc000 bits. The station + * ID is defined by the caller. The peer information must + * be supplied. + * + * NB: All values are in host byte order; any byte swapping + * is handled by the hal. + */ +typedef struct { + uint32_t LegacyRateBitMap; + uint32_t HTRateBitMap; + uint16_t CapInfo; + uint16_t HTCapabilitiesInfo; + uint8_t MacHTParamInfo; + uint8_t Rev; + struct { + uint8_t ControlChan; + uint8_t AddChan; + uint8_t OpMode; + uint8_t stbc; + } AddHtInfo; +} MWL_HAL_PEERINFO; + +typedef struct { + uint8_t McastRate; /* rate for multicast frames */ +#define RATE_MCS 0x80 /* rate is an MCS index */ + uint8_t MgtRate; /* rate for management frames */ + struct { + uint8_t TryCount; /* try this many times */ + uint8_t Rate; /* use this tx rate */ + } RateSeries[4]; /* rate series */ +} MWL_HAL_TXRATE; + +#pragma pack() + +/* driver-specific node state */ +struct mwl_node { + struct ieee80211_node mn_node; /* base class */ + struct mwl_ant_info mn_ai; /* antenna info */ + uint32_t mn_avgrssi; /* average rssi over all rx frames */ + uint16_t mn_staid; /* firmware station id */ +}; +#define MWL_NODE(ni) ((struct mwl_node *)(ni)) +#define MWL_NODE_CONST(ni) ((const struct mwl_node *)(ni)) + +/* + * DMA state for tx/rx. + */ + +/* + * Software backed version of tx/rx descriptors. We keep + * the software state out of the h/w descriptor structure + * so that may be allocated in uncached memory w/o paying + * performance hit. + */ +struct dma_area { + ddi_acc_handle_t acc_hdl; /* handle for memory */ + caddr_t mem_va; /* CPU VA of memory */ + uint32_t nslots; /* number of slots */ + uint32_t size; /* size per slot */ + size_t alength; /* allocated size */ + ddi_dma_handle_t dma_hdl; /* DMA handle */ + offset_t offset; /* relative to handle */ + ddi_dma_cookie_t cookie; /* associated cookie */ + uint32_t ncookies; /* must be 1 */ + uint32_t token; /* arbitrary identifier */ +}; + +struct mwl_rxbuf { + struct dma_area rxbuf_dma; /* dma area for buf */ + uint32_t bf_baddr; + uint8_t *bf_mem; + void *bf_desc; + uint32_t bf_daddr; +}; + +struct mwl_rx_ring { + struct dma_area rxdesc_dma; + uint32_t physaddr; + struct mwl_rxdesc *desc; + struct mwl_rxbuf *buf; + int count; + int cur; + int next; +}; + +struct mwl_txbuf { + struct dma_area txbuf_dma; + uint32_t bf_baddr; /* physical addr of buf */ + uint8_t *bf_mem; + uint32_t bf_daddr; /* physical addr of desc */ + void *bf_desc; /* h/w descriptor */ + int bf_nseg; + struct ieee80211_node *bf_node; + struct mwl_txq *bf_txq; /* backpointer to tx q/ring */ +}; + +struct mwl_tx_ring { + struct dma_area txdesc_dma; + uint32_t physaddr; + struct mwl_txdesc *desc; + struct mwl_txbuf *buf; + int qnum; /* f/w q number */ + int txpri; /* f/w tx priority */ + int count; + int queued; + int cur; + int next; + int stat; +}; + +struct mwl_softc { + ieee80211com_t sc_ic; + dev_info_t *sc_dev; + + /* ddi reg handler */ + ddi_acc_handle_t sc_cfg_handle; + caddr_t sc_cfg_base; + + /* bar0 handler */ + ddi_acc_handle_t sc_mem_handle; + caddr_t sc_mem_base; + + /* bar1 handler */ + ddi_acc_handle_t sc_io_handle; + caddr_t sc_io_base; + + uint16_t sc_cachelsz; + uint32_t sc_dmabuf_size; + uchar_t sc_macaddr[6]; + + struct dma_area sc_cmd_dma; + uint16_t *sc_cmd_mem; /* f/w cmd buffer */ + uint32_t sc_cmd_dmaaddr; /* physaddr of cmd buffer */ + + int sc_hw_flags; + uint32_t sc_flags; + + /* SDRAM addr in the chipset */ + int sc_SDRAMSIZE_Addr; + + MWL_HAL_CHANNELINFO sc_20M; + MWL_HAL_CHANNELINFO sc_40M; + MWL_HAL_CHANNELINFO sc_20M_5G; + MWL_HAL_CHANNELINFO sc_40M_5G; + + struct mwl_hal_hwspec sc_hwspecs; /* h/w capabilities */ + MWL_DIAG_REVS sc_revs; + + int sc_nchans; /* # entries in ic_channels */ + struct mwl_channel sc_channels[IEEE80211_CHAN_MAX]; + struct mwl_channel *sc_cur_chan; + MWL_HAL_CHANNEL sc_curchan; + struct mwl_regdomain sc_regdomain; /* regulatory data */ + + struct mwl_rx_ring sc_rxring; + struct mwl_tx_ring sc_txring[MWL_NUM_TX_QUEUES]; + struct mwl_tx_ring *sc_ac2q[5]; /* WME AC -> h/w q map */ + + struct mwl_hal_txrxdma sc_hwdma; /* h/w dma setup */ + + /* interrupt */ + ddi_iblock_cookie_t sc_iblock; + ddi_softint_handle_t sc_softintr_hdl; + ddi_intr_handle_t *sc_intr_htable; + uint_t sc_intr_pri; + uint32_t sc_imask; /* interrupt mask */ + uint32_t sc_hal_imask; /* interrupt mask copy */ + uint32_t sc_rx_pend; + + /* mutex lock */ + kmutex_t sc_glock; + kmutex_t sc_rxlock; + kmutex_t sc_txlock; + + uint16_t sc_rxantenna; /* rx antenna */ + uint16_t sc_txantenna; /* tx antenna */ + + timeout_id_t sc_scan_id; + + /* kstats */ + uint32_t sc_tx_nobuf; + uint32_t sc_rx_nobuf; + uint32_t sc_tx_err; + uint32_t sc_rx_err; + uint32_t sc_tx_retries; + + uint32_t sc_need_sched; + uint32_t sc_rcr; + + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); +}; + +#define mwl_mem_write4(sc, off, x) \ + ddi_put32((sc)->sc_mem_handle, \ + (uint32_t *)((sc)->sc_mem_base + (off)), x) + +#define mwl_mem_read4(sc, off) \ + ddi_get32((sc)->sc_mem_handle, \ + (uint32_t *)((sc)->sc_mem_base + (off))) + +#define mwl_ctl_write4(sc, off, x) \ + ddi_put32((sc)->sc_io_handle, \ + (uint32_t *)((sc)->sc_io_base + (off)), x) + +#define mwl_ctl_read4(sc, off) \ + ddi_get32((sc)->sc_io_handle, \ + (uint32_t *)((sc)->sc_io_base + (off))) + +#define mwl_ctl_read1(sc, off) \ + ddi_get8((sc)->sc_io_handle, \ + (uint8_t *)((sc)->sc_io_base + (off))) + +#define _CMD_SETUP(pCmd, type, cmd) do { \ + pCmd = (type *)&sc->sc_cmd_mem[0]; \ + memset(pCmd, 0, sizeof (type)); \ + pCmd->CmdHdr.Cmd = LE_16(cmd); \ + pCmd->CmdHdr.Length = LE_16(sizeof (type)); \ + _NOTE(CONSTCOND) \ +} while (0) + +#define _VCMD_SETUP(pCmd, type, cmd) do { \ + _CMD_SETUP(pCmd, type, cmd); \ + pCmd->CmdHdr.MacId = 8; \ + _NOTE(CONSTCOND) \ +} while (0) + +#define MWL_GLOCK(_sc) mutex_enter(&(_sc)->sc_glock) +#define MWL_GUNLOCK(_sc) mutex_exit(&(_sc)->sc_glock) + +#define MWL_RXLOCK(_sc) mutex_enter(&(_sc)->sc_rxlock) +#define MWL_RXUNLOCK(_sc) mutex_exit(&(_sc)->sc_rxlock) + +#define MWL_TXLOCK(_sc) mutex_enter(&(_sc)->sc_txlock) +#define MWL_TXUNLOCK(_sc) mutex_exit(&(_sc)->sc_txlock) + +#define MWL_F_RUNNING (1 << 0) +#define MWL_F_SUSPEND (1 << 1) +#define MWL_F_QUIESCE (1 << 2) + +#define MWL_RCR_PROMISC (1 << 0) +#define MWL_RCR_MULTI (1 << 1) + +#define MWL_IS_RUNNING(_sc) (((_sc)->sc_flags & MWL_F_RUNNING)) +#define MWL_IS_SUSPEND(_sc) (((_sc)->sc_flags & MWL_F_SUSPEND)) +#define MWL_IS_QUIESCE(_sc) (((_sc)->sc_flags & MWL_F_QUIESCE)) + +/* + * 802.11 regulatory domain definitions. + */ +enum ISOCountryCode { + CTRY_AFGHANISTAN = 4, + CTRY_ALBANIA = 8, /* Albania */ + CTRY_ALGERIA = 12, /* Algeria */ + CTRY_AMERICAN_SAMOA = 16, + CTRY_ANDORRA = 20, + CTRY_ANGOLA = 24, + CTRY_ANGUILLA = 660, + CTRY_ANTARTICA = 10, + CTRY_ANTIGUA = 28, /* Antigua and Barbuda */ + CTRY_ARGENTINA = 32, /* Argentina */ + CTRY_ARMENIA = 51, /* Armenia */ + CTRY_ARUBA = 533, /* Aruba */ + CTRY_AUSTRALIA = 36, /* Australia */ + CTRY_AUSTRIA = 40, /* Austria */ + CTRY_AZERBAIJAN = 31, /* Azerbaijan */ + CTRY_BAHAMAS = 44, /* Bahamas */ + CTRY_BAHRAIN = 48, /* Bahrain */ + CTRY_BANGLADESH = 50, /* Bangladesh */ + CTRY_BARBADOS = 52, + CTRY_BELARUS = 112, /* Belarus */ + CTRY_BELGIUM = 56, /* Belgium */ + CTRY_BELIZE = 84, + CTRY_BENIN = 204, + CTRY_BERMUDA = 60, + CTRY_BHUTAN = 64, + CTRY_BOLIVIA = 68, /* Bolivia */ + CTRY_BOSNIA_AND_HERZEGOWINA = 70, + CTRY_BOTSWANA = 72, + CTRY_BOUVET_ISLAND = 74, + CTRY_BRAZIL = 76, /* Brazil */ + CTRY_BRITISH_INDIAN_OCEAN_TERRITORY = 86, + CTRY_BRUNEI_DARUSSALAM = 96, /* Brunei Darussalam */ + CTRY_BULGARIA = 100, /* Bulgaria */ + CTRY_BURKINA_FASO = 854, + CTRY_BURUNDI = 108, + CTRY_CAMBODIA = 116, + CTRY_CAMEROON = 120, + CTRY_CANADA = 124, /* Canada */ + CTRY_CAPE_VERDE = 132, + CTRY_CAYMAN_ISLANDS = 136, + CTRY_CENTRAL_AFRICAN_REPUBLIC = 140, + CTRY_CHAD = 148, + CTRY_CHILE = 152, /* Chile */ + CTRY_CHINA = 156, /* People's Republic of China */ + CTRY_CHRISTMAS_ISLAND = 162, + CTRY_COCOS_ISLANDS = 166, + CTRY_COLOMBIA = 170, /* Colombia */ + CTRY_COMOROS = 174, + CTRY_CONGO = 178, + CTRY_COOK_ISLANDS = 184, + CTRY_COSTA_RICA = 188, /* Costa Rica */ + CTRY_COTE_DIVOIRE = 384, + CTRY_CROATIA = 191, /* Croatia (local name: Hrvatska) */ + CTRY_CYPRUS = 196, /* Cyprus */ + CTRY_CZECH = 203, /* Czech Republic */ + CTRY_DENMARK = 208, /* Denmark */ + CTRY_DJIBOUTI = 262, + CTRY_DOMINICA = 212, + CTRY_DOMINICAN_REPUBLIC = 214, /* Dominican Republic */ + CTRY_EAST_TIMOR = 626, + CTRY_ECUADOR = 218, /* Ecuador */ + CTRY_EGYPT = 818, /* Egypt */ + CTRY_EL_SALVADOR = 222, /* El Salvador */ + CTRY_EQUATORIAL_GUINEA = 226, + CTRY_ERITREA = 232, + CTRY_ESTONIA = 233, /* Estonia */ + CTRY_ETHIOPIA = 210, + CTRY_FALKLAND_ISLANDS = 238, /* (Malvinas) */ + CTRY_FAEROE_ISLANDS = 234, /* Faeroe Islands */ + CTRY_FIJI = 242, + CTRY_FINLAND = 246, /* Finland */ + CTRY_FRANCE = 250, /* France */ + CTRY_FRANCE2 = 255, /* France (Metropolitan) */ + CTRY_FRENCH_GUIANA = 254, + CTRY_FRENCH_POLYNESIA = 258, + CTRY_FRENCH_SOUTHERN_TERRITORIES = 260, + CTRY_GABON = 266, + CTRY_GAMBIA = 270, + CTRY_GEORGIA = 268, /* Georgia */ + CTRY_GERMANY = 276, /* Germany */ + CTRY_GHANA = 288, + CTRY_GIBRALTAR = 292, + CTRY_GREECE = 300, /* Greece */ + CTRY_GREENLAND = 304, + CTRY_GRENADA = 308, + CTRY_GUADELOUPE = 312, + CTRY_GUAM = 316, + CTRY_GUATEMALA = 320, /* Guatemala */ + CTRY_GUINEA = 324, + CTRY_GUINEA_BISSAU = 624, + CTRY_GUYANA = 328, + /* XXX correct remainder */ + CTRY_HAITI = 332, + CTRY_HONDURAS = 340, /* Honduras */ + CTRY_HONG_KONG = 344, /* Hong Kong S.A.R., P.R.C. */ + CTRY_HUNGARY = 348, /* Hungary */ + CTRY_ICELAND = 352, /* Iceland */ + CTRY_INDIA = 356, /* India */ + CTRY_INDONESIA = 360, /* Indonesia */ + CTRY_IRAN = 364, /* Iran */ + CTRY_IRAQ = 368, /* Iraq */ + CTRY_IRELAND = 372, /* Ireland */ + CTRY_ISRAEL = 376, /* Israel */ + CTRY_ITALY = 380, /* Italy */ + CTRY_JAMAICA = 388, /* Jamaica */ + CTRY_JAPAN = 392, /* Japan */ + CTRY_JORDAN = 400, /* Jordan */ + CTRY_KAZAKHSTAN = 398, /* Kazakhstan */ + CTRY_KENYA = 404, /* Kenya */ + CTRY_KOREA_NORTH = 408, /* North Korea */ + CTRY_KOREA_ROC = 410, /* South Korea */ + CTRY_KOREA_ROC2 = 411, /* South Korea */ + CTRY_KUWAIT = 414, /* Kuwait */ + CTRY_LATVIA = 428, /* Latvia */ + CTRY_LEBANON = 422, /* Lebanon */ + CTRY_LIBYA = 434, /* Libya */ + CTRY_LIECHTENSTEIN = 438, /* Liechtenstein */ + CTRY_LITHUANIA = 440, /* Lithuania */ + CTRY_LUXEMBOURG = 442, /* Luxembourg */ + CTRY_MACAU = 446, /* Macau */ + CTRY_MACEDONIA = 807, /* Macedonia */ + CTRY_MALAYSIA = 458, /* Malaysia */ + CTRY_MALTA = 470, /* Malta */ + CTRY_MEXICO = 484, /* Mexico */ + CTRY_MONACO = 492, /* Principality of Monaco */ + CTRY_MOROCCO = 504, /* Morocco */ + CTRY_NEPAL = 524, /* Nepal */ + CTRY_NETHERLANDS = 528, /* Netherlands */ + CTRY_NEW_ZEALAND = 554, /* New Zealand */ + CTRY_NICARAGUA = 558, /* Nicaragua */ + CTRY_NORWAY = 578, /* Norway */ + CTRY_OMAN = 512, /* Oman */ + CTRY_PAKISTAN = 586, /* Islamic Republic of Pakistan */ + CTRY_PANAMA = 591, /* Panama */ + CTRY_PARAGUAY = 600, /* Paraguay */ + CTRY_PERU = 604, /* Peru */ + CTRY_PHILIPPINES = 608, /* Republic of the Philippines */ + CTRY_POLAND = 616, /* Poland */ + CTRY_PORTUGAL = 620, /* Portugal */ + CTRY_PUERTO_RICO = 630, /* Puerto Rico */ + CTRY_QATAR = 634, /* Qatar */ + CTRY_ROMANIA = 642, /* Romania */ + CTRY_RUSSIA = 643, /* Russia */ + CTRY_SAUDI_ARABIA = 682, /* Saudi Arabia */ + CTRY_SINGAPORE = 702, /* Singapore */ + CTRY_SLOVAKIA = 703, /* Slovak Republic */ + CTRY_SLOVENIA = 705, /* Slovenia */ + CTRY_SOUTH_AFRICA = 710, /* South Africa */ + CTRY_SPAIN = 724, /* Spain */ + CTRY_SRILANKA = 144, /* Sri Lanka */ + CTRY_SWEDEN = 752, /* Sweden */ + CTRY_SWITZERLAND = 756, /* Switzerland */ + CTRY_SYRIA = 760, /* Syria */ + CTRY_TAIWAN = 158, /* Taiwan */ + CTRY_THAILAND = 764, /* Thailand */ + CTRY_TRINIDAD_Y_TOBAGO = 780, /* Trinidad y Tobago */ + CTRY_TUNISIA = 788, /* Tunisia */ + CTRY_TURKEY = 792, /* Turkey */ + CTRY_UAE = 784, /* U.A.E. */ + CTRY_UKRAINE = 804, /* Ukraine */ + CTRY_UNITED_KINGDOM = 826, /* United Kingdom */ + CTRY_UNITED_STATES = 840, /* United States */ + CTRY_URUGUAY = 858, /* Uruguay */ + CTRY_UZBEKISTAN = 860, /* Uzbekistan */ + CTRY_VENEZUELA = 862, /* Venezuela */ + CTRY_VIET_NAM = 704, /* Viet Nam */ + CTRY_YEMEN = 887, /* Yemen */ + CTRY_ZIMBABWE = 716, /* Zimbabwe */ + + /* NB: from here down not listed in 3166; they come from Atheros */ + CTRY_DEBUG = 0x1ff, /* debug */ + CTRY_DEFAULT = 0, /* default */ + + CTRY_UNITED_STATES_FCC49 = 842, /* United States (Public Safety) */ + CTRY_KOREA_ROC3 = 412, /* South Korea */ + + CTRY_JAPAN1 = 393, /* Japan (JP1) */ + CTRY_JAPAN2 = 394, /* Japan (JP0) */ + CTRY_JAPAN3 = 395, /* Japan (JP1-1) */ + CTRY_JAPAN4 = 396, /* Japan (JE1) */ + CTRY_JAPAN5 = 397, /* Japan (JE2) */ + CTRY_JAPAN6 = 399, /* Japan (JP6) */ + CTRY_JAPAN7 = 4007, /* Japan (J7) */ + CTRY_JAPAN8 = 4008, /* Japan (J8) */ + CTRY_JAPAN9 = 4009, /* Japan (J9) */ + CTRY_JAPAN10 = 4010, /* Japan (J10) */ + CTRY_JAPAN11 = 4011, /* Japan (J11) */ + CTRY_JAPAN12 = 4012, /* Japan (J12) */ + CTRY_JAPAN13 = 4013, /* Japan (J13) */ + CTRY_JAPAN14 = 4014, /* Japan (J14) */ + CTRY_JAPAN15 = 4015, /* Japan (J15) */ + CTRY_JAPAN16 = 4016, /* Japan (J16) */ + CTRY_JAPAN17 = 4017, /* Japan (J17) */ + CTRY_JAPAN18 = 4018, /* Japan (J18) */ + CTRY_JAPAN19 = 4019, /* Japan (J19) */ + CTRY_JAPAN20 = 4020, /* Japan (J20) */ + CTRY_JAPAN21 = 4021, /* Japan (J21) */ + CTRY_JAPAN22 = 4022, /* Japan (J22) */ + CTRY_JAPAN23 = 4023, /* Japan (J23) */ + CTRY_JAPAN24 = 4024, /* Japan (J24) */ +}; + +enum RegdomainCode { + SKU_FCC = 0x10, /* FCC, aka United States */ + SKU_CA = 0x20, /* North America, aka Canada */ + SKU_ETSI = 0x30, /* Europe */ + SKU_ETSI2 = 0x32, /* Europe w/o HT40 in 5GHz */ + SKU_ETSI3 = 0x33, /* Europe - channel 36 */ + SKU_FCC3 = 0x3a, /* FCC w/5470 band, 11h, DFS */ + SKU_JAPAN = 0x40, + SKU_KOREA = 0x45, + SKU_APAC = 0x50, /* Asia Pacific */ + SKU_APAC2 = 0x51, /* Asia Pacific w/ DFS on mid-band */ + SKU_APAC3 = 0x5d, /* Asia Pacific w/o ISM band */ + SKU_ROW = 0x81, /* China/Taiwan/Rest of World */ + SKU_NONE = 0xf0, /* "Region Free" */ + SKU_DEBUG = 0x1ff, + + /* NB: from here down private */ + SKU_SR9 = 0x0298, /* Ubiquiti SR9 (900MHz/GSM) */ + SKU_XR9 = 0x0299, /* Ubiquiti XR9 (900MHz/GSM) */ + SKU_GZ901 = 0x029a, /* Zcomax GZ-901 (900MHz/GSM) */ +}; + +/* + * Set regdomain code (IEEE SKU). + */ +enum { + DOMAIN_CODE_FCC = 0x10, /* USA */ + DOMAIN_CODE_IC = 0x20, /* Canda */ + DOMAIN_CODE_ETSI = 0x30, /* Europe */ + DOMAIN_CODE_SPAIN = 0x31, /* Spain */ + DOMAIN_CODE_FRANCE = 0x32, /* France */ + DOMAIN_CODE_ETSI_131 = 0x130, /* ETSI w/ 1.3.1 radar type */ + DOMAIN_CODE_MKK = 0x40, /* Japan */ + DOMAIN_CODE_MKK2 = 0x41, /* Japan w/ 10MHz chan spacing */ + DOMAIN_CODE_DGT = 0x80, /* Taiwan */ + DOMAIN_CODE_AUS = 0x81, /* Australia */ +}; + + +#ifdef __cplusplus +} +#endif + +#endif /* _MWL_VAR_H */
--- a/usr/src/uts/intel/Makefile.intel.shared Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/uts/intel/Makefile.intel.shared Thu Oct 08 14:27:17 2009 +0800 @@ -277,6 +277,7 @@ DRV_KMODS += mouse8042 DRV_KMODS += mpt_sas DRV_KMODS += mr_sas +DRV_KMODS += mwl DRV_KMODS += nca DRV_KMODS += nsmb DRV_KMODS += nulldriver @@ -610,6 +611,7 @@ MISC_KMODS += ksocket MISC_KMODS += mac MISC_KMODS += mii +MISC_KMODS += mwlfw MISC_KMODS += net80211 MISC_KMODS += nfs_dlboot MISC_KMODS += nfssrv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/mwl/Makefile Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,97 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# This makefile drives the production of the rwd driver kernel module. +# +# i86pc architecture dependent +# + +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = mwl +OBJECTS = $(MWL_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(MWL_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# Driver depends on GLDv3 & wifi kernel support module. +# +LDFLAGS += -dy -Nmisc/mac -Nmisc/net80211 + +# +# Overrides +# +LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN + +# +# 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 + +# +# If you have any special case that general +# Makefile rules don't serve for you, just do +# it yourself. +#
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/mwlfw/Makefile Thu Oct 08 14:27:17 2009 +0800 @@ -0,0 +1,116 @@ +# +# 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 2009 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +# This makefile drives the production of the mwlfw kernel module. +# +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = mwlfw +OBJECTS = $(MWLFW_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(MWLFW_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_MISC_DIR)/$(MODULE) + +FWOBJ = $(OBJS_DIR)/$(MODULE).o + +OBJECTS += $(FWOBJ) + +# +# Include common rules. +# +include $(UTSBASE)/intel/Makefile.intel + +LDFLAGS = +# +# Define targets +# +ALL_TARGET = $(BINARY) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) + +# +# For now, disable these lint checks; maintainers should endeavor +# to investigate and remove these for maximum lint coverage. +# Please do not carry these forward to new Makefiles. +# +LINTTAGS += -erroff=E_BAD_PTR_CAST_ALIGN +LINTTAGS += -erroff=E_PTRDIFF_OVERFLOW +LINTTAGS += -erroff=E_ASSIGN_NARROW_CONV +LINTTAGS += -erroff=E_STATIC_UNUSED + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) $(FWOBJ) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + $(RM) $(BINDEST)/$(FWBINCOPY) + $(RM) $(BINDEST)/$(BOOTBINCOPY) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + + +# customized section for transforming firmware binary +ELFWRAP = elfwrap +BINSRC = $(UTSBASE)/common/io/mwl/mwl_fw +BINDEST = $(UTSBASE)/intel/mwlfw +FWBINCOPY = mw88W8363fw +BOOTBINCOPY = mwlboot +ORIGIN_FWBIN = mw88W8363 +ORIGIN_BOOTBIN = mwlboot + +#get build type, 32/64 +WRAPTYPE = $(CLASS:%=-%) + +#set elfwrap option +WRAPOPT = $(WRAPTYPE:-32=) + +$(FWOBJ): + cp $(BINSRC)/$(ORIGIN_FWBIN) $(BINDEST)/$(FWBINCOPY) + cp $(BINSRC)/$(ORIGIN_BOOTBIN) $(BINDEST)/$(BOOTBINCOPY) + $(ELFWRAP) $(WRAPOPT) -o $@ $(BINDEST)/$(FWBINCOPY) $(BINDEST)/$(BOOTBINCOPY) + +# +# Include common targets. +# +include $(UTSBASE)/intel/Makefile.targ +
--- a/usr/src/uts/intel/os/minor_perm Thu Oct 08 10:37:32 2009 +0800 +++ b/usr/src/uts/intel/os/minor_perm Thu Oct 08 14:27:17 2009 +0800 @@ -137,6 +137,7 @@ clone:iwh 0666 root sys clone:iwi 0666 root sys clone:iwk 0666 root sys +clone:mwl 0666 root sys clone:pcwl 0666 root sys clone:pcan 0666 root sys clone:ral 0666 root sys @@ -172,6 +173,7 @@ iwh:* 0666 root sys iwi:* 0666 root sys iwk:* 0666 root sys +mwl:* 0666 root sys pcwl:* 0666 root sys pcan:* 0666 root sys ral:* 0666 root sys