summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorQuaker Fang <Quaker.Fang@Sun.COM>2009-08-06 13:05:38 +0800
committerQuaker Fang <Quaker.Fang@Sun.COM>2009-08-06 13:05:38 +0800
commite2cf88ac9d753a00c17aa235f6afdc76574fe3a6 (patch)
tree894b2df5cf690a33664c4bcd17481540914b8698 /usr/src
parent26fd77009b17f8c8fb32eb362584cfd635e87ad9 (diff)
downloadillumos-joyent-e2cf88ac9d753a00c17aa235f6afdc76574fe3a6.tar.gz
6814606 Solaris gldv3/wifi needs to support 802.11n
6814289 iwh needs to support 802.11n
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/lib/libdladm/common/libdlwlan.c13
-rw-r--r--usr/src/lib/libdladm/common/libdlwlan.h6
-rw-r--r--usr/src/pkgdefs/etc/exception_list_i3863
-rw-r--r--usr/src/pkgdefs/etc/exception_list_sparc3
-rw-r--r--usr/src/uts/common/Makefile.files3
-rw-r--r--usr/src/uts/common/inet/wifi_ioctl.h6
-rw-r--r--usr/src/uts/common/io/arn/arn_core.h5
-rw-r--r--usr/src/uts/common/io/iwh/iwh.c2198
-rw-r--r--usr/src/uts/common/io/iwh/iwh_hw.h366
-rw-r--r--usr/src/uts/common/io/iwh/iwh_var.h56
-rw-r--r--usr/src/uts/common/io/mac/plugins/mac_wifi.c21
-rw-r--r--usr/src/uts/common/io/net80211/net80211.c146
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c26
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto_tkip.c19
-rw-r--r--usr/src/uts/common/io/net80211/net80211_crypto_wep.c15
-rw-r--r--usr/src/uts/common/io/net80211/net80211_ht.c1900
-rw-r--r--usr/src/uts/common/io/net80211/net80211_impl.h67
-rw-r--r--usr/src/uts/common/io/net80211/net80211_input.c342
-rw-r--r--usr/src/uts/common/io/net80211/net80211_ioctl.c8
-rw-r--r--usr/src/uts/common/io/net80211/net80211_node.c60
-rw-r--r--usr/src/uts/common/io/net80211/net80211_output.c266
-rw-r--r--usr/src/uts/common/io/net80211/net80211_proto.c337
-rw-r--r--usr/src/uts/common/sys/Makefile1
-rw-r--r--usr/src/uts/common/sys/mac_wifi.h12
-rw-r--r--usr/src/uts/common/sys/net80211.h230
-rw-r--r--usr/src/uts/common/sys/net80211_ht.h149
-rw-r--r--usr/src/uts/common/sys/net80211_proto.h466
27 files changed, 5879 insertions, 845 deletions
diff --git a/usr/src/lib/libdladm/common/libdlwlan.c b/usr/src/lib/libdladm/common/libdlwlan.c
index 82dd9e49e5..0593ae3cfc 100644
--- a/usr/src/lib/libdladm/common/libdlwlan.c
+++ b/usr/src/lib/libdladm/common/libdlwlan.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -122,6 +122,8 @@ static val_desc_t mode_vals[] = {
{ "a", DLADM_WLAN_MODE_80211A },
{ "b", DLADM_WLAN_MODE_80211B },
{ "g", DLADM_WLAN_MODE_80211G },
+ { "n", DLADM_WLAN_MODE_80211GN },
+ { "n", DLADM_WLAN_MODE_80211AN }
};
static val_desc_t auth_vals[] = {
@@ -161,11 +163,16 @@ dladm_wlan_wlresult2status(wldp_t *gbuf)
static dladm_wlan_mode_t
do_convert_mode(wl_phy_conf_t *phyp)
{
+ wl_erp_t *wlep = &phyp->wl_phy_erp_conf;
+ wl_ofdm_t *wlop = &phyp->wl_phy_ofdm_conf;
+
switch (phyp->wl_phy_fhss_conf.wl_fhss_subtype) {
case WL_ERP:
- return (DLADM_WLAN_MODE_80211G);
+ return (wlep->wl_erp_ht_enabled ?
+ DLADM_WLAN_MODE_80211GN : DLADM_WLAN_MODE_80211G);
case WL_OFDM:
- return (DLADM_WLAN_MODE_80211A);
+ return (wlop->wl_ofdm_ht_enabled ?
+ DLADM_WLAN_MODE_80211AN : DLADM_WLAN_MODE_80211A);
case WL_DSSS:
case WL_FHSS:
return (DLADM_WLAN_MODE_80211B);
diff --git a/usr/src/lib/libdladm/common/libdlwlan.h b/usr/src/lib/libdladm/common/libdlwlan.h
index 87dc01c863..2176910d35 100644
--- a/usr/src/lib/libdladm/common/libdlwlan.h
+++ b/usr/src/lib/libdladm/common/libdlwlan.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -110,7 +110,9 @@ typedef enum {
DLADM_WLAN_MODE_NONE = 0,
DLADM_WLAN_MODE_80211A,
DLADM_WLAN_MODE_80211B,
- DLADM_WLAN_MODE_80211G
+ DLADM_WLAN_MODE_80211G,
+ DLADM_WLAN_MODE_80211GN,
+ DLADM_WLAN_MODE_80211AN
} dladm_wlan_mode_t;
typedef enum {
diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386
index 35a1da4e18..e8a62d448c 100644
--- a/usr/src/pkgdefs/etc/exception_list_i386
+++ b/usr/src/pkgdefs/etc/exception_list_i386
@@ -670,8 +670,9 @@ usr/include/sys/1394/t1394.h i386
usr/include/sys/1394/id1394.h i386
# Private net80211 headers
usr/include/sys/net80211.h i386
-usr/include/sys/net80211_proto.h i386
usr/include/sys/net80211_crypto.h i386
+usr/include/sys/net80211_ht.h i386
+usr/include/sys/net80211_proto.h i386
#
# PPPoE files not delivered to customers.
usr/include/net/pppoe.h i386
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc
index 073235cd4b..2837635c65 100644
--- a/usr/src/pkgdefs/etc/exception_list_sparc
+++ b/usr/src/pkgdefs/etc/exception_list_sparc
@@ -677,8 +677,9 @@ usr/include/net/simnet.h sparc
usr/include/net/wpa.h sparc
# Private net80211 headers
usr/include/sys/net80211.h sparc
-usr/include/sys/net80211_proto.h sparc
usr/include/sys/net80211_crypto.h sparc
+usr/include/sys/net80211_ht.h sparc
+usr/include/sys/net80211_proto.h sparc
#
# User<->kernel interface used by cfgadm/USB only
#
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index 3d4d975854..a7f0e2ef49 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -625,7 +625,8 @@ SOFTMAC_OBJS += softmac_main.o softmac_ctl.o softmac_capab.o \
NET80211_OBJS += net80211.o net80211_proto.o net80211_input.o \
net80211_output.o net80211_node.o net80211_crypto.o \
net80211_crypto_none.o net80211_crypto_wep.o net80211_ioctl.o \
- net80211_crypto_tkip.o net80211_crypto_ccmp.o
+ net80211_crypto_tkip.o net80211_crypto_ccmp.o \
+ net80211_ht.o
VNIC_OBJS += vnic_ctl.o vnic_dev.o
diff --git a/usr/src/uts/common/inet/wifi_ioctl.h b/usr/src/uts/common/inet/wifi_ioctl.h
index 985e1d2f86..08b780994b 100644
--- a/usr/src/uts/common/inet/wifi_ioctl.h
+++ b/usr/src/uts/common/inet/wifi_ioctl.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,8 +30,6 @@
#ifndef __WIFI_IOCTL_H
#define __WIFI_IOCTL_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#ifdef __cplusplus
@@ -262,6 +260,7 @@ typedef struct wl_ofdm {
uint32_t wl_ofdm_subtype;
uint32_t wl_ofdm_frequency;
uint32_t wl_ofdm_freq_supported;
+ boolean_t wl_ofdm_ht_enabled;
} wl_ofdm_t;
typedef struct wl_erp {
@@ -277,6 +276,7 @@ typedef struct wl_erp {
boolean_t wl_erp_dsss_ofdm_enabled;
boolean_t wl_erp_have_sst;
boolean_t wl_erp_sst_enabled;
+ boolean_t wl_erp_ht_enabled;
} wl_erp_t;
typedef union wl_phy_conf {
diff --git a/usr/src/uts/common/io/arn/arn_core.h b/usr/src/uts/common/io/arn/arn_core.h
index 01b7df10b3..f85290ab6e 100644
--- a/usr/src/uts/common/io/arn/arn_core.h
+++ b/usr/src/uts/common/io/arn/arn_core.h
@@ -359,11 +359,6 @@ void arn_setdefantenna(struct arn_softc *sc, uint32_t antenna);
#define WME_BA_BMP_SIZE 64
#define WME_MAX_BA WME_BA_BMP_SIZE
#define ATH_TID_MAX_BUFS (2 * WME_MAX_BA)
-#define TID_TO_WME_AC(_tid) \
- ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \
- (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \
- (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \
- WME_AC_VO)
/* Wireless Multimedia Extension Defines */
#define WME_AC_BE 0 /* best effort */
diff --git a/usr/src/uts/common/io/iwh/iwh.c b/usr/src/uts/common/io/iwh/iwh.c
index c951e9de1c..4c283648ee 100644
--- a/usr/src/uts/common/io/iwh/iwh.c
+++ b/usr/src/uts/common/io/iwh/iwh.c
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
* All rights reserved.
*/
@@ -52,6 +52,7 @@
#include <sys/mac_wifi.h>
#include <sys/net80211.h>
#include <sys/net80211_proto.h>
+#include <sys/net80211_ht.h>
#include <sys/varargs.h>
#include <sys/policy.h>
#include <sys/pci.h>
@@ -79,6 +80,11 @@
#define IWH_DEBUG_RADIO (1 << 13)
#define IWH_DEBUG_RESUME (1 << 14)
#define IWH_DEBUG_CALIBRATION (1 << 15)
+#define IWH_DEBUG_BA (1 << 16)
+#define IWH_DEBUG_RXON (1 << 17)
+#define IWH_DEBUG_HWRATE (1 << 18)
+#define IWH_DEBUG_HTRATE (1 << 19)
+#define IWH_DEBUG_QOS (1 << 20)
/*
* if want to see debug message of a given section,
* please set this flag to one of above values
@@ -90,6 +96,8 @@ uint32_t iwh_dbg_flags = 0;
#define IWH_DBG(x)
#endif
+#define MS(v, f) (((v) & f) >> f##_S)
+
static void *iwh_soft_state_p = NULL;
/*
@@ -324,8 +332,34 @@ static int iwh_detach(dev_info_t *, ddi_detach_cmd_t);
static void iwh_destroy_locks(iwh_sc_t *);
static int iwh_send(ieee80211com_t *, mblk_t *, uint8_t);
static void iwh_thread(iwh_sc_t *);
-static int iwh_run_state_config(iwh_sc_t *sc);
-static int iwh_fast_recover(iwh_sc_t *sc);
+static int iwh_run_state_config(iwh_sc_t *);
+static int iwh_fast_recover(iwh_sc_t *);
+static int iwh_wme_update(ieee80211com_t *);
+static int iwh_qosparam_to_hw(iwh_sc_t *, int);
+static int iwh_wme_to_qos_ac(int);
+static uint16_t iwh_cw_e_to_cw(uint8_t);
+static int iwh_wmeparam_check(struct wmeParams *);
+static inline int iwh_wme_tid_qos_ac(int);
+static inline int iwh_qos_ac_to_txq(int);
+static int iwh_wme_tid_to_txq(int);
+static void iwh_init_ht_conf(iwh_sc_t *);
+static void iwh_overwrite_11n_rateset(iwh_sc_t *);
+static void iwh_overwrite_ic_default(iwh_sc_t *);
+static void iwh_config_rxon_chain(iwh_sc_t *);
+static int iwh_add_ap_sta(iwh_sc_t *);
+static int iwh_ap_lq(iwh_sc_t *);
+static void iwh_recv_action(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+static int iwh_send_action(struct ieee80211_node *,
+ int, int, uint16_t[4]);
+static int iwh_is_max_rate(ieee80211_node_t *);
+static int iwh_is_min_rate(ieee80211_node_t *);
+static void iwh_increase_rate(ieee80211_node_t *);
+static void iwh_decrease_rate(ieee80211_node_t *);
+static int iwh_alloc_dma_mem(iwh_sc_t *, size_t,
+ ddi_dma_attr_t *, ddi_device_acc_attr_t *,
+ uint_t, iwh_dma_t *);
+static void iwh_free_dma_mem(iwh_dma_t *);
/*
* GLD specific operations
@@ -346,7 +380,6 @@ static int iwh_m_getprop(void *arg, const char *pr_name,
/*
* Supported rates for 802.11b/g modes (in 500Kbps unit).
- * 11n support will be added later.
*/
static const struct ieee80211_rateset iwh_rateset_11b =
{ 4, { 2, 4, 11, 22 } };
@@ -355,6 +388,11 @@ static const struct ieee80211_rateset iwh_rateset_11g =
{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
/*
+ * Default 11n reates supported by this station.
+ */
+extern struct ieee80211_htrateset ieee80211_rateset_11n;
+
+/*
* For mfthread only
*/
extern pri_t minclsyspri;
@@ -452,7 +490,7 @@ iwh_dbg(uint32_t flags, const char *fmt, ...)
va_end(ap);
}
}
-#endif
+#endif /* DEBUG */
/*
* device operations
@@ -462,41 +500,41 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
iwh_sc_t *sc;
ieee80211com_t *ic;
- int instance, err, i;
+ int instance, i;
char strbuf[32];
wifi_data_t wd = { 0 };
mac_register_t *macp;
int intr_type;
int intr_count;
int intr_actual;
+ int err = DDI_FAILURE;
switch (cmd) {
case DDI_ATTACH:
break;
+
case DDI_RESUME:
+ instance = ddi_get_instance(dip);
sc = ddi_get_soft_state(iwh_soft_state_p,
- ddi_get_instance(dip));
+ instance);
ASSERT(sc != NULL);
- mutex_enter(&sc->sc_glock);
+ mutex_enter(&sc->sc_suspend_lock);
sc->sc_flags &= ~IWH_F_SUSPEND;
- mutex_exit(&sc->sc_glock);
- if (sc->sc_flags & IWH_F_RUNNING)
+ if (sc->sc_flags & IWH_F_RUNNING) {
(void) iwh_init(sc);
+ }
+ mutex_exit(&sc->sc_suspend_lock);
- mutex_enter(&sc->sc_glock);
- sc->sc_flags |= IWH_F_LAZY_RESUME;
- mutex_exit(&sc->sc_glock);
-
- IWH_DBG((IWH_DEBUG_RESUME, "iwh: resume\n"));
+ IWH_DBG((IWH_DEBUG_RESUME, "iwh_attach(): "
+ "resume\n"));
return (DDI_SUCCESS);
+
default:
- err = DDI_FAILURE;
goto attach_fail1;
}
-
instance = ddi_get_instance(dip);
err = ddi_soft_state_zalloc(iwh_soft_state_p, instance);
if (err != DDI_SUCCESS) {
@@ -504,7 +542,10 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
"failed to allocate soft state\n");
goto attach_fail1;
}
+
sc = ddi_get_soft_state(iwh_soft_state_p, instance);
+ ASSERT(sc != NULL);
+
sc->sc_dip = dip;
/*
@@ -518,6 +559,20 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
goto attach_fail2;
}
+ sc->sc_dev_id = ddi_get16(sc->sc_cfg_handle,
+ (uint16_t *)(sc->sc_cfg_base + PCI_CONF_DEVID));
+ if ((sc->sc_dev_id != 0x4232) &&
+ (sc->sc_dev_id != 0x4235) &&
+ (sc->sc_dev_id != 0x4236) &&
+ (sc->sc_dev_id != 0x4237) &&
+ (sc->sc_dev_id != 0x423a)) {
+ cmn_err(CE_WARN, "iwh_attach(): "
+ "Do not support this device\n");
+ goto attach_fail3;
+ }
+
+ iwh_init_ht_conf(sc);
+ iwh_overwrite_11n_rateset(sc);
sc->sc_rev = ddi_get8(sc->sc_cfg_handle,
(uint8_t *)(sc->sc_cfg_base + PCI_CONF_REVID));
@@ -526,17 +581,18 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
* keep from disturbing C3 state of CPU
*/
ddi_put8(sc->sc_cfg_handle, (uint8_t *)(sc->sc_cfg_base + 0x41), 0);
+
+ /*
+ * determine the size of buffer for frame and command to ucode
+ */
sc->sc_clsz = ddi_get16(sc->sc_cfg_handle,
(uint16_t *)(sc->sc_cfg_base + PCI_CONF_CACHE_LINESZ));
if (!sc->sc_clsz) {
sc->sc_clsz = 16;
}
-
- /*
- * determine the size of buffer for frame and command to ucode
- */
sc->sc_clsz = (sc->sc_clsz << 2);
- sc->sc_dmabuf_sz = roundup(0x1000 + sizeof (struct ieee80211_frame) +
+
+ sc->sc_dmabuf_sz = roundup(0x2000 + sizeof (struct ieee80211_frame) +
IEEE80211_MTU + IEEE80211_CRC_LEN +
(IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
IEEE80211_WEP_CRCLEN), sc->sc_clsz);
@@ -594,10 +650,9 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
DDI_INTR_PRI(sc->sc_intr_pri));
mutex_init(&sc->sc_mt_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(sc->sc_intr_pri));
- mutex_init(&sc->sc_ucode_lock, NULL, MUTEX_DRIVER,
+ mutex_init(&sc->sc_suspend_lock, NULL, MUTEX_DRIVER,
DDI_INTR_PRI(sc->sc_intr_pri));
-
cv_init(&sc->sc_fw_cv, NULL, CV_DRIVER, NULL);
cv_init(&sc->sc_cmd_cv, NULL, CV_DRIVER, NULL);
cv_init(&sc->sc_tx_cv, "tx-ring", CV_DRIVER, NULL);
@@ -647,14 +702,15 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
* get hardware configurations from eeprom
*/
err = iwh_eep_load(sc);
- if (err != 0) {
+ if (err != IWH_SUCCESS) {
cmn_err(CE_WARN, "iwh_attach(): "
"failed to load eeprom\n");
goto attach_fail9;
}
if (IWH_READ_EEP_SHORT(sc, EEP_VERSION) < 0x011a) {
- IWH_DBG((IWH_DEBUG_EEPROM, "unsupported eeprom detected"));
+ IWH_DBG((IWH_DEBUG_EEPROM, "iwh_attach(): "
+ "unsupported eeprom detected\n"));
goto attach_fail9;
}
@@ -696,7 +752,7 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
* 802.11 module
*/
ic = &sc->sc_ic;
- ic->ic_phytype = IEEE80211_T_OFDM;
+ ic->ic_phytype = IEEE80211_T_HT;
ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
ic->ic_state = IEEE80211_S_INIT;
ic->ic_maxrssi = 100; /* experimental number */
@@ -709,6 +765,21 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ic->ic_caps |= IEEE80211_C_WPA;
/*
+ * Support QoS/WME
+ */
+ ic->ic_caps |= IEEE80211_C_WME;
+ ic->ic_wme.wme_update = iwh_wme_update;
+
+ /*
+ * Support 802.11n/HT
+ */
+ if (sc->sc_ht_conf.ht_support) {
+ ic->ic_htcaps = IEEE80211_HTC_HT |
+ IEEE80211_HTC_AMSDU;
+ ic->ic_htcaps |= IEEE80211_HTCAP_MAXAMSDU_7935;
+ }
+
+ /*
* set supported .11b and .11g rates
*/
ic->ic_sup_rates[IEEE80211_MODE_11B] = iwh_rateset_11b;
@@ -724,6 +795,14 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ |
IEEE80211_CHAN_PASSIVE;
+
+ if (sc->sc_ht_conf.cap & HT_CAP_SUP_WIDTH) {
+ ic->ic_sup_channels[i].ich_flags |=
+ IEEE80211_CHAN_HT40;
+ } else {
+ ic->ic_sup_channels[i].ich_flags |=
+ IEEE80211_CHAN_HT20;
+ }
}
ic->ic_ibss_chan = &ic->ic_sup_channels[0];
@@ -742,12 +821,9 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
ddi_get_instance(dip));
/*
- * Override 80211 default routines
+ * Overwrite 80211 default configurations.
*/
- sc->sc_newstate = ic->ic_newstate;
- ic->ic_newstate = iwh_newstate;
- ic->ic_node_alloc = iwh_node_alloc;
- ic->ic_node_free = iwh_node_free;
+ iwh_overwrite_ic_default(sc);
/*
* initialize 802.11 module
@@ -793,7 +869,7 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
* create relation to GLD
*/
macp = mac_alloc(MAC_VERSION);
- if (macp == NULL) {
+ if (NULL == macp) {
cmn_err(CE_WARN, "iwh_attach(): "
"failed to do mac_alloc()\n");
goto attach_fail15;
@@ -826,9 +902,10 @@ iwh_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
(void) snprintf(strbuf, sizeof (strbuf), DRV_NAME_SP"%d", instance);
err = ddi_create_minor_node(dip, strbuf, S_IFCHR,
instance + 1, DDI_NT_NET_WIFI, 0);
- if (err != DDI_SUCCESS)
+ if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "iwh_attach(): "
"failed to do ddi_create_minor_node()\n");
+ }
/*
* Notify link is down now
@@ -893,32 +970,41 @@ attach_fail2:
ddi_soft_state_free(iwh_soft_state_p, instance);
attach_fail1:
- return (err);
+ return (DDI_FAILURE);
}
int
iwh_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
iwh_sc_t *sc;
+ ieee80211com_t *ic;
int err;
sc = ddi_get_soft_state(iwh_soft_state_p, ddi_get_instance(dip));
ASSERT(sc != NULL);
+ ic = &sc->sc_ic;
switch (cmd) {
case DDI_DETACH:
break;
+
case DDI_SUSPEND:
- mutex_enter(&sc->sc_glock);
+ mutex_enter(&sc->sc_suspend_lock);
sc->sc_flags |= IWH_F_SUSPEND;
- mutex_exit(&sc->sc_glock);
if (sc->sc_flags & IWH_F_RUNNING) {
iwh_stop(sc);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+
+ sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
+ sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
}
+ mutex_exit(&sc->sc_suspend_lock);
- IWH_DBG((IWH_DEBUG_RESUME, "iwh: suspend\n"));
+ IWH_DBG((IWH_DEBUG_RESUME, "iwh_detach(): "
+ "suspend\n"));
return (DDI_SUCCESS);
+
default:
return (DDI_FAILURE);
}
@@ -926,23 +1012,25 @@ iwh_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
if (!(sc->sc_flags & IWH_F_ATTACHED)) {
return (DDI_FAILURE);
}
- err = mac_disable(sc->sc_ic.ic_mach);
- if (err != DDI_SUCCESS)
- return (err);
/*
* Destroy the mf_thread
*/
- mutex_enter(&sc->sc_mt_lock);
sc->sc_mf_thread_switch = 0;
+
+ mutex_enter(&sc->sc_mt_lock);
while (sc->sc_mf_thread != NULL) {
if (cv_wait_sig(&sc->sc_mt_cv, &sc->sc_mt_lock) == 0) {
break;
}
}
-
mutex_exit(&sc->sc_mt_lock);
+ err = mac_disable(sc->sc_ic.ic_mach);
+ if (err != DDI_SUCCESS) {
+ return (err);
+ }
+
/*
* stop chipset
*/
@@ -967,7 +1055,6 @@ iwh_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
iwh_free_shared(sc);
mutex_exit(&sc->sc_glock);
-
(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]);
@@ -976,7 +1063,6 @@ iwh_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
(void) ddi_intr_remove_softint(sc->sc_soft_hdl);
sc->sc_soft_hdl = NULL;
-
/*
* detach from 80211 module
*/
@@ -1007,7 +1093,7 @@ iwh_destroy_locks(iwh_sc_t *sc)
mutex_destroy(&sc->sc_mt_lock);
mutex_destroy(&sc->sc_tx_lock);
mutex_destroy(&sc->sc_glock);
- mutex_destroy(&sc->sc_ucode_lock);
+ mutex_destroy(&sc->sc_suspend_lock);
}
/*
@@ -1019,7 +1105,7 @@ iwh_alloc_dma_mem(iwh_sc_t *sc, size_t memsize,
uint_t dma_flags, iwh_dma_t *dma_p)
{
caddr_t vaddr;
- int err;
+ int err = DDI_FAILURE;
/*
* Allocate handle
@@ -1093,7 +1179,7 @@ iwh_free_dma_mem(iwh_dma_t *dma_p)
static int
iwh_alloc_fw_dma(iwh_sc_t *sc)
{
- int err = DDI_SUCCESS;
+ int err = DDI_FAILURE;
iwh_dma_t *dma_p;
char *t;
@@ -1110,19 +1196,19 @@ iwh_alloc_fw_dma(iwh_sc_t *sc)
&fw_dma_attr, &iwh_dma_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&sc->sc_dma_fw_text);
-
- dma_p = &sc->sc_dma_fw_text;
-
- IWH_DBG((IWH_DEBUG_DMA, "text[ncookies:%d addr:%lx size:%lx]\n",
- dma_p->ncookies, dma_p->cookie.dmac_address,
- dma_p->cookie.dmac_size));
-
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
"failed to allocate text dma memory.\n");
goto fail;
}
+ dma_p = &sc->sc_dma_fw_text;
+
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+ "text[ncookies:%d addr:%lx size:%lx]\n",
+ dma_p->ncookies, dma_p->cookie.dmac_address,
+ dma_p->cookie.dmac_size));
+
(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->textsz));
/*
@@ -1133,39 +1219,39 @@ iwh_alloc_fw_dma(iwh_sc_t *sc)
&fw_dma_attr, &iwh_dma_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&sc->sc_dma_fw_data);
-
- dma_p = &sc->sc_dma_fw_data;
-
- IWH_DBG((IWH_DEBUG_DMA, "data[ncookies:%d addr:%lx size:%lx]\n",
- dma_p->ncookies, dma_p->cookie.dmac_address,
- dma_p->cookie.dmac_size));
-
if (err != DDI_SUCCESS) {
cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
"failed to allocate data dma memory\n");
goto fail;
}
+ dma_p = &sc->sc_dma_fw_data;
+
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+ "data[ncookies:%d addr:%lx size:%lx]\n",
+ dma_p->ncookies, dma_p->cookie.dmac_address,
+ dma_p->cookie.dmac_size));
+
(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz));
err = iwh_alloc_dma_mem(sc, LE_32(sc->sc_hdr->datasz),
&fw_dma_attr, &iwh_dma_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&sc->sc_dma_fw_data_bak);
+ if (err != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
+ "failed to allocate data bakup dma memory\n");
+ goto fail;
+ }
dma_p = &sc->sc_dma_fw_data_bak;
- IWH_DBG((IWH_DEBUG_DMA, "data_bak[ncookies:%d addr:%lx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+ "data_bak[ncookies:%d addr:%lx "
"size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
- if (err != DDI_SUCCESS) {
- cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
- "failed to allocate data bakup dma memory\n");
- goto fail;
- }
-
(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->datasz));
/*
@@ -1176,20 +1262,20 @@ iwh_alloc_fw_dma(iwh_sc_t *sc)
&fw_dma_attr, &iwh_dma_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&sc->sc_dma_fw_init_text);
+ if (err != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
+ "failed to allocate init text dma memory\n");
+ goto fail;
+ }
dma_p = &sc->sc_dma_fw_init_text;
- IWH_DBG((IWH_DEBUG_DMA, "init_text[ncookies:%d addr:%lx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+ "init_text[ncookies:%d addr:%lx "
"size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
- if (err != DDI_SUCCESS) {
- cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
- "failed to allocate init text dma memory\n");
- goto fail;
- }
-
(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_textsz));
/*
@@ -1200,20 +1286,20 @@ iwh_alloc_fw_dma(iwh_sc_t *sc)
&fw_dma_attr, &iwh_dma_accattr,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&sc->sc_dma_fw_init_data);
+ if (err != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
+ "failed to allocate init data dma memory\n");
+ goto fail;
+ }
dma_p = &sc->sc_dma_fw_init_data;
- IWH_DBG((IWH_DEBUG_DMA, "init_data[ncookies:%d addr:%lx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_fw_dma(): "
+ "init_data[ncookies:%d addr:%lx "
"size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
- if (err != DDI_SUCCESS) {
- cmn_err(CE_WARN, "iwh_alloc_fw_dma(): "
- "failed to allocate init data dma memory\n");
- goto fail;
- }
-
(void) memcpy(dma_p->mem_va, t, LE_32(sc->sc_hdr->init_datasz));
sc->sc_boot = t + LE_32(sc->sc_hdr->init_datasz);
@@ -1240,7 +1326,7 @@ iwh_alloc_shared(iwh_sc_t *sc)
#ifdef DEBUG
iwh_dma_t *dma_p;
#endif
- int err = DDI_SUCCESS;
+ int err = DDI_FAILURE;
/*
* must be aligned on a 4K-page boundary
@@ -1258,7 +1344,8 @@ iwh_alloc_shared(iwh_sc_t *sc)
#ifdef DEBUG
dma_p = &sc->sc_dma_sh;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "sh[ncookies:%d addr:%lx size:%lx]\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_shared(): "
+ "sh[ncookies:%d addr:%lx size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1283,7 +1370,7 @@ iwh_alloc_kw(iwh_sc_t *sc)
#ifdef DEBUG
iwh_dma_t *dma_p;
#endif
- int err = DDI_SUCCESS;
+ int err = DDI_FAILURE;
/*
* must be aligned on a 4K-page boundary
@@ -1299,7 +1386,8 @@ iwh_alloc_kw(iwh_sc_t *sc)
#ifdef DEBUG
dma_p = &sc->sc_dma_kw;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "kw[ncookies:%d addr:%lx size:%lx]\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_kw(): "
+ "kw[ncookies:%d addr:%lx size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1326,7 +1414,7 @@ iwh_alloc_rx_ring(iwh_sc_t *sc)
#ifdef DEBUG
iwh_dma_t *dma_p;
#endif
- int i, err = DDI_SUCCESS;
+ int i, err = DDI_FAILURE;
ring = &sc->sc_rxq;
ring->cur = 0;
@@ -1339,7 +1427,8 @@ iwh_alloc_rx_ring(iwh_sc_t *sc)
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&ring->dma_desc);
if (err != DDI_SUCCESS) {
- IWH_DBG((IWH_DEBUG_DMA, "dma alloc rx ring desc "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+ "dma alloc rx ring desc "
"failed\n"));
goto fail;
}
@@ -1348,7 +1437,8 @@ iwh_alloc_rx_ring(iwh_sc_t *sc)
#ifdef DEBUG
dma_p = &ring->dma_desc;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "rx bd[ncookies:%d addr:%lx size:%lx]\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+ "rx bd[ncookies:%d addr:%lx size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1362,7 +1452,8 @@ iwh_alloc_rx_ring(iwh_sc_t *sc)
DDI_DMA_READ | DDI_DMA_STREAMING,
&data->dma_data);
if (err != DDI_SUCCESS) {
- IWH_DBG((IWH_DEBUG_DMA, "dma alloc rx ring "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+ "dma alloc rx ring "
"buf[%d] failed\n", i));
goto fail;
}
@@ -1377,7 +1468,8 @@ iwh_alloc_rx_ring(iwh_sc_t *sc)
#ifdef DEBUG
dma_p = &ring->data[0].dma_data;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "rx buffer[0][ncookies:%d addr:%lx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_rx_ring(): "
+ "rx buffer[0][ncookies:%d addr:%lx "
"size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1409,7 +1501,8 @@ iwh_reset_rx_ring(iwh_sc_t *sc)
}
#ifdef DEBUG
if (2000 == n) {
- IWH_DBG((IWH_DEBUG_DMA, "timeout resetting Rx ring\n"));
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_reset_rx_ring(): "
+ "timeout resetting Rx ring\n"));
}
#endif
iwh_mac_access_exit(sc);
@@ -1453,8 +1546,7 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
#ifdef DEBUG
iwh_dma_t *dma_p;
#endif
- int i, err = DDI_SUCCESS;
-
+ int i, err = DDI_FAILURE;
ring->qid = qid;
ring->count = TFD_QUEUE_SIZE_MAX;
ring->window = slots;
@@ -1470,15 +1562,17 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&ring->dma_desc);
if (err != DDI_SUCCESS) {
- IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx ring desc[%d]"
- " failed\n", qid));
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "dma alloc tx ring desc[%d] "
+ "failed\n", qid));
goto fail;
}
#ifdef DEBUG
dma_p = &ring->dma_desc;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "tx bd[ncookies:%d addr:%lx size:%lx]\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "tx bd[ncookies:%d addr:%lx size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1494,7 +1588,8 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
DDI_DMA_RDWR | DDI_DMA_CONSISTENT,
&ring->dma_cmd);
if (err != DDI_SUCCESS) {
- IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx ring cmd[%d]"
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "dma alloc tx ring cmd[%d]"
" failed\n", qid));
goto fail;
}
@@ -1502,7 +1597,8 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
#ifdef DEBUG
dma_p = &ring->dma_cmd;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "tx cmd[ncookies:%d addr:%lx size:%lx]\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "tx cmd[ncookies:%d addr:%lx size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1515,7 +1611,8 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
ring->data = kmem_zalloc(sizeof (iwh_tx_data_t) * TFD_QUEUE_SIZE_MAX,
KM_NOSLEEP);
if (NULL == ring->data) {
- IWH_DBG((IWH_DEBUG_DMA, "could not allocate "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "could not allocate "
"tx data slots\n"));
goto fail;
}
@@ -1527,7 +1624,8 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
DDI_DMA_WRITE | DDI_DMA_STREAMING,
&data->dma_data);
if (err != DDI_SUCCESS) {
- IWH_DBG((IWH_DEBUG_DMA, "dma alloc tx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "dma alloc tx "
"ring buf[%d] failed\n", i));
goto fail;
}
@@ -1535,15 +1633,15 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
data->desc = desc_h + i;
data->paddr_desc = paddr_desc_h +
_PTRDIFF(data->desc, desc_h);
- data->cmd = cmd_h + i; /* (i % slots); */
+ data->cmd = cmd_h + i;
data->paddr_cmd = paddr_cmd_h +
_PTRDIFF(data->cmd, cmd_h);
- /* ((i % slots) * sizeof (iwh_cmd_t)); */
}
#ifdef DEBUG
dma_p = &ring->data[0].dma_data;
#endif
- IWH_DBG((IWH_DEBUG_DMA, "tx buffer[0][ncookies:%d addr:%lx "
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_alloc_tx_ring(): "
+ "tx buffer[0][ncookies:%d addr:%lx "
"size:%lx]\n",
dma_p->ncookies, dma_p->cookie.dmac_address,
dma_p->cookie.dmac_size));
@@ -1551,11 +1649,6 @@ iwh_alloc_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring,
return (err);
fail:
- if (ring->data) {
- kmem_free(ring->data,
- sizeof (iwh_tx_data_t) * TFD_QUEUE_SIZE_MAX);
- }
-
iwh_free_tx_ring(ring);
return (err);
@@ -1580,10 +1673,15 @@ iwh_reset_tx_ring(iwh_sc_t *sc, iwh_tx_ring_t *ring)
}
DELAY(10);
}
+
+#ifdef DEBUG
if (200 == n) {
- IWH_DBG((IWH_DEBUG_DMA, "timeout reset tx ring %d\n",
+ IWH_DBG((IWH_DEBUG_DMA, "iwh_reset_tx_ring(): "
+ "timeout reset tx ring %d\n",
ring->qid));
}
+#endif
+
iwh_mac_access_exit(sc);
/* by pass, if it's quiesce */
@@ -1631,7 +1729,7 @@ iwh_free_tx_ring(iwh_tx_ring_t *ring)
static int
iwh_ring_init(iwh_sc_t *sc)
{
- int i, err = DDI_SUCCESS;
+ int i, err = DDI_FAILURE;
for (i = 0; i < IWH_NUM_QUEUES; i++) {
if (IWH_CMD_QUEUE_NUM == i) {
@@ -1676,36 +1774,57 @@ iwh_ring_free(iwh_sc_t *sc)
}
}
-/*
- * allocate buffer for a node
- */
-/*ARGSUSED*/
+/* ARGSUSED */
static ieee80211_node_t *
iwh_node_alloc(ieee80211com_t *ic)
{
iwh_amrr_t *amrr;
amrr = kmem_zalloc(sizeof (iwh_amrr_t), KM_SLEEP);
- if (amrr != NULL) {
- iwh_amrr_init(amrr);
+ if (NULL == amrr) {
+ cmn_err(CE_WARN, "iwh_node_alloc(): "
+ "failed to allocate memory for amrr structure\n");
+ return (NULL);
}
+ iwh_amrr_init(amrr);
+
return (&amrr->in);
}
static void
iwh_node_free(ieee80211_node_t *in)
{
- ieee80211com_t *ic = in->in_ic;
+ ieee80211com_t *ic;
+
+ if ((NULL == in) ||
+ (NULL == in->in_ic)) {
+ cmn_err(CE_WARN, "iwh_node_free() "
+ "Got a NULL point from Net80211 module\n");
+ return;
+ }
+ ic = in->in_ic;
+
+ if (ic->ic_node_cleanup != NULL) {
+ ic->ic_node_cleanup(in);
+ }
- ic->ic_node_cleanup(in);
if (in->in_wpa_ie != NULL) {
ieee80211_free(in->in_wpa_ie);
}
+ if (in->in_wme_ie != NULL) {
+ ieee80211_free(in->in_wme_ie);
+ }
+
+ if (in->in_htcap_ie != NULL) {
+ ieee80211_free(in->in_htcap_ie);
+ }
+
kmem_free(in, sizeof (iwh_amrr_t));
}
+
/*
* change station's state. this function will be invoked by 80211 module
* when need to change staton's state.
@@ -1713,10 +1832,20 @@ iwh_node_free(ieee80211_node_t *in)
static int
iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
{
- iwh_sc_t *sc = (iwh_sc_t *)ic;
- ieee80211_node_t *in = ic->ic_bss;
- enum ieee80211_state ostate = ic->ic_state;
- int i, err = IWH_SUCCESS;
+ iwh_sc_t *sc;
+ ieee80211_node_t *in;
+ enum ieee80211_state ostate;
+ iwh_add_sta_t node;
+ iwh_amrr_t *amrr;
+ uint8_t r;
+ int i, err = IWH_FAIL;
+
+ if (NULL == ic) {
+ return (err);
+ }
+ sc = (iwh_sc_t *)ic;
+ in = ic->ic_bss;
+ ostate = ic->ic_state;
mutex_enter(&sc->sc_glock);
@@ -1724,8 +1853,6 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
case IEEE80211_S_SCAN:
switch (ostate) {
case IEEE80211_S_INIT:
- {
- iwh_add_sta_t node;
sc->sc_flags |= IWH_F_SCANNING;
iwh_set_led(sc, 2, 10, 2);
@@ -1737,7 +1864,8 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
sc->sc_config.filter_flags &=
~LE_32(RXON_FILTER_ASSOC_MSK);
- IWH_DBG((IWH_DEBUG_80211, "config chan %d "
+ IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
+ "config chan %d "
"flags %x filter_flags %x\n",
LE_16(sc->sc_config.chan),
LE_32(sc->sc_config.flags),
@@ -1746,7 +1874,7 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
err = iwh_cmd(sc, REPLY_RXON, &sc->sc_config,
sizeof (iwh_rxon_cmd_t), 1);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN,
+ cmn_err(CE_WARN, "iwh_newstate(): "
"could not clear association\n");
sc->sc_flags &= ~IWH_F_SCANNING;
mutex_exit(&sc->sc_glock);
@@ -1760,21 +1888,20 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
err = iwh_cmd(sc, REPLY_ADD_STA, &node,
sizeof (node), 1);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "could not add "
- "broadcast node\n");
+ cmn_err(CE_WARN, "iwh_newstate(): "
+ "could not add broadcast node\n");
sc->sc_flags &= ~IWH_F_SCANNING;
mutex_exit(&sc->sc_glock);
return (err);
}
break;
- }
case IEEE80211_S_SCAN:
mutex_exit(&sc->sc_glock);
/* step to next channel before actual FW scan */
err = sc->sc_newstate(ic, nstate, arg);
mutex_enter(&sc->sc_glock);
if ((err != 0) || ((err = iwh_scan(sc)) != 0)) {
- cmn_err(CE_WARN,
+ cmn_err(CE_WARN, "iwh_newstate(): "
"could not initiate scan\n");
sc->sc_flags &= ~IWH_F_SCANNING;
ieee80211_cancel_scan(ic);
@@ -1804,7 +1931,7 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
* channel same to the target AP...
*/
if ((err = iwh_hw_set_before_auth(sc)) != 0) {
- IWH_DBG((IWH_DEBUG_80211,
+ IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
"could not send authentication request\n"));
mutex_exit(&sc->sc_glock);
return (err);
@@ -1822,7 +1949,8 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
break;
}
- IWH_DBG((IWH_DEBUG_80211, "iwh: associated."));
+ IWH_DBG((IWH_DEBUG_80211, "iwh_newstate(): "
+ "associated.\n"));
err = iwh_run_state_config(sc);
if (err != IWH_SUCCESS) {
@@ -1835,22 +1963,41 @@ iwh_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
/*
* start automatic rate control
*/
- mutex_enter(&sc->sc_mt_lock);
- if (IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) {
- sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
- /*
- * set rate to some reasonable initial value
- */
- i = in->in_rates.ir_nrates - 1;
- while (i > 0 && IEEE80211_RATE(i) > 72) {
- i--;
+ if ((in->in_flags & IEEE80211_NODE_HT) &&
+ (sc->sc_ht_conf.ht_support) &&
+ (in->in_htrates.rs_nrates > 0) &&
+ (in->in_htrates.rs_nrates <= IEEE80211_HTRATE_MAXSIZE)) {
+ amrr = (iwh_amrr_t *)in;
+
+ for (i = in->in_htrates.rs_nrates - 1; i > 0; i--) {
+
+ r = in->in_htrates.rs_rates[i] &
+ IEEE80211_RATE_VAL;
+ if ((r != 0) && (r <= 0xd) &&
+ (sc->sc_ht_conf.tx_support_mcs[r/8] &
+ (1 << (r%8)))) {
+ amrr->ht_mcs_idx = r;
+ sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
+ break;
+ }
}
- in->in_txrate = i;
} else {
- sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
- }
+ if (IEEE80211_FIXED_RATE_NONE == ic->ic_fixed_rate) {
+ sc->sc_flags |= IWH_F_RATE_AUTO_CTL;
- mutex_exit(&sc->sc_mt_lock);
+ /*
+ * set rate to some reasonable initial value
+ */
+ i = in->in_rates.ir_nrates - 1;
+ while (i > 0 && IEEE80211_RATE(i) > 72) {
+ i--;
+ }
+ in->in_txrate = i;
+
+ } else {
+ sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
+ }
+ }
/*
* set LED on after associated
@@ -1906,7 +2053,8 @@ iwh_mac_access_enter(iwh_sc_t *sc)
#ifdef DEBUG
if (1000 == n) {
- IWH_DBG((IWH_DEBUG_PIO, "could not lock memory\n"));
+ IWH_DBG((IWH_DEBUG_PIO, "iwh_mac_access_enter(): "
+ "could not lock memory\n"));
}
#endif
}
@@ -1973,7 +2121,7 @@ iwh_reg_write(iwh_sc_t *sc, uint32_t addr, uint32_t data)
static int
iwh_load_init_firmware(iwh_sc_t *sc)
{
- int err;
+ int err = IWH_FAIL;
clock_t clk;
sc->sc_flags &= ~IWH_F_PUT_SEG;
@@ -2042,7 +2190,7 @@ iwh_load_init_firmware(iwh_sc_t *sc)
static int
iwh_load_run_firmware(iwh_sc_t *sc)
{
- int err;
+ int err = IWH_FAIL;
clock_t clk;
sc->sc_flags &= ~IWH_F_PUT_SEG;
@@ -2189,7 +2337,8 @@ iwh_rx_mpdu_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
sizeof (struct iwh_rx_mpdu_body_size) + len);
bcopy(tail, &crc, 4);
- IWH_DBG((IWH_DEBUG_RX, "rx intr: idx=%d phy_len=%x len=%d "
+ IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+ "rx intr: idx=%d phy_len=%x len=%d "
"rate=%x chan=%d tstamp=%x non_cfg_phy_count=%x "
"cfg_phy_count=%x tail=%x", ring->cur, sizeof (*stat),
len, stat->rate.r.s.rate, stat->channel,
@@ -2197,7 +2346,8 @@ iwh_rx_mpdu_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
stat->cfg_phy_cnt, LE_32(crc)));
if ((len < 16) || (len > sc->sc_dmabuf_sz)) {
- IWH_DBG((IWH_DEBUG_RX, "rx frame oversize\n"));
+ IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+ "rx frame oversize\n"));
return;
}
@@ -2207,7 +2357,8 @@ iwh_rx_mpdu_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
if ((LE_32(crc) &
(RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) !=
(RX_RES_STATUS_NO_CRC32_ERROR | RX_RES_STATUS_NO_RXE_OVERFLOW)) {
- IWH_DBG((IWH_DEBUG_RX, "rx crc error tail: %x\n",
+ IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+ "rx crc error tail: %x\n",
LE_32(crc)));
sc->sc_rx_err++;
return;
@@ -2218,7 +2369,8 @@ iwh_rx_mpdu_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
if (IEEE80211_FC0_SUBTYPE_ASSOC_RESP == *(uint8_t *)wh) {
sc->sc_assoc_id = *((uint16_t *)(wh + 1) + 2);
- IWH_DBG((IWH_DEBUG_RX, "rx : association id = %x\n",
+ IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+ "rx : association id = %x\n",
sc->sc_assoc_id));
}
@@ -2240,8 +2392,8 @@ iwh_rx_mpdu_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
(void) ieee80211_input(ic, mp, in, rssi, 0);
} else {
sc->sc_rx_nobuf++;
- IWH_DBG((IWH_DEBUG_RX,
- "iwh_rx_mpdu_intr(): alloc rx buf failed\n"));
+ IWH_DBG((IWH_DEBUG_RX, "iwh_rx_mpdu_intr(): "
+ "alloc rx buf failed\n"));
}
/*
@@ -2259,19 +2411,29 @@ iwh_tx_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
ieee80211com_t *ic = &sc->sc_ic;
iwh_tx_ring_t *ring = &sc->sc_txq[desc->hdr.qid & 0x3];
iwh_tx_stat_t *stat = (iwh_tx_stat_t *)(desc + 1);
- iwh_amrr_t *amrr = (iwh_amrr_t *)ic->ic_bss;
+ iwh_amrr_t *amrr;
+
+ if (NULL == ic->ic_bss) {
+ return;
+ }
+
+ amrr = (iwh_amrr_t *)ic->ic_bss;
amrr->txcnt++;
- IWH_DBG((IWH_DEBUG_RATECTL, "tx: %d cnt\n", amrr->txcnt));
+ IWH_DBG((IWH_DEBUG_RATECTL, "iwh_tx_intr(): "
+ "tx: %d cnt\n", amrr->txcnt));
if (stat->ntries > 0) {
amrr->retrycnt++;
sc->sc_tx_retries++;
- IWH_DBG((IWH_DEBUG_TX, "tx: %d retries\n",
+ IWH_DBG((IWH_DEBUG_TX, "iwh_tx_intr(): "
+ "tx: %d retries\n",
sc->sc_tx_retries));
}
+ mutex_enter(&sc->sc_mt_lock);
sc->sc_tx_timer = 0;
+ mutex_exit(&sc->sc_mt_lock);
mutex_enter(&sc->sc_tx_lock);
@@ -2280,7 +2442,7 @@ iwh_tx_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
ring->queued = 0;
}
- if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count << 3))) {
+ if ((sc->sc_need_reschedule) && (ring->queued <= (ring->count >> 3))) {
sc->sc_need_reschedule = 0;
mutex_exit(&sc->sc_tx_lock);
mac_tx_update(ic->ic_mach);
@@ -2300,14 +2462,20 @@ iwh_cmd_intr(iwh_sc_t *sc, iwh_rx_desc_t *desc)
return;
}
+ if (sc->sc_cmd_accum > 0) {
+ sc->sc_cmd_accum--;
+ return;
+ }
+
mutex_enter(&sc->sc_glock);
- sc->sc_flags |= IWH_F_CMD_DONE;
+ sc->sc_cmd_flag = SC_CMD_FLG_DONE;
+
cv_signal(&sc->sc_cmd_cv);
mutex_exit(&sc->sc_glock);
- IWH_DBG((IWH_DEBUG_CMD, "rx cmd: "
+ IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd_intr(): "
"qid=%x idx=%d flags=%x type=0x%x\n",
desc->hdr.qid, desc->hdr.idx, desc->hdr.flags,
desc->hdr.type));
@@ -2328,14 +2496,14 @@ iwh_ucode_alive(iwh_sc_t *sc, iwh_rx_desc_t *desc)
/*
* the microcontroller is ready
*/
- IWH_DBG((IWH_DEBUG_FW,
- "microcode alive notification minor: %x major: %x type:"
- " %x subtype: %x\n",
+ IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
+ "microcode alive notification minor: %x major: %x type: "
+ "%x subtype: %x\n",
ar->ucode_minor, ar->ucode_minor, ar->ver_type, ar->ver_subtype));
#ifdef DEBUG
if (LE_32(ar->is_valid) != UCODE_VALID_OK) {
- IWH_DBG((IWH_DEBUG_FW,
+ IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
"microcontroller initialization failed\n"));
}
#endif
@@ -2344,7 +2512,7 @@ iwh_ucode_alive(iwh_sc_t *sc, iwh_rx_desc_t *desc)
* determine if init alive or runtime alive.
*/
if (INITIALIZE_SUBTYPE == ar->ver_subtype) {
- IWH_DBG((IWH_DEBUG_FW,
+ IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
"initialization alive received.\n"));
(void) memcpy(&sc->sc_card_alive_init, ar,
@@ -2385,7 +2553,8 @@ iwh_ucode_alive(iwh_sc_t *sc, iwh_rx_desc_t *desc)
} else { /* runtime alive */
- IWH_DBG((IWH_DEBUG_FW, "runtime alive received.\n"));
+ IWH_DBG((IWH_DEBUG_FW, "iwh_ucode_alive(): "
+ "runtime alive received.\n"));
(void) memcpy(&sc->sc_card_alive_run, ar,
sizeof (struct iwh_alive_resp));
@@ -2455,10 +2624,10 @@ iwh_ucode_alive(iwh_sc_t *sc, iwh_rx_desc_t *desc)
DELAY(1000);
}
- mutex_exit(&sc->sc_glock);
-
sc->sc_flags |= IWH_F_FW_INIT;
cv_signal(&sc->sc_ucode_cv);
+
+ mutex_exit(&sc->sc_glock);
}
}
@@ -2467,29 +2636,21 @@ iwh_ucode_alive(iwh_sc_t *sc, iwh_rx_desc_t *desc)
* deal with receiving frames, command response
* and all notifications from ucode.
*/
+/* ARGSUSED */
static uint_t
-/* LINTED: argument unused in function: unused */
iwh_rx_softintr(caddr_t arg, caddr_t unused)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
iwh_rx_desc_t *desc;
iwh_rx_data_t *data;
uint32_t index;
- mutex_enter(&sc->sc_glock);
-
- if (sc->sc_rx_softint_pending != 1) {
- mutex_exit(&sc->sc_glock);
+ if (NULL == arg) {
return (DDI_INTR_UNCLAIMED);
}
-
- /*
- * disable interrupts
- */
- IWH_WRITE(sc, CSR_INT_MASK, 0);
-
- mutex_exit(&sc->sc_glock);
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
/*
* firmware has moved the index of the rx queue, driver get it,
@@ -2501,7 +2662,8 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
data = &sc->sc_rxq.data[sc->sc_rxq.cur];
desc = (iwh_rx_desc_t *)data->dma_data.mem_va;
- IWH_DBG((IWH_DEBUG_INTR, "rx notification index = %d"
+ IWH_DBG((IWH_DEBUG_INTR, "iwh_rx_softintr(): "
+ "rx notification index = %d"
" cur = %d qid=%x idx=%d flags=%x type=%x len=%d\n",
index, sc->sc_rxq.cur, desc->hdr.qid, desc->hdr.idx,
desc->hdr.flags, desc->hdr.type, LE_32(desc->len)));
@@ -2510,10 +2672,8 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
* a command other than a tx need to be replied
*/
if (!(desc->hdr.qid & 0x80) &&
- (desc->hdr.type != REPLY_RX_PHY_CMD) &&
- (desc->hdr.type != REPLY_RX_MPDU_CMD) &&
- (desc->hdr.type != REPLY_TX) &&
- (desc->hdr.type != REPLY_PHY_CALIBRATION_CMD)) {
+ (desc->hdr.type != REPLY_SCAN_CMD) &&
+ (desc->hdr.type != REPLY_TX)) {
iwh_cmd_intr(sc, desc);
}
@@ -2538,7 +2698,8 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
{
uint32_t *status = (uint32_t *)(desc + 1);
- IWH_DBG((IWH_DEBUG_RADIO, "state changed to %x\n",
+ IWH_DBG((IWH_DEBUG_RADIO, "iwh_rx_softintr(): "
+ "state changed to %x\n",
LE_32(*status)));
if (LE_32(*status) & 1) {
@@ -2556,6 +2717,7 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
sc->sc_flags |=
(IWH_F_HW_ERR_RECOVER | IWH_F_RADIO_OFF);
}
+
break;
}
@@ -2564,7 +2726,7 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
iwh_start_scan_t *scan =
(iwh_start_scan_t *)(desc + 1);
- IWH_DBG((IWH_DEBUG_SCAN,
+ IWH_DBG((IWH_DEBUG_SCAN, "iwh_rx_softintr(): "
"scanning channel %d status %x\n",
scan->chan, LE_32(scan->status)));
@@ -2574,12 +2736,14 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
case SCAN_COMPLETE_NOTIFICATION:
{
+#ifdef DEBUG
iwh_stop_scan_t *scan =
(iwh_stop_scan_t *)(desc + 1);
- IWH_DBG((IWH_DEBUG_SCAN,
+ IWH_DBG((IWH_DEBUG_SCAN, "iwh_rx_softintr(): "
"completed channel %d (burst of %d) status %02x\n",
scan->chan, scan->nchan, scan->status));
+#endif
sc->sc_scan_pending++;
break;
@@ -2598,8 +2762,10 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
break;
case CALIBRATION_COMPLETE_NOTIFICATION:
+ mutex_enter(&sc->sc_glock);
sc->sc_flags |= IWH_F_FW_INIT;
cv_signal(&sc->sc_ucode_cv);
+ mutex_exit(&sc->sc_glock);
break;
case MISSED_BEACONS_NOTIFICATION:
@@ -2608,7 +2774,7 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
(struct iwh_beacon_missed *)(desc + 1);
if ((ic->ic_state == IEEE80211_S_RUN) &&
- (LE_32(miss->consecutive) > 10)) {
+ (LE_32(miss->consecutive) > 50)) {
cmn_err(CE_NOTE, "iwh: iwh_rx_softintr(): "
"beacon missed %d/%d\n",
LE_32(miss->consecutive),
@@ -2630,15 +2796,10 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
index = (0 == index) ? RX_QUEUE_SIZE - 1 : index - 1;
IWH_WRITE(sc, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, index & (~7));
- mutex_enter(&sc->sc_glock);
-
/*
* re-enable interrupts
*/
IWH_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK);
- sc->sc_rx_softint_pending = 0;
-
- mutex_exit(&sc->sc_glock);
return (DDI_INTR_CLAIMED);
}
@@ -2646,30 +2807,38 @@ iwh_rx_softintr(caddr_t arg, caddr_t unused)
/*
* the handle of interrupt
*/
+/* ARGSUSED */
static uint_t
-/* LINTED: argument unused in function: unused */
iwh_intr(caddr_t arg, caddr_t unused)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
+ iwh_sc_t *sc;
uint32_t r, rfh;
- mutex_enter(&sc->sc_glock);
+ if (NULL == arg) {
+ return (DDI_INTR_UNCLAIMED);
+ }
+ sc = (iwh_sc_t *)arg;
+
+ mutex_enter(&sc->sc_suspend_lock);
if (sc->sc_flags & IWH_F_SUSPEND) {
- mutex_exit(&sc->sc_glock);
+ mutex_exit(&sc->sc_suspend_lock);
return (DDI_INTR_UNCLAIMED);
}
+
r = IWH_READ(sc, CSR_INT);
if (0 == r || 0xffffffff == r) {
- mutex_exit(&sc->sc_glock);
+ mutex_exit(&sc->sc_suspend_lock);
return (DDI_INTR_UNCLAIMED);
}
- IWH_DBG((IWH_DEBUG_INTR, "interrupt reg %x\n", r));
+ IWH_DBG((IWH_DEBUG_INTR, "iwh_intr(): "
+ "interrupt reg %x\n", r));
rfh = IWH_READ(sc, CSR_FH_INT_STATUS);
- IWH_DBG((IWH_DEBUG_INTR, "FH interrupt reg %x\n", rfh));
+ IWH_DBG((IWH_DEBUG_INTR, "iwh_intr(): "
+ "FH interrupt reg %x\n", rfh));
/*
* disable interrupts
@@ -2682,20 +2851,17 @@ iwh_intr(caddr_t arg, caddr_t unused)
IWH_WRITE(sc, CSR_INT, r);
IWH_WRITE(sc, CSR_FH_INT_STATUS, rfh);
- if (NULL == sc->sc_soft_hdl) {
- mutex_exit(&sc->sc_glock);
- return (DDI_INTR_CLAIMED);
- }
-
if (r & (BIT_INT_SWERROR | BIT_INT_ERR)) {
- IWH_DBG((IWH_DEBUG_FW, "fatal firmware error\n"));
- mutex_exit(&sc->sc_glock);
+ IWH_DBG((IWH_DEBUG_FW, "iwh_intr(): "
+ "fatal firmware error\n"));
+ mutex_exit(&sc->sc_suspend_lock);
iwh_stop(sc);
sc->sc_ostate = sc->sc_ic.ic_state;
/* notify upper layer */
- if (!IWH_CHK_FAST_RECOVER(sc))
+ if (!IWH_CHK_FAST_RECOVER(sc)) {
ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
+ }
sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
return (DDI_INTR_CLAIMED);
@@ -2703,24 +2869,29 @@ iwh_intr(caddr_t arg, caddr_t unused)
if (r & BIT_INT_RF_KILL) {
uint32_t tmp = IWH_READ(sc, CSR_GP_CNTRL);
- if (tmp & (1 << 27))
+ if (tmp & (1 << 27)) {
cmn_err(CE_NOTE, "RF switch: radio on\n");
+ }
}
if ((r & (BIT_INT_FH_RX | BIT_INT_SW_RX)) ||
(rfh & FH_INT_RX_MASK)) {
- sc->sc_rx_softint_pending = 1;
(void) ddi_intr_trigger_softint(sc->sc_soft_hdl, NULL);
+ mutex_exit(&sc->sc_suspend_lock);
+ return (DDI_INTR_CLAIMED);
}
if (r & BIT_INT_FH_TX) {
+ mutex_enter(&sc->sc_glock);
sc->sc_flags |= IWH_F_PUT_SEG;
cv_signal(&sc->sc_put_seg_cv);
+ mutex_exit(&sc->sc_glock);
}
#ifdef DEBUG
if (r & BIT_INT_ALIVE) {
- IWH_DBG((IWH_DEBUG_FW, "firmware initialized.\n"));
+ IWH_DBG((IWH_DEBUG_FW, "iwh_intr(): "
+ "firmware initialized.\n"));
}
#endif
@@ -2729,7 +2900,7 @@ iwh_intr(caddr_t arg, caddr_t unused)
*/
IWH_WRITE(sc, CSR_INT_MASK, CSR_INI_SET_MASK);
- mutex_exit(&sc->sc_glock);
+ mutex_exit(&sc->sc_suspend_lock);
return (DDI_INTR_CLAIMED);
}
@@ -2808,14 +2979,15 @@ iwh_rate_to_plcp(int rate)
static mblk_t *
iwh_m_tx(void *arg, mblk_t *mp)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
mblk_t *next;
- if (sc->sc_flags & IWH_F_SUSPEND) {
- freemsgchain(mp);
+ if (NULL == arg) {
return (NULL);
}
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
if (ic->ic_state != IEEE80211_S_RUN) {
freemsgchain(mp);
@@ -2824,10 +2996,18 @@ iwh_m_tx(void *arg, mblk_t *mp)
if ((sc->sc_flags & IWH_F_HW_ERR_RECOVER) &&
IWH_CHK_FAST_RECOVER(sc)) {
- IWH_DBG((IWH_DEBUG_FW, "iwh_m_tx(): hold queue\n"));
+ IWH_DBG((IWH_DEBUG_FW, "iwh_m_tx(): "
+ "hold queue\n"));
return (mp);
}
+ mutex_enter(&sc->sc_suspend_lock);
+ if (sc->sc_flags & IWH_F_SUSPEND) {
+ mutex_exit(&sc->sc_suspend_lock);
+ freemsgchain(mp);
+ return (NULL);
+ }
+
while (mp != NULL) {
next = mp->b_next;
mp->b_next = NULL;
@@ -2838,6 +3018,10 @@ iwh_m_tx(void *arg, mblk_t *mp)
mp = next;
}
+ if (mutex_owned(&sc->sc_suspend_lock)) {
+ mutex_exit(&sc->sc_suspend_lock);
+ }
+
return (mp);
}
@@ -2847,7 +3031,7 @@ iwh_m_tx(void *arg, mblk_t *mp)
static int
iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
{
- iwh_sc_t *sc = (iwh_sc_t *)ic;
+ iwh_sc_t *sc;
iwh_tx_ring_t *ring;
iwh_tx_desc_t *desc;
iwh_tx_data_t *data;
@@ -2857,44 +3041,32 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
struct ieee80211_frame *wh;
struct ieee80211_key *k = NULL;
mblk_t *m, *m0;
- int rate, hdrlen, len, len0, mblen, off, err = IWH_SUCCESS;
+ int hdrlen, len, len0, mblen, off, err = IWH_SUCCESS;
uint16_t masks = 0;
- uint32_t s_id = 0;
+ uint32_t rate, s_id = 0;
+ int txq_id = NON_QOS_TXQ;
+ struct ieee80211_qosframe *qwh = NULL;
+ int tid = WME_TID_INVALID;
- ring = &sc->sc_txq[0];
- data = &ring->data[ring->cur];
- desc = data->desc;
- cmd = data->cmd;
- bzero(desc, sizeof (*desc));
- bzero(cmd, sizeof (*cmd));
+ if (NULL == ic) {
+ return (IWH_FAIL);
+ }
+ sc = (iwh_sc_t *)ic;
- mutex_enter(&sc->sc_tx_lock);
- if (sc->sc_flags & IWH_F_SUSPEND) {
- mutex_exit(&sc->sc_tx_lock);
- if ((type & IEEE80211_FC0_TYPE_MASK) !=
- IEEE80211_FC0_TYPE_DATA) {
- freemsg(mp);
- }
- err = IWH_FAIL;
- goto exit;
+ if (!mutex_owned(&sc->sc_suspend_lock)) {
+ mutex_enter(&sc->sc_suspend_lock);
}
- if (ring->queued > ring->count - 64) {
- IWH_DBG((IWH_DEBUG_TX, "iwh_send(): no txbuf\n"));
- sc->sc_need_reschedule = 1;
- mutex_exit(&sc->sc_tx_lock);
+ if (sc->sc_flags & IWH_F_SUSPEND) {
if ((type & IEEE80211_FC0_TYPE_MASK) !=
IEEE80211_FC0_TYPE_DATA) {
freemsg(mp);
}
- sc->sc_tx_nobuf++;
err = IWH_FAIL;
goto exit;
}
- mutex_exit(&sc->sc_tx_lock);
-
- hdrlen = sizeof (struct ieee80211_frame);
+ hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
m = allocb(msgdsize(mp) + 32, BPRI_MED);
if (NULL == m) { /* can not alloc buf, drop this package */
@@ -2913,8 +3085,6 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
m->b_wptr += off;
- freemsg(mp);
-
wh = (struct ieee80211_frame *)m->b_rptr;
/*
@@ -2924,13 +3094,92 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
if (NULL == in) {
cmn_err(CE_WARN, "iwh_send(): "
"failed to find tx node\n");
+ freemsg(mp);
freemsg(m);
sc->sc_tx_err++;
err = IWH_SUCCESS;
goto exit;
}
- (void) ieee80211_encap(ic, m, in);
+ /*
+ * Net80211 module encapsulate outbound data frames.
+ * Add some feilds of 80211 frame.
+ */
+ if ((type & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_DATA) {
+ (void) ieee80211_encap(ic, m, in);
+ }
+
+ /*
+ * Determine TX queue according to traffic ID in frame
+ * if working in QoS mode.
+ */
+ if (in->in_flags & IEEE80211_NODE_QOS) {
+
+ if ((type & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_DATA) {
+
+ if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+ qwh = (struct ieee80211_qosframe *)wh;
+
+ tid = qwh->i_qos[0] & IEEE80211_QOS_TID;
+ txq_id = iwh_wme_tid_to_txq(tid);
+
+ if (txq_id < TXQ_FOR_AC_MIN ||
+ (txq_id > TXQ_FOR_AC_MAX)) {
+ freemsg(m);
+ freemsg(mp);
+ sc->sc_tx_err++;
+ err = IWH_SUCCESS;
+ goto exit;
+ }
+
+ } else {
+ txq_id = NON_QOS_TXQ;
+ }
+
+ } else if ((type & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_MGT) {
+ txq_id = QOS_TXQ_FOR_MGT;
+ } else {
+ txq_id = NON_QOS_TXQ;
+ }
+
+ } else {
+ txq_id = NON_QOS_TXQ;
+ }
+
+ ring = &sc->sc_txq[txq_id];
+ data = &ring->data[ring->cur];
+ desc = data->desc;
+ cmd = data->cmd;
+ bzero(desc, sizeof (*desc));
+ bzero(cmd, sizeof (*cmd));
+
+ mutex_enter(&sc->sc_tx_lock);
+
+ /*
+ * Need reschedule TX if TX buffer is full.
+ */
+ if (ring->queued > ring->count - IWH_MAX_WIN_SIZE) {
+ IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+ "no txbuf\n"));
+
+ sc->sc_need_reschedule = 1;
+ mutex_exit(&sc->sc_tx_lock);
+
+ freemsg(m);
+ if ((type & IEEE80211_FC0_TYPE_MASK) !=
+ IEEE80211_FC0_TYPE_DATA) {
+ freemsg(mp);
+ }
+ sc->sc_tx_nobuf++;
+ err = IWH_FAIL;
+ goto exit;
+ }
+ mutex_exit(&sc->sc_tx_lock);
+
+ freemsg(mp);
cmd->hdr.type = REPLY_TX;
cmd->hdr.flags = 0;
@@ -2967,43 +3216,87 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
}
#endif
+ tx->rts_retry_limit = IWH_TX_RTS_RETRY_LIMIT;
+ tx->data_retry_limit = IWH_TX_DATA_RETRY_LIMIT;
+
/*
- * pickup a rate
+ * specific TX parameters for management frames
*/
if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
IEEE80211_FC0_TYPE_MGT) {
/*
* mgmt frames are sent at 1M
*/
- rate = in->in_rates.ir_rates[0];
+ if ((in->in_rates.ir_rates[0] &
+ IEEE80211_RATE_VAL) != 0) {
+ rate = in->in_rates.ir_rates[0] & IEEE80211_RATE_VAL;
+ } else {
+ rate = 2;
+ }
+
+ tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK);
+
+ /*
+ * tell h/w to set timestamp in probe responses
+ */
+ if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
+ tx->tx_flags |= LE_32(TX_CMD_FLG_TSF_MSK);
+
+ tx->data_retry_limit = 3;
+ if (tx->data_retry_limit < tx->rts_retry_limit) {
+ tx->rts_retry_limit = tx->data_retry_limit;
+ }
+ }
+
+ if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_ASSOC_REQ) ||
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
+ IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) {
+ tx->timeout.pm_frame_timeout = LE_16(3);
+ } else {
+ tx->timeout.pm_frame_timeout = LE_16(2);
+ }
+
} else {
/*
- * do it here for the software way rate control.
+ * do it here for the software way rate scaling.
* later for rate scaling in hardware.
- * maybe like the following, for management frame:
- * tx->initial_rate_index = LINK_QUAL_MAX_RETRY_NUM - 1;
- * for data frame:
- * tx->tx_flags |= (LE_32(TX_CMD_FLG_STA_RATE_MSK));
- * rate = in->in_rates.ir_rates[in->in_txrate];
- * tx->initial_rate_index = 1;
*
* now the txrate is determined in tx cmd flags, set to the
- * max value 54M for 11g and 11M for 11b.
+ * max value 54M for 11g and 11M for 11b and 96M for 11n
+ * originally.
*/
-
if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) {
rate = ic->ic_fixed_rate;
} else {
- rate = in->in_rates.ir_rates[in->in_txrate];
+ if ((in->in_flags & IEEE80211_NODE_HT) &&
+ (sc->sc_ht_conf.ht_support)) {
+ iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+ rate = amrr->ht_mcs_idx;
+ } else {
+ if ((in->in_rates.ir_rates[in->in_txrate] &
+ IEEE80211_RATE_VAL) != 0) {
+ rate = in->in_rates.
+ ir_rates[in->in_txrate] &
+ IEEE80211_RATE_VAL;
+ }
+ }
}
- }
- rate &= IEEE80211_RATE_VAL;
+ if (tid != WME_TID_INVALID) {
+ tx->tid_tspec = (uint8_t)tid;
+ tx->tx_flags &= LE_32(~TX_CMD_FLG_SEQ_CTL_MSK);
+ } else {
+ tx->tx_flags |= LE_32(TX_CMD_FLG_SEQ_CTL_MSK);
+ }
- IWH_DBG((IWH_DEBUG_TX, "tx rate[%d of %d] = %x",
- in->in_txrate, in->in_rates.ir_nrates, rate));
+ tx->timeout.pm_frame_timeout = 0;
+ }
- tx->tx_flags |= (LE_32(TX_CMD_FLG_SEQ_CTL_MSK));
+ IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+ "tx rate[%d of %d] = %x",
+ in->in_txrate, in->in_rates.ir_nrates, rate));
len0 = roundup(4 + sizeof (iwh_tx_cmd_t) + hdrlen, 4);
if (len0 != (4 + sizeof (iwh_tx_cmd_t) + hdrlen)) {
@@ -3019,39 +3312,33 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
tx->sta_id = IWH_AP_ID;
}
- if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
- IEEE80211_FC0_TYPE_MGT) {
- /* tell h/w to set timestamp in probe responses */
- if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_PROBE_RESP) {
- tx->tx_flags |= LE_32(TX_CMD_FLG_TSF_MSK);
- }
-
- if (((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_ASSOC_REQ) ||
- ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
- IEEE80211_FC0_SUBTYPE_REASSOC_REQ)) {
- tx->timeout.pm_frame_timeout = LE_16(3);
+ if ((in->in_flags & IEEE80211_NODE_HT) &&
+ (sc->sc_ht_conf.ht_support) &&
+ ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
+ IEEE80211_FC0_TYPE_DATA)) {
+ if (rate >= HT_2CHAIN_RATE_MIN_IDX) {
+ rate |= LE_32(RATE_MCS_ANT_AB_MSK);
} else {
- tx->timeout.pm_frame_timeout = LE_16(2);
+ rate |= LE_32(RATE_MCS_ANT_B_MSK);
}
+
+ rate |= LE_32((1 << RATE_MCS_HT_POS));
+
+ tx->rate.r.rate_n_flags = rate;
+
} else {
- tx->timeout.pm_frame_timeout = 0;
- }
+ if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
+ masks |= RATE_MCS_CCK_MSK;
+ }
- if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
- masks |= RATE_MCS_CCK_MSK;
+ masks |= RATE_MCS_ANT_B_MSK;
+ tx->rate.r.rate_n_flags = LE_32(iwh_rate_to_plcp(rate) | masks);
}
- masks |= RATE_MCS_ANT_B_MSK;
- tx->rate.r.rate_n_flags = LE_32(iwh_rate_to_plcp(rate) | masks);
-
- IWH_DBG((IWH_DEBUG_TX, "tx flag = %x",
+ IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+ "tx flag = %x",
tx->tx_flags));
- tx->rts_retry_limit = 60;
- tx->data_retry_limit = 15;
-
tx->stop_time.life_time = LE_32(0xffffffff);
tx->len = LE_16(len);
@@ -3066,7 +3353,8 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
m->b_rptr += hdrlen;
(void) memcpy(data->dma_data.mem_va, m->b_rptr, len - hdrlen);
- IWH_DBG((IWH_DEBUG_TX, "sending data: qid=%d idx=%d len=%d",
+ IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+ "sending data: qid=%d idx=%d len=%d",
ring->qid, ring->cur, len));
/*
@@ -3080,7 +3368,8 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
desc->pa[0].val2 =
((data->dma_data.cookie.dmac_address & 0xffff0000) >> 16) |
((len - hdrlen) << 20);
- IWH_DBG((IWH_DEBUG_TX, "phy addr1 = 0x%x phy addr2 = 0x%x "
+ IWH_DBG((IWH_DEBUG_TX, "iwh_send(): "
+ "phy addr1 = 0x%x phy addr2 = 0x%x "
"len1 = 0x%x, len2 = 0x%x val1 = 0x%x val2 = 0x%x",
data->paddr_cmd, data->dma_data.cookie.dmac_address,
len0, len - hdrlen, desc->pa[0].val1, desc->pa[0].val2));
@@ -3118,11 +3407,17 @@ iwh_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type)
ic->ic_stats.is_tx_bytes += len;
ic->ic_stats.is_tx_frags++;
+ mutex_enter(&sc->sc_mt_lock);
if (0 == sc->sc_tx_timer) {
sc->sc_tx_timer = 4;
}
+ mutex_exit(&sc->sc_mt_lock);
exit:
+ if (mutex_owned(&sc->sc_suspend_lock)) {
+ mutex_exit(&sc->sc_suspend_lock);
+ }
+
return (err);
}
@@ -3132,9 +3427,15 @@ exit:
static void
iwh_m_ioctl(void* arg, queue_t *wq, mblk_t *mp)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
- int err;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
+ int err = EINVAL;
+
+ if (NULL == arg) {
+ return;
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
err = ieee80211_ioctl(ic, wq, mp);
if (ENETRESET == err) {
@@ -3164,8 +3465,13 @@ static int
iwh_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)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- int err = 0;
+ iwh_sc_t *sc;
+ int err = EINVAL;
+
+ if (NULL == arg) {
+ return (EINVAL);
+ }
+ sc = (iwh_sc_t *)arg;
err = ieee80211_getprop(&sc->sc_ic, pr_name, wldp_pr_num,
pr_flags, wldp_length, wldp_buf, perm);
@@ -3177,9 +3483,15 @@ static int
iwh_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
uint_t wldp_length, const void *wldp_buf)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
- int err;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
+ int err = EINVAL;
+
+ if (NULL == arg) {
+ return (EINVAL);
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length,
wldp_buf);
@@ -3204,10 +3516,16 @@ iwh_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num,
static int
iwh_m_stat(void *arg, uint_t stat, uint64_t *val)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
ieee80211_node_t *in;
+ if (NULL == arg) {
+ return (EINVAL);
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
+
mutex_enter(&sc->sc_glock);
switch (stat) {
@@ -3285,29 +3603,30 @@ iwh_m_stat(void *arg, uint_t stat, uint64_t *val)
static int
iwh_m_start(void *arg)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
- int err;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
+ int err = IWH_FAIL;
- err = iwh_init(sc);
+ if (NULL == arg) {
+ return (EINVAL);
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
+ err = iwh_init(sc);
if (err != IWH_SUCCESS) {
/*
* The hw init err(eg. RF is OFF). Return Success to make
* the 'plumb' succeed. The iwh_thread() tries to re-init
* background.
*/
- mutex_enter(&sc->sc_glock);
sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
- mutex_exit(&sc->sc_glock);
return (IWH_SUCCESS);
}
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
- mutex_enter(&sc->sc_glock);
sc->sc_flags |= IWH_F_RUNNING;
- mutex_exit(&sc->sc_glock);
return (IWH_SUCCESS);
}
@@ -3318,8 +3637,14 @@ iwh_m_start(void *arg)
static void
iwh_m_stop(void *arg)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
+
+ if (NULL == arg) {
+ return;
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
iwh_stop(sc);
@@ -3330,16 +3655,11 @@ iwh_m_stop(void *arg)
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
- mutex_enter(&sc->sc_mt_lock);
-
sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
sc->sc_flags &= ~IWH_F_RATE_AUTO_CTL;
- mutex_exit(&sc->sc_mt_lock);
- mutex_enter(&sc->sc_glock);
+
sc->sc_flags &= ~IWH_F_RUNNING;
sc->sc_flags &= ~IWH_F_SCANNING;
-
- mutex_exit(&sc->sc_glock);
}
/*
@@ -3348,9 +3668,15 @@ iwh_m_stop(void *arg)
static int
iwh_m_unicst(void *arg, const uint8_t *macaddr)
{
- iwh_sc_t *sc = (iwh_sc_t *)arg;
- ieee80211com_t *ic = &sc->sc_ic;
- int err;
+ iwh_sc_t *sc;
+ ieee80211com_t *ic;
+ int err = IWH_SUCCESS;
+
+ if (NULL == arg) {
+ return (EINVAL);
+ }
+ sc = (iwh_sc_t *)arg;
+ ic = &sc->sc_ic;
if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) {
IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
@@ -3364,21 +3690,21 @@ iwh_m_unicst(void *arg, const uint8_t *macaddr)
}
}
- return (IWH_SUCCESS);
+ return (err);
fail:
return (err);
}
+/* ARGSUSED */
static int
-/* LINTED: argument unused in function: arg add m */
iwh_m_multicst(void *arg, boolean_t add, const uint8_t *m)
{
return (IWH_SUCCESS);
}
+/* ARGSUSED */
static int
-/* LINTED: argument unused in function: arg on */
iwh_m_promisc(void *arg, boolean_t on)
{
return (IWH_SUCCESS);
@@ -3398,8 +3724,6 @@ iwh_thread(iwh_sc_t *sc)
int times = 0;
#endif
- mutex_enter(&sc->sc_mt_lock);
-
while (sc->sc_mf_thread_switch) {
tmp = IWH_READ(sc, CSR_GP_CNTRL);
if (tmp & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) {
@@ -3411,11 +3735,8 @@ iwh_thread(iwh_sc_t *sc)
/*
* If in SUSPEND or the RF is OFF, do nothing.
*/
- if ((sc->sc_flags & IWH_F_SUSPEND) ||
- (sc->sc_flags & IWH_F_RADIO_OFF)) {
- mutex_exit(&sc->sc_mt_lock);
+ if (sc->sc_flags & IWH_F_RADIO_OFF) {
delay(drv_usectohz(100000));
- mutex_enter(&sc->sc_mt_lock);
continue;
}
@@ -3425,8 +3746,7 @@ iwh_thread(iwh_sc_t *sc)
if (ic->ic_mach &&
(sc->sc_flags & IWH_F_HW_ERR_RECOVER)) {
- IWH_DBG((IWH_DEBUG_FW,
- "iwh_thread(): "
+ IWH_DBG((IWH_DEBUG_FW, "iwh_thread(): "
"try to recover fatal hw error: %d\n", times++));
iwh_stop(sc);
@@ -3436,10 +3756,8 @@ iwh_thread(iwh_sc_t *sc)
bcopy(&sc->sc_config, &sc->sc_config_save,
sizeof (sc->sc_config));
} else {
- mutex_exit(&sc->sc_mt_lock);
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
delay(drv_usectohz(2000000 + n*500000));
- mutex_enter(&sc->sc_mt_lock);
}
err = iwh_init(sc);
@@ -3460,43 +3778,22 @@ iwh_thread(iwh_sc_t *sc)
iwh_fast_recover(sc) != IWH_SUCCESS) {
sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
- mutex_exit(&sc->sc_mt_lock);
delay(drv_usectohz(2000000));
- if (sc->sc_ostate != IEEE80211_S_INIT)
+ if (sc->sc_ostate != IEEE80211_S_INIT) {
ieee80211_new_state(ic,
IEEE80211_S_SCAN, 0);
- mutex_enter(&sc->sc_mt_lock);
+ }
}
}
- if (ic->ic_mach && (sc->sc_flags & IWH_F_LAZY_RESUME)) {
- IWH_DBG((IWH_DEBUG_RESUME,
- "iwh_thread(): "
- "lazy resume\n"));
- sc->sc_flags &= ~IWH_F_LAZY_RESUME;
- mutex_exit(&sc->sc_mt_lock);
- /*
- * NB: under WPA mode, this call hangs (door problem?)
- * when called in iwh_attach() and iwh_detach() while
- * system is in the procedure of CPR. To be safe, let
- * the thread do this.
- */
- ieee80211_new_state(&sc->sc_ic, IEEE80211_S_INIT, -1);
- mutex_enter(&sc->sc_mt_lock);
- }
-
if (ic->ic_mach &&
(sc->sc_flags & IWH_F_SCANNING) && sc->sc_scan_pending) {
- IWH_DBG((IWH_DEBUG_SCAN,
- "iwh_thread(): "
+ IWH_DBG((IWH_DEBUG_SCAN, "iwh_thread(): "
"wait for probe response\n"));
sc->sc_scan_pending--;
- mutex_exit(&sc->sc_mt_lock);
delay(drv_usectohz(200000));
- if (sc->sc_flags & IWH_F_SCANNING)
- ieee80211_next_scan(ic);
- mutex_enter(&sc->sc_mt_lock);
+ ieee80211_next_scan(ic);
}
/*
@@ -3505,15 +3802,14 @@ iwh_thread(iwh_sc_t *sc)
if (ic->ic_mach &&
(sc->sc_flags & IWH_F_RATE_AUTO_CTL)) {
clk = ddi_get_lbolt();
- if (clk > sc->sc_clk + drv_usectohz(500000)) {
+ if (clk > sc->sc_clk + drv_usectohz(1000000)) {
iwh_amrr_timeout(sc);
}
}
- mutex_exit(&sc->sc_mt_lock);
delay(drv_usectohz(100000));
- mutex_enter(&sc->sc_mt_lock);
+ mutex_enter(&sc->sc_mt_lock);
if (sc->sc_tx_timer) {
timeout++;
if (10 == timeout) {
@@ -3521,16 +3817,17 @@ iwh_thread(iwh_sc_t *sc)
if (0 == sc->sc_tx_timer) {
sc->sc_flags |= IWH_F_HW_ERR_RECOVER;
sc->sc_ostate = IEEE80211_S_RUN;
- IWH_DBG((IWH_DEBUG_FW,
- "iwh_thread(): try to recover from"
- " 'send fail\n"));
+ IWH_DBG((IWH_DEBUG_FW, "iwh_thread(): "
+ "try to recover from "
+ "send fail\n"));
}
timeout = 0;
}
}
-
+ mutex_exit(&sc->sc_mt_lock);
}
+ mutex_enter(&sc->sc_mt_lock);
sc->sc_mf_thread = NULL;
cv_signal(&sc->sc_mt_cv);
mutex_exit(&sc->sc_mt_lock);
@@ -3550,7 +3847,8 @@ iwh_cmd(iwh_sc_t *sc, int code, const void *buf, int size, int async)
ASSERT(size <= sizeof (cmd->data));
ASSERT(mutex_owned(&sc->sc_glock));
- IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd() code[%d]", code));
+ IWH_DBG((IWH_DEBUG_CMD, "iwh_cmd() "
+ "code[%d]", code));
desc = ring->data[ring->cur].desc;
cmd = ring->data[ring->cur].cmd;
@@ -3566,6 +3864,10 @@ iwh_cmd(iwh_sc_t *sc, int code, const void *buf, int size, int async)
(uint32_t)(ring->data[ring->cur].paddr_cmd & 0xffffffff);
desc->pa[0].val1 = ((4 + size) << 4) & 0xfff0;
+ if (async) {
+ sc->sc_cmd_accum++;
+ }
+
/*
* kick cmd ring XXX
*/
@@ -3582,18 +3884,20 @@ iwh_cmd(iwh_sc_t *sc, int code, const void *buf, int size, int async)
return (IWH_SUCCESS);
} else {
clock_t clk;
- sc->sc_flags &= ~IWH_F_CMD_DONE;
+
clk = ddi_get_lbolt() + drv_usectohz(2000000);
- while (!(sc->sc_flags & IWH_F_CMD_DONE)) {
+ while (sc->sc_cmd_flag != SC_CMD_FLG_DONE) {
if (cv_timedwait(&sc->sc_cmd_cv,
&sc->sc_glock, clk) < 0) {
break;
}
}
- if (sc->sc_flags & IWH_F_CMD_DONE) {
+ if (SC_CMD_FLG_DONE == sc->sc_cmd_flag) {
+ sc->sc_cmd_flag = SC_CMD_FLG_NONE;
return (IWH_SUCCESS);
} else {
+ sc->sc_cmd_flag = SC_CMD_FLG_NONE;
return (IWH_FAIL);
}
}
@@ -3623,11 +3927,7 @@ iwh_hw_set_before_auth(iwh_sc_t *sc)
{
ieee80211com_t *ic = &sc->sc_ic;
ieee80211_node_t *in = ic->ic_bss;
- iwh_add_sta_t node;
- iwh_link_quality_cmd_t link_quality;
- struct ieee80211_rateset rs;
- uint16_t masks = 0, rate;
- int i, err;
+ int err = IWH_FAIL;
/*
* update adapter's configuration according
@@ -3635,16 +3935,24 @@ iwh_hw_set_before_auth(iwh_sc_t *sc)
*/
IEEE80211_ADDR_COPY(sc->sc_config.bssid, in->in_bssid);
sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, in->in_chan));
- if (IEEE80211_MODE_11B == ic->ic_curmode) {
- sc->sc_config.cck_basic_rates = 0x03;
- sc->sc_config.ofdm_basic_rates = 0;
- } else if ((in->in_chan != IEEE80211_CHAN_ANYC) &&
- (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) {
- sc->sc_config.cck_basic_rates = 0;
- sc->sc_config.ofdm_basic_rates = 0x15;
- } else { /* assume 802.11b/g */
- sc->sc_config.cck_basic_rates = 0x0f;
- sc->sc_config.ofdm_basic_rates = 0xff;
+
+ if (ic->ic_curmode != IEEE80211_MODE_11NG) {
+
+ sc->sc_config.ofdm_ht_triple_stream_basic_rates = 0;
+ sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0;
+ sc->sc_config.ofdm_ht_single_stream_basic_rates = 0;
+
+ if (IEEE80211_MODE_11B == ic->ic_curmode) {
+ sc->sc_config.cck_basic_rates = 0x03;
+ sc->sc_config.ofdm_basic_rates = 0;
+ } else if ((in->in_chan != IEEE80211_CHAN_ANYC) &&
+ (IEEE80211_IS_CHAN_5GHZ(in->in_chan))) {
+ sc->sc_config.cck_basic_rates = 0;
+ sc->sc_config.ofdm_basic_rates = 0x15;
+ } else { /* assume 802.11b/g */
+ sc->sc_config.cck_basic_rates = 0x0f;
+ sc->sc_config.ofdm_basic_rates = 0xff;
+ }
}
sc->sc_config.flags &= ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK |
@@ -3662,7 +3970,8 @@ iwh_hw_set_before_auth(iwh_sc_t *sc)
sc->sc_config.flags &= LE_32(~RXON_FLG_SHORT_PREAMBLE_MSK);
}
- IWH_DBG((IWH_DEBUG_80211, "config chan %d flags %x "
+ IWH_DBG((IWH_DEBUG_80211, "iwh_hw_set_before_auth(): "
+ "config chan %d flags %x "
"filter_flags %x cck %x ofdm %x"
" bssid:%02x:%02x:%02x:%02x:%02x:%2x\n",
LE_16(sc->sc_config.chan), LE_32(sc->sc_config.flags),
@@ -3682,61 +3991,26 @@ iwh_hw_set_before_auth(iwh_sc_t *sc)
err = iwh_tx_power_table(sc, 1);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_config(): "
- "failed to set tx power table.\n");
return (err);
}
/*
* add default AP node
*/
- (void) memset(&node, 0, sizeof (node));
- IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
- node.mode = 0;
- node.sta.sta_id = IWH_AP_ID;
- node.station_flags = 0;
- err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+ err = iwh_add_ap_sta(sc);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_hw_set_before_auth(): "
- "failed to add BSS node\n");
return (err);
}
/*
- * TX_LINK_QUALITY cmd
+ * set up retry rate table for AP node
*/
- (void) memset(&link_quality, 0, sizeof (link_quality));
- rs = ic->ic_sup_rates[ieee80211_chan2mode(ic, ic->ic_curchan)];
- for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
- if (i < rs.ir_nrates) {
- rate = rs.ir_rates[rs.ir_nrates - i];
- } else {
- rate = 2;
- }
-
- if (2 == rate || 4 == rate || 11 == rate || 22 == rate) {
- masks |= RATE_MCS_CCK_MSK;
- }
- masks |= RATE_MCS_ANT_B_MSK;
- masks &= ~RATE_MCS_ANT_A_MSK;
- link_quality.rate_n_flags[i] =
- LE_32(iwh_rate_to_plcp(rate) | masks);
- }
-
- link_quality.general_params.single_stream_ant_msk = 2;
- link_quality.general_params.dual_stream_ant_msk = 3;
- link_quality.agg_params.agg_dis_start_th = 3;
- link_quality.agg_params.agg_time_limit = LE_16(4000);
- link_quality.sta_id = IWH_AP_ID;
- err = iwh_cmd(sc, REPLY_TX_LINK_QUALITY_CMD, &link_quality,
- sizeof (link_quality), 1);
+ err = iwh_ap_lq(sc);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_hw_set_before_auth(): "
- "failed to config link quality table\n");
return (err);
}
- return (IWH_SUCCESS);
+ return (err);
}
/*
@@ -3796,7 +4070,8 @@ iwh_scan(iwh_sc_t *sc)
if (ic->ic_des_esslen) {
bcopy(ic->ic_des_essid, essid, ic->ic_des_esslen);
essid[ic->ic_des_esslen] = '\0';
- IWH_DBG((IWH_DEBUG_SCAN, "directed scan %s\n", essid));
+ IWH_DBG((IWH_DEBUG_SCAN, "iwh_scan(): "
+ "directed scan %s\n", essid));
bcopy(ic->ic_des_essid, hdr->direct_scan[0].ssid,
ic->ic_des_esslen);
@@ -3826,7 +4101,8 @@ iwh_scan(iwh_sc_t *sc)
if (in->in_esslen) {
bcopy(in->in_essid, essid, in->in_esslen);
essid[in->in_esslen] = '\0';
- IWH_DBG((IWH_DEBUG_SCAN, "probe with ESSID %s\n",
+ IWH_DBG((IWH_DEBUG_SCAN, "iwh_scan(): "
+ "probe with ESSID %s\n",
essid));
}
*frm++ = IEEE80211_ELEMID_SSID;
@@ -3936,7 +4212,7 @@ iwh_config(iwh_sc_t *sc)
iwh_rem_sta_t rm_sta;
const uint8_t bcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
iwh_link_quality_cmd_t link_quality;
- int i, err;
+ int i, err = IWH_FAIL;
uint16_t masks = 0;
/*
@@ -3975,12 +4251,14 @@ iwh_config(iwh_sc_t *sc)
IEEE80211_ADDR_COPY(sc->sc_config.wlap_bssid, ic->ic_macaddr);
sc->sc_config.chan = LE_16(ieee80211_chan2ieee(ic, ic->ic_curchan));
sc->sc_config.flags = LE_32(RXON_FLG_BAND_24G_MSK);
+ sc->sc_config.flags &= LE_32(~(RXON_FLG_CHANNEL_MODE_MIXED_MSK |
+ RXON_FLG_CHANNEL_MODE_PURE_40_MSK));
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
sc->sc_config.dev_type = RXON_DEV_TYPE_ESS;
- sc->sc_config.filter_flags |=
- LE_32(RXON_FILTER_DIS_DECRYPT_MSK |
+ sc->sc_config.filter_flags |= LE_32(RXON_FILTER_ACCEPT_GRP_MSK |
+ RXON_FILTER_DIS_DECRYPT_MSK |
RXON_FILTER_DIS_GRP_DECRYPT_MSK);
break;
@@ -4005,16 +4283,43 @@ iwh_config(iwh_sc_t *sc)
break;
}
+ /*
+ * Support all CCK rates.
+ */
sc->sc_config.cck_basic_rates = 0x0f;
+
+ /*
+ * Support all OFDM rates.
+ */
sc->sc_config.ofdm_basic_rates = 0xff;
/*
- * set antenna
+ * Determine HT supported rates.
*/
- sc->sc_config.rx_chain = LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK |
- (0x7 << RXON_RX_CHAIN_VALID_POS) |
- (0x2 << RXON_RX_CHAIN_FORCE_SEL_POS) |
- (0x2 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));
+ switch (sc->sc_ht_conf.rx_stream_count) {
+ case 3:
+ sc->sc_config.ofdm_ht_triple_stream_basic_rates = 0xff;
+ sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0xff;
+ sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+ break;
+ case 2:
+ sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0xff;
+ sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+ break;
+ case 1:
+ sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+ break;
+ default:
+ cmn_err(CE_WARN, "iwh_config(): "
+ "RX stream count %d is not in suitable range\n",
+ sc->sc_ht_conf.rx_stream_count);
+ return (IWH_FAIL);
+ }
+
+ /*
+ * set RX chains/antennas.
+ */
+ iwh_config_rxon_chain(sc);
err = iwh_cmd(sc, REPLY_RXON, &sc->sc_config,
sizeof (iwh_rxon_cmd_t), 0);
@@ -4039,12 +4344,10 @@ iwh_config(iwh_sc_t *sc)
}
/*
- * configure TX pwoer table
+ * configure TX power table
*/
err = iwh_tx_power_table(sc, 0);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_config(): "
- "failed to set tx power table.\n");
return (err);
}
@@ -4089,7 +4392,7 @@ iwh_config(iwh_sc_t *sc)
return (err);
}
- return (IWH_SUCCESS);
+ return (err);
}
/*
@@ -4106,8 +4409,9 @@ iwh_quiesce(dev_info_t *dip)
iwh_sc_t *sc;
sc = ddi_get_soft_state(iwh_soft_state_p, ddi_get_instance(dip));
- if (sc == NULL)
+ if (sc == NULL) {
return (DDI_FAILURE);
+ }
#ifdef DEBUG
/* by pass any messages, if it's quiesce */
@@ -4153,7 +4457,7 @@ iwh_stop_master(iwh_sc_t *sc)
#ifdef DEBUG
if (2000 == n) {
- IWH_DBG((IWH_DEBUG_HW,
+ IWH_DBG((IWH_DEBUG_HW, "iwh_stop_master(): "
"timeout waiting for master stop\n"));
}
#endif
@@ -4334,13 +4638,15 @@ iwh_eep_load(iwh_sc_t *sc)
eep_gp = IWH_READ(sc, CSR_EEPROM_GP);
if ((eep_gp & CSR_EEPROM_GP_VALID_MSK) ==
CSR_EEPROM_GP_BAD_SIGNATURE) {
- IWH_DBG((IWH_DEBUG_EEPROM, "not find eeprom\n"));
+ IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
+ "not find eeprom\n"));
return (IWH_FAIL);
}
rr = iwh_eep_sem_down(sc);
if (rr != 0) {
- IWH_DBG((IWH_DEBUG_EEPROM, "driver failed to own EEPROM\n"));
+ IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
+ "driver failed to own EEPROM\n"));
return (IWH_FAIL);
}
@@ -4358,7 +4664,7 @@ iwh_eep_load(iwh_sc_t *sc)
}
if (!(rv & 1)) {
- IWH_DBG((IWH_DEBUG_EEPROM,
+ IWH_DBG((IWH_DEBUG_EEPROM, "iwh_eep_load(): "
"time out when read eeprome\n"));
iwh_eep_sem_up(sc);
return (IWH_FAIL);
@@ -4381,7 +4687,8 @@ iwh_get_mac_from_eep(iwh_sc_t *sc)
IEEE80211_ADDR_COPY(ic->ic_macaddr, &sc->sc_eep_map[EEP_MAC_ADDRESS]);
- IWH_DBG((IWH_DEBUG_EEPROM, "mac:%2x:%2x:%2x:%2x:%2x:%2x\n",
+ IWH_DBG((IWH_DEBUG_EEPROM, "iwh_get_mac_from_eep(): "
+ "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]));
}
@@ -4392,7 +4699,7 @@ iwh_get_mac_from_eep(iwh_sc_t *sc)
static int
iwh_init(iwh_sc_t *sc)
{
- int n, err;
+ int err = IWH_FAIL;
clock_t clk;
/*
@@ -4416,36 +4723,24 @@ iwh_init(iwh_sc_t *sc)
sc->sc_dma_fw_data.mem_va,
sc->sc_dma_fw_data.alength);
- for (n = 0; n < 2; n++) {
- /* load firmware init segment into NIC */
- err = iwh_load_init_firmware(sc);
- if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_init(): "
- "failed to setup init firmware\n");
- continue;
- }
-
- /*
- * now press "execute" start running
- */
- IWH_WRITE(sc, CSR_RESET, 0);
- break;
- }
-
- mutex_exit(&sc->sc_glock);
-
- if (2 == n) {
+ /* load firmware init segment into NIC */
+ err = iwh_load_init_firmware(sc);
+ if (err != IWH_SUCCESS) {
cmn_err(CE_WARN, "iwh_init(): "
- "failed to load init firmware\n");
+ "failed to setup init firmware\n");
+ mutex_exit(&sc->sc_glock);
return (IWH_FAIL);
}
- mutex_enter(&sc->sc_ucode_lock);
+ /*
+ * now press "execute" start running
+ */
+ IWH_WRITE(sc, CSR_RESET, 0);
clk = ddi_get_lbolt() + drv_usectohz(1000000);
while (!(sc->sc_flags & IWH_F_FW_INIT)) {
if (cv_timedwait(&sc->sc_ucode_cv,
- &sc->sc_ucode_lock, clk) < 0) {
+ &sc->sc_glock, clk) < 0) {
break;
}
}
@@ -4453,11 +4748,11 @@ iwh_init(iwh_sc_t *sc)
if (!(sc->sc_flags & IWH_F_FW_INIT)) {
cmn_err(CE_WARN, "iwh_init(): "
"failed to process init alive.\n");
- mutex_exit(&sc->sc_ucode_lock);
+ mutex_exit(&sc->sc_glock);
return (IWH_FAIL);
}
- mutex_exit(&sc->sc_ucode_lock);
+ mutex_exit(&sc->sc_glock);
/*
* stop chipset for initializing chipset again
@@ -4473,38 +4768,26 @@ iwh_init(iwh_sc_t *sc)
return (IWH_FAIL);
}
- for (n = 0; n < 2; n++) {
- /*
- * load firmware run segment into NIC
- */
- err = iwh_load_run_firmware(sc);
- if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_init(): "
- "failed to setup run firmware\n");
- continue;
- }
-
- /*
- * now press "execute" start running
- */
- IWH_WRITE(sc, CSR_RESET, 0);
- break;
- }
-
- mutex_exit(&sc->sc_glock);
-
- if (2 == n) {
+ /*
+ * load firmware run segment into NIC
+ */
+ err = iwh_load_run_firmware(sc);
+ if (err != IWH_SUCCESS) {
cmn_err(CE_WARN, "iwh_init(): "
- "failed to load run firmware\n");
+ "failed to setup run firmware\n");
+ mutex_exit(&sc->sc_glock);
return (IWH_FAIL);
}
- mutex_enter(&sc->sc_ucode_lock);
+ /*
+ * now press "execute" start running
+ */
+ IWH_WRITE(sc, CSR_RESET, 0);
clk = ddi_get_lbolt() + drv_usectohz(1000000);
while (!(sc->sc_flags & IWH_F_FW_INIT)) {
if (cv_timedwait(&sc->sc_ucode_cv,
- &sc->sc_ucode_lock, clk) < 0) {
+ &sc->sc_glock, clk) < 0) {
break;
}
}
@@ -4512,11 +4795,13 @@ iwh_init(iwh_sc_t *sc)
if (!(sc->sc_flags & IWH_F_FW_INIT)) {
cmn_err(CE_WARN, "iwh_init(): "
"failed to process runtime alive.\n");
- mutex_exit(&sc->sc_ucode_lock);
+ mutex_exit(&sc->sc_glock);
return (IWH_FAIL);
}
- mutex_exit(&sc->sc_ucode_lock);
+ mutex_exit(&sc->sc_glock);
+
+ DELAY(1000);
mutex_enter(&sc->sc_glock);
sc->sc_flags &= ~IWH_F_FW_INIT;
@@ -4550,8 +4835,9 @@ iwh_stop(iwh_sc_t *sc)
int i;
/* by pass if it's quiesced */
- if (!(sc->sc_flags & IWH_F_QUIESCED))
+ if (!(sc->sc_flags & IWH_F_QUIESCED)) {
mutex_enter(&sc->sc_glock);
+ }
IWH_WRITE(sc, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
/*
@@ -4581,13 +4867,17 @@ iwh_stop(iwh_sc_t *sc)
iwh_stop_master(sc);
+ mutex_enter(&sc->sc_mt_lock);
sc->sc_tx_timer = 0;
+ mutex_exit(&sc->sc_mt_lock);
+
tmp = IWH_READ(sc, CSR_RESET);
IWH_WRITE(sc, CSR_RESET, tmp | CSR_RESET_REG_FLAG_SW_RESET);
/* by pass if it's quiesced */
- if (!(sc->sc_flags & IWH_F_QUIESCED))
+ if (!(sc->sc_flags & IWH_F_QUIESCED)) {
mutex_exit(&sc->sc_glock);
+ }
}
/*
@@ -4602,7 +4892,9 @@ iwh_stop(iwh_sc_t *sc)
#define is_failure(amrr) \
((amrr)->retrycnt > (amrr)->txcnt / 3)
#define is_enough(amrr) \
- ((amrr)->txcnt > 100)
+ ((amrr)->txcnt > 200)
+#define not_very_few(amrr) \
+ ((amrr)->txcnt > 40)
#define is_min_rate(in) \
(0 == (in)->in_txrate)
#define is_max_rate(in) \
@@ -4624,6 +4916,7 @@ iwh_amrr_init(iwh_amrr_t *amrr)
amrr->recovery = 0;
amrr->txcnt = amrr->retrycnt = 0;
amrr->success_threshold = IWH_AMRR_MIN_SUCCESS_THRESHOLD;
+ amrr->ht_mcs_idx = 0; /* 6Mbps */
}
static void
@@ -4631,7 +4924,8 @@ iwh_amrr_timeout(iwh_sc_t *sc)
{
ieee80211com_t *ic = &sc->sc_ic;
- IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_timeout() enter\n"));
+ IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_timeout(): "
+ "enter\n"));
if (IEEE80211_M_STA == ic->ic_opmode) {
iwh_amrr_ratectl(NULL, ic->ic_bss);
@@ -4642,8 +4936,120 @@ iwh_amrr_timeout(iwh_sc_t *sc)
sc->sc_clk = ddi_get_lbolt();
}
+static int
+iwh_is_max_rate(ieee80211_node_t *in)
+{
+ int i;
+ iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+ uint8_t r = (uint8_t)amrr->ht_mcs_idx;
+ ieee80211com_t *ic = in->in_ic;
+ iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ for (i = in->in_htrates.rs_nrates - 1; i >= 0; i--) {
+ r = in->in_htrates.rs_rates[i] &
+ IEEE80211_RATE_VAL;
+ if (sc->sc_ht_conf.tx_support_mcs[r/8] &
+ (1 << (r%8))) {
+ break;
+ }
+ }
+
+ return (r == (uint8_t)amrr->ht_mcs_idx);
+ } else {
+ return (is_max_rate(in));
+ }
+}
+
+static int
+iwh_is_min_rate(ieee80211_node_t *in)
+{
+ int i;
+ uint8_t r = 0;
+ iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+ ieee80211com_t *ic = in->in_ic;
+ iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+ r = in->in_htrates.rs_rates[i] &
+ IEEE80211_RATE_VAL;
+ if (sc->sc_ht_conf.tx_support_mcs[r/8] &
+ (1 << (r%8))) {
+ break;
+ }
+ }
+
+ return (r == (uint8_t)amrr->ht_mcs_idx);
+ } else {
+ return (is_min_rate(in));
+ }
+}
+
+static void
+iwh_increase_rate(ieee80211_node_t *in)
+{
+ int i;
+ uint8_t r;
+ iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+ ieee80211com_t *ic = in->in_ic;
+ iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+again:
+ amrr->ht_mcs_idx++;
+
+ for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+ r = in->in_htrates.rs_rates[i] &
+ IEEE80211_RATE_VAL;
+ if ((r == (uint8_t)amrr->ht_mcs_idx) &&
+ (sc->sc_ht_conf.tx_support_mcs[r/8] &
+ (1 << (r%8)))) {
+ break;
+ }
+ }
+
+ if (i >= in->in_htrates.rs_nrates) {
+ goto again;
+ }
+ } else {
+ increase_rate(in);
+ }
+}
+
+static void
+iwh_decrease_rate(ieee80211_node_t *in)
+{
+ int i;
+ uint8_t r;
+ iwh_amrr_t *amrr = (iwh_amrr_t *)in;
+ ieee80211com_t *ic = in->in_ic;
+ iwh_sc_t *sc = (iwh_sc_t *)ic;
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+again:
+ amrr->ht_mcs_idx--;
+
+ for (i = 0; i < in->in_htrates.rs_nrates; i++) {
+ r = in->in_htrates.rs_rates[i] &
+ IEEE80211_RATE_VAL;
+ if ((r == (uint8_t)amrr->ht_mcs_idx) &&
+ (sc->sc_ht_conf.tx_support_mcs[r/8] &
+ (1 << (r%8)))) {
+ break;
+ }
+ }
+
+ if (i >= in->in_htrates.rs_nrates) {
+ goto again;
+ }
+ } else {
+ decrease_rate(in);
+ }
+}
+
+/* ARGSUSED */
static void
-/* LINTED: argument unused in function: arg */
iwh_amrr_ratectl(void *arg, ieee80211_node_t *in)
{
iwh_amrr_t *amrr = (iwh_amrr_t *)in;
@@ -4652,20 +5058,22 @@ iwh_amrr_ratectl(void *arg, ieee80211_node_t *in)
if (is_success(amrr) && is_enough(amrr)) {
amrr->success++;
if (amrr->success >= amrr->success_threshold &&
- !is_max_rate(in)) {
+ !iwh_is_max_rate(in)) {
amrr->recovery = 1;
amrr->success = 0;
- increase_rate(in);
- IWH_DBG((IWH_DEBUG_RATECTL,
- "AMRR increasing rate %d (txcnt=%d retrycnt=%d)\n",
- in->in_txrate, amrr->txcnt, amrr->retrycnt));
+ iwh_increase_rate(in);
+ IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_ratectl(): "
+ "AMRR increasing rate %d "
+ "(txcnt=%d retrycnt=%d), mcs_idx=%d\n",
+ in->in_txrate, amrr->txcnt,
+ amrr->retrycnt, amrr->ht_mcs_idx));
need_change = 1;
} else {
amrr->recovery = 0;
}
- } else if (is_failure(amrr)) {
+ } else if (not_very_few(amrr) && is_failure(amrr)) {
amrr->success = 0;
- if (!is_min_rate(in)) {
+ if (!iwh_is_min_rate(in)) {
if (amrr->recovery) {
amrr->success_threshold++;
if (amrr->success_threshold >
@@ -4677,10 +5085,12 @@ iwh_amrr_ratectl(void *arg, ieee80211_node_t *in)
amrr->success_threshold =
IWH_AMRR_MIN_SUCCESS_THRESHOLD;
}
- decrease_rate(in);
- IWH_DBG((IWH_DEBUG_RATECTL,
- "AMRR decreasing rate %d (txcnt=%d retrycnt=%d)\n",
- in->in_txrate, amrr->txcnt, amrr->retrycnt));
+ iwh_decrease_rate(in);
+ IWH_DBG((IWH_DEBUG_RATECTL, "iwh_amrr_ratectl(): "
+ "AMRR decreasing rate %d "
+ "(txcnt=%d retrycnt=%d), mcs_idx=%d\n",
+ in->in_txrate, amrr->txcnt,
+ amrr->retrycnt, amrr->ht_mcs_idx));
need_change = 1;
}
amrr->recovery = 0; /* paper is incorrect */
@@ -4788,7 +5198,7 @@ iwh_alive_common(iwh_sc_t *sc)
uint32_t i;
iwh_wimax_coex_cmd_t w_cmd;
iwh_calibration_crystal_cmd_t c_cmd;
- uint32_t rv;
+ uint32_t rv = IWH_FAIL;
/*
* initialize SCD related registers to make TX work.
@@ -4980,7 +5390,7 @@ static int
iwh_tx_power_table(iwh_sc_t *sc, int async)
{
iwh_tx_power_table_cmd_t txpower;
- int i, err;
+ int i, err = IWH_FAIL;
(void) memset(&txpower, 0, sizeof (txpower));
@@ -5015,7 +5425,7 @@ iwh_tx_power_table(iwh_sc_t *sc, int async)
return (err);
}
- return (IWH_SUCCESS);
+ return (err);
}
static void
@@ -5042,7 +5452,7 @@ iwh_release_calib_buffer(iwh_sc_t *sc)
}
/*
- * a section of intialization
+ * common section of intialization
*/
static int
iwh_init_common(iwh_sc_t *sc)
@@ -5076,7 +5486,7 @@ iwh_init_common(iwh_sc_t *sc)
IWH_WRITE(sc, FH_MEM_RCSR_CHNL0_CONFIG_REG,
FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
- IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K |
+ IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K |
(RX_QUEUE_SIZE_LOG <<
FH_RCSR_RX_CONFIG_RBDCB_SIZE_BITSHIFT));
iwh_mac_access_exit(sc);
@@ -5132,7 +5542,7 @@ static int
iwh_fast_recover(iwh_sc_t *sc)
{
ieee80211com_t *ic = &sc->sc_ic;
- int err;
+ int err = IWH_FAIL;
mutex_enter(&sc->sc_glock);
@@ -5143,7 +5553,7 @@ iwh_fast_recover(iwh_sc_t *sc)
sc->sc_config.assoc_id = 0;
sc->sc_config.filter_flags &= ~LE_32(RXON_FILTER_ASSOC_MSK);
- if ((err = iwh_hw_set_before_auth(sc)) != 0) {
+ if ((err = iwh_hw_set_before_auth(sc)) != IWH_SUCCESS) {
cmn_err(CE_WARN, "iwh_fast_recover(): "
"could not setup authentication\n");
mutex_exit(&sc->sc_glock);
@@ -5169,7 +5579,8 @@ iwh_fast_recover(iwh_sc_t *sc)
sc->sc_flags &= ~IWH_F_HW_ERR_RECOVER;
/* start queue */
- IWH_DBG((IWH_DEBUG_FW, "iwh_fast_recover(): resume xmit\n"));
+ IWH_DBG((IWH_DEBUG_FW, "iwh_fast_recover(): "
+ "resume xmit\n"));
mac_tx_update(ic->ic_mach);
return (IWH_SUCCESS);
@@ -5180,17 +5591,12 @@ iwh_run_state_config(iwh_sc_t *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
ieee80211_node_t *in = ic->ic_bss;
- int err = IWH_SUCCESS;
+ uint32_t ht_protec = (uint32_t)(-1);
+ int err = IWH_FAIL;
/*
* update adapter's configuration
*/
- if (sc->sc_assoc_id != in->in_associd) {
- cmn_err(CE_WARN,
- "associate ID mismatch: expected %d, "
- "got %d\n",
- in->in_associd, sc->sc_assoc_id);
- }
sc->sc_config.assoc_id = in->in_associd & 0x3fff;
/*
@@ -5211,6 +5617,25 @@ iwh_run_state_config(iwh_sc_t *sc)
LE_32(RXON_FLG_SHORT_PREAMBLE_MSK);
}
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ ht_protec = in->in_htopmode;
+ if (ht_protec > 3) {
+ cmn_err(CE_WARN, "iwh_run_state_config(): "
+ "HT protection mode is not correct.\n");
+ return (IWH_FAIL);
+ } else if (NO_HT_PROT == ht_protec) {
+ ht_protec = sc->sc_ht_conf.ht_protection;
+ }
+
+ sc->sc_config.flags |=
+ LE_32(ht_protec << RXON_FLG_HT_OPERATING_MODE_POS);
+ }
+
+ /*
+ * set RX chains/antennas.
+ */
+ iwh_config_rxon_chain(sc);
+
sc->sc_config.filter_flags |=
LE_32(RXON_FILTER_ASSOC_MSK);
@@ -5219,7 +5644,8 @@ iwh_run_state_config(iwh_sc_t *sc)
LE_32(RXON_FILTER_BCON_AWARE_MSK);
}
- IWH_DBG((IWH_DEBUG_80211, "config chan %d flags %x"
+ IWH_DBG((IWH_DEBUG_80211, "iwh_run_state_config(): "
+ "config chan %d flags %x"
" filter_flags %x\n",
sc->sc_config.chan, sc->sc_config.flags,
sc->sc_config.filter_flags));
@@ -5237,10 +5663,716 @@ iwh_run_state_config(iwh_sc_t *sc)
*/
err = iwh_tx_power_table(sc, 1);
if (err != IWH_SUCCESS) {
- cmn_err(CE_WARN, "iwh_run_state_config(): "
- "failed to set tx power table.\n");
return (err);
}
+ /*
+ * Not need to update retry rate table for AP node
+ */
+ err = iwh_qosparam_to_hw(sc, 1);
+ if (err != IWH_SUCCESS) {
+ return (err);
+ }
+
+ return (err);
+}
+
+/*
+ * This function is only for compatibility with Net80211 module.
+ * iwh_qosparam_to_hw() is the actual function updating EDCA
+ * parameters to hardware.
+ */
+/* ARGSUSED */
+static int
+iwh_wme_update(ieee80211com_t *ic)
+{
+ return (0);
+}
+
+static int
+iwh_wme_to_qos_ac(int wme_ac)
+{
+ int qos_ac = QOS_AC_INVALID;
+
+ if (wme_ac < WME_AC_BE || wme_ac > WME_AC_VO) {
+ cmn_err(CE_WARN, "iwh_wme_to_qos_ac(): "
+ "WME AC index is not in suitable range.\n");
+ return (qos_ac);
+ }
+
+ switch (wme_ac) {
+ case WME_AC_BE:
+ qos_ac = QOS_AC_BK;
+ break;
+ case WME_AC_BK:
+ qos_ac = QOS_AC_BE;
+ break;
+ case WME_AC_VI:
+ qos_ac = QOS_AC_VI;
+ break;
+ case WME_AC_VO:
+ qos_ac = QOS_AC_VO;
+ break;
+ }
+
+ return (qos_ac);
+}
+
+static uint16_t
+iwh_cw_e_to_cw(uint8_t cw_e)
+{
+ uint16_t cw = 1;
+
+ while (cw_e > 0) {
+ cw <<= 1;
+ cw_e--;
+ }
+
+ cw -= 1;
+ return (cw);
+}
+
+static int
+iwh_wmeparam_check(struct wmeParams *wmeparam)
+{
+ int i;
+
+ for (i = 0; i < WME_NUM_AC; i++) {
+
+ if ((wmeparam[i].wmep_logcwmax > QOS_CW_RANGE_MAX) ||
+ (wmeparam[i].wmep_logcwmin >= wmeparam[i].wmep_logcwmax)) {
+ cmn_err(CE_WARN, "iwh_wmeparam_check(): "
+ "Contention window is not in suitable range.\n");
+ return (IWH_FAIL);
+ }
+
+ if ((wmeparam[i].wmep_aifsn < QOS_AIFSN_MIN) ||
+ (wmeparam[i].wmep_aifsn > QOS_AIFSN_MAX)) {
+ cmn_err(CE_WARN, "iwh_wmeparam_check(): "
+ "Arbitration interframe space number"
+ "is not in suitable range.\n");
+ return (IWH_FAIL);
+ }
+ }
+
return (IWH_SUCCESS);
}
+
+/*
+ * This function updates EDCA parameters into hardware.
+ * FIFO0-background, FIFO1-best effort, FIFO2-viedo, FIFO3-voice.
+ */
+static int
+iwh_qosparam_to_hw(iwh_sc_t *sc, int async)
+{
+ ieee80211com_t *ic = &sc->sc_ic;
+ ieee80211_node_t *in = ic->ic_bss;
+ struct wmeParams *wmeparam;
+ iwh_qos_param_cmd_t qosparam_cmd;
+ int i, j;
+ int err = IWH_FAIL;
+
+ if ((in->in_flags & IEEE80211_NODE_QOS) &&
+ (IEEE80211_M_STA == ic->ic_opmode)) {
+ wmeparam = ic->ic_wme.wme_chanParams.cap_wmeParams;
+ } else {
+ return (IWH_SUCCESS);
+ }
+
+ (void) memset(&qosparam_cmd, 0, sizeof (qosparam_cmd));
+
+ err = iwh_wmeparam_check(wmeparam);
+ if (err != IWH_SUCCESS) {
+ return (err);
+ }
+
+ if (in->in_flags & IEEE80211_NODE_QOS) {
+ qosparam_cmd.flags |= QOS_PARAM_FLG_UPDATE_EDCA;
+ }
+
+ if (in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT)) {
+ qosparam_cmd.flags |= QOS_PARAM_FLG_TGN;
+ }
+
+ for (i = 0; i < WME_NUM_AC; i++) {
+
+ j = iwh_wme_to_qos_ac(i);
+ if (j < QOS_AC_BK || j > QOS_AC_VO) {
+ return (IWH_FAIL);
+ }
+
+ qosparam_cmd.ac[j].cw_min =
+ iwh_cw_e_to_cw(wmeparam[i].wmep_logcwmin);
+ qosparam_cmd.ac[j].cw_max =
+ iwh_cw_e_to_cw(wmeparam[i].wmep_logcwmax);
+ qosparam_cmd.ac[j].aifsn =
+ wmeparam[i].wmep_aifsn;
+ qosparam_cmd.ac[j].txop =
+ (uint16_t)(wmeparam[i].wmep_txopLimit * 32);
+ }
+
+ err = iwh_cmd(sc, REPLY_QOS_PARAM, &qosparam_cmd,
+ sizeof (qosparam_cmd), async);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_qosparam_to_hw(): "
+ "failed to update QoS parameters into hardware.\n");
+ return (err);
+ }
+
+#ifdef DEBUG
+ IWH_DBG((IWH_DEBUG_QOS, "iwh_qosparam_to_hw(): "
+ "EDCA parameters are as follows:\n"));
+
+ IWH_DBG((IWH_DEBUG_QOS, "BK parameters are: "
+ "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+ qosparam_cmd.ac[0].cw_min, qosparam_cmd.ac[0].cw_max,
+ qosparam_cmd.ac[0].aifsn, qosparam_cmd.ac[0].txop));
+
+ IWH_DBG((IWH_DEBUG_QOS, "BE parameters are: "
+ "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+ qosparam_cmd.ac[1].cw_min, qosparam_cmd.ac[1].cw_max,
+ qosparam_cmd.ac[1].aifsn, qosparam_cmd.ac[1].txop));
+
+ IWH_DBG((IWH_DEBUG_QOS, "VI parameters are: "
+ "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+ qosparam_cmd.ac[2].cw_min, qosparam_cmd.ac[2].cw_max,
+ qosparam_cmd.ac[2].aifsn, qosparam_cmd.ac[2].txop));
+
+ IWH_DBG((IWH_DEBUG_QOS, "VO parameters are: "
+ "cw_min = %d, cw_max = %d, aifsn = %d, txop = %d\n",
+ qosparam_cmd.ac[3].cw_min, qosparam_cmd.ac[3].cw_max,
+ qosparam_cmd.ac[3].aifsn, qosparam_cmd.ac[3].txop));
+#endif
+ return (err);
+}
+
+static inline int
+iwh_wme_tid_qos_ac(int tid)
+{
+ switch (tid) {
+ case 1:
+ case 2:
+ return (QOS_AC_BK);
+ case 0:
+ case 3:
+ return (QOS_AC_BE);
+ case 4:
+ case 5:
+ return (QOS_AC_VI);
+ case 6:
+ case 7:
+ return (QOS_AC_VO);
+ }
+
+ return (QOS_AC_BE);
+}
+
+static inline int
+iwh_qos_ac_to_txq(int qos_ac)
+{
+ switch (qos_ac) {
+ case QOS_AC_BK:
+ return (QOS_AC_BK_TO_TXQ);
+ case QOS_AC_BE:
+ return (QOS_AC_BE_TO_TXQ);
+ case QOS_AC_VI:
+ return (QOS_AC_VI_TO_TXQ);
+ case QOS_AC_VO:
+ return (QOS_AC_VO_TO_TXQ);
+ }
+
+ return (QOS_AC_BE_TO_TXQ);
+}
+
+static int
+iwh_wme_tid_to_txq(int tid)
+{
+ int queue_n = TXQ_FOR_AC_INVALID;
+ int qos_ac;
+
+ if (tid < WME_TID_MIN ||
+ tid > WME_TID_MAX) {
+ cmn_err(CE_WARN, "wme_tid_to_txq(): "
+ "TID is not in suitable range.\n");
+ return (queue_n);
+ }
+
+ qos_ac = iwh_wme_tid_qos_ac(tid);
+ queue_n = iwh_qos_ac_to_txq(qos_ac);
+
+ return (queue_n);
+}
+
+/*
+ * This function is used for intializing HT relevant configurations.
+ */
+static void
+iwh_init_ht_conf(iwh_sc_t *sc)
+{
+ (void) memset(&sc->sc_ht_conf, 0, sizeof (iwh_ht_conf_t));
+
+ if ((0x4235 == sc->sc_dev_id) ||
+ (0x4236 == sc->sc_dev_id) ||
+ (0x423a == sc->sc_dev_id)) {
+ sc->sc_ht_conf.ht_support = 1;
+
+ sc->sc_ht_conf.valid_chains = 3;
+ sc->sc_ht_conf.tx_stream_count = 2;
+ sc->sc_ht_conf.rx_stream_count = 2;
+
+ sc->sc_ht_conf.tx_support_mcs[0] = 0xff;
+ sc->sc_ht_conf.tx_support_mcs[1] = 0xff;
+ sc->sc_ht_conf.rx_support_mcs[0] = 0xff;
+ sc->sc_ht_conf.rx_support_mcs[1] = 0xff;
+ } else {
+ sc->sc_ht_conf.ht_support = 1;
+
+ sc->sc_ht_conf.valid_chains = 2;
+ sc->sc_ht_conf.tx_stream_count = 1;
+ sc->sc_ht_conf.rx_stream_count = 2;
+
+ sc->sc_ht_conf.tx_support_mcs[0] = 0xff;
+ sc->sc_ht_conf.rx_support_mcs[0] = 0xff;
+ sc->sc_ht_conf.rx_support_mcs[1] = 0xff;
+ }
+
+ if (sc->sc_ht_conf.ht_support) {
+ sc->sc_ht_conf.cap |= HT_CAP_GRN_FLD;
+ sc->sc_ht_conf.cap |= HT_CAP_SGI_20;
+ sc->sc_ht_conf.cap |= HT_CAP_MAX_AMSDU;
+ /* should disable MIMO */
+ sc->sc_ht_conf.cap |= HT_CAP_MIMO_PS;
+
+ sc->sc_ht_conf.ampdu_p.factor = HT_RX_AMPDU_FACTOR;
+ sc->sc_ht_conf.ampdu_p.density = HT_MPDU_DENSITY;
+
+ sc->sc_ht_conf.ht_protection = HT_PROT_CHAN_NON_HT;
+ }
+}
+
+/*
+ * This function overwrites default ieee80211_rateset_11n struc.
+ */
+static void
+iwh_overwrite_11n_rateset(iwh_sc_t *sc)
+{
+ uint8_t *ht_rs = sc->sc_ht_conf.rx_support_mcs;
+ int mcs_idx, mcs_count = 0;
+ int i, j;
+
+ for (i = 0; i < HT_RATESET_NUM; i++) {
+ for (j = 0; j < 8; j++) {
+ if (ht_rs[i] & (1 << j)) {
+ mcs_idx = i * 8 + j;
+ if (mcs_idx >= IEEE80211_HTRATE_MAXSIZE) {
+ break;
+ }
+
+ ieee80211_rateset_11n.rs_rates[mcs_idx] =
+ (uint8_t)mcs_idx;
+ mcs_count++;
+ }
+ }
+ }
+
+ ieee80211_rateset_11n.rs_nrates = (uint8_t)mcs_count;
+
+#ifdef DEBUG
+ IWH_DBG((IWH_DEBUG_HTRATE, "iwh_overwrite_11n_rateset(): "
+ "HT rates supported by this station is as follows:\n"));
+
+ for (i = 0; i < ieee80211_rateset_11n.rs_nrates; i++) {
+ IWH_DBG((IWH_DEBUG_HTRATE, "Rate %d is %d\n",
+ i, ieee80211_rateset_11n.rs_rates[i]));
+ }
+#endif
+}
+
+/*
+ * This function overwrites default configurations of
+ * ieee80211com structure in Net80211 module.
+ */
+static void
+iwh_overwrite_ic_default(iwh_sc_t *sc)
+{
+ ieee80211com_t *ic = &sc->sc_ic;
+
+ sc->sc_newstate = ic->ic_newstate;
+ ic->ic_newstate = iwh_newstate;
+ ic->ic_node_alloc = iwh_node_alloc;
+ ic->ic_node_free = iwh_node_free;
+
+ if (sc->sc_ht_conf.ht_support) {
+ sc->sc_recv_action = ic->ic_recv_action;
+ ic->ic_recv_action = iwh_recv_action;
+ sc->sc_send_action = ic->ic_send_action;
+ ic->ic_send_action = iwh_send_action;
+
+ ic->ic_ampdu_rxmax = sc->sc_ht_conf.ampdu_p.factor;
+ ic->ic_ampdu_density = sc->sc_ht_conf.ampdu_p.density;
+ ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
+ }
+}
+
+/*
+ * This function sets "RX chain selection" feild
+ * in RXON command during plumb driver.
+ */
+static void
+iwh_config_rxon_chain(iwh_sc_t *sc)
+{
+ ieee80211com_t *ic = &sc->sc_ic;
+ ieee80211_node_t *in = ic->ic_bss;
+
+ if (3 == sc->sc_ht_conf.valid_chains) {
+ sc->sc_config.rx_chain = LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+ RXON_RX_CHAIN_VALID_POS);
+
+ sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+ RXON_RX_CHAIN_FORCE_SEL_POS);
+
+ sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK | RXON_RX_CHAIN_C_MSK) <<
+ RXON_RX_CHAIN_FORCE_MIMO_SEL_POS);
+ } else {
+ sc->sc_config.rx_chain = LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK) << RXON_RX_CHAIN_VALID_POS);
+
+ sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK) << RXON_RX_CHAIN_FORCE_SEL_POS);
+
+ sc->sc_config.rx_chain |= LE_16((RXON_RX_CHAIN_A_MSK |
+ RXON_RX_CHAIN_B_MSK) <<
+ RXON_RX_CHAIN_FORCE_MIMO_SEL_POS);
+ }
+
+ sc->sc_config.rx_chain |= LE_16(RXON_RX_CHAIN_DRIVER_FORCE_MSK);
+
+ if ((in != NULL) &&
+ (in->in_flags & IEEE80211_NODE_HT) &&
+ sc->sc_ht_conf.ht_support) {
+ if (3 == sc->sc_ht_conf.valid_chains) {
+ sc->sc_config.rx_chain |= LE_16(3 <<
+ RXON_RX_CHAIN_CNT_POS);
+ sc->sc_config.rx_chain |= LE_16(3 <<
+ RXON_RX_CHAIN_MIMO_CNT_POS);
+ } else {
+ sc->sc_config.rx_chain |= LE_16(2 <<
+ RXON_RX_CHAIN_CNT_POS);
+ sc->sc_config.rx_chain |= LE_16(2 <<
+ RXON_RX_CHAIN_MIMO_CNT_POS);
+ }
+
+ sc->sc_config.rx_chain |= LE_16(1 <<
+ RXON_RX_CHAIN_MIMO_FORCE_POS);
+ }
+
+ IWH_DBG((IWH_DEBUG_RXON, "iwh_config_rxon_chain(): "
+ "rxon->rx_chain = %x\n", sc->sc_config.rx_chain));
+}
+
+/*
+ * This function adds AP station into hardware.
+ */
+static int
+iwh_add_ap_sta(iwh_sc_t *sc)
+{
+ ieee80211com_t *ic = &sc->sc_ic;
+ ieee80211_node_t *in = ic->ic_bss;
+ iwh_add_sta_t node;
+ uint32_t ampdu_factor, ampdu_density;
+ int err = IWH_FAIL;
+
+ /*
+ * Add AP node into hardware.
+ */
+ (void) memset(&node, 0, sizeof (node));
+ IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+ node.mode = STA_MODE_ADD_MSK;
+ node.sta.sta_id = IWH_AP_ID;
+
+ if (sc->sc_ht_conf.ht_support &&
+ (in->in_htcap_ie != NULL) &&
+ (in->in_htcap != 0) &&
+ (in->in_htparam != 0)) {
+
+ if (((in->in_htcap & HT_CAP_MIMO_PS) >> 2)
+ == HT_CAP_MIMO_PS_DYNAMIC) {
+ node.station_flags |= LE_32(STA_FLG_RTS_MIMO_PROT);
+ }
+
+ ampdu_factor = in->in_htparam & HT_RX_AMPDU_FACTOR_MSK;
+ node.station_flags |=
+ LE_32(ampdu_factor << STA_FLG_MAX_AMPDU_POS);
+
+ ampdu_density = (in->in_htparam & HT_MPDU_DENSITY_MSK) >>
+ HT_MPDU_DENSITY_POS;
+ node.station_flags |=
+ LE_32(ampdu_density << STA_FLG_AMPDU_DENSITY_POS);
+
+ if (in->in_htcap & LE_16(HT_CAP_SUP_WIDTH)) {
+ node.station_flags |=
+ LE_32(STA_FLG_FAT_EN);
+ }
+ }
+
+ err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_add_ap_lq(): "
+ "failed to add AP node\n");
+ return (err);
+ }
+
+ return (err);
+}
+
+/*
+ * Each station in the Shirley Peak's internal station table has
+ * its own table of 16 TX rates and modulation modes for retrying
+ * TX when an ACK is not received. This function replaces the entire
+ * table for one station.Station must already be in Shirley Peak's
+ * station talbe.
+ */
+static int
+iwh_ap_lq(iwh_sc_t *sc)
+{
+ ieee80211com_t *ic = &sc->sc_ic;
+ ieee80211_node_t *in = ic->ic_bss;
+ iwh_link_quality_cmd_t link_quality;
+ const struct ieee80211_rateset *rs_sup = NULL;
+ uint32_t masks = 0, rate;
+ int i, err = IWH_FAIL;
+
+ /*
+ * TX_LINK_QUALITY cmd
+ */
+ (void) memset(&link_quality, 0, sizeof (link_quality));
+ if (in->in_chan == IEEE80211_CHAN_ANYC) /* skip null node */
+ return (err);
+ rs_sup = ieee80211_get_suprates(ic, in->in_chan);
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ if (i < rs_sup->ir_nrates) {
+ rate = rs_sup->ir_rates[rs_sup->ir_nrates - i] &
+ IEEE80211_RATE_VAL;
+ } else {
+ rate = 2;
+ }
+
+ if (2 == rate || 4 == rate ||
+ 11 == rate || 22 == rate) {
+ masks |= LE_32(RATE_MCS_CCK_MSK);
+ }
+
+ masks |= LE_32(RATE_MCS_ANT_B_MSK);
+
+ link_quality.rate_n_flags[i] =
+ LE_32(iwh_rate_to_plcp(rate) | masks);
+ }
+
+ link_quality.general_params.single_stream_ant_msk = LINK_QUAL_ANT_B_MSK;
+ link_quality.general_params.dual_stream_ant_msk = LINK_QUAL_ANT_MSK;
+ link_quality.agg_params.agg_dis_start_th = 3;
+ link_quality.agg_params.agg_time_limit = LE_16(4000);
+ link_quality.sta_id = IWH_AP_ID;
+ err = iwh_cmd(sc, REPLY_TX_LINK_QUALITY_CMD, &link_quality,
+ sizeof (link_quality), 1);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_ap_lq(): "
+ "failed to config link quality table\n");
+ return (err);
+ }
+
+#ifdef DEBUG
+ IWH_DBG((IWH_DEBUG_HWRATE, "iwh_ap_lq(): "
+ "Rates in HW are as follows:\n"));
+
+ for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+ IWH_DBG((IWH_DEBUG_HWRATE,
+ "Rate %d in HW is %x\n", i, link_quality.rate_n_flags[i]));
+ }
+#endif
+
+ return (err);
+}
+
+/*
+ * When block ACK agreement has been set up between station and AP,
+ * Net80211 module will call this function to inform hardware about
+ * informations of this BA agreement.
+ * When AP wants to delete BA agreement that was originated by it,
+ * Net80211 modele will call this function to clean up relevant
+ * information in hardware.
+ */
+static void
+iwh_recv_action(struct ieee80211_node *in,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211com *ic;
+ iwh_sc_t *sc;
+ const struct ieee80211_action *ia;
+ uint16_t baparamset, baseqctl;
+ uint32_t tid, ssn;
+ iwh_add_sta_t node;
+ int err = IWH_FAIL;
+
+ if ((NULL == in) || (NULL == frm)) {
+ return;
+ }
+
+ ic = in->in_ic;
+ if (NULL == ic) {
+ return;
+ }
+
+ sc = (iwh_sc_t *)ic;
+
+ sc->sc_recv_action(in, frm, efrm);
+
+ ia = (const struct ieee80211_action *)frm;
+ if (ia->ia_category != IEEE80211_ACTION_CAT_BA) {
+ return;
+ }
+
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ baparamset = *(uint16_t *)(frm + 3);
+ baseqctl = *(uint16_t *)(frm + 7);
+
+ tid = MS(baparamset, IEEE80211_BAPS_TID);
+ ssn = MS(baseqctl, IEEE80211_BASEQ_START);
+
+ (void) memset(&node, 0, sizeof (node));
+ IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+ node.mode = STA_MODE_MODIFY_MSK;
+ node.sta.sta_id = IWH_AP_ID;
+
+ node.station_flags_msk = 0;
+ node.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK;
+ node.add_immediate_ba_tid = (uint8_t)tid;
+ node.add_immediate_ba_ssn = LE_16(ssn);
+
+ mutex_enter(&sc->sc_glock);
+ err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_recv_action(): "
+ "failed to setup RX block ACK\n");
+ mutex_exit(&sc->sc_glock);
+ return;
+ }
+ mutex_exit(&sc->sc_glock);
+
+ IWH_DBG((IWH_DEBUG_BA, "iwh_recv_action(): "
+ "RX block ACK "
+ "was setup on TID %d and SSN is %d.\n", tid, ssn));
+
+ return;
+
+ case IEEE80211_ACTION_BA_DELBA:
+ baparamset = *(uint16_t *)(frm + 2);
+
+ if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+ return;
+ }
+
+ tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+ (void) memset(&node, 0, sizeof (node));
+ IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+ node.mode = STA_MODE_MODIFY_MSK;
+ node.sta.sta_id = IWH_AP_ID;
+
+ node.station_flags_msk = 0;
+ node.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+ node.add_immediate_ba_tid = (uint8_t)tid;
+
+ mutex_enter(&sc->sc_glock);
+ err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_recv_action(): "
+ "failed to delete RX block ACK\n");
+ mutex_exit(&sc->sc_glock);
+ return;
+ }
+ mutex_exit(&sc->sc_glock);
+
+ IWH_DBG((IWH_DEBUG_BA, "iwh_recv_action(): "
+ "RX block ACK "
+ "was deleted on TID %d.\n", tid));
+
+ return;
+ }
+}
+
+/*
+ * When local station wants to delete BA agreement that was originated by AP,
+ * Net80211 module will call this function to clean up relevant information
+ * in hardware.
+ */
+static int
+iwh_send_action(struct ieee80211_node *in,
+ int category, int action, uint16_t args[4])
+{
+ struct ieee80211com *ic;
+ iwh_sc_t *sc;
+ uint32_t tid;
+ iwh_add_sta_t node;
+ int ret = EIO;
+ int err = IWH_FAIL;
+
+
+ if (NULL == in) {
+ return (ret);
+ }
+
+ ic = in->in_ic;
+ if (NULL == ic) {
+ return (ret);
+ }
+
+ sc = (iwh_sc_t *)ic;
+
+ ret = sc->sc_send_action(in, category, action, args);
+
+ if (category != IEEE80211_ACTION_CAT_BA) {
+ return (ret);
+ }
+
+ switch (action) {
+ case IEEE80211_ACTION_BA_DELBA:
+ if (IEEE80211_DELBAPS_INIT == args[1]) {
+ return (ret);
+ }
+
+ tid = args[0];
+
+ (void) memset(&node, 0, sizeof (node));
+ IEEE80211_ADDR_COPY(node.sta.addr, in->in_bssid);
+ node.mode = STA_MODE_MODIFY_MSK;
+ node.sta.sta_id = IWH_AP_ID;
+
+ node.station_flags_msk = 0;
+ node.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK;
+ node.add_immediate_ba_tid = (uint8_t)tid;
+
+ mutex_enter(&sc->sc_glock);
+ err = iwh_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+ if (err != IWH_SUCCESS) {
+ cmn_err(CE_WARN, "iwh_send_action(): "
+ "failed to delete RX balock ACK\n");
+ mutex_exit(&sc->sc_glock);
+ return (EIO);
+ }
+ mutex_exit(&sc->sc_glock);
+
+ IWH_DBG((IWH_DEBUG_BA, "iwh_send_action(): "
+ "RX block ACK "
+ "was deleted on TID %d.\n", tid));
+
+ break;
+ }
+
+ return (ret);
+}
diff --git a/usr/src/uts/common/io/iwh/iwh_hw.h b/usr/src/uts/common/io/iwh/iwh_hw.h
index eae5330e40..a837781969 100644
--- a/usr/src/uts/common/io/iwh/iwh_hw.h
+++ b/usr/src/uts/common/io/iwh/iwh_hw.h
@@ -1,10 +1,10 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
* All rights reserved.
*/
@@ -18,7 +18,7 @@
*
* GPL LICENSE SUMMARY
*
- * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU Geeral Public License as
@@ -43,7 +43,7 @@
*
* BSD LICENSE
*
- * Copyright(c) 2005 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2009 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -621,6 +621,12 @@ extern "C" {
#define FH_RCSR_RX_CONFIG_RDRBD_ENABLE_VAL (0x20000000)
#define IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000)
+#define IWH_TX_RTS_RETRY_LIMIT (60)
+#define IWH_TX_DATA_RETRY_LIMIT (15)
+
+#define IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000)
+#define IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000)
+#define IWH_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000)
/*
* RCSR channel 0 config register values
@@ -1248,110 +1254,146 @@ extern "C" {
/*
* QoS definitions
*/
-#define CW_MIN_OFDM 15
-#define CW_MAX_OFDM 1023
-#define CW_MIN_CCK 31
-#define CW_MAX_CCK 1023
-
-#define QOS_TX0_CW_MIN_OFDM CW_MIN_OFDM
-#define QOS_TX1_CW_MIN_OFDM CW_MIN_OFDM
-#define QOS_TX2_CW_MIN_OFDM ((CW_MIN_OFDM + 1) / 2 - 1)
-#define QOS_TX3_CW_MIN_OFDM ((CW_MIN_OFDM + 1) / 4 - 1)
-
-#define QOS_TX0_CW_MIN_CCK CW_MIN_CCK
-#define QOS_TX1_CW_MIN_CCK CW_MIN_CCK
-#define QOS_TX2_CW_MIN_CCK ((CW_MIN_CCK + 1) / 2 - 1)
-#define QOS_TX3_CW_MIN_CCK ((CW_MIN_CCK + 1) / 4 - 1)
-
-#define QOS_TX0_CW_MAX_OFDM CW_MAX_OFDM
-#define QOS_TX1_CW_MAX_OFDM CW_MAX_OFDM
-#define QOS_TX2_CW_MAX_OFDM CW_MIN_OFDM
-#define QOS_TX3_CW_MAX_OFDM ((CW_MIN_OFDM + 1) / 2 - 1)
-
-#define QOS_TX0_CW_MAX_CCK CW_MAX_CCK
-#define QOS_TX1_CW_MAX_CCK CW_MAX_CCK
-#define QOS_TX2_CW_MAX_CCK CW_MIN_CCK
-#define QOS_TX3_CW_MAX_CCK ((CW_MIN_CCK + 1) / 2 - 1)
-
-#define QOS_TX0_AIFS (3)
-#define QOS_TX1_AIFS (7)
-#define QOS_TX2_AIFS (2)
-#define QOS_TX3_AIFS (2)
-
-#define QOS_TX0_ACM 0
-#define QOS_TX1_ACM 0
-#define QOS_TX2_ACM 0
-#define QOS_TX3_ACM 0
-
-#define QOS_TX0_TXOP_LIMIT_CCK 0
-#define QOS_TX1_TXOP_LIMIT_CCK 0
-#define QOS_TX2_TXOP_LIMIT_CCK 6016
-#define QOS_TX3_TXOP_LIMIT_CCK 3264
-
-#define QOS_TX0_TXOP_LIMIT_OFDM 0
-#define QOS_TX1_TXOP_LIMIT_OFDM 0
-#define QOS_TX2_TXOP_LIMIT_OFDM 3008
-#define QOS_TX3_TXOP_LIMIT_OFDM 1504
-
-#define DEF_TX0_CW_MIN_OFDM CW_MIN_OFDM
-#define DEF_TX1_CW_MIN_OFDM CW_MIN_OFDM
-#define DEF_TX2_CW_MIN_OFDM CW_MIN_OFDM
-#define DEF_TX3_CW_MIN_OFDM CW_MIN_OFDM
-
-#define DEF_TX0_CW_MIN_CCK CW_MIN_CCK
-#define DEF_TX1_CW_MIN_CCK CW_MIN_CCK
-#define DEF_TX2_CW_MIN_CCK CW_MIN_CCK
-#define DEF_TX3_CW_MIN_CCK CW_MIN_CCK
-
-#define DEF_TX0_CW_MAX_OFDM CW_MAX_OFDM
-#define DEF_TX1_CW_MAX_OFDM CW_MAX_OFDM
-#define DEF_TX2_CW_MAX_OFDM CW_MAX_OFDM
-#define DEF_TX3_CW_MAX_OFDM CW_MAX_OFDM
-
-#define DEF_TX0_CW_MAX_CCK CW_MAX_CCK
-#define DEF_TX1_CW_MAX_CCK CW_MAX_CCK
-#define DEF_TX2_CW_MAX_CCK CW_MAX_CCK
-#define DEF_TX3_CW_MAX_CCK CW_MAX_CCK
-
-#define DEF_TX0_AIFS (2)
-#define DEF_TX1_AIFS (2)
-#define DEF_TX2_AIFS (2)
-#define DEF_TX3_AIFS (2)
-
-#define DEF_TX0_ACM (0)
-#define DEF_TX1_ACM (0)
-#define DEF_TX2_ACM (0)
-#define DEF_TX3_ACM (0)
-
-#define DEF_TX0_TXOP_LIMIT_CCK (0)
-#define DEF_TX1_TXOP_LIMIT_CCK (0)
-#define DEF_TX2_TXOP_LIMIT_CCK (0)
-#define DEF_TX3_TXOP_LIMIT_CCK (0)
-
-#define DEF_TX0_TXOP_LIMIT_OFDM (0)
-#define DEF_TX1_TXOP_LIMIT_OFDM (0)
-#define DEF_TX2_TXOP_LIMIT_OFDM (0)
-#define DEF_TX3_TXOP_LIMIT_OFDM (0)
-
-#define QOS_QOS_SETS (3)
-#define QOS_PARAM_SET_ACTIVE (0)
-#define QOS_PARAM_SET_DEF_CCK (1)
-#define QOS_PARAM_SET_DEF_OFDM (2)
-
-#define CTRL_QOS_NO_ACK (0x0020)
-#define DCT_FLAG_EXT_QOS_ENABLED (0x10)
-
-#define IWH_TX_QUEUE_AC0 (0)
-#define IWH_TX_QUEUE_AC1 (1)
-#define IWH_TX_QUEUE_AC2 (2)
-#define IWH_TX_QUEUE_AC3 (3)
-#define IWH_TX_QUEUE_HCCA_1 (5)
-#define IWH_TX_QUEUE_HCCA_2 (6)
-
-#define U32_PAD(n) ((4-(n%4))%4)
-
-#define AC_BE_TID_MASK 0x9 /* TID 0 and 3 */
-#define AC_BK_TID_MASK 0x6 /* TID 1 and 2 */
+
+#define AC_NUM (4) /* the number of access category */
+
+/*
+ * index of every AC in firmware
+ */
+#define QOS_AC_BK (0)
+#define QOS_AC_BE (1)
+#define QOS_AC_VI (2)
+#define QOS_AC_VO (3)
+#define QOS_AC_INVALID (-1)
+
+#define QOS_CW_RANGE_MIN (0) /* exponential of 2 */
+#define QOS_CW_RANGE_MAX (15) /* exponential of 2 */
+#define QOS_TXOP_MIN (0) /* unit of 32 microsecond */
+#define QOS_TXOP_MAX (255) /* unit of 32 microsecond */
+#define QOS_AIFSN_MIN (2)
+#define QOS_AIFSN_MAX (15) /* undefined */
+
+/*
+ * masks for flags of QoS parameter command
+ */
+#define QOS_PARAM_FLG_UPDATE_EDCA (0x01)
+#define QOS_PARAM_FLG_TGN (0x02)
+
+/*
+ * index of TX queue for every AC
+ */
+#define QOS_AC_BK_TO_TXQ (3)
+#define QOS_AC_BE_TO_TXQ (2)
+#define QOS_AC_VI_TO_TXQ (1)
+#define QOS_AC_VO_TO_TXQ (0)
+#define TXQ_FOR_AC_MIN (0)
+#define TXQ_FOR_AC_MAX (3)
+#define TXQ_FOR_AC_INVALID (-1)
+#define NON_QOS_TXQ QOS_AC_BE_TO_TXQ
+#define QOS_TXQ_FOR_MGT QOS_AC_VO_TO_TXQ
+
+#define WME_TID_MIN (0)
+#define WME_TID_MAX (7)
+#define WME_TID_INVALID (-1)
+
+/*
+ * HT definitions
+ */
+
+/*
+ * HT capabilities masks
+ */
+#define HT_CAP_SUP_WIDTH (0x0002)
+#define HT_CAP_MIMO_PS (0x000c)
+#define HT_CAP_GRN_FLD (0x0010)
+#define HT_CAP_SGI_20 (0x0020)
+#define HT_CAP_SGI_40 (0x0040)
+#define HT_CAP_DELAY_BA (0x0400)
+#define HT_CAP_MAX_AMSDU (0x0800)
+#define HT_CAP_MCS_TX_DEFINED (0x01)
+#define HT_CAP_MCS_TX_RX_DIFF (0x02)
+#define HT_CAP_MCS_TX_STREAMS (0x0c)
+#define HT_CAP_MCS_TX_UEQM (0x10)
+
+#define HT_CAP_MIMO_PS_STATIC (0)
+#define HT_CAP_MIMO_PS_DYNAMIC (1)
+#define HT_CAP_MIMO_PS_INVALID (2)
+#define HT_CAP_MIMO_PS_NONE (3)
+
+#define HT_RX_AMPDU_FACTOR_8K (0x0)
+#define HT_RX_AMPDU_FACTOR_16K (0x1)
+#define HT_RX_AMPDU_FACTOR_32K (0x2)
+#define HT_RX_AMPDU_FACTOR_64K (0x3)
+#define HT_RX_AMPDU_FACTOR HT_RX_AMPDU_FACTOR_8K
+#define HT_RX_AMPDU_FACTOR_MSK (0x3)
+
+#define HT_MPDU_DENSITY_4USEC (0x5)
+#define HT_MPDU_DENSITY_8USEC (0x6)
+#define HT_MPDU_DENSITY HT_MPDU_DENSITY_4USEC
+#define HT_MPDU_DENSITY_MSK (0x1c)
+#define HT_MPDU_DENSITY_POS (2)
+
+#define HT_RATESET_NUM (16)
+#define HT_1CHAIN_RATE_MIN_IDX (0x0)
+#define HT_1CHAIN_RATE_MAX_IDX (0x7)
+#define HT_2CHAIN_RATE_MIN_IDX (0x8)
+#define HT_2CHAIN_RATE_MAX_IDX (0xf)
+
+struct iwh_ampdu_param {
+ uint8_t factor;
+ uint8_t density;
+};
+
+typedef struct iwh_ht_conf {
+ uint8_t ht_support;
+ uint16_t cap;
+ struct iwh_ampdu_param ampdu_p;
+ uint8_t tx_support_mcs[HT_RATESET_NUM];
+ uint8_t rx_support_mcs[HT_RATESET_NUM];
+ uint8_t valid_chains;
+ uint8_t tx_stream_count;
+ uint8_t rx_stream_count;
+ uint8_t ht_protection;
+} iwh_ht_conf_t;
+
+#define NO_HT_PROT (0)
+#define HT_PROT_CHAN_NON_HT (1)
+#define HT_PROT_FAT (2)
+#define HT_PROT_ASSOC_NON_HT (3)
+
+/*
+ * HT flags for RXON command.
+ */
+#define RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK 0x400000
+#define RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK 0x000000
+#define RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK 0x400000
+
+#define RXON_FLG_HT_OPERATING_MODE_POS (23)
+#define RXON_FLG_HT_PROT_MSK 0x800000
+#define RXON_FLG_FAT_PROT_MSK 0x1000000
+
+#define RXON_FLG_CHANNEL_MODE_POS (25)
+#define RXON_FLG_CHANNEL_MODE_MSK 0x06000000
+#define RXON_FLG_CHANNEL_MODE_LEGACY_MSK 0x00000000
+#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK 0x02000000
+#define RXON_FLG_CHANNEL_MODE_MIXED_MSK 0x04000000
+
+#define RXON_RX_CHAIN_DRIVER_FORCE_MSK (0x1<<0)
+#define RXON_RX_CHAIN_VALID_MSK (0x7<<1)
+#define RXON_RX_CHAIN_VALID_POS (1)
+#define RXON_RX_CHAIN_FORCE_SEL_MSK (0x7<<4)
+#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK (0x7<<7)
+#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
+#define RXON_RX_CHAIN_CNT_MSK (0x3<<10)
+#define RXON_RX_CHAIN_CNT_POS (10)
+#define RXON_RX_CHAIN_MIMO_CNT_MSK (0x3<<12)
+#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
+#define RXON_RX_CHAIN_MIMO_FORCE_MSK (0x1<<14)
+#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
+#define RXON_RX_CHAIN_A_MSK (1)
+#define RXON_RX_CHAIN_B_MSK (2)
+#define RXON_RX_CHAIN_C_MSK (4)
/*
* Generic queue structure
@@ -1442,40 +1484,32 @@ typedef struct iwh_tx_power_table_cmd {
struct iwh_tx_power_db db;
} iwh_tx_power_table_cmd_t;
-
-
/*
- * HT flags
+ * Hardware rate scaling set by iwh_ap_lq function.
+ * Given a particular initial rate and mode, the driver uses the
+ * following formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM]
+ * rate table in the Link Quality command:
+ *
+ * 1) If using High-throughput(HT)(SISO or MIMO) initial rate:
+ * a) Use this same initial rate for first 3 entries.
+ * b) Find next lower available rate using same mode(SISO or MIMO),
+ * use for next 3 entries. If no lower rate available, switch to
+ * legacy mode(no FAT channel, no MIMO, no short guard interval).
+ * c) If using MIMO, set command's mimo_delimeter to number of
+ * entries using MIMO(3 or 6).
+ * d) After trying 2 HT rates, switch to legacy mode(no FAT channel,
+ * no MIMO, no short qguard interval), at the next lower bit rate
+ * (e.g. if second HT bit rate was 54, try 48 legacy),and follow
+ * legacy procedure for remaining table entries.
+ *
+ * 2) If using legacy initial rate:
+ * a) Use the initial rate for only one entry.
+ * b) For each following entry, reduce the rate to next lower available
+ * rate, until reaching the lowest available rate.
+ * c) When reducing rate, also switch antenna selection.
+ * b) Once lowest available rate is reached, repreat this rate until
+ * rate table is filled(16 entries),switching antenna each entry.
*/
-#define RXON_FLG_CONTROL_CHANNEL_LOCATION_MSK 0x400000
-#define RXON_FLG_CONTROL_CHANNEL_LOC_LOW_MSK 0x000000
-#define RXON_FLG_CONTROL_CHANNEL_LOC_HIGH_MSK 0x400000
-
-#define RXON_FLG_HT_OPERATING_MODE_POS (23)
-#define RXON_FLG_HT_PROT_MSK 0x800000
-#define RXON_FLG_FAT_PROT_MSK 0x1000000
-
-#define RXON_FLG_CHANNEL_MODE_POS (25)
-#define RXON_FLG_CHANNEL_MODE_MSK 0x06000000
-#define RXON_FLG_CHANNEL_MODE_LEGACY_MSK 0x00000000
-#define RXON_FLG_CHANNEL_MODE_PURE_40_MSK 0x02000000
-#define RXON_FLG_CHANNEL_MODE_MIXED_MSK 0x04000000
-
-#define RXON_RX_CHAIN_DRIVER_FORCE_MSK (0x1<<0)
-#define RXON_RX_CHAIN_VALID_MSK (0x7<<1)
-#define RXON_RX_CHAIN_VALID_POS (1)
-#define RXON_RX_CHAIN_FORCE_SEL_MSK (0x7<<4)
-#define RXON_RX_CHAIN_FORCE_SEL_POS (4)
-#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK (0x7<<7)
-#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7)
-#define RXON_RX_CHAIN_CNT_MSK (0x3<<10)
-#define RXON_RX_CHAIN_CNT_POS (10)
-#define RXON_RX_CHAIN_MIMO_CNT_MSK (0x3<<12)
-#define RXON_RX_CHAIN_MIMO_CNT_POS (12)
-#define RXON_RX_CHAIN_MIMO_FORCE_MSK (0x1<<14)
-#define RXON_RX_CHAIN_MIMO_FORCE_POS (14)
-
-#define MCS_DUP_6M_PLCP (0x20)
/*
* OFDM HT rate masks
@@ -2204,20 +2238,6 @@ typedef struct iwh_assoc {
} iwh_assoc_t;
/*
- * structure for command IWH_CMD_SET_WME
- */
-typedef struct iwh_wme_setup {
- uint32_t flags;
- struct {
- uint16_t cwmin;
- uint16_t cwmax;
- uint8_t aifsn;
- uint8_t reserved;
- uint16_t txop;
- } ac[WME_NUM_AC];
-} iwh_wme_setup_t;
-
-/*
* structure for command IWH_CMD_TSF
*/
typedef struct iwh_cmd_tsf {
@@ -2233,6 +2253,20 @@ typedef struct iwh_cmd_tsf {
/*
* structure for IWH_CMD_ADD_NODE
*/
+#define STA_MODE_ADD_MSK (0)
+#define STA_MODE_MODIFY_MSK (1)
+
+#define STA_FLG_RTS_MIMO_PROT (1 << 17)
+#define STA_FLG_MAX_AMPDU_POS (19)
+#define STA_FLG_AMPDU_DENSITY_POS (23)
+#define STA_FLG_FAT_EN (1 << 21)
+
+#define STA_MODIFY_KEY_MASK (0x01)
+#define STA_MODIFY_TID_DISABLE_TX (0x02)
+#define STA_MODIFY_TX_RATE_MSK (0x04)
+#define STA_MODIFY_ADDBA_TID_MSK (0x08)
+#define STA_MODIFY_DELBA_TID_MSK (0x10)
+
struct sta_id_modify {
uint8_t addr[6];
uint16_t reserved1;
@@ -2459,6 +2493,30 @@ typedef struct iwh_bt_cmd {
uint32_t kill_cts_mask;
} iwh_bt_cmd_t;
+typedef struct iwh_wme_param {
+ uint8_t aifsn;
+ uint8_t cwmin_e;
+ uint8_t cwmax_e;
+ uint16_t txop;
+} iwh_wme_param_t;
+/*
+ * QoS parameter command (REPLY_QOS_PARAM = 0x13)
+ * FIFO0-background, FIFO1-best effort, FIFO2-video, FIFO3-voice
+ */
+
+struct iwh_edca_param {
+ uint16_t cw_min;
+ uint16_t cw_max;
+ uint8_t aifsn;
+ uint8_t reserved;
+ uint16_t txop;
+};
+
+typedef struct iwh_qos_param_cmd {
+ uint32_t flags;
+ struct iwh_edca_param ac[AC_NUM];
+} iwh_qos_param_cmd_t;
+
/*
* firmware image header
*/
diff --git a/usr/src/uts/common/io/iwh/iwh_var.h b/usr/src/uts/common/io/iwh/iwh_var.h
index 2a87f22b31..55c667b003 100644
--- a/usr/src/uts/common/io/iwh/iwh_var.h
+++ b/usr/src/uts/common/io/iwh/iwh_var.h
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (c) 2008, Intel Corporation
+ * Copyright (c) 2009, Intel Corporation
* All rights reserved.
*/
@@ -87,12 +87,13 @@ typedef struct iwh_rx_ring {
typedef struct iwh_amrr {
- ieee80211_node_t in; /* must be the first */
- int txcnt;
- int retrycnt;
- int success;
- int success_threshold;
- int recovery;
+ ieee80211_node_t in;
+ uint32_t txcnt;
+ uint32_t retrycnt;
+ uint32_t success;
+ uint32_t success_threshold;
+ int recovery;
+ volatile uint32_t ht_mcs_idx;
} iwh_amrr_t;
struct iwh_phy_rx {
@@ -113,12 +114,18 @@ typedef struct iwh_softc {
dev_info_t *sc_dip;
int (*sc_newstate)(struct ieee80211com *,
enum ieee80211_state, int);
+ void (*sc_recv_action)(ieee80211_node_t *,
+ const uint8_t *, const uint8_t *);
+ int (*sc_send_action)(ieee80211_node_t *,
+ int, int, uint16_t[4]);
+ volatile uint32_t sc_cmd_flag;
+ volatile uint32_t sc_cmd_accum;
enum ieee80211_state sc_ostate;
kmutex_t sc_glock;
kmutex_t sc_mt_lock;
kmutex_t sc_tx_lock;
- kmutex_t sc_ucode_lock;
+ kmutex_t sc_suspend_lock;
kcondvar_t sc_mt_cv;
kcondvar_t sc_tx_cv;
kcondvar_t sc_cmd_cv;
@@ -177,6 +184,8 @@ typedef struct iwh_softc {
struct iwh_alive_resp sc_card_alive_run;
struct iwh_init_alive_resp sc_card_alive_init;
+ iwh_ht_conf_t sc_ht_conf;
+ uint16_t sc_dev_id;
uint32_t sc_tx_timer;
uint32_t sc_scan_pending;
@@ -197,6 +206,10 @@ typedef struct iwh_softc {
uint32_t sc_tx_retries;
} iwh_sc_t;
+#define SC_CMD_FLG_NONE (0)
+#define SC_CMD_FLG_PENDING (1)
+#define SC_CMD_FLG_DONE (2)
+
#define IWH_F_ATTACHED (1 << 0)
#define IWH_F_CMD_DONE (1 << 1)
#define IWH_F_FW_INIT (1 << 2)
@@ -214,6 +227,33 @@ typedef struct iwh_softc {
#define IWH_SUCCESS 0
#define IWH_FAIL EIO
+
+/*
+ * Interaction steps for 802.11e/n between net80211 module
+ * and iwh driver:
+ * -- setup link with 802.11n AP: net80211 module is responsible
+ * for setup link with 802.11n AP. iwh driver monitors current
+ * state and make relevant configurations according work mode.
+ * -- QoS parameter updating: net80211 module is responsible for
+ * extract EDCA parameters from the fram of AP, iwh driver get
+ * these parameters and make relevant configuration to HW.
+ * -- TX queue management: iwh driver place a frame into suitable
+ * TX queue according to frame type and user priority extracted
+ * from frame head.
+ * -- MIMO: iwh driver make relevant configurations in TX and RX
+ * direction according to AP mode from net80211 module.
+ * -- Link aggregation: AMSDU is implemented by net80211 module and
+ * AMPDU is implemented by both iwh driver and net80211 module.
+ * iwh driver distinguish frames in one AMPDU frame and net80211
+ * module is responsible reordering every frame.
+ * -- Block ACK: net80211 module is responsible for setup agreement
+ * with AP and iwh driver is responsible for realistic ACK.
+ * -- Rate scaling: This function is implemented independently by
+ * iwh driver.
+ * -- HT protection: This feature is also implemented by iwh driver
+ * no interaction with net80211 module.
+ */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/io/mac/plugins/mac_wifi.c b/usr/src/uts/common/io/mac/plugins/mac_wifi.c
index fb45c8ef1c..1bd338c8b9 100644
--- a/usr/src/uts/common/io/mac/plugins/mac_wifi.c
+++ b/usr/src/uts/common/io/mac/plugins/mac_wifi.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -62,7 +62,7 @@ static mac_stat_info_t wifi_stats[] = {
static struct modlmisc mac_wifi_modlmisc = {
&mod_miscops,
- "WiFi MAC plugin"
+ "WiFi MAC plugin 1.4"
};
static struct modlinkage mac_wifi_modlinkage = {
@@ -200,7 +200,7 @@ mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
* Fill in the fixed parts of the ieee80211_frame.
*/
wh = (struct ieee80211_frame *)mp->b_rptr;
- mp->b_wptr += sizeof (struct ieee80211_frame);
+ mp->b_wptr += sizeof (struct ieee80211_frame) + wdp->wd_qospad;
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA;
switch (wdp->wd_opmode) {
@@ -227,6 +227,13 @@ mac_wifi_header(const void *saddr, const void *daddr, uint32_t sap,
break;
}
+ if (wdp->wd_qospad) {
+ struct ieee80211_qosframe *qwh =
+ (struct ieee80211_qosframe *)wh;
+ qwh->i_qos[1] = 0;
+ qwh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_QOS;
+ }
+
switch (wdp->wd_secalloc) {
case WIFI_SEC_WEP:
/*
@@ -282,6 +289,14 @@ mac_wifi_header_info(mblk_t *mp, void *pdata, mac_header_info_t *mhp)
llcp = mp->b_rptr + sizeof (struct ieee80211_frame);
/*
+ * Generally, QoS data field takes 2 bytes, but some special hardware,
+ * such as Atheros, will need the 802.11 header padded to a 32-bit
+ * boundary for 4-address and QoS frames, at this time, it's 4 bytes.
+ */
+ if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS)
+ llcp += wdp->wd_qospad;
+
+ /*
* When we receive frames from other hosts, the hardware will have
* already performed WEP decryption, and thus there will not be a WEP
* portion. However, when we receive a loopback copy of our own
diff --git a/usr/src/uts/common/io/net80211/net80211.c b/usr/src/uts/common/io/net80211/net80211.c
index fd49066fcc..7b8d614190 100644
--- a/usr/src/uts/common/io/net80211/net80211.c
+++ b/usr/src/uts/common/io/net80211/net80211.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -58,6 +58,9 @@ const char *ieee80211_phymode_name[] = {
"FH", /* IEEE80211_MODE_FH */
"turboA", /* IEEE80211_MODE_TURBO_A */
"turboG", /* IEEE80211_MODE_TURBO_G */
+ "sturboA", /* IEEE80211_MODE_STURBO_A */
+ "11na", /* IEEE80211_MODE_11NA */
+ "11ng", /* IEEE80211_MODE_11NG */
};
#define IEEE80211_DPRINT(_level, _fmt) do { \
@@ -122,6 +125,12 @@ ieee80211_mac_update(ieee80211com_t *ic)
wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
wd.wd_opmode = ic->ic_opmode;
IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+ wd.wd_qospad = 0;
+ if (in->in_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) {
+ wd.wd_qospad = 2;
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ wd.wd_qospad = roundup(wd.wd_qospad, sizeof (uint32_t));
+ }
(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
mac_tx_update(ic->ic_mach);
ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_mac_update"
@@ -380,6 +389,9 @@ ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode)
IEEE80211_CHAN_FHSS, /* IEEE80211_MODE_FH */
IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO_A */
IEEE80211_CHAN_108G, /* IEEE80211_MODE_TURBO_G */
+ IEEE80211_CHAN_ST, /* IEEE80211_MODE_STURBO_A */
+ IEEE80211_CHAN_A, /* IEEE80211_MODE_11NA (check legacy) */
+ IEEE80211_CHAN_G, /* IEEE80211_MODE_11NG (check legacy) */
};
struct ieee80211_channel *ch;
uint32_t modeflags;
@@ -465,6 +477,7 @@ ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode)
ic->ic_bss->in_rates = ic->ic_sup_rates[mode];
ic->ic_curmode = mode;
ieee80211_reset_erp(ic); /* reset ERP state */
+ ieee80211_wme_initparams(ic); /* reset WME stat */
return (0);
}
@@ -475,35 +488,74 @@ ieee80211_setmode(ieee80211com_t *ic, enum ieee80211_phymode mode)
* where multiple operating modes are possible (e.g. 11g+11b).
* In those cases we defer to the current operating mode when set.
*/
+/* ARGSUSED */
enum ieee80211_phymode
ieee80211_chan2mode(ieee80211com_t *ic, struct ieee80211_channel *chan)
{
- if (IEEE80211_IS_CHAN_T(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_T(chan))
return (IEEE80211_MODE_TURBO_A);
- } else if (IEEE80211_IS_CHAN_5GHZ(chan)) {
+ else if (IEEE80211_IS_CHAN_A(chan))
return (IEEE80211_MODE_11A);
- } else if (IEEE80211_IS_CHAN_FHSS(chan)) {
- return (IEEE80211_MODE_FH);
- } else if (chan->ich_flags & (IEEE80211_CHAN_OFDM|IEEE80211_CHAN_DYN)) {
- /*
- * This assumes all 11g channels are also usable
- * for 11b, which is currently true.
- */
- if (ic->ic_curmode == IEEE80211_MODE_TURBO_G)
- return (IEEE80211_MODE_TURBO_G);
- if (ic->ic_curmode == IEEE80211_MODE_11B)
- return (IEEE80211_MODE_11B);
+ else if (IEEE80211_IS_CHAN_ANYG(chan))
return (IEEE80211_MODE_11G);
- } else {
+ 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 */
+ ieee80211_err("cannot map channel to mode; freq %u flags 0x%x\n",
+ chan->ich_freq, chan->ich_flags);
+
+ return (IEEE80211_MODE_11B);
+}
+
+const struct ieee80211_rateset *
+ieee80211_get_suprates(ieee80211com_t *ic, struct ieee80211_channel *c)
+{
+ if (IEEE80211_IS_CHAN_HTA(c))
+ return (&ic->ic_sup_rates[IEEE80211_MODE_11A]);
+ if (IEEE80211_IS_CHAN_HTG(c)) {
+ return (&ic->ic_sup_rates[IEEE80211_MODE_11G]);
}
+ return (&ic->ic_sup_rates[ieee80211_chan2mode(ic, c)]);
+}
+
+/*
+ * Locate a channel given a frequency+flags. We cache
+ * the previous lookup to optimize swithing between two
+ * channels--as happens with dynamic turbo.
+ */
+struct ieee80211_channel *
+ieee80211_find_channel(ieee80211com_t *ic, int freq, int flags)
+{
+ struct ieee80211_channel *c;
+ int i;
+
+ flags &= IEEE80211_CHAN_ALLTURBO;
+ /* brute force search */
+ for (i = 0; i < IEEE80211_CHAN_MAX; i++) {
+ c = &ic->ic_sup_channels[i];
+ if (c->ich_freq == freq &&
+ (c->ich_flags & IEEE80211_CHAN_ALLTURBO) == flags)
+ return (c);
+ }
+ return (NULL);
}
/*
* Return the size of the 802.11 header for a management or data frame.
*/
int
-ieee80211_hdrspace(const void *data)
+ieee80211_hdrsize(const void *data)
{
const struct ieee80211_frame *wh = data;
int size = sizeof (struct ieee80211_frame);
@@ -513,7 +565,56 @@ ieee80211_hdrspace(const void *data)
IEEE80211_FC0_TYPE_CTL);
if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
size += IEEE80211_ADDR_LEN;
+ if (IEEE80211_QOS_HAS_SEQ(wh))
+ size += sizeof (uint16_t);
+
+ return (size);
+}
+/*
+ * Return the space occupied by the 802.11 header and any
+ * padding required by the driver. This works for a
+ * management or data frame.
+ */
+int
+ieee80211_hdrspace(ieee80211com_t *ic, const void *data)
+{
+ int size = ieee80211_hdrsize(data);
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ size = roundup(size, sizeof (uint32_t));
+ return (size);
+}
+
+/*
+ * Like ieee80211_hdrsize, but handles any type of frame.
+ */
+int
+ieee80211_anyhdrsize(const void *data)
+{
+ const struct ieee80211_frame *wh = data;
+
+ if ((wh->i_fc[0]&IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
+ switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) {
+ case IEEE80211_FC0_SUBTYPE_CTS:
+ case IEEE80211_FC0_SUBTYPE_ACK:
+ return (sizeof (struct ieee80211_frame_ack));
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ return (sizeof (struct ieee80211_frame_bar));
+ }
+ return (sizeof (struct ieee80211_frame_min));
+ } else
+ return (ieee80211_hdrsize(data));
+}
+
+/*
+ * Like ieee80211_hdrspace, but handles any type of frame.
+ */
+int
+ieee80211_anyhdrspace(ieee80211com_t *ic, const void *data)
+{
+ int size = ieee80211_anyhdrsize(data);
+ if (ic->ic_flags & IEEE80211_F_DATAPAD)
+ size = roundup(size, sizeof (uint32_t));
return (size);
}
@@ -567,6 +668,7 @@ ieee80211_notify_node_leave(ieee80211com_t *ic, ieee80211_node_t *in)
ieee80211_notify(ic, EVENT_DISASSOC); /* notify WPA service */
}
+
/*
* Get 802.11 kstats defined in ieee802.11(5)
*
@@ -674,6 +776,12 @@ ieee80211_attach(ieee80211com_t *ic)
ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
if (IEEE80211_IS_CHAN_108G(ch))
ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
+ if (IEEE80211_IS_CHAN_ST(ch))
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_STURBO_A;
+ if (IEEE80211_IS_CHAN_HTA(ch))
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_11NA;
+ if (IEEE80211_IS_CHAN_HTG(ch))
+ ic->ic_modecaps |= 1 << IEEE80211_MODE_11NG;
if (ic->ic_curchan == NULL) {
/* arbitrarily pick the first channel */
ic->ic_curchan = &ic->ic_sup_channels[i];
@@ -686,6 +794,8 @@ ieee80211_attach(ieee80211com_t *ic)
ic->ic_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */
(void) ieee80211_setmode(ic, ic->ic_curmode);
+ if (ic->ic_caps & IEEE80211_C_WME) /* enable if capable */
+ ic->ic_flags |= IEEE80211_F_WME;
if (ic->ic_caps & IEEE80211_C_BURST)
ic->ic_flags |= IEEE80211_F_BURST;
ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT;
@@ -698,6 +808,7 @@ ieee80211_attach(ieee80211com_t *ic)
ieee80211_node_attach(ic);
ieee80211_proto_attach(ic);
ieee80211_crypto_attach(ic);
+ ieee80211_ht_attach(ic);
ic->ic_watchdog_timer = 0;
}
@@ -717,6 +828,7 @@ ieee80211_detach(ieee80211com_t *ic)
if (ic->ic_opt_ie != NULL)
ieee80211_free(ic->ic_opt_ie);
+ ieee80211_ht_detach(ic);
ieee80211_node_detach(ic);
ieee80211_crypto_detach(ic);
@@ -726,7 +838,7 @@ ieee80211_detach(ieee80211com_t *ic)
static struct modlmisc i_wifi_modlmisc = {
&mod_miscops,
- "IEEE80211 Kernel Module v1.3"
+ "IEEE80211 Kernel Module v2.0"
};
static struct modlinkage i_wifi_modlinkage = {
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c
index 17d7c9f237..679b62cace 100644
--- a/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_ccmp.c
@@ -1,11 +1,11 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -118,10 +118,11 @@ ccmp_setkey(struct ieee80211_key *k)
static int
ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
{
+ struct ccmp_ctx *ctx = k->wk_private;
uint8_t *ivp;
int hdrlen;
- hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ hdrlen = ieee80211_hdrspace(ctx->cc_ic, mp->b_rptr);
/*
* Copy down 802.11 header and add the IV, KeyID, and ExtIV.
*/
@@ -156,7 +157,6 @@ ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
static int
ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
{
- struct ieee80211_frame tmp;
uint8_t *ivp;
uint64_t pn;
@@ -194,8 +194,7 @@ ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
/*
* Copy up 802.11 header and strip crypto bits.
*/
- bcopy(mp->b_rptr, &tmp, hdrlen);
- bcopy(&tmp, mp->b_rptr + ccmp.ic_header, hdrlen);
+ (void) memmove(mp->b_rptr + ccmp.ic_header, mp->b_rptr, hdrlen);
mp->b_rptr += ccmp.ic_header;
mp->b_wptr -= ccmp.ic_trailer;
@@ -406,9 +405,18 @@ ccmp_init(struct ieee80211_frame *wh, uint64_t pn, size_t dlen,
* initial block as we know whether or not we have
* a QOS frame.
*/
- *(uint16_t *)&aad[24] = 0;
- b0[1] = 0;
- aad[1] = 22;
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ struct ieee80211_qosframe *qwh =
+ (struct ieee80211_qosframe *)wh;
+ aad[24] = qwh->i_qos[0] & 0x0f; /* just priority bits */
+ aad[25] = 0;
+ b0[1] = aad[24];
+ aad[1] = 22 + 2;
+ } else {
+ *(uint16_t *)&aad[24] = 0;
+ b0[1] = 0;
+ aad[1] = 22;
+ }
*(uint16_t *)&aad[26] = 0;
*(uint32_t *)&aad[28] = 0;
}
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c
index 8a99b86062..e689c3ad2b 100644
--- a/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_tkip.c
@@ -5,7 +5,7 @@
/*
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -147,7 +147,7 @@ tkip_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
if (ic->ic_flags & IEEE80211_F_COUNTERM)
return (0);
- hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ hdrlen = ieee80211_hdrspace(ic, mp->b_rptr);
/*
* Copy down 802.11 header and add the IV, KeyID, and ExtIV.
*/
@@ -194,7 +194,6 @@ tkip_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
{
struct tkip_ctx *ctx = k->wk_private;
struct ieee80211com *ic = ctx->tc_ic;
- struct ieee80211_frame tmp;
uint8_t *ivp;
uint64_t pn;
@@ -242,8 +241,7 @@ tkip_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
/*
* Copy up 802.11 header and strip crypto bits.
*/
- bcopy(mp->b_rptr, &tmp, hdrlen);
- bcopy(&tmp, mp->b_rptr + tkip.ic_header, hdrlen);
+ (void) memmove(mp->b_rptr + tkip.ic_header, mp->b_rptr, hdrlen);
mp->b_rptr += tkip.ic_header;
mp->b_wptr -= tkip.ic_trailer;
@@ -262,7 +260,7 @@ tkip_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
int hdrlen;
uint8_t *mic;
- hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ hdrlen = ieee80211_hdrspace(ctx->tc_ic, mp->b_rptr);
mic = mp->b_wptr;
mp->b_wptr += tkip.ic_miclen;
@@ -287,7 +285,7 @@ tkip_demic(struct ieee80211_key *k, mblk_t *mp, int force)
struct tkip_ctx *ctx = k->wk_private;
if (force || (k->wk_flags & IEEE80211_KEY_SWMIC)) {
- int hdrlen = ieee80211_hdrspace(mp->b_rptr);
+ int hdrlen = ieee80211_hdrspace(ctx->tc_ic, mp->b_rptr);
uint8_t mic[IEEE80211_WEP_MICLEN];
uint8_t mic0[IEEE80211_WEP_MICLEN];
@@ -640,7 +638,12 @@ michael_mic_hdr(const struct ieee80211_frame *wh0, uint8_t hdr[16])
break;
}
- hdr[12] = 0; /* QoS not supported */
+ if (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_QOS) {
+ const struct ieee80211_qosframe *qwh =
+ (const struct ieee80211_qosframe *)wh;
+ hdr[12] = qwh->i_qos[0] & IEEE80211_QOS_TID;
+ } else
+ hdr[12] = 0;
hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
}
diff --git a/usr/src/uts/common/io/net80211/net80211_crypto_wep.c b/usr/src/uts/common/io/net80211/net80211_crypto_wep.c
index c0a4faf5e3..d8d3f0a65d 100644
--- a/usr/src/uts/common/io/net80211/net80211_crypto_wep.c
+++ b/usr/src/uts/common/io/net80211/net80211_crypto_wep.c
@@ -1,11 +1,11 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,8 +35,6 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* IEEE 802.11 WEP crypto support.
*/
@@ -135,7 +133,7 @@ wep_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
if (mp == NULL)
return (0);
- hdrlen = ieee80211_hdrspace(wh);
+ hdrlen = ieee80211_hdrspace(ctx->wc_ic, wh);
ivp = (uint8_t *)wh;
ivp += hdrlen;
@@ -182,10 +180,6 @@ wep_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
static int
wep_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
{
- struct ieee80211_frame *wh, whbuf;
-
- wh = (struct ieee80211_frame *)mp->b_rptr;
-
/*
* Check if the device handled the decrypt in hardware.
* If so we just strip the header; otherwise we need to
@@ -200,9 +194,8 @@ wep_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
/*
* Copy up 802.11 header and strip crypto bits.
*/
- bcopy(wh, &whbuf, sizeof (whbuf));
+ (void) memmove(mp->b_rptr + wep.ic_header, mp->b_rptr, hdrlen);
mp->b_rptr += wep.ic_header;
- bcopy(&whbuf, mp->b_rptr, hdrlen);
mp->b_wptr -= wep.ic_trailer;
return (1);
diff --git a/usr/src/uts/common/io/net80211/net80211_ht.c b/usr/src/uts/common/io/net80211/net80211_ht.c
new file mode 100644
index 0000000000..7da58a1cf0
--- /dev/null
+++ b/usr/src/uts/common/io/net80211/net80211_ht.c
@@ -0,0 +1,1900 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * IEEE 802.11n protocol support.
+ */
+#include <sys/mac_provider.h>
+#include <sys/strsun.h>
+#include <sys/byteorder.h>
+
+#include "net80211_impl.h"
+
+/* define here, used throughout file */
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+
+/* need max array size */
+/* NB: these are for HT20 w/ long GI */
+const int ieee80211_htrates[16] = {
+ 13, /* IFM_IEEE80211_MCS0 */
+ 26, /* IFM_IEEE80211_MCS1 */
+ 39, /* IFM_IEEE80211_MCS2 */
+ 52, /* IFM_IEEE80211_MCS3 */
+ 78, /* IFM_IEEE80211_MCS4 */
+ 104, /* IFM_IEEE80211_MCS5 */
+ 117, /* IFM_IEEE80211_MCS6 */
+ 130, /* IFM_IEEE80211_MCS7 */
+ 26, /* IFM_IEEE80211_MCS8 */
+ 52, /* IFM_IEEE80211_MCS9 */
+ 78, /* IFM_IEEE80211_MCS10 */
+ 104, /* IFM_IEEE80211_MCS11 */
+ 156, /* IFM_IEEE80211_MCS12 */
+ 208, /* IFM_IEEE80211_MCS13 */
+ 234, /* IFM_IEEE80211_MCS14 */
+ 260, /* IFM_IEEE80211_MCS15 */
+};
+
+struct ieee80211_htrateset ieee80211_rateset_11n =
+ { 16, {
+ /* MCS: 6.5 13 19.5 26 39 52 58.5 65 13 26 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ /* 39 52 78 104 117, 130 */
+ 10, 11, 12, 13, 14, 15 }
+ };
+
+#define IEEE80211_AMPDU_AGE
+
+#define IEEE80211_AGGR_TIMEOUT 250 /* msecs */
+#define IEEE80211_AGGR_MINRETRY (10 * hz) /* ticks */
+#define IEEE80211_AGGR_MAXTRIES 3
+
+/*
+ * Receive processing.
+ */
+
+/*
+ * Decap the encapsulated A-MSDU frames and dispatch all but
+ * the last for delivery. The last frame is returned for
+ * delivery via the normal path.
+ */
+#define FF_LLC_SIZE \
+ (sizeof (struct ether_header) + sizeof (struct ieee80211_llc))
+mblk_t *
+ieee80211_decap_amsdu(struct ieee80211_node *in, mblk_t *mp)
+{
+ struct ieee80211com *ic = in->in_ic;
+ struct ether_header *eh;
+ struct ieee80211_frame *wh;
+ int framelen, hdrspace;
+ mblk_t *m0;
+
+ /* all msdu has same ieee80211_frame header */
+ wh = (struct ieee80211_frame *)mp->b_rptr;
+ hdrspace = ieee80211_hdrspace(ic, wh);
+ mp->b_rptr += hdrspace; /* A-MSDU subframe follows */
+
+ for (;;) {
+ /*
+ * The frame has an 802.3 header followed by an 802.2
+ * LLC header. The encapsulated frame length is in the
+ * first header type field;
+ */
+ if (MBLKL(mp) < FF_LLC_SIZE) {
+ ieee80211_err("too short, decap failed\n");
+ goto out;
+ }
+ /*
+ * Decap frames, encapsulate to 802.11 frame then deliver.
+ * 802.3 header is first (struct ether_header)
+ * 802.2 header follows (struct ieee80211_llc)
+ * data, msdu = llc + data
+ */
+ eh = (struct ether_header *)mp->b_rptr;
+ /* 802.2 header follows */
+ framelen = ntohs(eh->ether_type); /* llc + data */
+ m0 = allocb(hdrspace + framelen, BPRI_MED);
+ if (m0 == NULL) {
+ ieee80211_err("decap_msdu(): can't alloc mblk\n");
+ goto out;
+ }
+ (void) memcpy(m0->b_wptr, (uint8_t *)wh, hdrspace);
+ m0->b_wptr += hdrspace;
+ (void) memcpy(m0->b_wptr,
+ mp->b_rptr + sizeof (struct ether_header), framelen);
+ m0->b_wptr += framelen;
+
+ ic->ic_stats.is_rx_frags++;
+ ic->ic_stats.is_rx_bytes += MBLKL(m0);
+ IEEE80211_UNLOCK(ic);
+ mac_rx(ic->ic_mach, NULL, m0); /* deliver to mac */
+ IEEE80211_LOCK(ic);
+
+ framelen += sizeof (struct ether_header);
+ if (MBLKL(mp) == framelen) /* last, no padding */
+ goto out;
+ /*
+ * Remove frame contents; each intermediate frame
+ * is required to be aligned to a 4-byte boundary.
+ */
+ mp->b_rptr += roundup(framelen, 4); /* padding */
+ }
+
+out:
+ freemsg(mp);
+ return (NULL); /* none delivered by caller */
+}
+#undef FF_LLC_SIZE
+
+/*
+ * Start A-MPDU rx/re-order processing for the specified TID.
+ */
+static void
+ampdu_rx_start(struct ieee80211_rx_ampdu *rap, int bufsiz, int start)
+{
+ (void) memset(rap, 0, sizeof (*rap));
+ rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
+ : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
+ rap->rxa_start = (uint16_t)start;
+ rap->rxa_flags |= IEEE80211_AGGR_XCHGPEND;
+}
+
+/*
+ * Purge all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_purge(struct ieee80211_rx_ampdu *rap)
+{
+ mblk_t *m;
+ int i;
+
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m != NULL) {
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= MBLKL(m);
+ freemsg(m);
+ if (--rap->rxa_qframes == 0)
+ break;
+ }
+ }
+ ASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0);
+}
+
+/*
+ * Stop A-MPDU rx processing for the specified TID.
+ */
+static void
+ampdu_rx_stop(struct ieee80211_rx_ampdu *rap)
+{
+ rap->rxa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ ampdu_rx_purge(rap);
+}
+
+/*
+ * Dispatch a frame from the A-MPDU reorder queue. The
+ * frame is fed back into ieee80211_input marked with an
+ * M_AMPDU flag so it doesn't come back to us (it also
+ * permits ieee80211_input to optimize re-processing).
+ */
+static void
+ampdu_dispatch(struct ieee80211_node *in, mblk_t *m)
+{
+ m->b_flag |= M_AMPDU; /* bypass normal processing */
+ /* NB: rssi and rstamp are ignored w/ M_AMPDU set */
+ (void) ieee80211_input(in->in_ic, m, in, 0, 0);
+}
+
+/*
+ * Dispatch as many frames as possible from the re-order queue.
+ * Frames will always be "at the front"; we process all frames
+ * up to the first empty slot in the window. On completion we
+ * cleanup state if there are still pending frames in the current
+ * BA window. We assume the frame at slot 0 is already handled
+ * by the caller; we always start at slot 1.
+ */
+static void
+ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *in)
+{
+ mblk_t *m;
+ int i;
+
+ /* flush run of frames */
+ for (i = 1; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m == NULL)
+ break;
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= MBLKL(m);
+ rap->rxa_qframes--;
+
+ ampdu_dispatch(in, m);
+ }
+ /*
+ * If frames remain, copy the mbuf pointers down so
+ * they correspond to the offsets in the new window.
+ */
+ if (rap->rxa_qframes != 0) {
+ int n = rap->rxa_qframes, j;
+ for (j = i+1; j < rap->rxa_wnd; j++) {
+ if (rap->rxa_m[j] != NULL) {
+ rap->rxa_m[j-i] = rap->rxa_m[j];
+ rap->rxa_m[j] = NULL;
+ if (--n == 0)
+ break;
+ }
+ }
+ ASSERT(n == 0);
+ }
+ /*
+ * Adjust the start of the BA window to
+ * reflect the frames just dispatched.
+ */
+ rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i);
+}
+
+#ifdef IEEE80211_AMPDU_AGE
+/*
+ * Dispatch all frames in the A-MPDU re-order queue.
+ */
+static void
+ampdu_rx_flush(struct ieee80211_node *in, struct ieee80211_rx_ampdu *rap)
+{
+ mblk_t *m;
+ int i;
+
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "ampdu_rx_flush(%d)\n",
+ rap->rxa_wnd);
+
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m == NULL)
+ continue;
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= MBLKL(m);
+ rap->rxa_qframes--;
+
+ ampdu_dispatch(in, m);
+ if (rap->rxa_qframes == 0)
+ break;
+ }
+}
+#endif /* IEEE80211_AMPDU_AGE */
+
+/*
+ * Dispatch all frames in the A-MPDU re-order queue
+ * preceding the specified sequence number. This logic
+ * handles window moves due to a received MSDU or BAR.
+ */
+static void
+ampdu_rx_flush_upto(struct ieee80211_node *in,
+ struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart)
+{
+ mblk_t *m;
+ ieee80211_seq seqno;
+ int i;
+
+ /*
+ * Flush any complete MSDU's with a sequence number lower
+ * than winstart. Gaps may exist. Note that we may actually
+ * dispatch frames past winstart if a run continues; this is
+ * an optimization that avoids having to do a separate pass
+ * to dispatch frames after moving the BA window start.
+ */
+ seqno = rap->rxa_start;
+ for (i = 0; i < rap->rxa_wnd; i++) {
+ m = rap->rxa_m[i];
+ if (m != NULL) {
+ rap->rxa_m[i] = NULL;
+ rap->rxa_qbytes -= MBLKL(m);
+ rap->rxa_qframes--;
+
+ ampdu_dispatch(in, m);
+ } else {
+ if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart))
+ break;
+ }
+ seqno = IEEE80211_SEQ_INC(seqno);
+ }
+ /*
+ * If frames remain, copy the mbuf pointers down so
+ * they correspond to the offsets in the new window.
+ */
+ if (rap->rxa_qframes != 0) {
+ int n = rap->rxa_qframes, j;
+ for (j = i+1; j < rap->rxa_wnd; j++) {
+ if (rap->rxa_m[j] != NULL) {
+ rap->rxa_m[j-i] = rap->rxa_m[j];
+ rap->rxa_m[j] = NULL;
+ if (--n == 0)
+ break;
+ }
+ }
+ if (n != 0) {
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "ampdu_rx_flush_upto(): "
+ "lost %d frames, qframes %d off %d "
+ "BA win <%d:%d> winstart %d\n",
+ n, rap->rxa_qframes, i, rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ winstart);
+ }
+ }
+ /*
+ * Move the start of the BA window; we use the
+ * sequence number of the last MSDU that was
+ * passed up the stack+1 or winstart if stopped on
+ * a gap in the reorder buffer.
+ */
+ rap->rxa_start = seqno;
+}
+
+/*
+ * Process a received QoS data frame for an HT station. Handle
+ * A-MPDU reordering: if this frame is received out of order
+ * and falls within the BA window hold onto it. Otherwise if
+ * this frame completes a run, flush any pending frames. We
+ * return 1 if the frame is consumed. A 0 is returned if
+ * the frame should be processed normally by the caller.
+ */
+int
+ieee80211_ampdu_reorder(struct ieee80211_node *in, mblk_t *m)
+{
+#define IEEE80211_FC0_QOSDATA \
+ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS | \
+ IEEE80211_FC0_VERSION_0)
+
+#define PROCESS 0 /* caller should process frame */
+#define CONSUMED 1 /* frame consumed, caller does nothing */
+
+ struct ieee80211_qosframe *wh;
+ struct ieee80211_rx_ampdu *rap;
+ ieee80211_seq rxseq;
+ uint8_t tid;
+ int off;
+
+ ASSERT(in->in_flags & IEEE80211_NODE_HT);
+
+ /* NB: m_len known to be sufficient */
+ wh = (struct ieee80211_qosframe *)m->b_rptr;
+ ASSERT(wh->i_fc[0] == IEEE80211_FC0_QOSDATA);
+
+ if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS)
+ tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
+ else
+ tid = wh->i_qos[0];
+ tid &= IEEE80211_QOS_TID;
+ rap = &in->in_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ /*
+ * No ADDBA request yet, don't touch.
+ */
+ return (PROCESS);
+ }
+ rxseq = LE_16(*(uint16_t *)wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ rap->rxa_nframes++;
+again:
+ if (rxseq == rap->rxa_start) {
+ /*
+ * First frame in window.
+ */
+ if (rap->rxa_qframes != 0) {
+ /*
+ * Dispatch as many packets as we can.
+ */
+ ASSERT(rap->rxa_m[0] == NULL); /* [0] is m */
+ ampdu_dispatch(in, m);
+ ampdu_rx_dispatch(rap, in);
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "ieee80211_ampdu_reorder(%u), CONSUMED ...\n",
+ rap->rxa_qframes);
+ return (CONSUMED);
+ } else {
+ /*
+ * In order; advance window and notify
+ * caller to dispatch directly.
+ */
+ rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "ieee80211_ampdu_reorder(%u), PROCESS ...\n",
+ rap->rxa_start);
+ return (PROCESS);
+ }
+ }
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "ieee80211_ampdu_reorder(%u, %u), out of order ...\n",
+ rxseq, rap->rxa_start);
+ /*
+ * Frame is out of order; store if in the BA window.
+ */
+ /* calculate offset in BA window */
+ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+ if (off < rap->rxa_wnd) {
+#ifdef IEEE80211_AMPDU_AGE
+ /*
+ * Common case (hopefully): in the BA window.
+ * Sec 9.10.7.6 a) (D2.04 p.118 line 47)
+ * --
+ * Check for frames sitting too long in the reorder queue.
+ * This should only ever happen if frames are not delivered
+ * without the sender otherwise notifying us (e.g. with a
+ * BAR to move the window). Typically this happens because
+ * of vendor bugs that cause the sequence number to jump.
+ * When this happens we get a gap in the reorder queue that
+ * leaves frame sitting on the queue until they get pushed
+ * out due to window moves. When the vendor does not send
+ * BAR this move only happens due to explicit packet sends
+ *
+ * NB: we only track the time of the oldest frame in the
+ * reorder q; this means that if we flush we might push
+ * frames that still "new"; if this happens then subsequent
+ * frames will result in BA window moves which cost something
+ * but is still better than a big throughput dip.
+ */
+ clock_t ticks;
+
+ ticks = ddi_get_lbolt();
+ if (rap->rxa_qframes != 0) {
+ /* honor batimeout? */
+ if (ticks - rap->rxa_age > drv_usectohz(500*1000)) {
+ /*
+ * Too long since we received the first
+ * frame; flush the reorder buffer.
+ */
+ if (rap->rxa_qframes != 0) {
+ ampdu_rx_flush(in, rap);
+ }
+ rap->rxa_start = IEEE80211_SEQ_INC(rxseq);
+ return (PROCESS);
+ }
+ } else {
+ /*
+ * First frame, start aging timer.
+ */
+ rap->rxa_age = ticks;
+ }
+#endif /* IEEE80211_AMPDU_AGE */
+ /* save packet */
+ if (rap->rxa_m[off] == NULL) {
+ rap->rxa_m[off] = m;
+ rap->rxa_qframes++;
+ rap->rxa_qbytes += MBLKL(m);
+ } else {
+ ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+ "a-mpdu duplicate "
+ "seqno %u tid %u BA win <%u:%u>\n",
+ rxseq, tid, rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start,
+ rap->rxa_wnd - 1));
+ freemsg(m);
+ }
+ return (CONSUMED);
+ }
+ if (off < IEEE80211_SEQ_BA_RANGE) {
+ /*
+ * Outside the BA window, but within range;
+ * flush the reorder q and move the window.
+ * Sec 9.10.7.6 b) (D2.04 p.118 line 60)
+ */
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "move BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd - 1),
+ rap->rxa_qframes, rxseq, tid);
+
+ /*
+ * The spec says to flush frames up to but not including:
+ * WinStart_B = rxseq - rap->rxa_wnd + 1
+ * Then insert the frame or notify the caller to process
+ * it immediately. We can safely do this by just starting
+ * over again because we know the frame will now be within
+ * the BA window.
+ */
+ /* NB: rxa_wnd known to be >0 */
+ ampdu_rx_flush_upto(in, rap,
+ IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1));
+ goto again;
+ } else {
+ /*
+ * Outside the BA window and out of range; toss.
+ * Sec 9.10.7.6 c) (D2.04 p.119 line 16)
+ */
+ ieee80211_dbg(IEEE80211_MSG_HT, "MSDU"
+ "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid,
+ wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+ freemsg(m);
+ return (CONSUMED);
+ }
+
+#undef CONSUMED
+#undef PROCESS
+#undef IEEE80211_FC0_QOSDATA
+}
+
+/*
+ * Process a BAR ctl frame. Dispatch all frames up to
+ * the sequence number of the frame. If this frame is
+ * out of range it's discarded.
+ */
+void
+ieee80211_recv_bar(struct ieee80211_node *in, mblk_t *m0)
+{
+ struct ieee80211_frame_bar *wh;
+ struct ieee80211_rx_ampdu *rap;
+ ieee80211_seq rxseq;
+ int tid, off;
+
+ wh = (struct ieee80211_frame_bar *)m0->b_rptr;
+ /* check basic BAR */
+ tid = MS(LE_16(wh->i_ctl), IEEE80211_BAR_TID);
+ rap = &in->in_rx_ampdu[tid];
+ if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ /*
+ * No ADDBA request yet, don't touch.
+ */
+ ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+ "BAR no BA stream, tid %u\n", tid);
+ return;
+ }
+ rxseq = LE_16(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT;
+ if (rxseq == rap->rxa_start)
+ return;
+ /* calculate offset in BA window */
+ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start);
+ if (off < IEEE80211_SEQ_BA_RANGE) {
+ /*
+ * Flush the reorder q up to rxseq and move the window.
+ * Sec 9.10.7.6 a) (D2.04 p.119 line 22)
+ */
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u\n",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid);
+
+ ampdu_rx_flush_upto(in, rap, rxseq);
+ if (off >= rap->rxa_wnd) {
+ /*
+ * BAR specifies a window start to the right of BA
+ * window; we must move it explicitly since
+ * ampdu_rx_flush_upto will not.
+ */
+ rap->rxa_start = rxseq;
+ }
+ } else {
+ /*
+ * Out of range; toss.
+ * Sec 9.10.7.6 b) (D2.04 p.119 line 41)
+ */
+ ieee80211_dbg(IEEE80211_MSG_HT, "BAR "
+ "BA win <%u:%u> (%u frames) rxseq %u tid %u%s\n",
+ rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ rap->rxa_qframes, rxseq, tid,
+ wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : "");
+ }
+}
+
+/*
+ * Setup HT-specific state in a node. Called only
+ * when HT use is negotiated so we don't do extra
+ * work for temporary and/or legacy sta's.
+ */
+void
+ieee80211_ht_node_init(struct ieee80211_node *in, const uint8_t *htcap)
+{
+ struct ieee80211_tx_ampdu *tap;
+ int ac;
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ /*
+ * Clean AMPDU state on re-associate. This handles the case
+ * where a station leaves w/o notifying us and then returns
+ * before node is reaped for inactivity.
+ */
+ ieee80211_ht_node_cleanup(in);
+ }
+ ieee80211_parse_htcap(in, htcap);
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ tap = &in->in_tx_ampdu[ac];
+ tap->txa_ac = (uint8_t)ac;
+ /* NB: further initialization deferred */
+ }
+ in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Cleanup HT-specific state in a node. Called only
+ * when HT use has been marked.
+ */
+void
+ieee80211_ht_node_cleanup(struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+ int i;
+
+ ASSERT(in->in_flags & IEEE80211_NODE_HT);
+
+ /* optimize this */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[i];
+ if (tap->txa_flags & IEEE80211_AGGR_SETUP) {
+ /*
+ * Stop BA stream if setup so driver has a chance
+ * to reclaim any resources it might have allocated.
+ */
+ ic->ic_addba_stop(in, &in->in_tx_ampdu[i]);
+ /* IEEE80211_TAPQ_DESTROY(tap); */
+ /* NB: clearing NAK means we may re-send ADDBA */
+ tap->txa_flags &=
+ ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK);
+ }
+ }
+ for (i = 0; i < WME_NUM_TID; i++)
+ ampdu_rx_stop(&in->in_rx_ampdu[i]);
+
+ in->in_htcap = 0;
+ in->in_flags &= ~(IEEE80211_NODE_HT | IEEE80211_NODE_HTCOMPAT |
+ IEEE80211_NODE_AMPDU);
+}
+
+static struct ieee80211_channel *
+findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags)
+{
+ return ieee80211_find_channel(ic, c->ich_freq,
+ (c->ich_flags &~ IEEE80211_CHAN_HT) | htflags);
+}
+
+/*
+ * Adjust a channel to be HT/non-HT according to the vap's configuration.
+ */
+struct ieee80211_channel *
+ieee80211_ht_adjust_channel(struct ieee80211com *ic,
+ struct ieee80211_channel *chan, int flags)
+{
+ struct ieee80211_channel *c;
+
+ if (flags & IEEE80211_FEXT_HT) {
+ /* promote to HT if possible */
+ if (flags & IEEE80211_FEXT_USEHT40) {
+ if (!IEEE80211_IS_CHAN_HT40(chan)) {
+ /* NB: arbitrarily pick ht40+ over ht40- */
+ c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U);
+ if (c == NULL)
+ c = findhtchan(ic, chan,
+ IEEE80211_CHAN_HT40D);
+ if (c == NULL)
+ c = findhtchan(ic, chan,
+ IEEE80211_CHAN_HT20);
+ if (c != NULL)
+ chan = c;
+ }
+ } else if (!IEEE80211_IS_CHAN_HT20(chan)) {
+ c = findhtchan(ic, chan, IEEE80211_CHAN_HT20);
+ if (c != NULL)
+ chan = c;
+ }
+ } else if (IEEE80211_IS_CHAN_HT(chan)) {
+ /* demote to legacy, HT use is disabled */
+ c = ieee80211_find_channel(ic, chan->ich_freq,
+ chan->ich_flags &~ IEEE80211_CHAN_HT);
+ if (c != NULL)
+ chan = c;
+ }
+ return (chan);
+}
+
+/*
+ * Setup HT-specific state for a legacy WDS peer.
+ */
+void
+ieee80211_ht_wds_init(struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+ struct ieee80211_tx_ampdu *tap;
+ int ac;
+
+ ASSERT(ic->ic_flags_ext & IEEE80211_FEXT_HT);
+
+ /* check scan cache in case peer has an ap and we have info */
+ /*
+ * If setup with a legacy channel; locate an HT channel.
+ * Otherwise if the inherited channel (from a companion
+ * AP) is suitable use it so we use the same location
+ * for the extension channel).
+ */
+ in->in_chan = ieee80211_ht_adjust_channel(ic, in->in_chan,
+ ic->ic_flags_ext);
+
+ in->in_htcap = 0;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20)
+ in->in_htcap |= IEEE80211_HTCAP_SHORTGI20;
+ if (IEEE80211_IS_CHAN_HT40(in->in_chan)) {
+ in->in_htcap |= IEEE80211_HTCAP_CHWIDTH40;
+ in->in_chw = 40;
+ if (IEEE80211_IS_CHAN_HT40U(in->in_chan))
+ in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(in->in_chan))
+ in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW;
+ if (ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40)
+ in->in_htcap |= IEEE80211_HTCAP_SHORTGI40;
+ } else {
+ in->in_chw = 20;
+ in->in_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE;
+ }
+ in->in_htctlchan = ieee80211_chan2ieee(ic, in->in_chan);
+
+ in->in_htopmode = 0; /* need protection state */
+ in->in_htstbc = 0; /* need info */
+
+ for (ac = 0; ac < WME_NUM_AC; ac++) {
+ tap = &in->in_tx_ampdu[ac];
+ tap->txa_ac = (uint8_t)ac;
+ }
+ /* NB: AMPDU tx/rx governed by IEEE80211_FEXT_AMPDU_{TX,RX} */
+ in->in_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU;
+}
+
+/*
+ * Notify hostap vaps of a change in the HTINFO ie.
+ */
+static void
+htinfo_notify(struct ieee80211com *ic)
+{
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP)
+ return;
+ ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+ "HT bss occupancy change: %d sta, %d ht, "
+ "%d ht40%s, HT protmode now 0x%x\n",
+ ic->ic_sta_assoc,
+ ic->ic_ht_sta_assoc,
+ ic->ic_ht40_sta_assoc,
+ (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) ?
+ ", non-HT sta present" : "",
+ ic->ic_curhtprotmode);
+}
+
+/*
+ * Calculate HT protection mode from current
+ * state and handle updates.
+ */
+static void
+htinfo_update(struct ieee80211com *ic)
+{
+ uint8_t protmode;
+
+ if (ic->ic_flags_ext & IEEE80211_FEXT_NONHT_PR) {
+ protmode = IEEE80211_HTINFO_OPMODE_PROTOPT
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (ic->ic_sta_assoc != ic->ic_ht_sta_assoc) {
+ protmode = IEEE80211_HTINFO_OPMODE_MIXED
+ | IEEE80211_HTINFO_NONHT_PRESENT;
+ } else if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan) &&
+ ic->ic_sta_assoc != ic->ic_ht40_sta_assoc) {
+ protmode = IEEE80211_HTINFO_OPMODE_HT20PR;
+ } else {
+ protmode = IEEE80211_HTINFO_OPMODE_PURE;
+ }
+ if (protmode != ic->ic_curhtprotmode) {
+ ic->ic_curhtprotmode = protmode;
+ htinfo_notify(ic);
+ }
+}
+
+/*
+ * Handle an HT station joining a BSS.
+ */
+void
+ieee80211_ht_node_join(struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ ic->ic_ht_sta_assoc++;
+ if (in->in_chw == 40)
+ ic->ic_ht40_sta_assoc++;
+ }
+ htinfo_update(ic);
+}
+
+/*
+ * Handle an HT station leaving a BSS.
+ */
+void
+ieee80211_ht_node_leave(struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ if (in->in_flags & IEEE80211_NODE_HT) {
+ ic->ic_ht_sta_assoc--;
+ if (in->in_chw == 40)
+ ic->ic_ht40_sta_assoc--;
+ }
+ htinfo_update(ic);
+}
+
+/*
+ * Public version of htinfo_update; used for processing
+ * beacon frames from overlapping bss in hostap_recv_mgmt.
+ */
+void
+ieee80211_htinfo_update(struct ieee80211com *ic, int protmode)
+{
+ if (protmode != ic->ic_curhtprotmode) {
+ ic->ic_curhtprotmode = (uint8_t)protmode;
+ htinfo_notify(ic);
+ }
+}
+
+/* unalligned little endian access */
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ ((((const uint8_t *)(p))[0]) | \
+ (((const uint8_t *)(p))[1] << 8)))
+
+/*
+ * Process an 802.11n HT capabilities ie.
+ */
+void
+ieee80211_parse_htcap(struct ieee80211_node *in, const uint8_t *ie)
+{
+ struct ieee80211com *ic = in->in_ic;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR) {
+ /*
+ * Station used Vendor OUI ie to associate;
+ * mark the node so when we respond we'll use
+ * the Vendor OUI's and not the standard ie's.
+ */
+ in->in_flags |= IEEE80211_NODE_HTCOMPAT;
+ ie += 4;
+ } else
+ in->in_flags &= ~IEEE80211_NODE_HTCOMPAT;
+
+ in->in_htcap = *(uint16_t *)(ie +
+ offsetof(struct ieee80211_ie_htcap, hc_cap));
+ in->in_htparam = ie[offsetof(struct ieee80211_ie_htcap, hc_param)];
+ /* needed or will ieee80211_parse_htinfo always be called? */
+ in->in_chw = (in->in_htcap & IEEE80211_HTCAP_CHWIDTH40) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40) ? 40 : 20;
+}
+
+/*
+ * Process an 802.11n HT info ie and update the node state.
+ * Note that we handle use this information to identify the
+ * correct channel (HT20, HT40+, HT40-, legacy). The caller
+ * is responsible for insuring any required channel change is
+ * done (e.g. in sta mode when parsing the contents of a
+ * beacon frame).
+ */
+void
+ieee80211_parse_htinfo(struct ieee80211_node *in, const uint8_t *ie)
+{
+ struct ieee80211com *ic = in->in_ic;
+ const struct ieee80211_ie_htinfo *htinfo;
+ struct ieee80211_channel *c;
+ uint16_t w;
+ int htflags, chanflags;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htinfo = (const struct ieee80211_ie_htinfo *)ie;
+ in->in_htctlchan = htinfo->hi_ctrlchannel;
+ in->in_ht2ndchan = SM(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN);
+ w = *(uint16_t *)(&htinfo->hi_byte2);
+ in->in_htopmode = SM(w, IEEE80211_HTINFO_OPMODE);
+ w = *(uint16_t *)(&htinfo->hi_byte45);
+ in->in_htstbc = SM(w, IEEE80211_HTINFO_BASIC_STBCMCS);
+ /*
+ * Handle 11n channel switch. Use the received HT ie's to
+ * identify the right channel to use. If we cannot locate it
+ * in the channel table then fallback to legacy operation.
+ */
+ htflags = (ic->ic_flags_ext & IEEE80211_FEXT_HT) ?
+ IEEE80211_CHAN_HT20 : 0;
+ /* NB: honor operating mode constraint */
+ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)) {
+ if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE)
+ htflags = IEEE80211_CHAN_HT40U;
+ else if (in->in_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW)
+ htflags = IEEE80211_CHAN_HT40D;
+ }
+ chanflags = (in->in_chan->ich_flags &~ IEEE80211_CHAN_HT) | htflags;
+ if (chanflags != in->in_chan->ich_flags) {
+ c = ieee80211_find_channel(ic,
+ in->in_chan->ich_freq, chanflags);
+ if (c == NULL && htflags != IEEE80211_CHAN_HT20) {
+ /*
+ * No HT40 channel entry in our table; fall back
+ * to HT20 operation. This should not happen.
+ */
+ c = findhtchan(ic, in->in_chan, IEEE80211_CHAN_HT20);
+ ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+ "no HT40 channel (freq %u), falling back to HT20\n",
+ in->in_chan->ich_freq);
+ /* stat */
+ }
+ if (c != NULL && c != in->in_chan) {
+ ieee80211_dbg(IEEE80211_MSG_ASSOC | IEEE80211_MSG_HT,
+ "switch station to HT%d channel %u/0x%x\n",
+ IEEE80211_IS_CHAN_HT40(c) ? 40 : 20,
+ c->ich_freq, c->ich_flags);
+ in->in_chan = c;
+ }
+ /* NB: caller responsible for forcing any channel change */
+ }
+ /* update node's tx channel width */
+ in->in_chw = IEEE80211_IS_CHAN_HT40(in->in_chan)? 40 : 20;
+}
+
+/*
+ * Install received HT rate set by parsing the HT cap ie.
+ */
+int
+ieee80211_setup_htrates(struct ieee80211_node *in, const uint8_t *ie, int flags)
+{
+ const struct ieee80211_ie_htcap *htcap;
+ struct ieee80211_htrateset *rs;
+ int i;
+
+ rs = &in->in_htrates;
+ (void) memset(rs, 0, sizeof (*rs));
+ if (ie != NULL) {
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htcap = (const struct ieee80211_ie_htcap *) ie;
+ for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+ if (ieee80211_isclr(htcap->hc_mcsset, i))
+ continue;
+ if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) {
+ ieee80211_dbg(
+ IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
+ "WARNING, HT rate set too large; only "
+ "using %u rates\n",
+ IEEE80211_HTRATE_MAXSIZE);
+ break;
+ }
+ rs->rs_rates[rs->rs_nrates++] = (uint8_t)i;
+ }
+ }
+ return (ieee80211_fix_rate(in, (struct ieee80211_rateset *)rs, flags));
+}
+
+/*
+ * Mark rates in a node's HT rate set as basic according
+ * to the information in the supplied HT info ie.
+ */
+void
+ieee80211_setup_basic_htrates(struct ieee80211_node *in, const uint8_t *ie)
+{
+ const struct ieee80211_ie_htinfo *htinfo;
+ struct ieee80211_htrateset *rs;
+ int i, j;
+
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ htinfo = (const struct ieee80211_ie_htinfo *) ie;
+ rs = &in->in_htrates;
+ if (rs->rs_nrates == 0) {
+ ieee80211_dbg(IEEE80211_MSG_XRATE | IEEE80211_MSG_HT,
+ "WARNING, empty HT rate set\n");
+ return;
+ }
+ for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) {
+ if (ieee80211_isclr(htinfo->hi_basicmcsset, i))
+ continue;
+ for (j = 0; j < rs->rs_nrates; j++)
+ if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i)
+ rs->rs_rates[j] |= IEEE80211_RATE_BASIC;
+ }
+}
+
+static void
+addba_timeout(void *arg)
+{
+ struct ieee80211_tx_ampdu *tap = arg;
+
+ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ tap->txa_attempts++;
+}
+
+static void
+addba_start_timeout(struct ieee80211_tx_ampdu *tap)
+{
+ tap->txa_timer = timeout(addba_timeout, (void *)tap,
+ drv_usectohz(IEEE80211_AGGR_TIMEOUT * 1000));
+ tap->txa_flags |= IEEE80211_AGGR_XCHGPEND;
+ tap->txa_lastrequest = ddi_get_lbolt();
+}
+
+static void
+addba_stop_timeout(struct ieee80211_tx_ampdu *tap)
+{
+ if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) {
+ if (tap->txa_timer != NULL) {
+ (void) untimeout(tap->txa_timer);
+ tap->txa_timer = NULL;
+ }
+ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND;
+ }
+}
+
+/*
+ * Default method for requesting A-MPDU tx aggregation.
+ * We setup the specified state block and start a timer
+ * to wait for an ADDBA response frame.
+ */
+/* ARGSUSED */
+static int
+ieee80211_addba_request(struct ieee80211_node *in,
+ struct ieee80211_tx_ampdu *tap,
+ int dialogtoken, int baparamset, int batimeout)
+{
+ int bufsiz;
+
+ tap->txa_token = (uint8_t)dialogtoken;
+ tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE;
+ tap->txa_start = tap->txa_seqstart = 0;
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
+ : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
+ addba_start_timeout(tap);
+ return (1);
+}
+
+/*
+ * Default method for processing an A-MPDU tx aggregation
+ * response. We shutdown any pending timer and update the
+ * state block according to the reply.
+ */
+/* ARGSUSED */
+static int
+ieee80211_addba_response(struct ieee80211_node *in,
+ struct ieee80211_tx_ampdu *tap,
+ int status, int baparamset, int batimeout)
+{
+ int bufsiz;
+
+ addba_stop_timeout(tap);
+ if (status == IEEE80211_STATUS_SUCCESS) {
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ /* override our request? */
+ tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX
+ : min((uint16_t)bufsiz, IEEE80211_AGGR_BAWMAX);
+ tap->txa_flags |= IEEE80211_AGGR_RUNNING;
+ } else {
+ /* mark tid so we don't try again */
+ tap->txa_flags |= IEEE80211_AGGR_NAK;
+ }
+ return (1);
+}
+
+/*
+ * Default method for stopping A-MPDU tx aggregation.
+ * Any timer is cleared and we drain any pending frames.
+ */
+/* ARGSUSED */
+static void
+ieee80211_addba_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap)
+{
+ addba_stop_timeout(tap);
+ if (tap->txa_flags & IEEE80211_AGGR_RUNNING) {
+ /* clear aggregation queue */
+ tap->txa_flags &= ~IEEE80211_AGGR_RUNNING;
+ }
+ tap->txa_attempts = 0;
+}
+
+/*
+ * Process a received action frame using the default aggregation
+ * policy. We intercept ADDBA-related frames and use them to
+ * update our aggregation state. All other frames are passed up
+ * for processing by ieee80211_recv_action.
+ */
+static void
+ieee80211_aggr_recv_action(struct ieee80211_node *in,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211com *ic = in->in_ic;
+ const struct ieee80211_action *ia;
+ struct ieee80211_rx_ampdu *rap;
+ struct ieee80211_tx_ampdu *tap;
+ uint8_t dialogtoken;
+ uint16_t baparamset, batimeout, baseqctl, code;
+ uint16_t args[4];
+ int tid, ac, bufsiz;
+
+ ia = (const struct ieee80211_action *) frm;
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ dialogtoken = frm[2];
+ baparamset = *(uint16_t *)(frm+3);
+ batimeout = *(uint16_t *)(frm+5);
+ baseqctl = *(uint16_t *)(frm+7);
+
+ tid = MS(baparamset, IEEE80211_BAPS_TID);
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "recv ADDBA request: dialogtoken %u "
+ "baparamset 0x%x (tid %d bufsiz %d) batimeout %d "
+ "baseqctl %d:%d\n",
+ dialogtoken, baparamset, tid, bufsiz, batimeout,
+ MS(baseqctl, IEEE80211_BASEQ_START),
+ MS(baseqctl, IEEE80211_BASEQ_FRAG));
+
+ rap = &in->in_rx_ampdu[tid];
+
+ /* Send ADDBA response */
+ args[0] = dialogtoken;
+ /*
+ * NB: We ack only if the sta associated with HT and
+ * the ap is configured to do AMPDU rx (the latter
+ * violates the 11n spec and is mostly for testing).
+ */
+ if ((in->in_flags & IEEE80211_NODE_AMPDU_RX) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_RX)) {
+ ampdu_rx_start(rap, bufsiz,
+ MS(baseqctl, IEEE80211_BASEQ_START));
+
+ args[1] = IEEE80211_STATUS_SUCCESS;
+ } else {
+ ieee80211_dbg(
+ IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "reject ADDBA request: %s\n",
+ in->in_flags & IEEE80211_NODE_AMPDU_RX ?
+ "administratively disabled" :
+ "not negotiated for station");
+ args[1] = IEEE80211_STATUS_UNSPECIFIED;
+ }
+ /* honor rap flags? */
+ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ);
+ args[3] = 0;
+ ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_ADDBA_RESPONSE, args);
+ return;
+
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ dialogtoken = frm[2];
+ code = *(uint16_t *)(frm+3);
+ baparamset = *(uint16_t *)(frm+5);
+ tid = MS(baparamset, IEEE80211_BAPS_TID);
+ bufsiz = MS(baparamset, IEEE80211_BAPS_BUFSIZ);
+ batimeout = *(uint16_t *)(frm+7);
+
+ ac = TID_TO_WME_AC(tid);
+ tap = &in->in_tx_ampdu[ac];
+ if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) {
+ ieee80211_err("ADDBA response"
+ "no pending ADDBA, tid %d dialogtoken %u "
+ "code %d\n", tid, dialogtoken, code);
+ return;
+ }
+ if (dialogtoken != tap->txa_token) {
+ ieee80211_err("ADDBA response"
+ "dialogtoken mismatch: waiting for %d, "
+ "received %d, tid %d code %d\n",
+ tap->txa_token, dialogtoken, tid, code);
+ return;
+ }
+
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "recv ADDBA response: dialogtoken %u code %d "
+ "baparamset 0x%x (tid %d bufsiz %d) batimeout %d\n",
+ dialogtoken, code, baparamset, tid, bufsiz,
+ batimeout);
+ ic->ic_addba_response(in, tap,
+ code, baparamset, batimeout);
+ return;
+
+ case IEEE80211_ACTION_BA_DELBA:
+ baparamset = *(uint16_t *)(frm+2);
+ code = *(uint16_t *)(frm+4);
+
+ tid = MS(baparamset, IEEE80211_DELBAPS_TID);
+
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "recv DELBA: baparamset 0x%x (tid %d initiator %d) "
+ "code %d\n", baparamset, tid,
+ MS(baparamset, IEEE80211_DELBAPS_INIT), code);
+
+ if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) {
+ ac = TID_TO_WME_AC(tid);
+ tap = &in->in_tx_ampdu[ac];
+ ic->ic_addba_stop(in, tap);
+ } else {
+ rap = &in->in_rx_ampdu[tid];
+ ampdu_rx_stop(rap);
+ }
+ return;
+ }
+ break;
+ }
+ ieee80211_recv_action(in, frm, efrm);
+}
+
+/*
+ * Process a received 802.11n action frame.
+ * Aggregation-related frames are assumed to be handled
+ * already; we handle any other frames we can, otherwise
+ * complain about being unsupported (with debugging).
+ */
+/* ARGSUSED */
+void
+ieee80211_recv_action(struct ieee80211_node *in,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ const struct ieee80211_action *ia;
+ int chw;
+
+ ia = (const struct ieee80211_action *) frm;
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "BA action %d not implemented\n",
+ ia->ia_action);
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ chw = frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040 ? 40 : 20;
+ if (chw != in->in_chw) {
+ in->in_chw = (uint8_t)chw;
+ in->in_flags |= IEEE80211_NODE_CHWUPDATE;
+ }
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "HT txchwidth, width %d (%s)\n",
+ chw,
+ in->in_flags & IEEE80211_NODE_CHWUPDATE ?
+ "new" : "no change");
+ break;
+ case IEEE80211_ACTION_HT_MIMOPWRSAVE:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "HT MIMO PS\n");
+ break;
+ default:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "HT action %d not implemented\n",
+ ia->ia_action);
+ break;
+ }
+ break;
+ default:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "category %d not implemented\n",
+ ia->ia_category);
+ break;
+ }
+}
+
+/*
+ * Transmit processing.
+ */
+
+/*
+ * Request A-MPDU tx aggregation. Setup local state and
+ * issue an ADDBA request. BA use will only happen after
+ * the other end replies with ADDBA response.
+ */
+int
+ieee80211_ampdu_request(struct ieee80211_node *in,
+ struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic = in->in_ic;
+ uint16_t args[4];
+ int tid, dialogtoken;
+ static int tokens = 0; /* tokens */
+ clock_t ticks;
+
+ ticks = ddi_get_lbolt();
+ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) {
+ /* do deferred setup of state */
+ tap->txa_flags |= IEEE80211_AGGR_SETUP;
+ }
+ if (tap->txa_attempts >= IEEE80211_AGGR_MAXTRIES &&
+ (ticks - tap->txa_lastrequest) < IEEE80211_AGGR_MINRETRY) {
+ /*
+ * Don't retry too often; IEEE80211_AGGR_MINRETRY
+ * defines the minimum interval we'll retry after
+ * IEEE80211_AGGR_MAXTRIES failed attempts to
+ * negotiate use.
+ */
+ return (0);
+ }
+ /* hack for not doing proper locking */
+ tap->txa_flags &= ~IEEE80211_AGGR_NAK;
+
+ dialogtoken = (tokens+1) % 63; /* algorithm */
+
+ tid = WME_AC_TO_TID(tap->txa_ac);
+ args[0] = (uint16_t)dialogtoken;
+ args[1] = IEEE80211_BAPS_POLICY_IMMEDIATE
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ);
+ args[2] = 0; /* batimeout */
+ args[3] = SM(0, IEEE80211_BASEQ_START)
+ | SM(0, IEEE80211_BASEQ_FRAG);
+ /* NB: do first so there's no race against reply */
+ if (!ic->ic_addba_request(in, tap, dialogtoken, args[1], args[2])) {
+ /* unable to setup state, don't make request */
+ ieee80211_dbg(IEEE80211_MSG_HT,
+ "could not setup BA stream for AC %d\n",
+ tap->txa_ac);
+ /* defer next try so we don't slam the driver with requests */
+ tap->txa_attempts = IEEE80211_AGGR_MAXTRIES;
+ tap->txa_lastrequest = ticks;
+ return (0);
+ }
+ tokens = dialogtoken; /* allocate token */
+ return (ic->ic_send_action(in, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_ADDBA_REQUEST, args));
+}
+
+/*
+ * Terminate an AMPDU tx stream. State is reclaimed
+ * and the peer notified with a DelBA Action frame.
+ */
+void
+ieee80211_ampdu_stop(struct ieee80211_node *in, struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic = in->in_ic;
+ uint16_t args[4];
+
+ if (IEEE80211_AMPDU_RUNNING(tap)) {
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "stop BA stream for AC %d\n", tap->txa_ac);
+
+ ic->ic_addba_stop(in, tap);
+ args[0] = WME_AC_TO_TID(tap->txa_ac);
+ args[1] = IEEE80211_DELBAPS_INIT;
+ args[2] = 1; /* reason code */
+ (void) ieee80211_send_action(in, IEEE80211_ACTION_CAT_BA,
+ IEEE80211_ACTION_BA_DELBA, args);
+ } else {
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "BA stream for AC %d not running\n",
+ tap->txa_ac);
+ }
+}
+
+/*
+ * Transmit a BAR frame to the specified node. The
+ * BAR contents are drawn from the supplied aggregation
+ * state associated with the node.
+ */
+int
+ieee80211_send_bar(struct ieee80211_node *in,
+ const struct ieee80211_tx_ampdu *tap)
+{
+#define ADDSHORT(frm, v) do { \
+ _NOTE(CONSTCOND) \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+ _NOTE(CONSTCOND) \
+} while (0)
+ struct ieee80211com *ic = in->in_ic;
+ struct ieee80211_frame_min *wh;
+ mblk_t *m;
+ uint8_t *frm;
+ uint16_t barctl, barseqctl;
+ int tid;
+
+
+ m = ieee80211_getmgtframe(&frm, sizeof (struct ieee80211_ba_request));
+ if (m == NULL)
+ return (ENOMEM);
+
+ wh = (struct ieee80211_frame_min *)m->b_rptr;
+ wh->i_fc[0] = IEEE80211_FC0_VERSION_0 |
+ IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR;
+ wh->i_fc[1] = 0;
+ IEEE80211_ADDR_COPY(wh->i_addr1, in->in_macaddr);
+ IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_macaddr);
+
+ tid = WME_AC_TO_TID(tap->txa_ac);
+ barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ?
+ IEEE80211_BAPS_POLICY_IMMEDIATE :
+ IEEE80211_BAPS_POLICY_DELAYED)
+ | SM(tid, IEEE80211_BAPS_TID)
+ | SM(tap->txa_wnd, IEEE80211_BAPS_BUFSIZ);
+ barseqctl = SM(tap->txa_start, IEEE80211_BASEQ_START)
+ | SM(0, IEEE80211_BASEQ_FRAG);
+ ADDSHORT(frm, barctl);
+ ADDSHORT(frm, barseqctl);
+ m->b_wptr = frm;
+
+ ieee80211_dbg(IEEE80211_MSG_DEBUG,
+ "send bar frame (tid %u start %u) on channel %u\n",
+ tid, tap->txa_start, ieee80211_chan2ieee(ic, ic->ic_curchan));
+
+ (void) (*ic->ic_xmit)(ic, m, IEEE80211_FC0_TYPE_CTL); /* MGT? */
+
+ return (0);
+#undef ADDSHORT
+}
+
+/*
+ * Send an action management frame. The arguments are stuff
+ * into a frame without inspection; the caller is assumed to
+ * prepare them carefully (e.g. based on the aggregation state).
+ */
+int
+ieee80211_send_action(struct ieee80211_node *in,
+ int category, int action, uint16_t args[4])
+{
+#define ADDSHORT(frm, v) do { \
+ _NOTE(CONSTCOND) \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+ _NOTE(CONSTCOND) \
+} while (0)
+ struct ieee80211com *ic = in->in_ic;
+ mblk_t *m;
+ uint8_t *frm;
+ uint16_t baparamset;
+ int ret;
+
+ ASSERT(in != NULL);
+
+ m = ieee80211_getmgtframe(&frm,
+ sizeof (uint16_t) /* action+category */
+ /* may action payload */
+ + sizeof (struct ieee80211_action_ba_addbaresponse));
+ if (m == NULL)
+ return (ENOMEM);
+
+ *frm++ = (uint8_t)category;
+ *frm++ = (uint8_t)action;
+ switch (category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "send ADDBA request: dialogtoken %d "
+ "baparamset 0x%x (tid %d) "
+ "batimeout 0x%x baseqctl 0x%x\n",
+ args[0], args[1], MS(args[1], IEEE80211_BAPS_TID),
+ args[2], args[3]);
+
+ *frm++ = args[0]; /* dialog token */
+ ADDSHORT(frm, args[1]); /* baparamset */
+ ADDSHORT(frm, args[2]); /* batimeout */
+ ADDSHORT(frm, args[3]); /* baseqctl */
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "send ADDBA response: dialogtoken %d status %d "
+ "baparamset 0x%x (tid %d) batimeout %d\n",
+ args[0], args[1], args[2],
+ MS(args[2], IEEE80211_BAPS_TID), args[3]);
+
+ *frm++ = args[0]; /* dialog token */
+ ADDSHORT(frm, args[1]); /* statuscode */
+ ADDSHORT(frm, args[2]); /* baparamset */
+ ADDSHORT(frm, args[3]); /* batimeout */
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ baparamset = SM(args[0], IEEE80211_DELBAPS_TID)
+ | SM(args[1], IEEE80211_DELBAPS_INIT);
+ ADDSHORT(frm, baparamset);
+ ADDSHORT(frm, args[2]); /* reason code */
+
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "send DELBA action: tid %d, initiator %d "
+ "reason %d\n",
+ args[0], args[1], args[2]);
+ break;
+ default:
+ goto badaction;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "send HT txchwidth: width %d\n",
+ IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ? 40 : 20);
+ *frm++ = IEEE80211_IS_CHAN_HT40(ic->ic_curchan) ?
+ IEEE80211_A_HT_TXCHWIDTH_2040 :
+ IEEE80211_A_HT_TXCHWIDTH_20;
+ break;
+ default:
+ goto badaction;
+ }
+ break;
+ default:
+ badaction:
+ ieee80211_dbg(IEEE80211_MSG_ACTION | IEEE80211_MSG_HT,
+ "unsupported category %d action %d\n",
+ category, action);
+ return (EINVAL);
+ /* NOTREACHED */
+ }
+ m->b_wptr = frm;
+
+ ret = ieee80211_mgmt_output(ic, in, m, IEEE80211_FC0_SUBTYPE_ACTION, 0);
+
+ return (ret);
+#undef ADDSHORT
+}
+
+/*
+ * Construct the MCS bit mask for inclusion
+ * in an HT information element.
+ */
+static void
+ieee80211_set_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ if (r < IEEE80211_HTRATE_MAXSIZE) {
+ /* NB: this assumes a particular implementation */
+ ieee80211_setbit(frm, r);
+ }
+ }
+}
+
+/*
+ * Add body of an HTCAP information element.
+ */
+static uint8_t *
+ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *in)
+{
+#define ADDSHORT(frm, v) do { \
+ _NOTE(CONSTCOND) \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+ _NOTE(CONSTCOND) \
+} while (0)
+ struct ieee80211com *ic = in->in_ic;
+ uint16_t caps;
+ int rxmax, density;
+
+ /* HT capabilities */
+ caps = ic->ic_htcaps & 0xffff;
+ /*
+ * Note channel width depends on whether we are operating as
+ * a sta or not. When operating as a sta we are generating
+ * a request based on our desired configuration. Otherwise
+ * we are operational and the channel attributes identify
+ * how we've been setup (which might be different if a fixed
+ * channel is specified).
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /* override 20/40 use based on config */
+ if (ic->ic_flags_ext & IEEE80211_FEXT_USEHT40)
+ caps |= IEEE80211_HTCAP_CHWIDTH40;
+ else
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+ /* use advertised setting (locally constraint) */
+ rxmax = MS(in->in_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
+ density = MS(in->in_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+ } else {
+ /* override 20/40 use based on current channel */
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ caps |= IEEE80211_HTCAP_CHWIDTH40;
+ else
+ caps &= ~IEEE80211_HTCAP_CHWIDTH40;
+ rxmax = ic->ic_ampdu_rxmax;
+ density = ic->ic_ampdu_density;
+ }
+ /* adjust short GI based on channel and config */
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI20) == 0)
+ caps &= ~IEEE80211_HTCAP_SHORTGI20;
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_SHORTGI40) == 0 ||
+ (caps & IEEE80211_HTCAP_CHWIDTH40) == 0)
+ caps &= ~IEEE80211_HTCAP_SHORTGI40;
+ ADDSHORT(frm, caps);
+
+ /* HT parameters */
+ *frm = SM(rxmax, IEEE80211_HTCAP_MAXRXAMPDU)
+ | SM(density, IEEE80211_HTCAP_MPDUDENSITY);
+ frm++;
+
+ /* pre-zero remainder of ie */
+ (void) memset(frm, 0, sizeof (struct ieee80211_ie_htcap) -
+ offsetof(struct ieee80211_ie_htcap, hc_mcsset));
+
+ /* supported MCS set */
+ /*
+ * it would better to get the rate set from in_htrates
+ * so we can restrict it but for sta mode in_htrates isn't
+ * setup when we're called to form an AssocReq frame so for
+ * now we're restricted to the default HT rate set.
+ */
+ ieee80211_set_htrates(frm, &ieee80211_rateset_11n);
+
+ frm += sizeof (struct ieee80211_ie_htcap) -
+ offsetof(struct ieee80211_ie_htcap, hc_mcsset);
+
+ return (frm);
+#undef ADDSHORT
+}
+
+/*
+ * Add 802.11n HT capabilities information element
+ */
+uint8_t *
+ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *in)
+{
+ frm[0] = IEEE80211_ELEMID_HTCAP;
+ frm[1] = sizeof (struct ieee80211_ie_htcap) - 2;
+ return (ieee80211_add_htcap_body(frm + 2, in));
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTCAP ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *in)
+{
+ frm[0] = IEEE80211_ELEMID_VENDOR;
+ frm[1] = 4 + sizeof (struct ieee80211_ie_htcap) - 2;
+ frm[2] = (BCM_OUI >> 0) & 0xff;
+ frm[3] = (BCM_OUI >> 8) & 0xff;
+ frm[4] = (BCM_OUI >> 16) & 0xff;
+ frm[5] = BCM_OUI_HTCAP;
+ return (ieee80211_add_htcap_body(frm + 6, in));
+}
+
+/*
+ * Construct the MCS bit mask of basic rates
+ * for inclusion in an HT information element.
+ */
+static void
+ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs)
+{
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ int r = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) &&
+ r < IEEE80211_HTRATE_MAXSIZE) {
+ /* NB: this assumes a particular implementation */
+ ieee80211_setbit(frm, r);
+ }
+ }
+}
+
+/*
+ * Update the HTINFO ie for a beacon frame.
+ */
+void
+ieee80211_ht_update_beacon(struct ieee80211com *ic,
+ struct ieee80211_beacon_offsets *bo)
+{
+#define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT)
+ struct ieee80211_ie_htinfo *ht =
+ (struct ieee80211_ie_htinfo *)bo->bo_htinfo;
+
+ /* only update on channel change */
+ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, ic->ic_curchan);
+ ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH;
+ if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+ else /* LINTED */
+ ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE;
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+ /* protection mode */
+ ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode;
+
+ /* propagate to vendor ie's */
+#undef PROTMODE
+}
+
+/*
+ * Add body of an HTINFO information element.
+ *
+ * NB: We don't use struct ieee80211_ie_htinfo because we can
+ * be called to fillin both a standard ie and a compat ie that
+ * has a vendor OUI at the front.
+ */
+static uint8_t *
+ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *in)
+{
+ struct ieee80211com *ic = in->in_ic;
+
+ /* pre-zero remainder of ie */
+ (void) memset(frm, 0, sizeof (struct ieee80211_ie_htinfo) - 2);
+
+ /* primary/control channel center */
+ *frm++ = ieee80211_chan2ieee(ic, ic->ic_curchan);
+
+ frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH;
+ if (IEEE80211_IS_CHAN_HT40U(ic->ic_curchan))
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE;
+ else if (IEEE80211_IS_CHAN_HT40D(ic->ic_curchan))
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW;
+ else /* LINTED */
+ frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE;
+ if (IEEE80211_IS_CHAN_HT40(ic->ic_curchan))
+ frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040;
+
+ frm[1] = ic->ic_curhtprotmode;
+
+ frm += 5;
+
+ /* basic MCS set */
+ ieee80211_set_basic_htrates(frm, &in->in_htrates);
+ frm += sizeof (struct ieee80211_ie_htinfo) -
+ offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset);
+ return (frm);
+}
+
+/*
+ * Add 802.11n HT information information element.
+ */
+uint8_t *
+ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *in)
+{
+ frm[0] = IEEE80211_ELEMID_HTINFO;
+ frm[1] = sizeof (struct ieee80211_ie_htinfo) - 2;
+
+ return (ieee80211_add_htinfo_body(frm + 2, in));
+}
+
+/*
+ * Add Broadcom OUI wrapped standard HTINFO ie; this is
+ * used for compatibility w/ pre-draft implementations.
+ */
+uint8_t *
+ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *in)
+{
+ frm[0] = IEEE80211_ELEMID_VENDOR;
+ frm[1] = 4 + sizeof (struct ieee80211_ie_htinfo) - 2;
+ frm[2] = (BCM_OUI >> 0) & 0xff;
+ frm[3] = (BCM_OUI >> 8) & 0xff;
+ frm[4] = (BCM_OUI >> 16) & 0xff;
+ frm[5] = BCM_OUI_HTINFO;
+
+ return (ieee80211_add_htinfo_body(frm + 6, in));
+}
+
+void
+ieee80211_ht_attach(struct ieee80211com *ic)
+{
+ /* setup default aggregation policy */
+ ic->ic_recv_action = ieee80211_aggr_recv_action;
+ ic->ic_send_action = ieee80211_send_action;
+ ic->ic_addba_request = ieee80211_addba_request;
+ ic->ic_addba_response = ieee80211_addba_response;
+ ic->ic_addba_stop = ieee80211_addba_stop;
+
+ ic->ic_htprotmode = IEEE80211_PROT_RTSCTS;
+ ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE;
+
+ /* get from driver */
+ ic->ic_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K;
+ ic->ic_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA;
+ ic->ic_ampdu_limit = ic->ic_ampdu_rxmax;
+ ic->ic_amsdu_limit = IEEE80211_HTCAP_MAXAMSDU_3839;
+
+ if (ic->ic_htcaps & IEEE80211_HTC_HT) {
+ /*
+ * Device is HT capable; enable all HT-related
+ * facilities by default.
+ * these choices may be too aggressive.
+ */
+ ic->ic_flags_ext |= IEEE80211_FEXT_HT | IEEE80211_FEXT_HTCOMPAT;
+ if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI20;
+ /* infer from channel list? */
+ if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ ic->ic_flags_ext |= IEEE80211_FEXT_USEHT40;
+ if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)
+ ic->ic_flags_ext |= IEEE80211_FEXT_SHORTGI40;
+ }
+ /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_RX;
+ if (ic->ic_htcaps & IEEE80211_HTC_AMPDU)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMPDU_TX;
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_RX;
+ if (ic->ic_htcaps & IEEE80211_HTC_AMSDU)
+ ic->ic_flags_ext |= IEEE80211_FEXT_AMSDU_TX;
+ }
+
+#define ieee80211_isset16(a, i) ((a) & (1 << (i)))
+ /* fill default rate sets for 11NA/11NG if driver has no specified */
+ if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NA) &&
+ ic->ic_sup_rates[IEEE80211_MODE_11NA].ir_nrates == 0) {
+ ic->ic_sup_rates[IEEE80211_MODE_11NA] =
+ ic->ic_sup_rates[IEEE80211_MODE_11A];
+ }
+
+ if (ieee80211_isset16(ic->ic_modecaps, IEEE80211_MODE_11NG) &&
+ ic->ic_sup_rates[IEEE80211_MODE_11NG].ir_nrates == 0) {
+ ic->ic_sup_rates[IEEE80211_MODE_11NG] =
+ ic->ic_sup_rates[IEEE80211_MODE_11G];
+ }
+#undef ieee80211_isset16
+}
+
+/* ARGSUSED */
+void
+ieee80211_ht_detach(struct ieee80211com *ic)
+{
+}
+
+/* ARGSUSED */
+static void
+ht_announce(struct ieee80211com *ic, int mode,
+ const struct ieee80211_htrateset *rs)
+{
+ int i, rate;
+
+ ieee80211_dbg(IEEE80211_MSG_HT, "%s MCS: \n",
+ ieee80211_phymode_name[mode]);
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rate = ieee80211_htrates[rs->rs_rates[i]];
+ ieee80211_dbg(IEEE80211_MSG_HT, "%s%d%sMbps\n",
+ (i != 0 ? " " : ""),
+ rate / 2, ((rate & 0x1) != 0 ? ".5" : ""));
+ }
+}
+
+void
+ieee80211_ht_announce(struct ieee80211com *ic)
+{
+ if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NA))
+ ht_announce(ic, IEEE80211_MODE_11NA, &ieee80211_rateset_11n);
+ if (ic->ic_modecaps & (1 << IEEE80211_MODE_11NG))
+ ht_announce(ic, IEEE80211_MODE_11NG, &ieee80211_rateset_11n);
+}
+
+/* ARGSUSED */
+const struct ieee80211_htrateset *
+ieee80211_get_suphtrates(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ return (&ieee80211_rateset_11n);
+}
diff --git a/usr/src/uts/common/io/net80211/net80211_impl.h b/usr/src/uts/common/io/net80211/net80211_impl.h
index 85e5fe37d4..135b93ccbb 100644
--- a/usr/src/uts/common/io/net80211/net80211_impl.h
+++ b/usr/src/uts/common/io/net80211/net80211_impl.h
@@ -1,11 +1,11 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -133,12 +133,15 @@ extern "C" {
(IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
#define IEEE80211_CHAN_108G \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define IEEE80211_CHAN_ST \
+ (IEEE80211_CHAN_T | IEEE80211_CHAN_STURBO)
#define IEEE80211_CHAN_ALL \
(IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_GFSK | \
- IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN)
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | \
+ IEEE80211_CHAN_HT)
#define IEEE80211_CHAN_ALLTURBO \
- (IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO)
+ (IEEE80211_CHAN_ALL | IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)
#define IEEE80211_IS_CHAN_FHSS(_c) \
(((_c)->ich_flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
@@ -154,8 +157,11 @@ extern "C" {
(IEEE80211_IS_CHAN_PUREG(_c) || IEEE80211_IS_CHAN_G(_c))
#define IEEE80211_IS_CHAN_T(_c) \
(((_c)->ich_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T)
+ /* IEEE80211_IS_CHAN_108A */
#define IEEE80211_IS_CHAN_108G(_c) \
(((_c)->ich_flags & IEEE80211_CHAN_108G) == IEEE80211_CHAN_108G)
+#define IEEE80211_IS_CHAN_ST(_c) \
+ (((_c)->ich_flags & IEEE80211_CHAN_ST) == IEEE80211_CHAN_ST)
#define IEEE80211_IS_CHAN_OFDM(_c) \
((_c)->ich_flags & IEEE80211_CHAN_OFDM)
@@ -166,6 +172,45 @@ extern "C" {
#define IEEE80211_IS_CHAN_PASSIVE(_c) \
((_c)->ich_flags & IEEE80211_CHAN_PASSIVE)
+#define IEEE80211_IS_CHAN_STURBO(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_STURBO)
+#define IEEE80211_IS_CHAN_DTURBO(_c) \
+ (((_c)->ich_flags & \
+ (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) == IEEE80211_CHAN_TURBO)
+#define IEEE80211_IS_CHAN_HALF(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HALF)
+#define IEEE80211_IS_CHAN_QUARTER(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_QUARTER)
+#define IEEE80211_IS_CHAN_FULL(_c) \
+ ((_c)->ich_flags & (IEEE80211_CHAN_QUARTER | IEEE80211_CHAN_HALF))
+#define IEEE80211_IS_CHAN_GSM(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_GSM)
+
+#define IEEE80211_IS_CHAN_HT(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT)
+#define IEEE80211_IS_CHAN_HT20(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT20)
+#define IEEE80211_IS_CHAN_HT40(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT40)
+#define IEEE80211_IS_CHAN_HT40U(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT40U)
+#define IEEE80211_IS_CHAN_HT40D(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT40D)
+#define IEEE80211_IS_CHAN_HTA(_c) \
+ (IEEE80211_IS_CHAN_5GHZ(_c) && \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT))
+#define IEEE80211_IS_CHAN_HTG(_c) \
+ (IEEE80211_IS_CHAN_2GHZ(_c) && \
+ ((_c)->ich_flags & IEEE80211_CHAN_HT))
+#define IEEE80211_IS_CHAN_DFS(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_DFS)
+#define IEEE80211_IS_CHAN_NOADHOC(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_NOADHOC)
+#define IEEE80211_IS_CHAN_NOHOSTAP(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_NOHOSTAP)
+#define IEEE80211_IS_CHAN_11D(_c) \
+ ((_c)->ich_flags & IEEE80211_CHAN_11D)
+
/* ni_chan encoding for FH phy */
#define IEEE80211_FH_CHANMOD 80
#define IEEE80211_FH_CHAN(set, pat) \
@@ -211,6 +256,8 @@ extern "C" {
#define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */
#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */
#define IEEE80211_MSG_CONFIG 0x00000020 /* wificonfig/dladm */
+#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */
+#define IEEE80211_MSG_HT 0x00000008 /* 11n mode debug */
#define IEEE80211_MSG_ANY 0xffffffff /* anything */
/* Error flags returned by ieee80211_match_bss */
@@ -268,6 +315,8 @@ struct ieee80211_scanparams {
uint8_t *xrates;
uint8_t *wpa;
uint8_t *wme;
+ uint8_t *htcap;
+ uint8_t *htinfo;
};
#define IEEE80211_SEND_MGMT(_ic, _in, _type, _arg) \
@@ -351,7 +400,6 @@ extern const char *ieee80211_phymode_name[];
void ieee80211_err(const int8_t *, ...);
void ieee80211_dbg(uint32_t, const int8_t *, ...);
-int ieee80211_hdrspace(const void *);
void ieee80211_notify(ieee80211com_t *, wpa_event_type);
void ieee80211_mac_update(ieee80211com_t *);
@@ -374,10 +422,11 @@ void ieee80211_create_ibss(ieee80211com_t *, struct ieee80211_channel *);
ieee80211_node_t *ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *,
const uint8_t *);
ieee80211_node_t *ieee80211_tmp_node(ieee80211com_t *, const uint8_t *);
+void ieee80211_setcurchan(ieee80211com_t *, struct ieee80211_channel *);
/* proto */
void ieee80211_proto_attach(ieee80211com_t *);
-int ieee80211_fix_rate(ieee80211_node_t *, int);
+int ieee80211_fix_rate(ieee80211_node_t *, struct ieee80211_rateset *, int);
void ieee80211_setbasicrates(struct ieee80211_rateset *,
enum ieee80211_phymode);
void ieee80211_reset_erp(ieee80211com_t *);
@@ -395,6 +444,8 @@ int ieee80211_send_probereq(ieee80211_node_t *, const uint8_t *,
size_t);
int ieee80211_send_mgmt(ieee80211com_t *, ieee80211_node_t *, int, int);
int ieee80211_send_nulldata(ieee80211_node_t *);
+int ieee80211_mgmt_output(ieee80211com_t *, ieee80211_node_t *, mblk_t *,
+ int, int);
/* crypto */
struct ieee80211_key *ieee80211_crypto_getkey(ieee80211com_t *);
@@ -405,6 +456,10 @@ mblk_t *ieee80211_getmgtframe(uint8_t **, int);
void ieee80211_notify_node_join(ieee80211com_t *, ieee80211_node_t *);
void ieee80211_notify_node_leave(ieee80211com_t *, ieee80211_node_t *);
+/* WME */
+void ieee80211_wme_initparams(struct ieee80211com *);
+void ieee80211_wme_updateparams(struct ieee80211com *);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/io/net80211/net80211_input.c b/usr/src/uts/common/io/net80211/net80211_input.c
index 82ef763fe0..7e720932e6 100644
--- a/usr/src/uts/common/io/net80211/net80211_input.c
+++ b/usr/src/uts/common/io/net80211/net80211_input.c
@@ -71,6 +71,27 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
uint8_t type;
uint8_t subtype;
uint8_t tid;
+ uint8_t qos;
+
+ if (mp->b_flag & M_AMPDU) {
+ /*
+ * Fastpath for A-MPDU reorder q resubmission. Frames
+ * w/ M_AMPDU marked have already passed through here
+ * but were received out of order and been held on the
+ * reorder queue. When resubmitted they are marked
+ * with the M_AMPDU flag and we can bypass most of the
+ * normal processing.
+ */
+ IEEE80211_LOCK(ic);
+ wh = (struct ieee80211_frame *)mp->b_rptr;
+ type = IEEE80211_FC0_TYPE_DATA;
+ dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK;
+ subtype = IEEE80211_FC0_SUBTYPE_QOS;
+ hdrspace = ieee80211_hdrspace(ic, wh); /* optimize */
+ /* clear driver/net80211 flags before passing up */
+ mp->b_flag &= ~M_AMPDU;
+ goto resubmit_ampdu;
+ }
ASSERT(in != NULL);
in->in_inact = in->in_inact_reload;
@@ -160,9 +181,18 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
in->in_rssi = (uint8_t)rssi;
in->in_rstamp = rstamp;
if (!(type & IEEE80211_FC0_TYPE_CTL)) {
- tid = 0;
+ if (IEEE80211_QOS_HAS_SEQ(wh)) {
+ tid = ((struct ieee80211_qosframe *)wh)->
+ i_qos[0] & IEEE80211_QOS_TID;
+ if (TID_TO_WME_AC(tid) >= WME_AC_VI)
+ ic->ic_wme.wme_hipri_traffic++;
+ tid++;
+ } else {
+ tid = IEEE80211_NONQOS_TID;
+ }
rxseq = LE_16(*(uint16_t *)wh->i_seq);
- if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
+ if ((in->in_flags & IEEE80211_NODE_HT) == 0 &&
+ (wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
(rxseq - in->in_rxseqs[tid]) <= 0) {
/* duplicate, discard */
ieee80211_dbg(IEEE80211_MSG_INPUT,
@@ -184,7 +214,7 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
switch (type) {
case IEEE80211_FC0_TYPE_DATA:
- hdrspace = ieee80211_hdrspace(wh);
+ hdrspace = ieee80211_hdrspace(ic, wh);
if (len < hdrspace) {
ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
"data too short: expecting %u", hdrspace);
@@ -228,6 +258,26 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
}
/*
+ * Handle A-MPDU re-ordering. The station must be
+ * associated and negotiated HT. The frame must be
+ * a QoS frame (not QoS null data) and not previously
+ * processed for A-MPDU re-ordering. If the frame is
+ * to be processed directly then ieee80211_ampdu_reorder
+ * will return 0; otherwise it has consumed the mbuf
+ * and we should do nothing more with it.
+ */
+ if ((in->in_flags & IEEE80211_NODE_HT) &&
+ (subtype == IEEE80211_FC0_SUBTYPE_QOS)) {
+ IEEE80211_UNLOCK(ic);
+ if (ieee80211_ampdu_reorder(in, mp) != 0) {
+ mp = NULL; /* CONSUMED */
+ goto out;
+ }
+ IEEE80211_LOCK(ic);
+ }
+ resubmit_ampdu:
+
+ /*
* Handle privacy requirements.
*/
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
@@ -253,6 +303,17 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
}
/*
+ * Save QoS bits for use below--before we strip the header.
+ */
+ if (subtype == IEEE80211_FC0_SUBTYPE_QOS) {
+ qos = (dir == IEEE80211_FC1_DIR_DSTODS) ?
+ ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0] :
+ ((struct ieee80211_qosframe *)wh)->i_qos[0];
+ } else {
+ qos = 0;
+ }
+
+ /*
* Next up, any fragmentation
*/
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
@@ -273,6 +334,15 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
goto out_exit_mutex;
}
+ if (qos & IEEE80211_QOS_AMSDU) {
+ ieee80211_dbg(IEEE80211_MSG_INPUT | IEEE80211_MSG_HT,
+ "ieee80211_input: QOS_AMSDU (%x)\n", qos);
+
+ mp = ieee80211_decap_amsdu(in, mp);
+ if (mp == NULL) /* MSDU processed by HT */
+ goto out_exit_mutex;
+ }
+
ic->ic_stats.is_rx_frags++;
ic->ic_stats.is_rx_bytes += len;
IEEE80211_UNLOCK(ic);
@@ -307,7 +377,7 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
ic->ic_stats.is_wep_errors++;
goto out_exit_mutex;
}
- hdrspace = ieee80211_hdrspace(wh);
+ hdrspace = ieee80211_hdrspace(ic, wh);
key = ieee80211_crypto_decap(ic, mp, hdrspace);
if (key == NULL) {
/* NB: stats+msgs handled in crypto_decap */
@@ -321,6 +391,15 @@ ieee80211_input(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
goto out;
case IEEE80211_FC0_TYPE_CTL:
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_BAR:
+ ieee80211_recv_bar(in, mp);
+ break;
+ }
+ }
+ break;
+
default:
ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_input: "
"bad frame type 0x%x", type);
@@ -464,7 +543,7 @@ ieee80211_setup_rates(struct ieee80211_node *in, const uint8_t *rates,
bcopy(xrates + 2, rs->ir_rates + rs->ir_nrates, nxrates);
rs->ir_nrates += nxrates;
}
- return (ieee80211_fix_rate(in, flags));
+ return (ieee80211_fix_rate(in, &in->in_rates, flags));
}
/*
@@ -654,6 +733,89 @@ iswpaoui(const uint8_t *frm)
return (frm[1] > 3 && LE_32(c) == ((WPA_OUI_TYPE << 24) | WPA_OUI));
}
+#define LE_READ_4(p) \
+ ((uint32_t) \
+ ((((uint8_t *)(p))[0]) | (((uint8_t *)(p))[1] << 8) | \
+ (((uint8_t *)(p))[2] << 16) | (((uint8_t *)(p))[3] << 24)))
+
+#define LE_READ_2(p) \
+ ((uint16_t) \
+ (((uint8_t *)(p))[0]) | (((uint8_t *)(p))[1] << 8))
+
+static int
+iswmeoui(const uint8_t *frm)
+{
+ return (frm[1] > 3 && LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI));
+}
+
+static int
+iswmeparam(const uint8_t *frm)
+{
+ return (frm[1] > 5 &&
+ LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_PARAM_OUI_SUBTYPE);
+}
+
+static int
+iswmeinfo(const uint8_t *frm)
+{
+ return (frm[1] > 5 &&
+ LE_READ_4(frm+2) == ((WME_OUI_TYPE<<24)|WME_OUI) &&
+ frm[6] == WME_INFO_OUI_SUBTYPE);
+}
+
+static int
+ishtcapoui(const uint8_t *frm)
+{
+ return (frm[1] > 3 &&
+ LE_READ_4(frm+2) == ((BCM_OUI_HTCAP<<24)|BCM_OUI));
+}
+
+static int
+ishtinfooui(const uint8_t *frm)
+{
+ return (frm[1] > 3 &&
+ LE_READ_4(frm+2) == ((BCM_OUI_HTINFO<<24)|BCM_OUI));
+}
+
+/* ARGSUSED */
+static int
+ieee80211_parse_wmeparams(struct ieee80211com *ic, uint8_t *frm,
+ const struct ieee80211_frame *wh)
+{
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ uint_t len = frm[1];
+ uint8_t qosinfo;
+ int i;
+
+ if (len < sizeof (struct ieee80211_wme_param) - 2) {
+ ieee80211_dbg(IEEE80211_MSG_ELEMID | IEEE80211_MSG_WME,
+ "WME too short, len %u", len);
+ return (-1);
+ }
+ qosinfo = frm[offsetof(struct ieee80211_wme_param, wme_qosInfo)];
+ qosinfo &= WME_QOSINFO_COUNT;
+ /* do proper check for wraparound */
+ if (qosinfo == wme->wme_wmeChanParams.cap_info)
+ return (0);
+ frm += offsetof(struct ieee80211_wme_param, wme_acParams);
+ for (i = 0; i < WME_NUM_AC; i++) {
+ struct wmeParams *wmep =
+ &wme->wme_wmeChanParams.cap_wmeParams[i];
+ /* NB: ACI not used */
+ wmep->wmep_acm = MS(frm[0], WME_PARAM_ACM);
+ wmep->wmep_aifsn = MS(frm[0], WME_PARAM_AIFSN);
+ wmep->wmep_logcwmin = MS(frm[1], WME_PARAM_LOGCWMIN);
+ wmep->wmep_logcwmax = MS(frm[1], WME_PARAM_LOGCWMAX);
+ wmep->wmep_txopLimit = LE_READ_2(frm+2);
+ frm += 4;
+ }
+ wme->wme_wmeChanParams.cap_info = qosinfo;
+ return (1);
+#undef MS
+}
+
/*
* Process a beacon/probe response frame.
* When the device is in station mode, create a node and add it
@@ -701,6 +863,8 @@ ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
* [tlv] extended supported rates
* [tlv] WME
* [tlv] WPA or RSN
+ * [tlv] HT capabilities
+ * [tlv] HT information
*/
IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
IEEE80211_BEACON_ELEM_MIN, return);
@@ -769,12 +933,33 @@ ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
scan.erp = frm[2];
scan.phytype = IEEE80211_T_OFDM;
break;
+ case IEEE80211_ELEMID_HTCAP:
+ scan.htcap = frm;
+ break;
case IEEE80211_ELEMID_RSN:
scan.wpa = frm;
break;
+ case IEEE80211_ELEMID_HTINFO:
+ scan.htinfo = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (iswpaoui(frm))
scan.wpa = frm; /* IEEE802.11i D3.0 */
+ else if (iswmeparam(frm) || iswmeinfo(frm))
+ scan.wme = frm;
+ else if (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (scan.htcap == NULL)
+ scan.htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (scan.htinfo == NULL)
+ scan.htinfo = frm;
+ }
+ }
break;
default:
ieee80211_dbg(IEEE80211_MSG_ELEMID,
@@ -824,6 +1009,25 @@ ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
IEEE80211_SUBTYPE_NAME(subtype), scan.bintval);
return;
}
+ /*
+ * Process HT ie's. This is complicated by our
+ * accepting both the standard ie's and the pre-draft
+ * vendor OUI ie's that some vendors still use/require.
+ */
+ if (scan.htcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htcap[1],
+ scan.htcap[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof (struct ieee80211_ie_htcap) - 2 :
+ sizeof (struct ieee80211_ie_htcap) - 2,
+ scan.htcap = NULL);
+ }
+ if (scan.htinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan.htinfo[1],
+ scan.htinfo[0] == IEEE80211_ELEMID_VENDOR ?
+ 4 + sizeof (struct ieee80211_ie_htinfo) - 2 :
+ sizeof (struct ieee80211_ie_htinfo) - 2,
+ scan.htinfo = NULL);
+ }
/*
* When operating in station mode, check for state updates.
@@ -858,7 +1062,24 @@ ieee80211_recv_beacon(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
IEEE80211_CAPINFO_SHORT_SLOTTIME));
in->in_capinfo = scan.capinfo;
}
-
+ if (scan.wme != NULL &&
+ (in->in_flags & IEEE80211_NODE_QOS) &&
+ ieee80211_parse_wmeparams(ic, scan.wme, wh) > 0) {
+ ieee80211_wme_updateparams(ic);
+ }
+ if (scan.htcap != NULL)
+ ieee80211_parse_htcap(in, scan.htcap);
+ if (scan.htinfo != NULL) {
+ ieee80211_parse_htinfo(in, scan.htinfo);
+ if (in->in_chan != ic->ic_curchan) {
+ /*
+ * Channel has been adjusted based on
+ * negotiated HT parameters; force the
+ * channel state to follow.
+ */
+ ieee80211_setcurchan(ic, in->in_chan);
+ }
+ }
if (scan.tim != NULL) {
struct ieee80211_tim_ie *ie;
@@ -918,6 +1139,8 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
uint8_t *ssid;
uint8_t *rates;
uint8_t *xrates; /* extended rates */
+ uint8_t *wme;
+ uint8_t *htcap, *htinfo;
boolean_t allocbs = B_FALSE;
uint8_t rate;
uint16_t algo; /* authentication algorithm */
@@ -925,6 +1148,7 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
uint16_t status;
uint16_t capinfo;
uint16_t associd; /* association ID */
+ const struct ieee80211_action *ia;
IEEE80211_LOCK(ic);
wh = (struct ieee80211_frame *)mp->b_rptr;
@@ -1086,6 +1310,8 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] HT info
*/
IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
IEEE80211_ASSOC_RESP_ELEM_MIN, break);
@@ -1107,7 +1333,7 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
associd = LE_16(*(uint16_t *)frm);
frm += 2;
- rates = xrates = NULL;
+ rates = xrates = wme = htcap = htinfo = NULL;
while (frm < efrm) {
/*
* Do not discard frames containing proprietary Agere
@@ -1131,6 +1357,30 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
case IEEE80211_ELEMID_XRATES:
xrates = frm;
break;
+ case IEEE80211_ELEMID_HTCAP:
+ htcap = frm;
+ break;
+ case IEEE80211_ELEMID_HTINFO:
+ htinfo = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswmeoui(frm))
+ wme = frm;
+ else if (ic->ic_flags_ext &
+ IEEE80211_FEXT_HTCOMPAT) {
+ /*
+ * Accept pre-draft HT ie's if the
+ * standard ones have not been seen.
+ */
+ if (ishtcapoui(frm)) {
+ if (htcap == NULL)
+ htcap = frm;
+ } else if (ishtinfooui(frm)) {
+ if (htinfo == NULL)
+ htinfo = frm;
+ }
+ }
+ break;
}
frm += frm[1] + 2;
}
@@ -1157,7 +1407,32 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
in->in_capinfo = capinfo;
in->in_associd = associd;
- in->in_flags &= ~IEEE80211_NODE_QOS;
+ if (wme != NULL &&
+ ieee80211_parse_wmeparams(ic, wme, wh) >= 0) {
+ in->in_flags |= IEEE80211_NODE_QOS;
+ ieee80211_wme_updateparams(ic);
+ } else {
+ in->in_flags &= ~IEEE80211_NODE_QOS;
+ }
+ /*
+ * Setup HT state according to the negotiation.
+ */
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
+ htcap != NULL && htinfo != NULL) {
+ ieee80211_ht_node_init(in, htcap);
+ ieee80211_parse_htinfo(in, htinfo);
+ (void) ieee80211_setup_htrates(in,
+ htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
+ ieee80211_setup_basic_htrates(in, htinfo);
+ if (in->in_chan != ic->ic_curchan) {
+ /*
+ * Channel has been adjusted based on
+ * negotiated HT parameters; force the
+ * channel state to follow.
+ */
+ ieee80211_setcurchan(ic, in->in_chan);
+ }
+ }
/*
* Configure state now that we are associated.
*/
@@ -1242,6 +1517,57 @@ ieee80211_recv_mgmt(ieee80211com_t *ic, mblk_t *mp, struct ieee80211_node *in,
}
break;
+ case IEEE80211_FC0_SUBTYPE_ACTION:
+ if (ic->ic_state != IEEE80211_S_RUN &&
+ ic->ic_state != IEEE80211_S_ASSOC &&
+ ic->ic_state != IEEE80211_S_AUTH)
+ break;
+
+ /*
+ * action frame format:
+ * [1] category
+ * [1] action
+ * [tlv] parameters
+ */
+ IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+ sizeof (struct ieee80211_action), break);
+ ia = (const struct ieee80211_action *) frm;
+
+ /* verify frame payloads but defer processing */
+ /* maybe push this to method */
+ switch (ia->ia_category) {
+ case IEEE80211_ACTION_CAT_BA:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_BA_ADDBA_REQUEST:
+ IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+ sizeof (struct ieee80211_action_ba_addbarequest),
+ break);
+ break;
+ case IEEE80211_ACTION_BA_ADDBA_RESPONSE:
+ IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+ sizeof (struct ieee80211_action_ba_addbaresponse),
+ break);
+ break;
+ case IEEE80211_ACTION_BA_DELBA:
+ IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+ sizeof (struct ieee80211_action_ba_delba),
+ break);
+ break;
+ }
+ break;
+ case IEEE80211_ACTION_CAT_HT:
+ switch (ia->ia_action) {
+ case IEEE80211_ACTION_HT_TXCHWIDTH:
+ IEEE80211_VERIFY_LENGTH(_PTRDIFF(efrm, frm),
+ sizeof (struct ieee80211_action_ht_txchwidth),
+ break);
+ break;
+ }
+ break;
+ }
+ ic->ic_recv_action(in, frm, efrm);
+ break;
+
default:
ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_recv_mgmt: "
"subtype 0x%x not handled\n", subtype);
diff --git a/usr/src/uts/common/io/net80211/net80211_ioctl.c b/usr/src/uts/common/io/net80211/net80211_ioctl.c
index 9a1f1137f8..cab1022105 100644
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c
@@ -719,6 +719,7 @@ wifi_wait_scan(struct ieee80211com *ic)
}
#define WIFI_HAVE_CAP(in, flag) (((in)->in_capinfo & (flag)) ? 1 : 0)
+#define WIFI_HAVE_HTCAP(in) (((in)->in_htcap != 0) ? 1 : 0)
/*
* Callback function used by ieee80211_iterate_nodes() in
@@ -768,6 +769,7 @@ wifi_read_ap(void *arg, struct ieee80211_node *in)
(wl_ofdm_t *)&((conf->wl_phy_conf).wl_phy_ofdm_conf);
ofdm->wl_ofdm_subtype = WL_OFDM;
ofdm->wl_ofdm_frequency = chan->ich_freq;
+ ofdm->wl_ofdm_ht_enabled = WIFI_HAVE_HTCAP(in);
} else {
switch (in->in_phytype) {
case IEEE80211_T_FH: {
@@ -809,6 +811,7 @@ wifi_read_ap(void *arg, struct ieee80211_node *in)
WIFI_HAVE_CAP(in, IEEE80211_CAPINFO_DSSSOFDM);
erp->wl_erp_sst_enabled = WIFI_HAVE_CAP(in,
IEEE80211_CAPINFO_SHORT_SLOTTIME);
+ erp->wl_erp_ht_enabled = WIFI_HAVE_HTCAP(in);
break;
} /* case IEEE80211_T_OFDM */
} /* switch in->in_phytype */
@@ -1863,12 +1866,15 @@ wl_set_phy(struct ieee80211com *ic, const void* wldp_buf)
return (err);
}
+#define WIFI_HT_MODE(in) (((in)->in_flags & IEEE80211_NODE_HT) ? 1 : 0)
+
static int
wl_get_phy(struct ieee80211com *ic, void *wldp_buf)
{
int err = 0;
wl_phy_conf_t *ow_phy;
struct ieee80211_channel *ch = ic->ic_curchan;
+ struct ieee80211_node *in = ic->ic_bss;
ow_phy = (wl_phy_conf_t *)wldp_buf;
bzero(wldp_buf, sizeof (wl_phy_conf_t));
@@ -1878,6 +1884,7 @@ wl_get_phy(struct ieee80211com *ic, void *wldp_buf)
wl_ofdm_t *ofdm = (wl_ofdm_t *)ow_phy;
ofdm->wl_ofdm_subtype = WL_OFDM;
ofdm->wl_ofdm_frequency = ch->ich_freq;
+ ofdm->wl_ofdm_ht_enabled = WIFI_HT_MODE(in);
} else {
switch (ic->ic_phytype) {
case IEEE80211_T_FH: {
@@ -1899,6 +1906,7 @@ wl_get_phy(struct ieee80211com *ic, void *wldp_buf)
erp->wl_erp_subtype = WL_ERP;
erp->wl_erp_channel =
ieee80211_chan2ieee(ic, ch);
+ erp->wl_erp_ht_enabled = WIFI_HT_MODE(in);
break;
}
default:
diff --git a/usr/src/uts/common/io/net80211/net80211_node.c b/usr/src/uts/common/io/net80211/net80211_node.c
index 9998725830..e53d63ee36 100644
--- a/usr/src/uts/common/io/net80211/net80211_node.c
+++ b/usr/src/uts/common/io/net80211/net80211_node.c
@@ -184,7 +184,18 @@ ieee80211_node_setchan(ieee80211com_t *ic, ieee80211_node_t *in,
if (chan == IEEE80211_CHAN_ANYC)
chan = ic->ic_curchan;
in->in_chan = chan;
- in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
+ if (IEEE80211_IS_CHAN_HT(chan)) {
+ /*
+ * Gotta be careful here; the rate set returned by
+ * ieee80211_get_suprates is actually any HT rate
+ * set so blindly copying it will be bad. We must
+ * install the legacy rate est in ni_rates and the
+ * HT rate set in ni_htrates.
+ */
+ in->in_htrates = *ieee80211_get_suphtrates(ic, chan);
+ }
+ in->in_rates = *ieee80211_get_suprates(ic, chan);
+ /* in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; */
}
/*
@@ -434,7 +445,8 @@ ieee80211_match_bss(ieee80211com_t *ic, ieee80211_node_t *in)
if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)
fail |= IEEE80211_BADPRIVACY;
}
- rate = ieee80211_fix_rate(in, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
+ rate = ieee80211_fix_rate(in, &in->in_rates,
+ IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
if (rate & IEEE80211_RATE_BASIC)
fail |= IEEE80211_BADRATE;
if (ic->ic_des_esslen != 0 &&
@@ -660,6 +672,18 @@ ieee80211_ibss_merge(ieee80211_node_t *in)
}
/*
+ * Change the bss channel.
+ */
+void
+ieee80211_setcurchan(ieee80211com_t *ic, struct ieee80211_channel *c)
+{
+ ic->ic_curchan = c;
+ ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_curchan);
+ if (ic->ic_set_channel != NULL)
+ ic->ic_set_channel(ic);
+}
+
+/*
* Join the specified IBSS/BSS network. The node is assumed to
* be passed in with a held reference.
*/
@@ -677,7 +701,8 @@ ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs)
* Delete unusable rates; we've already checked
* that the negotiated rate set is acceptable.
*/
- (void) ieee80211_fix_rate(selbs, IEEE80211_F_DODEL);
+ (void) ieee80211_fix_rate(selbs, &selbs->in_rates,
+ IEEE80211_F_DODEL);
/*
* Fillin the neighbor table
*/
@@ -706,6 +731,7 @@ ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs)
* mode is locked.
*/
ieee80211_reset_erp(ic);
+ ieee80211_wme_initparams(ic);
IEEE80211_UNLOCK(ic);
if (ic->ic_opmode == IEEE80211_M_STA)
@@ -773,6 +799,10 @@ ieee80211_node_free(ieee80211_node_t *in)
ic->ic_node_cleanup(in);
if (in->in_wpa_ie != NULL)
ieee80211_free(in->in_wpa_ie);
+ if (in->in_wme_ie != NULL)
+ ieee80211_free(in->in_wme_ie);
+ if (in->in_htcap_ie != NULL)
+ ieee80211_free(in->in_htcap_ie);
kmem_free(in, sizeof (ieee80211_node_t));
}
@@ -1108,7 +1138,12 @@ ieee80211_add_scan(ieee80211com_t *ic, const struct ieee80211_scanparams *sp,
* Record optional information elements that might be
* used by applications or drivers.
*/
+ saveie(&in->in_wme_ie, sp->wme);
saveie(&in->in_wpa_ie, sp->wpa);
+ saveie(&in->in_htcap_ie, sp->htcap);
+ /* parsed in ieee80211_sta_join() */
+ if (sp->htcap != NULL)
+ ieee80211_parse_htcap(in, in->in_htcap_ie);
/* NB: must be after in_chan is setup */
(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
@@ -1137,6 +1172,8 @@ ieee80211_init_neighbor(ieee80211_node_t *in, const struct ieee80211_frame *wh,
in->in_fhindex = sp->fhindex;
in->in_erp = sp->erp;
in->in_tim_off = sp->timoff;
+ if (sp->wme != NULL)
+ ieee80211_saveie(&in->in_wme_ie, sp->wme);
/* NB: must be after in_chan is setup */
(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
@@ -1164,9 +1201,17 @@ ieee80211_add_neighbor(ieee80211com_t *ic, const struct ieee80211_frame *wh,
return (in);
}
-#define IEEE80211_IS_CTL(wh) \
+#define IEEE80211_IS_CTL(wh) \
((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
+#define IEEE80211_IS_PSPOLL(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \
+ IEEE80211_FC0_SUBTYPE_PS_POLL)
+
+#define IEEE80211_IS_BAR(wh) \
+ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \
+ IEEE80211_FC0_SUBTYPE_BAR)
+
/*
* Locate the node for sender, track state, and then pass the
* (referenced) node up to the 802.11 layer for its use. We
@@ -1190,7 +1235,8 @@ ieee80211_find_rxnode(ieee80211com_t *ic, const struct ieee80211_frame *wh)
}
IEEE80211_NODE_LOCK(nt);
- if (IEEE80211_IS_CTL(wh))
+ if (IEEE80211_IS_CTL(wh) &&
+ !IEEE80211_IS_PSPOLL(wh) && !IEEE80211_IS_BAR(wh))
in = ieee80211_find_node_locked(nt, wh->i_addr1);
else
in = ieee80211_find_node_locked(nt, wh->i_addr2);
@@ -1202,6 +1248,10 @@ ieee80211_find_rxnode(ieee80211com_t *ic, const struct ieee80211_frame *wh)
return (in);
}
+#undef IEEE80211_IS_BAR
+#undef IEEE80211_IS_PSPOLL
+#undef IEEE80211_IS_CTL
+
/*
* Return a reference to the appropriate node for sending
* a data frame. This handles node discovery in adhoc networks.
diff --git a/usr/src/uts/common/io/net80211/net80211_output.c b/usr/src/uts/common/io/net80211/net80211_output.c
index 26f07541db..2f68bac444 100644
--- a/usr/src/uts/common/io/net80211/net80211_output.c
+++ b/usr/src/uts/common/io/net80211/net80211_output.c
@@ -82,9 +82,11 @@ ieee80211_send_setup(ieee80211com_t *ic, ieee80211_node_t *in,
IEEE80211_ADDR_COPY(wh->i_addr3, bssid);
}
*(uint16_t *)&wh->i_dur[0] = 0; /* set duration */
- *(uint16_t *)&wh->i_seq[0] = /* set sequence number */
- LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
- in->in_txseqs[0]++; /* increase sequence number by 1 */
+ /* NB: use non-QoS tid */
+ *(uint16_t *)&wh->i_seq[0] =
+ LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
+ IEEE80211_SEQ_SEQ_SHIFT);
+ in->in_txseqs[IEEE80211_NONQOS_TID]++;
}
/*
@@ -96,7 +98,7 @@ ieee80211_send_setup(ieee80211com_t *ic, ieee80211_node_t *in,
*
* Return 0 on success
*/
-static int
+int
ieee80211_mgmt_output(ieee80211com_t *ic, ieee80211_node_t *in, mblk_t *mp,
int type, int timer)
{
@@ -173,13 +175,70 @@ ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
{
struct ieee80211_frame *wh;
struct ieee80211_key *key;
+ int addqos, ac, tid;
ASSERT(mp != NULL && MBLKL(mp) >= sizeof (struct ieee80211_frame));
+ /*
+ * Some ap's don't handle QoS-encapsulated EAPOL
+ * frames so suppress use. This may be an issue if other
+ * ap's require all data frames to be QoS-encapsulated
+ * once negotiated in which case we'll need to make this
+ * configurable.
+ */
+ addqos = in->in_flags & (IEEE80211_NODE_QOS | IEEE80211_NODE_HT);
wh = (struct ieee80211_frame *)mp->b_rptr;
*(uint16_t *)wh->i_dur = 0;
- *(uint16_t *)wh->i_seq =
- LE_16(in->in_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
- in->in_txseqs[0]++;
+ if (addqos) {
+ struct ieee80211_qosframe *qwh =
+ (struct ieee80211_qosframe *)wh;
+
+ ac = ieee80211_classify(ic, mp, in);
+ /* map from access class/queue to 11e header priorty value */
+ tid = WME_AC_TO_TID(ac);
+ qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
+ /*
+ * Check if A-MPDU tx aggregation is setup or if we
+ * should try to enable it. The sta must be associated
+ * with HT and A-MPDU enabled for use. On the first
+ * frame that goes out We issue an ADDBA request and
+ * wait for a reply. The frame being encapsulated
+ * will go out w/o using A-MPDU, or possibly it might
+ * be collected by the driver and held/retransmit.
+ * ieee80211_ampdu_request handles staggering requests
+ * in case the receiver NAK's us or we are otherwise
+ * unable to establish a BA stream.
+ */
+ if ((in->in_flags & IEEE80211_NODE_AMPDU_TX) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_AMPDU_TX)) {
+ struct ieee80211_tx_ampdu *tap = &in->in_tx_ampdu[ac];
+
+ if (IEEE80211_AMPDU_RUNNING(tap)) {
+ /*
+ * Operational, mark frame for aggregation.
+ */
+ qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_BA;
+ } else if (!IEEE80211_AMPDU_REQUESTED(tap)) {
+ /*
+ * Not negotiated yet, request service.
+ */
+ (void) ieee80211_ampdu_request(in, tap);
+ }
+ }
+ /* works even when BA marked above */
+ if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].
+ wmep_noackPolicy) {
+ qwh->i_qos[0] |= IEEE80211_QOS_ACKPOLICY_NOACK;
+ }
+
+ *(uint16_t *)wh->i_seq =
+ LE_16(in->in_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT);
+ in->in_txseqs[tid]++;
+ } else {
+ *(uint16_t *)wh->i_seq =
+ LE_16(in->in_txseqs[IEEE80211_NONQOS_TID] <<
+ IEEE80211_SEQ_SEQ_SHIFT);
+ in->in_txseqs[IEEE80211_NONQOS_TID]++;
+ }
if (ic->ic_flags & IEEE80211_F_PRIVACY)
key = ieee80211_crypto_getkey(ic);
@@ -192,9 +251,8 @@ ieee80211_encap(ieee80211com_t *ic, mblk_t *mp, ieee80211_node_t *in)
*/
if (key != NULL && (ic->ic_flags & IEEE80211_F_WPA)) {
wh->i_fc[1] |= IEEE80211_FC1_WEP;
- if (!ieee80211_crypto_enmic(isc, key, mp, 0)) {
+ if (!ieee80211_crypto_enmic(isc, key, mp, 0))
ieee80211_err("ieee80211_crypto_enmic failed.\n");
- }
}
return (mp);
@@ -234,6 +292,72 @@ ieee80211_add_xrates(uint8_t *frm, const struct ieee80211_rateset *rs)
return (frm);
}
+#define WME_OUI_BYTES 0x00, 0x50, 0xf2
+/*
+ * Add a WME information element to a frame.
+ */
+/* ARGSUSED */
+static uint8_t *
+ieee80211_add_wme_info(uint8_t *frm, struct ieee80211_wme_state *wme)
+{
+ static const struct ieee80211_wme_info info = {
+ .wme_id = IEEE80211_ELEMID_VENDOR,
+ .wme_len = sizeof (struct ieee80211_wme_info) - 2,
+ .wme_oui = { WME_OUI_BYTES },
+ .wme_type = WME_OUI_TYPE,
+ .wme_subtype = WME_INFO_OUI_SUBTYPE,
+ .wme_version = WME_VERSION,
+ .wme_info = 0,
+ };
+ (void) memcpy(frm, &info, sizeof (info));
+ return (frm + sizeof (info));
+}
+
+/*
+ * Add a WME parameters element to a frame.
+ */
+static uint8_t *
+ieee80211_add_wme_param(uint8_t *frm, struct ieee80211_wme_state *wme)
+{
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+#define ADDSHORT(frm, v) do { \
+ _NOTE(CONSTCOND) \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+ _NOTE(CONSTCOND) \
+} while (0)
+ /* NB: this works 'cuz a param has an info at the front */
+ static const struct ieee80211_wme_info param = {
+ .wme_id = IEEE80211_ELEMID_VENDOR,
+ .wme_len = sizeof (struct ieee80211_wme_param) - 2,
+ .wme_oui = { WME_OUI_BYTES },
+ .wme_type = WME_OUI_TYPE,
+ .wme_subtype = WME_PARAM_OUI_SUBTYPE,
+ .wme_version = WME_VERSION,
+ };
+ int i;
+
+ (void) memcpy(frm, &param, sizeof (param));
+ frm += offsetof(struct ieee80211_wme_info, wme_info);
+ *frm++ = wme->wme_bssChanParams.cap_info; /* AC info */
+ *frm++ = 0; /* reserved field */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ const struct wmeParams *ac =
+ &wme->wme_bssChanParams.cap_wmeParams[i];
+ *frm++ = SM(i, WME_PARAM_ACI)
+ | SM(ac->wmep_acm, WME_PARAM_ACM)
+ | SM(ac->wmep_aifsn, WME_PARAM_AIFSN);
+ *frm++ = SM(ac->wmep_logcwmax, WME_PARAM_LOGCWMAX)
+ | SM(ac->wmep_logcwmin, WME_PARAM_LOGCWMIN);
+ ADDSHORT(frm, ac->wmep_txopLimit);
+ }
+ return (frm);
+#undef SM
+#undef ADDSHORT
+}
+#undef WME_OUI_BYTES
+
/*
* Add SSID element to a frame
*/
@@ -379,6 +503,10 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
* [tlv] extended supported rates
* [tlv] WPA
* [tlv] WME (optional)
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
*/
mp = ieee80211_getmgtframe(&frm,
8 /* time stamp */
@@ -394,8 +522,12 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
2 * sizeof (struct ieee80211_ie_wpa) : 0)
/* [tlv] WPA */
+ (ic->ic_flags & IEEE80211_F_WME ?
- sizeof (struct ieee80211_wme_param) : 0));
+ sizeof (struct ieee80211_wme_param) : 0)
/* [tlv] WME */
+ /* check for cluster requirement */
+ + 2 * sizeof (struct ieee80211_ie_htcap) + 4
+ + 2 * sizeof (struct ieee80211_ie_htinfo) + 4);
+
if (mp == NULL)
return (ENOMEM);
@@ -434,12 +566,30 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
*frm++ = IEEE80211_IBSS_LEN;
*frm++ = 0; *frm++ = 0; /* ATIM window */
}
- /* ERP */
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
frm = ieee80211_add_erp(frm, ic);
- /* Extended supported rates */
frm = ieee80211_add_xrates(frm, &in->in_rates);
- mp->b_wptr = frm;
+ /*
+ * NB: legacy 11b clients do not get certain ie's.
+ * The caller identifies such clients by passing
+ * a token in arg to us. Could expand this to be
+ * any legacy client for stuff like HT ie's.
+ */
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+ arg != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap(frm, in);
+ frm = ieee80211_add_htinfo(frm, in);
+ }
+ if (ic->ic_flags & IEEE80211_F_WME)
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT) &&
+ arg != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_htcap_vendor(frm, in);
+ frm = ieee80211_add_htinfo_vendor(frm, in);
+ }
+ mp->b_wptr = frm; /* allocated is greater than used */
+
break;
case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -517,6 +667,8 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME
+ * [tlv] HT capabilities
+ * [tlv] Vendor OUI HT capabilities (optional)
* [tlv] user-specified ie's
*/
mp = ieee80211_getmgtframe(&frm,
@@ -525,6 +677,8 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
+ 2 + IEEE80211_NWID_LEN
+ 2 + IEEE80211_RATE_SIZE
+ 2 + IEEE80211_XRATE_SIZE
+ + sizeof (struct ieee80211_wme_info)
+ + 2 * sizeof (struct ieee80211_ie_htcap) + 4
+ ic->ic_opt_ie_len);
if (mp == NULL)
return (ENOMEM);
@@ -556,6 +710,16 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
frm = ieee80211_add_rates(frm, &in->in_rates);
frm = ieee80211_add_xrates(frm, &in->in_rates);
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+ in->in_htcap_ie != NULL &&
+ in->in_htcap_ie[0] == IEEE80211_ELEMID_HTCAP)
+ frm = ieee80211_add_htcap(frm, in);
+ if ((ic->ic_flags & IEEE80211_F_WME) && in->in_wme_ie != NULL)
+ frm = ieee80211_add_wme_info(frm, &ic->ic_wme);
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_HT) &&
+ in->in_htcap_ie != NULL &&
+ in->in_htcap_ie[0] == IEEE80211_ELEMID_VENDOR)
+ frm = ieee80211_add_htcap_vendor(frm, in);
if (ic->ic_opt_ie != NULL) {
bcopy(ic->ic_opt_ie, frm, ic->ic_opt_ie_len);
frm += ic->ic_opt_ie_len;
@@ -575,6 +739,8 @@ ieee80211_send_mgmt(ieee80211com_t *ic, ieee80211_node_t *in, int type, int arg)
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] WME (if enabled and STA enabled)
+ * [tlv] HT capabilities (standard or vendor OUI)
+ * [tlv] HT information (standard or vendor OUI)
*/
mp = ieee80211_getmgtframe(&frm,
3 * sizeof (uint16_t)
@@ -629,7 +795,6 @@ ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
struct ieee80211_rateset *rs;
mblk_t *m;
uint8_t *frm;
- uint8_t *efrm;
int pktlen;
uint16_t capinfo;
@@ -647,6 +812,10 @@ ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
* [tlv] extended supported rates
* [tlv] WME parameters
* [tlv] WPA/RSN parameters
+ * [tlv] HT capabilities
+ * [tlv] HT information
+ * [tlv] Vendor OUI HT capabilities (optional)
+ * [tlv] Vendor OUI HT information (optional)
* Vendor-specific OIDs (e.g. Atheros)
* NB: we allocate the max space required for the TIM bitmap.
*/
@@ -659,7 +828,13 @@ ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
+ 2 + 1 /* DS parameters */
+ 2 + 4 + ic->ic_tim_len /* DTIM/IBSSPARMS */
+ 2 + 1 /* ERP */
- + 2 + IEEE80211_XRATE_SIZE;
+ + 2 + IEEE80211_XRATE_SIZE
+ + (ic->ic_caps & IEEE80211_C_WME ? /* WME */
+ sizeof (struct ieee80211_wme_param) : 0)
+ /* conditional? */
+ + 4 + 2 * sizeof (struct ieee80211_ie_htcap) /* HT caps */
+ + 4 + 2 * sizeof (struct ieee80211_ie_htinfo); /* HT info */
+
m = ieee80211_getmgtframe(&frm, pktlen);
if (m == NULL) {
ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_beacon_alloc: "
@@ -716,9 +891,23 @@ ieee80211_beacon_alloc(ieee80211com_t *ic, ieee80211_node_t *in,
bo->bo_erp = frm;
frm = ieee80211_add_erp(frm, ic);
}
- efrm = ieee80211_add_xrates(frm, rs);
- bo->bo_trailer_len = _PTRDIFF(efrm, bo->bo_trailer);
- m->b_wptr = efrm;
+ frm = ieee80211_add_xrates(frm, rs);
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan)) {
+ frm = ieee80211_add_htcap(frm, in);
+ bo->bo_htinfo = frm;
+ frm = ieee80211_add_htinfo(frm, in);
+ }
+ if (ic->ic_flags & IEEE80211_F_WME) {
+ bo->bo_wme = frm;
+ frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
+ }
+ if (IEEE80211_IS_CHAN_HT(ic->ic_curchan) &&
+ (ic->ic_flags_ext & IEEE80211_FEXT_HTCOMPAT)) {
+ frm = ieee80211_add_htcap_vendor(frm, in);
+ frm = ieee80211_add_htinfo_vendor(frm, in);
+ }
+ bo->bo_trailer_len = _PTRDIFF(frm, bo->bo_trailer);
+ m->b_wptr = frm;
wh = (struct ieee80211_frame *)m->b_rptr;
wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
@@ -752,3 +941,44 @@ ieee80211_beacon_update(ieee80211com_t *ic, ieee80211_node_t *in,
IEEE80211_UNLOCK(ic);
return (0);
}
+
+/*
+ * Assign priority to a frame based on any vlan tag assigned
+ * to the station and/or any Diffserv setting in an IP header.
+ * Finally, if an ACM policy is setup (in station mode) it's
+ * applied.
+ */
+int
+ieee80211_classify(struct ieee80211com *ic, mblk_t *m,
+ struct ieee80211_node *ni)
+/* ARGSUSED */
+{
+ int ac;
+
+ if ((ni->in_flags & IEEE80211_NODE_QOS) == 0)
+ return (WME_AC_BE);
+
+ /* Process VLan */
+ /* Process IPQoS */
+
+ ac = WME_AC_BE;
+
+ /*
+ * Apply ACM policy.
+ */
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ static const int acmap[4] = {
+ WME_AC_BK, /* WME_AC_BE */
+ WME_AC_BK, /* WME_AC_BK */
+ WME_AC_BE, /* WME_AC_VI */
+ WME_AC_VI, /* WME_AC_VO */
+ };
+ while (ac != WME_AC_BK &&
+ ic->ic_wme.wme_wmeBssChanParams.cap_wmeParams[ac].
+ wmep_acm) {
+ ac = acmap[ac];
+ }
+ }
+
+ return (ac);
+}
diff --git a/usr/src/uts/common/io/net80211/net80211_proto.c b/usr/src/uts/common/io/net80211/net80211_proto.c
index b98720f6c5..ebb54b6ec4 100644
--- a/usr/src/uts/common/io/net80211/net80211_proto.c
+++ b/usr/src/uts/common/io/net80211/net80211_proto.c
@@ -41,6 +41,12 @@
#include "net80211_impl.h"
+/* tunables */
+#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */
+#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */
+
+#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
+
const char *ieee80211_mgt_subtype_name[] = {
"assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp",
"probe_req", "probe_resp", "reserved#6", "reserved#7",
@@ -60,6 +66,13 @@ const char *ieee80211_state_name[IEEE80211_S_MAX] = {
"ASSOC", /* IEEE80211_S_ASSOC */
"RUN" /* IEEE80211_S_RUN */
};
+const char *ieee80211_wme_acnames[] = {
+ "WME_AC_BE",
+ "WME_AC_BK",
+ "WME_AC_VI",
+ "WME_AC_VO",
+ "WME_UPSD",
+};
static int ieee80211_newstate(ieee80211com_t *, enum ieee80211_state, int);
@@ -75,10 +88,12 @@ ieee80211_proto_attach(ieee80211com_t *ic)
ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT;
ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT;
ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE;
- ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT;
ic->ic_protmode = IEEE80211_PROT_CTSONLY;
im->im_bmiss_max = IEEE80211_BMISS_MAX;
+ ic->ic_wme.wme_hipri_switch_hysteresis =
+ AGGRESSIVE_MODE_SWITCH_HYSTERESIS;
+
/* protocol state change handler */
ic->ic_newstate = ieee80211_newstate;
@@ -210,11 +225,11 @@ ieee80211_dump_pkt(const uint8_t *buf, int32_t len, int32_t rate, int32_t rssi)
* The highest bit of returned rate value is set to 1 on failure.
*/
int
-ieee80211_fix_rate(ieee80211_node_t *in, int flags)
+ieee80211_fix_rate(ieee80211_node_t *in,
+ struct ieee80211_rateset *nrs, int flags)
{
ieee80211com_t *ic = in->in_ic;
struct ieee80211_rateset *srs;
- struct ieee80211_rateset *nrs;
boolean_t ignore;
int i;
int okrate;
@@ -235,7 +250,6 @@ ieee80211_fix_rate(ieee80211_node_t *in, int flags)
}
okrate = badrate = fixedrate = 0;
srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, in->in_chan)];
- nrs = &in->in_rates;
for (i = 0; i < nrs->ir_nrates; ) {
int j;
@@ -393,7 +407,12 @@ ieee80211_setbasicrates(struct ieee80211_rateset *rs,
{ 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G mixed b/g */
{ 0 }, /* IEEE80211_MODE_FH */
{ 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */
- { 4, { 2, 4, 11, 22 } } /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ { 4, { 2, 4, 11, 22 } },
+ /* IEEE80211_MODE_TURBO_G (mixed b/g) */
+ { 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */
+ /* IEEE80211_MODE_11NG (mixed b/g) */
+ { 7, { 2, 4, 11, 22, 12, 24, 48 } }
};
int i, j;
@@ -410,6 +429,301 @@ ieee80211_setbasicrates(struct ieee80211_rateset *rs,
}
/*
+ * WME protocol support. The following parameters come from the spec.
+ */
+typedef struct phyParamType {
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txopLimit;
+ uint8_t acm;
+} paramType;
+
+static const paramType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 3, 4, 6, 0, 0 } /* IEEE80211_MODE_11NG */
+};
+static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */
+};
+static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */
+ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */
+ { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */
+ { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */
+};
+static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */
+ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */
+ { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */
+ { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
+};
+
+static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */
+ { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */
+};
+static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = {
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */
+ { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */
+};
+static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = {
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */
+ { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */
+};
+
+void
+ieee80211_wme_initparams(struct ieee80211com *ic)
+{
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ const paramType *pPhyParam, *pBssPhyParam;
+ struct wmeParams *wmep;
+ enum ieee80211_phymode mode;
+ int i;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return;
+
+ /*
+ * Select mode; we can be called early in which case we
+ * always use auto mode. We know we'll be called when
+ * entering the RUN state with bsschan setup properly
+ * so state will eventually get set correctly
+ */
+ if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
+ mode = ieee80211_chan2mode(ic, ic->ic_curchan);
+ else
+ mode = IEEE80211_MODE_AUTO;
+ for (i = 0; i < WME_NUM_AC; i++) {
+ switch (i) {
+ case WME_AC_BK:
+ pPhyParam = &phyParamForAC_BK[mode];
+ pBssPhyParam = &phyParamForAC_BK[mode];
+ break;
+ case WME_AC_VI:
+ pPhyParam = &phyParamForAC_VI[mode];
+ pBssPhyParam = &bssPhyParamForAC_VI[mode];
+ break;
+ case WME_AC_VO:
+ pPhyParam = &phyParamForAC_VO[mode];
+ pBssPhyParam = &bssPhyParamForAC_VO[mode];
+ break;
+ case WME_AC_BE:
+ default:
+ pPhyParam = &phyParamForAC_BE[mode];
+ pBssPhyParam = &bssPhyParamForAC_BE[mode];
+ break;
+ }
+
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+ if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
+ wmep->wmep_acm = pPhyParam->acm;
+ wmep->wmep_aifsn = pPhyParam->aifsn;
+ wmep->wmep_logcwmin = pPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pPhyParam->txopLimit;
+ } else {
+ wmep->wmep_acm = pBssPhyParam->acm;
+ wmep->wmep_aifsn = pBssPhyParam->aifsn;
+ wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+
+ }
+ ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_initparams: "
+ "%s chan [acm %u aifsn %u log2(cwmin) %u "
+ "log2(cwmax) %u txpoLimit %u]\n",
+ ieee80211_wme_acnames[i],
+ wmep->wmep_acm,
+ wmep->wmep_aifsn,
+ wmep->wmep_logcwmin,
+ wmep->wmep_logcwmax,
+ wmep->wmep_txopLimit);
+
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+ wmep->wmep_acm = pBssPhyParam->acm;
+ wmep->wmep_aifsn = pBssPhyParam->aifsn;
+ wmep->wmep_logcwmin = pBssPhyParam->logcwmin;
+ wmep->wmep_logcwmax = pBssPhyParam->logcwmax;
+ wmep->wmep_txopLimit = pBssPhyParam->txopLimit;
+ ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_initparams: "
+ "%s bss [acm %u aifsn %u log2(cwmin) %u "
+ "log2(cwmax) %u txpoLimit %u]\n",
+ ieee80211_wme_acnames[i],
+ wmep->wmep_acm,
+ wmep->wmep_aifsn,
+ wmep->wmep_logcwmin,
+ wmep->wmep_logcwmax,
+ wmep->wmep_txopLimit);
+ }
+ /* NB: check ic_bss to avoid NULL deref on initial attach */
+ if (ic->ic_bss != NULL) {
+ /*
+ * Calculate agressive mode switching threshold based
+ * on beacon interval. This doesn't need locking since
+ * we're only called before entering the RUN state at
+ * which point we start sending beacon frames.
+ */
+ wme->wme_hipri_switch_thresh =
+ (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->in_intval) / 100;
+ ieee80211_wme_updateparams(ic);
+ }
+}
+
+/*
+ * Update WME parameters for ourself and the BSS.
+ */
+void
+ieee80211_wme_updateparams(struct ieee80211com *ic)
+{
+ static const paramType phyParam[IEEE80211_MODE_MAX] = {
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */
+ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */
+ { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */
+ { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */
+ { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */
+ };
+ struct ieee80211_wme_state *wme = &ic->ic_wme;
+ const struct wmeParams *wmep;
+ struct wmeParams *chanp, *bssp;
+ enum ieee80211_phymode mode;
+ int i;
+
+ if ((ic->ic_caps & IEEE80211_C_WME) == 0)
+ return;
+
+ /* set up the channel access parameters for the physical device */
+ for (i = 0; i < WME_NUM_AC; i++) {
+ chanp = &wme->wme_chanParams.cap_wmeParams[i];
+ wmep = &wme->wme_wmeChanParams.cap_wmeParams[i];
+ chanp->wmep_aifsn = wmep->wmep_aifsn;
+ chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+ chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+ chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+
+ chanp = &wme->wme_bssChanParams.cap_wmeParams[i];
+ wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i];
+ chanp->wmep_aifsn = wmep->wmep_aifsn;
+ chanp->wmep_logcwmin = wmep->wmep_logcwmin;
+ chanp->wmep_logcwmax = wmep->wmep_logcwmax;
+ chanp->wmep_txopLimit = wmep->wmep_txopLimit;
+ }
+
+ /*
+ * Select mode; we can be called early in which case we
+ * always use auto mode. We know we'll be called when
+ * entering the RUN state with bsschan setup properly
+ * so state will eventually get set correctly
+ */
+ if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
+ mode = ieee80211_chan2mode(ic, ic->ic_curchan);
+ else
+ mode = IEEE80211_MODE_AUTO;
+
+ /*
+ * This implements agressive mode as found in certain
+ * vendors' AP's. When there is significant high
+ * priority (VI/VO) traffic in the BSS throttle back BE
+ * traffic by using conservative parameters. Otherwise
+ * BE uses agressive params to optimize performance of
+ * legacy/non-QoS traffic.
+ */
+ if ((ic->ic_opmode == IEEE80211_M_HOSTAP &&
+ (wme->wme_flags & WME_F_AGGRMODE) != 0) ||
+ (ic->ic_opmode == IEEE80211_M_STA &&
+ (ic->ic_bss->in_flags & IEEE80211_NODE_QOS) == 0) ||
+ (ic->ic_flags & IEEE80211_F_WME) == 0) {
+ chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
+ bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
+
+ chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn;
+ chanp->wmep_logcwmin = bssp->wmep_logcwmin =
+ phyParam[mode].logcwmin;
+ chanp->wmep_logcwmax = bssp->wmep_logcwmax =
+ phyParam[mode].logcwmax;
+ chanp->wmep_txopLimit = bssp->wmep_txopLimit =
+ (ic->ic_flags & IEEE80211_F_BURST) ?
+ phyParam[mode].txopLimit : 0;
+ ieee80211_dbg(IEEE80211_MSG_WME,
+ "ieee80211_wme_updateparams_locked: "
+ "%s [acm %u aifsn %u log2(cwmin) %u "
+ "log2(cwmax) %u txpoLimit %u]\n",
+ ieee80211_wme_acnames[WME_AC_BE],
+ chanp->wmep_acm,
+ chanp->wmep_aifsn,
+ chanp->wmep_logcwmin,
+ chanp->wmep_logcwmax,
+ chanp->wmep_txopLimit);
+ }
+
+ wme->wme_update(ic);
+
+ ieee80211_dbg(IEEE80211_MSG_WME, "ieee80211_wme_updateparams(): "
+ "%s: WME params updated, cap_info 0x%x\n",
+ ic->ic_opmode == IEEE80211_M_STA ?
+ wme->wme_wmeChanParams.cap_info :
+ wme->wme_bssChanParams.cap_info);
+}
+
+/*
* Process STA mode beacon miss events. Send a direct probe request
* frame to the current ap bmiss_max times (w/o answer) before
* scanning for a new ap.
@@ -496,8 +810,8 @@ ieee80211_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
switch (ic->ic_opmode) {
case IEEE80211_M_STA:
IEEE80211_SEND_MGMT(ic, in,
- IEEE80211_FC0_SUBTYPE_DISASSOC,
- IEEE80211_REASON_ASSOC_LEAVE);
+ IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
ieee80211_sta_leave(ic, in);
break;
case IEEE80211_M_IBSS:
@@ -644,6 +958,15 @@ ieee80211_newstate(ieee80211com_t *ic, enum ieee80211_state nstate, int arg)
wd.wd_secalloc = ieee80211_crypto_getciphertype(ic);
wd.wd_opmode = ic->ic_opmode;
IEEE80211_ADDR_COPY(wd.wd_bssid, in->in_bssid);
+ wd.wd_qospad = 0;
+ if (in->in_flags &
+ (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) {
+ wd.wd_qospad = 2;
+ if (ic->ic_flags & IEEE80211_F_DATAPAD) {
+ wd.wd_qospad = roundup(wd.wd_qospad,
+ sizeof (uint32_t));
+ }
+ }
(void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd));
break;
}
diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile
index 842ab85923..808cc8e26d 100644
--- a/usr/src/uts/common/sys/Makefile
+++ b/usr/src/uts/common/sys/Makefile
@@ -390,6 +390,7 @@ CHKHDRS= \
ndi_impldefs.h \
net80211.h \
net80211_crypto.h \
+ net80211_ht.h \
net80211_proto.h \
netconfig.h \
neti.h \
diff --git a/usr/src/uts/common/sys/mac_wifi.h b/usr/src/uts/common/sys/mac_wifi.h
index a7d5523118..2ff46dbc6c 100644
--- a/usr/src/uts/common/sys/mac_wifi.h
+++ b/usr/src/uts/common/sys/mac_wifi.h
@@ -19,15 +19,13 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#ifndef _SYS_MAC_WIFI_H
#define _SYS_MAC_WIFI_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* WiFi MAC-Type Plugin
*/
@@ -47,7 +45,7 @@ extern "C" {
* Maximum size of a WiFi header based on current implementation.
* May change in the future as new features are added.
*/
-#define WIFI_HDRSIZE (sizeof (struct ieee80211_frame) + \
+#define WIFI_HDRSIZE (sizeof (struct ieee80211_qosframe_addr4) + \
IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN + IEEE80211_WEP_EXTIVLEN + \
sizeof (struct ieee80211_llc))
@@ -99,12 +97,18 @@ enum wifi_secmode {
* transmission. The plugin will allocate header
* space for the security portion, and fill in any
* fixed-contents fields.
+ *
+ * wd_qospad Generally, QoS data field takes 2 bytes, but
+ * some special hardwares, such as Atheros, will need the
+ * 802.11 header padded to a 32-bit boundary for 4-address
+ * and QoS frames, at this time, it's 4 bytes.
*/
typedef struct wifi_data {
uint_t wd_opts;
uint8_t wd_bssid[IEEE80211_ADDR_LEN];
enum ieee80211_opmode wd_opmode;
enum wifi_secmode wd_secalloc;
+ uint_t wd_qospad;
} wifi_data_t;
extern uint8_t wifi_bcastaddr[];
diff --git a/usr/src/uts/common/sys/net80211.h b/usr/src/uts/common/sys/net80211.h
index b182fcf5d3..6414156e34 100644
--- a/usr/src/uts/common/sys/net80211.h
+++ b/usr/src/uts/common/sys/net80211.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -42,6 +42,7 @@
#include <sys/ethernet.h>
#include <sys/net80211_proto.h>
#include <sys/net80211_crypto.h>
+#include <sys/net80211_ht.h>
#include <net/wpa.h>
/*
@@ -85,6 +86,17 @@ extern "C" {
#define IEEE80211_C_CRYPTO 0x0000001f /* CAPABILITY: crypto alg's */
+/*
+ * ic_htcaps: HT-specific device/driver capabilities
+ *
+ * NB: the low 16-bits are the 802.11 definitions, the upper
+ * 16-bits are used to define s/w/driver capabilities.
+ */
+#define IEEE80211_HTC_AMPDU 0x00010000 /* CAPABILITY: A-MPDU tx */
+#define IEEE80211_HTC_AMSDU 0x00020000 /* CAPABILITY: A-MSDU tx */
+/* NB: HT40 is implied by IEEE80211_HTCAP_CHWIDTH40 */
+#define IEEE80211_HTC_HT 0x00040000 /* CAPABILITY: HT operation */
+
/* ic_flags */
/* NB: bits 0x4c available */
#define IEEE80211_F_FF 0x00000001 /* CONF: ATH FF enabled */
@@ -124,25 +136,52 @@ extern "C" {
#define IEEE80211_F_WMEUPDATE 0x20000000 /* STATUS: update beacon wme */
/* ic_flags_ext */
-#define IEEE80211_FEXT_WDS 0x00000001 /* CONF: 4 addr allowed */
+#define IEEE80211_FEXT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
+#define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */
/* 0x00000006 reserved */
#define IEEE80211_FEXT_BGSCAN 0x00000008
/* STATUS: enable full bgscan completion */
#define IEEE80211_FEXT_ERPUPDATE 0x00000200 /* STATUS: update ERP element */
#define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */
+#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive chan */
+#define IEEE80211_FEXT_HT 0x00080000 /* CONF: HT supported */
+#define IEEE80211_FEXT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
+#define IEEE80211_FEXT_AMPDU_RX 0x00200000 /* CONF: A-MPDU tx supported */
+#define IEEE80211_FEXT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */
+#define IEEE80211_FEXT_AMSDU_RX 0x00800000 /* CONF: A-MSDU tx supported */
+#define IEEE80211_FEXT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */
+#define IEEE80211_FEXT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */
+#define IEEE80211_FEXT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */
+#define IEEE80211_FEXT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */
+#define IEEE80211_FEXT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */
/*
* Channel attributes (ich_flags)
* bits 0-3 are for private use by drivers
*/
-#define IEEE80211_CHAN_TURBO 0x0010 /* Turbo channel */
-#define IEEE80211_CHAN_CCK 0x0020 /* CCK channel */
-#define IEEE80211_CHAN_OFDM 0x0040 /* OFDM channel */
-#define IEEE80211_CHAN_2GHZ 0x0080 /* 2 GHz spectrum channel. */
-#define IEEE80211_CHAN_5GHZ 0x0100 /* 5 GHz spectrum channel */
-#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
-#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
-#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_TURBO 0x00000010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x00000020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x00000040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x00000080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x00000100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x00000200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x00000400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x00000800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x00001000 /* 900 MHz spectrum channel */
+#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_DFS 0x00080000 /* DFS required */
+#define IEEE80211_CHAN_4MSXMIT 0x00100000 /* 4ms limit on frame length */
+#define IEEE80211_CHAN_NOADHOC 0x00200000 /* adhoc mode not allowed */
+#define IEEE80211_CHAN_NOHOSTAP 0x00400000 /* hostap mode not allowed */
+#define IEEE80211_CHAN_11D 0x00800000 /* 802.11d required */
+
+#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
+#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
#define IEEE80211_CHAN_MAX 255
#define IEEE80211_CHAN_BYTES 32 /* howmany(IEEE80211_CHAN_MAX, NBBY) */
@@ -155,10 +194,30 @@ extern "C" {
#define IEEE80211_IS_CHAN_5GHZ(_c) \
(((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0)
+#define IEEE80211_NODE_CHWUPDATE 0x0400 /* 11n channel width change */
#define IEEE80211_NODE_HASHSIZE 32
+#define IEEE80211_NODE_AUTH 0x0001 /* authorized for data */
+#define IEEE80211_NODE_QOS 0x0002 /* QoS enabled */
+#define IEEE80211_NODE_ERP 0x0004 /* ERP enabled */
+/* NB: this must have the same value as IEEE80211_FC1_PWR_MGT */
+#define IEEE80211_NODE_PWR_MGT 0x0010 /* power save mode enabled */
+#define IEEE80211_NODE_AREF 0x0020 /* authentication ref held */
+#define IEEE80211_NODE_HT 0x0040 /* HT enabled */
+#define IEEE80211_NODE_HTCOMPAT 0x0080 /* HT setup w/ vendor OUI's */
+#define IEEE80211_NODE_AMPDU_RX 0x0400 /* AMPDU rx enabled */
+#define IEEE80211_NODE_AMPDU_TX 0x0800 /* AMPDU tx enabled */
+
+#define IEEE80211_NODE_AMPDU \
+ (IEEE80211_NODE_AMPDU_RX | IEEE80211_NODE_AMPDU_TX)
+
#define IEEE80211_FIXED_RATE_NONE 0
-#define IEEE80211_MCAST_RATE_DEFAULT (2*1) /* default mcast rate (1M) */
+
+#define WME_OUI 0xf25000
+#define WME_OUI_TYPE 0x02
+#define WME_INFO_OUI_SUBTYPE 0x00
+#define WME_PARAM_OUI_SUBTYPE 0x01
+#define WME_VERSION 1
/* WME stream classes */
#define WME_AC_BE 0 /* best effort */
@@ -169,6 +228,25 @@ extern "C" {
#define MAX_EVENT 16
#define MAX_IEEE80211STR 256
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define IEEE80211_RADIOTAP_F_CFP 0x01
+ /* sent/received during CFP */
+#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02
+ /* sent/received with short preamble */
+#define IEEE80211_RADIOTAP_F_WEP 0x04
+ /* sent/received with WEP encryption */
+#define IEEE80211_RADIOTAP_F_FRAG 0x08
+ /* sent/received with fragmentation */
+#define IEEE80211_RADIOTAP_F_DATAPAD 0x20
+ /*
+ * frame has padding between 802.11
+ * header and payload (to 32-bit
+ * boundary
+ */
+#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */
+#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */
+#define IEEE80211_RADIOTAP_F_SHORTGI 0x80 /* HT short GI */
+
/*
* Authentication mode.
*/
@@ -204,11 +282,26 @@ struct ieee80211_rateset {
};
/*
+ * 802.11n variant of ieee80211_rateset. Instead
+ * legacy rates the entries are MCS rates. We define
+ * the structure such that it can be used interchangeably
+ * with an ieee80211_rateset (modulo structure size).
+ */
+#define IEEE80211_HTRATE_MAXSIZE 127
+
+struct ieee80211_htrateset {
+ uint8_t rs_nrates;
+ uint8_t rs_rates[IEEE80211_HTRATE_MAXSIZE];
+};
+
+#define IEEE80211_RATE_MCS 0x80
+
+/*
* Channels are specified by frequency and attributes.
*/
struct ieee80211_channel {
uint16_t ich_freq; /* setting in Mhz */
- uint16_t ich_flags; /* see below */
+ uint32_t ich_flags; /* see below */
};
struct ieee80211_device_stats {
@@ -254,6 +347,9 @@ struct ieee80211_node_table {
list_t nt_hash[IEEE80211_NODE_HASHSIZE];
};
+#define IEEE80211_TID_SIZE (WME_NUM_TID+1) /* WME TID's +1 for non-QoS */
+#define IEEE80211_NONQOS_TID WME_NUM_TID /* index for non-QoS sta */
+
/*
* Node specific information. Note that drivers are expected
* to derive from this structure to add device-specific per-node
@@ -274,8 +370,8 @@ struct ieee80211_node {
* index 0 is used when QoS is not enabled. index 1-16 is used
* when QoS is enabled. 1-16 corresponds to TID 0-15.
*/
- uint16_t in_txseqs[17]; /* tx seq per-tid */
- uint16_t in_rxseqs[17]; /* rx seq previous per-tid */
+ uint16_t in_txseqs[IEEE80211_TID_SIZE];
+ uint16_t in_rxseqs[IEEE80211_TID_SIZE];
clock_t in_rxfragstamp; /* time stamp of last rx frag */
mblk_t *in_rxfrag; /* rx frag reassembly */
uint32_t in_scangen; /* gen# for timeout scan */
@@ -311,23 +407,74 @@ struct ieee80211_node {
uint32_t *in_challenge; /* shared-key challenge */
struct ieee80211_key in_ucastkey; /* unicast key */
uint8_t *in_wpa_ie; /* captured WPA/RSN ie */
+ uint8_t *in_wme_ie; /* captured WME ie */
+
+ /* 11n state */
+ uint8_t *in_htcap_ie; /* captured HTCAP ie */
+ uint16_t in_htcap; /* HT capabilities */
+ uint8_t in_htparam; /* HT params */
+ uint8_t in_htctlchan; /* HT control channel */
+ uint8_t in_ht2ndchan; /* HT 2nd channel */
+ uint8_t in_htopmode; /* HT operating mode */
+ uint8_t in_htstbc; /* HT */
+ uint8_t in_reqcw; /* requested tx channel width */
+ uint8_t in_chw; /* negotiated channel width */
+ struct ieee80211_htrateset in_htrates; /* negotiated ht rate set */
+ struct ieee80211_tx_ampdu in_tx_ampdu[WME_NUM_AC];
+ struct ieee80211_rx_ampdu in_rx_ampdu[WME_NUM_TID];
/* others */
int32_t in_fails; /* failure count to associate */
int16_t in_inact; /* inactivity mark count */
int16_t in_inact_reload; /* inactivity reload value */
- int32_t in_txrate; /* index to ni_rates[] */
+ int32_t in_txrate; /* index to in_rates[] */
list_node_t in_node; /* element of nt->nt_node */
list_node_t in_hash; /* element of nt->nt_hash */
};
+/*
+ * WME/WMM support.
+ */
+struct wmeParams {
+ uint8_t wmep_acm;
+ uint8_t wmep_aifsn;
+ uint8_t wmep_logcwmin; /* log2(cwmin) */
+ uint8_t wmep_logcwmax; /* log2(cwmax) */
+ uint8_t wmep_txopLimit;
+ uint8_t wmep_noackPolicy; /* 0 (ack), 1 (no ack) */
+};
+#define IEEE80211_TXOP_TO_US(_txop) ((_txop)<<5)
+#define IEEE80211_US_TO_TXOP(_us) ((_us)>>5)
+
+struct chanAccParams {
+ uint8_t cap_info; /* version of the current set */
+ struct wmeParams cap_wmeParams[WME_NUM_AC];
+};
+
+struct ieee80211_wme_state {
+ uint_t wme_flags;
+#define WME_F_AGGRMODE 0x00000001 /* STATUS: WME agressive mode */
+ uint_t wme_hipri_traffic; /* VI/VO frames in beacon interval */
+ uint_t wme_hipri_switch_thresh; /* agressive mode switch thresh */
+ uint_t wme_hipri_switch_hysteresis;
+ /* agressive mode switch hysteresis */
+ struct wmeParams wme_params[4]; /* from assoc resp for each AC */
+ struct chanAccParams wme_wmeChanParams; /* WME params applied to self */
+ struct chanAccParams wme_wmeBssChanParams;
+ /* WME params bcast to stations */
+ struct chanAccParams wme_chanParams; /* params applied to self */
+ struct chanAccParams wme_bssChanParams; /* params bcast to stations */
+ int (*wme_update)(struct ieee80211com *);
+};
+
struct ieee80211com {
mac_handle_t ic_mach;
/* Initialized by driver */
uint8_t ic_macaddr[IEEE80211_ADDR_LEN];
uint32_t ic_caps; /* capabilities */
+ uint32_t ic_htcaps; /* HT capabilities */
enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */
enum ieee80211_opmode ic_opmode; /* current operation mode */
enum ieee80211_state ic_state; /* current 802.11 state */
@@ -348,7 +495,6 @@ struct ieee80211com {
uint8_t ic_bmissthreshold;
uint16_t ic_rtsthreshold;
uint16_t ic_fragthreshold;
- int32_t ic_mcast_rate; /* rate for mcast frames */
uint8_t ic_fixed_rate; /* value of fixed rate */
int32_t ic_des_esslen; /* length of desired essid */
uint8_t ic_des_essid[IEEE80211_NWID_LEN];
@@ -378,6 +524,22 @@ struct ieee80211com {
struct ieee80211_node_table ic_scan; /* STA: scan candidates */
struct ieee80211_node_table ic_sta; /* AP:stations/IBSS:neighbors */
+ struct ieee80211_wme_state ic_wme; /* WME/WMM state */
+
+ int ic_ampdu_rxmax; /* A-MPDU rx limit (bytes) */
+ int ic_ampdu_density; /* A-MPDU density */
+ int ic_ampdu_limit; /* A-MPDU tx limit (bytes) */
+ int ic_amsdu_limit; /* A-MSDU tx limit (bytes) */
+
+ uint16_t ic_sta_assoc; /* stations associated */
+ uint16_t ic_ht_sta_assoc; /* HT stations associated */
+ uint16_t ic_ht40_sta_assoc; /* HT40 station associated */
+ uint8_t ic_curhtprotmode; /* HTINFO bss state */
+ enum ieee80211_protmode ic_htprotmode; /* HT protection mode */
+ int ic_lastnonerp; /* last time nonERP sta noted */
+ int ic_lastnonht; /* last time non-HT sta noted */
+
+
/* callback functions */
/*
* Functions initialized by driver before calling ieee80211_attach()
@@ -423,6 +585,29 @@ struct ieee80211com {
void (*ic_node_cleanup)(ieee80211_node_t *);
void (*ic_node_free)(ieee80211_node_t *);
uint8_t (*ic_node_getrssi)(const ieee80211_node_t *);
+ void (*ic_set_channel)(ieee80211com_t *);
+
+ /*
+ * 802.11n ADDBA support. A simple/generic implementation
+ * of A-MPDU tx aggregation is provided; the driver may
+ * override these methods to provide their own support.
+ * A-MPDU rx re-ordering happens automatically if the
+ * driver passes out-of-order frames to ieee80211_input
+ * from an assocated HT station.
+ */
+ void (*ic_recv_action)(ieee80211_node_t *,
+ const uint8_t *, const uint8_t *);
+ int (*ic_send_action)(ieee80211_node_t *,
+ int, int, uint16_t[4]);
+ /* start/stop doing A-MPDU tx aggregation for a station */
+ int (*ic_addba_request)(ieee80211_node_t *,
+ struct ieee80211_tx_ampdu *,
+ int, int, int);
+ int (*ic_addba_response)(ieee80211_node_t *,
+ struct ieee80211_tx_ampdu *,
+ int, int, int);
+ void (*ic_addba_stop)(ieee80211_node_t *,
+ struct ieee80211_tx_ampdu *);
kmutex_t ic_genlock;
void *ic_private; /* ieee80211 private data */
@@ -431,6 +616,7 @@ struct ieee80211com {
#define ic_def_txkey ic_crypto.cs_def_txkey
extern const char *ieee80211_state_name[IEEE80211_S_MAX];
+extern const char *ieee80211_wme_acnames[];
#define IEEE80211_RATE(_ix) \
(in->in_rates.ir_rates[(_ix)] & IEEE80211_RATE_VAL)
@@ -533,6 +719,12 @@ void ieee80211_dump_pkt(const uint8_t *, int32_t, int32_t, int32_t);
void ieee80211_watchdog(void *);
void ieee80211_start_watchdog(ieee80211com_t *, uint32_t);
void ieee80211_stop_watchdog(ieee80211com_t *);
+int ieee80211_classify(struct ieee80211com *, mblk_t *,
+ struct ieee80211_node *);
+int ieee80211_hdrsize(const void *);
+int ieee80211_hdrspace(ieee80211com_t *, const void *);
+int ieee80211_anyhdrsize(const void *);
+int ieee80211_anyhdrspace(ieee80211com_t *, const void *);
void *ieee80211_malloc(size_t);
void ieee80211_free(void *);
@@ -541,6 +733,12 @@ int ieee80211_setprop(void *, const char *, mac_prop_id_t, uint_t,
int ieee80211_getprop(void *, const char *, mac_prop_id_t, uint_t, uint_t,
void *, uint_t *);
+struct ieee80211_channel *ieee80211_find_channel(ieee80211com_t *, int, int);
+const struct ieee80211_rateset *ieee80211_get_suprates(ieee80211com_t *,
+ struct ieee80211_channel *);
+
+/* HT */
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/uts/common/sys/net80211_ht.h b/usr/src/uts/common/sys/net80211_ht.h
new file mode 100644
index 0000000000..449726aa0f
--- /dev/null
+++ b/usr/src/uts/common/sys/net80211_ht.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2007 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_NET80211_HT_H
+#define _SYS_NET80211_HT_H
+
+/*
+ * 802.11n protocol implementation definitions.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ieee80211com;
+struct ieee80211_node;
+struct ieee80211_channel;
+
+#define IEEE80211_AGGR_BAWMAX 64 /* max block ack window size */
+/* threshold for aging overlapping non-HT bss */
+#define IEEE80211_NONHT_PRESENT_AGE (60*1000) /* msec */
+
+#define M_AMPDU 0x8000 /* A-MPDU processing done */
+#define M_WEP 0x4000 /* WEP done by hardware */
+#define M_80211_RX (M_AMPDU | M_WEP)
+
+typedef uint16_t ieee80211_seq;
+
+struct ieee80211_tx_ampdu {
+ ushort_t txa_flags;
+#define IEEE80211_AGGR_IMMEDIATE 0x0001 /* BA policy */
+#define IEEE80211_AGGR_XCHGPEND 0x0002 /* ADDBA response pending */
+#define IEEE80211_AGGR_RUNNING 0x0004 /* ADDBA response received */
+#define IEEE80211_AGGR_SETUP 0x0008 /* deferred state setup */
+#define IEEE80211_AGGR_NAK 0x0010 /* peer NAK'd ADDBA request */
+ uint8_t txa_ac;
+ uint8_t txa_token; /* dialog token */
+ int txa_qbytes; /* data queued (bytes) */
+ short txa_qframes; /* data queued (frames) */
+ ieee80211_seq txa_seqstart;
+ ieee80211_seq txa_start;
+ uint16_t txa_wnd; /* BA window size */
+ uint8_t txa_attempts; /* # setup attempts */
+ clock_t txa_lastrequest; /* time of last ADDBA request */
+ timeout_id_t txa_timer;
+};
+
+/* return non-zero if AMPDU tx for the TID is running */
+#define IEEE80211_AMPDU_RUNNING(tap) \
+ (((tap)->txa_flags & IEEE80211_AGGR_RUNNING) != 0)
+
+/* return non-zero if AMPDU tx for the TID is running or started */
+#define IEEE80211_AMPDU_REQUESTED(tap) \
+ (((tap)->txa_flags & (IEEE80211_AGGR_RUNNING | \
+ IEEE80211_AGGR_XCHGPEND | IEEE80211_AGGR_NAK)) != 0)
+
+struct ieee80211_rx_ampdu {
+ int rxa_flags;
+ int rxa_qbytes; /* data queued (bytes) */
+ short rxa_qframes; /* data queued (frames) */
+ ieee80211_seq rxa_seqstart;
+ ieee80211_seq rxa_start; /* start of current BA window */
+ uint16_t rxa_wnd; /* BA window size */
+ clock_t rxa_age; /* age of oldest frame in window */
+ int rxa_nframes; /* frames since ADDBA */
+ mblk_t *rxa_m[IEEE80211_AGGR_BAWMAX];
+};
+
+void ieee80211_ht_attach(struct ieee80211com *);
+void ieee80211_ht_detach(struct ieee80211com *);
+
+void ieee80211_ht_announce(struct ieee80211com *);
+
+extern const int ieee80211_htrates[16];
+const struct ieee80211_htrateset *ieee80211_get_suphtrates(
+ struct ieee80211com *, const struct ieee80211_channel *);
+
+int ieee80211_setup_htrates(struct ieee80211_node *,
+ const uint8_t *htcap, int flags);
+void ieee80211_setup_basic_htrates(struct ieee80211_node *,
+ const uint8_t *htinfo);
+
+mblk_t *ieee80211_decap_amsdu(struct ieee80211_node *, mblk_t *);
+
+int ieee80211_ampdu_reorder(struct ieee80211_node *, mblk_t *);
+void ieee80211_recv_bar(struct ieee80211_node *, mblk_t *);
+void ieee80211_ht_node_init(struct ieee80211_node *, const uint8_t *);
+void ieee80211_ht_node_cleanup(struct ieee80211_node *);
+struct ieee80211_channel *ieee80211_ht_adjust_channel(struct ieee80211com *,
+ struct ieee80211_channel *, int);
+
+void ieee80211_ht_wds_init(struct ieee80211_node *);
+void ieee80211_ht_node_join(struct ieee80211_node *);
+void ieee80211_ht_node_leave(struct ieee80211_node *);
+void ieee80211_htinfo_update(struct ieee80211com *, int protmode);
+void ieee80211_ht_timeout(struct ieee80211com *);
+void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *);
+void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *);
+void ieee80211_recv_action(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+int ieee80211_ampdu_request(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+void ieee80211_ampdu_stop(struct ieee80211_node *,
+ struct ieee80211_tx_ampdu *);
+int ieee80211_send_bar(struct ieee80211_node *,
+ const struct ieee80211_tx_ampdu *);
+int ieee80211_send_action(struct ieee80211_node *, int, int, uint16_t [4]);
+
+uint8_t *ieee80211_add_htcap(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htcap_vendor(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htinfo(uint8_t *, struct ieee80211_node *);
+uint8_t *ieee80211_add_htinfo_vendor(uint8_t *, struct ieee80211_node *);
+
+struct ieee80211_beacon_offsets;
+void ieee80211_ht_update_beacon(struct ieee80211com *,
+ struct ieee80211_beacon_offsets *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYS_NET80211_HT_H */
diff --git a/usr/src/uts/common/sys/net80211_proto.h b/usr/src/uts/common/sys/net80211_proto.h
index 5df0c7aafa..4fe7d40604 100644
--- a/usr/src/uts/common/sys/net80211_proto.h
+++ b/usr/src/uts/common/sys/net80211_proto.h
@@ -1,11 +1,11 @@
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2001 Atsushi Onoe
- * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
+ * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -38,8 +38,6 @@
#ifndef _SYS_NET80211_PROTO_H
#define _SYS_NET80211_PROTO_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* 802.11 protocol definitions
*/
@@ -65,6 +63,16 @@ extern "C" {
#define WME_NUM_AC 4 /* 4 AC categories */
/*
+ * The formation of some management frames requires guidance to
+ * deal with legacy clients. When the client is identified as
+ * "legacy 11b" this parameter can be passed in the arg param of a
+ * IEEE80211_SEND_MGMT call.
+ */
+#define IEEE80211_SEND_LEGACY_11B 0x1 /* legacy 11b client */
+#define IEEE80211_SEND_LEGACY_11 0x2 /* other legacy client */
+#define IEEE80211_SEND_LEGACY 0x3 /* any legacy client */
+
+/*
* Protocol Physical Layer
*/
@@ -76,15 +84,19 @@ enum ieee80211_phymode {
IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */
IEEE80211_MODE_FH = 4, /* 2GHz, GFSK */
IEEE80211_MODE_TURBO_A = 5, /* 5GHz, OFDM, 2x clock */
- IEEE80211_MODE_TURBO_G = 6 /* 2GHz, OFDM, 2x clock */
+ IEEE80211_MODE_TURBO_G = 6, /* 2GHz, OFDM, 2x clock */
+ IEEE80211_MODE_STURBO_A = 7, /* 5GHz, OFDM, 2x clock, static */
+ IEEE80211_MODE_11NA = 8, /* 5GHz, w/ HT */
+ IEEE80211_MODE_11NG = 9 /* 2GHz, w/ HT */
};
-#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO_G+1)
+#define IEEE80211_MODE_MAX (IEEE80211_MODE_11NG + 1)
enum ieee80211_phytype {
IEEE80211_T_DS, /* direct sequence spread spectrum */
IEEE80211_T_FH, /* frequency hopping */
IEEE80211_T_OFDM, /* frequency division multiplexing */
- IEEE80211_T_TURBO /* high rate OFDM, aka turbo mode */
+ IEEE80211_T_TURBO, /* high rate OFDM, aka turbo mode */
+ IEEE80211_T_HT /* high throughput, full GI */
};
#define IEEE80211_T_CCK IEEE80211_T_DS /* more common nomenclature */
@@ -120,6 +132,22 @@ struct ieee80211_frame {
/* see below */
};
+struct ieee80211_qosframe {
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
+ uint8_t i_qos[2];
+ /* possibly followed by addr4[IEEE80211_ADDR_LEN]; */
+ /* see below */
+};
+
+struct ieee80211_qoscntl {
+ uint8_t i_qos[2];
+};
+
struct ieee80211_frame_addr4 {
uint8_t i_fc[2];
uint8_t i_dur[2];
@@ -130,6 +158,17 @@ struct ieee80211_frame_addr4 {
uint8_t i_addr4[IEEE80211_ADDR_LEN];
};
+struct ieee80211_qosframe_addr4 {
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_addr1[IEEE80211_ADDR_LEN];
+ uint8_t i_addr2[IEEE80211_ADDR_LEN];
+ uint8_t i_addr3[IEEE80211_ADDR_LEN];
+ uint8_t i_seq[2];
+ uint8_t i_addr4[IEEE80211_ADDR_LEN];
+ uint8_t i_qos[2];
+};
+
/* Start part(LLC and SNAP) of payload of IEEE80211 frame */
struct ieee80211_llc {
/* LLC */
@@ -150,9 +189,6 @@ struct ieee80211_mnf {
uint8_t mnf_dialog;
uint8_t mnf_status;
};
-#define IEEE80211_MNF_SETUP_REQ 0
-#define IEEE80211_MNF_SETUP_RESP 1
-#define IEEE80211_MNF_TEARDOWN 2
/*
* Control frames.
@@ -203,6 +239,16 @@ struct ieee80211_frame_cfend { /* NB: also CF-End+CF-Ack */
/* FCS */
};
+struct ieee80211_frame_bar {
+ uint8_t i_fc[2];
+ uint8_t i_dur[2];
+ uint8_t i_ra[IEEE80211_ADDR_LEN];
+ uint8_t i_ta[IEEE80211_ADDR_LEN];
+ uint16_t i_ctl;
+ uint16_t i_seq;
+ /* FCS */
+};
+
struct ieee80211_tim_ie {
uint8_t tim_ie; /* IEEE80211_ELEMID_TIM */
uint8_t tim_len;
@@ -251,9 +297,130 @@ struct ieee80211_wme_param {
uint8_t wme_oui_sybtype;
uint8_t wme_version;
uint8_t wme_qosInfo;
+#define WME_QOSINFO_COUNT 0x0f /* Mask for param count field */
uint8_t wme_reserved;
struct ieee80211_wme_acparams wme_acParams[WME_NUM_AC];
};
+
+/*
+ * WME/802.11e information element.
+ */
+struct ieee80211_wme_info {
+ uint8_t wme_id; /* IEEE80211_ELEMID_VENDOR */
+ uint8_t wme_len; /* length in bytes */
+ uint8_t wme_oui[3]; /* 0x00, 0x50, 0xf2 */
+ uint8_t wme_type; /* OUI type */
+ uint8_t wme_subtype; /* OUI subtype */
+ uint8_t wme_version; /* spec revision */
+ uint8_t wme_info; /* QoS info */
+};
+
+/*
+ * WME/802.11e Tspec Element
+ */
+struct ieee80211_wme_tspec {
+ uint8_t ts_id;
+ uint8_t ts_len;
+ uint8_t ts_oui[3];
+ uint8_t ts_oui_type;
+ uint8_t ts_oui_subtype;
+ uint8_t ts_version;
+ uint8_t ts_tsinfo[3];
+ uint8_t ts_nom_msdu[2];
+ uint8_t ts_max_msdu[2];
+ uint8_t ts_min_svc[4];
+ uint8_t ts_max_svc[4];
+ uint8_t ts_inactv_intv[4];
+ uint8_t ts_susp_intv[4];
+ uint8_t ts_start_svc[4];
+ uint8_t ts_min_rate[4];
+ uint8_t ts_mean_rate[4];
+ uint8_t ts_max_burst[4];
+ uint8_t ts_min_phy[4];
+ uint8_t ts_peak_rate[4];
+ uint8_t ts_delay[4];
+ uint8_t ts_surplus[2];
+ uint8_t ts_medium_time[2];
+};
+
+/*
+ * 802.11n Management Action Frames
+ */
+/* generic frame format */
+struct ieee80211_action {
+ uint8_t ia_category;
+ uint8_t ia_action;
+};
+
+/* HT - recommended transmission channel width */
+struct ieee80211_action_ht_txchwidth {
+ struct ieee80211_action at_header;
+ uint8_t at_chwidth;
+};
+
+struct ieee80211_action_ht_mimopowersave {
+ struct ieee80211_action am_header;
+ uint8_t am_control;
+};
+
+/* BA - ADDBA request */
+struct ieee80211_action_ba_addbarequest {
+ struct ieee80211_action rq_header;
+ uint8_t rq_dialogtoken;
+ uint16_t rq_baparamset;
+ uint16_t rq_batimeout; /* in TUs */
+ uint16_t rq_baseqctl;
+};
+
+/* BA - ADDBA response */
+struct ieee80211_action_ba_addbaresponse {
+ struct ieee80211_action rs_header;
+ uint8_t rs_dialogtoken;
+ uint16_t rs_statuscode;
+ uint16_t rs_baparamset;
+ uint16_t rs_batimeout; /* in TUs */
+};
+
+/* BA - DELBA */
+struct ieee80211_action_ba_delba {
+ struct ieee80211_action dl_header;
+ uint16_t dl_baparamset;
+ uint16_t dl_reasoncode;
+};
+
+struct ieee80211_ba_request {
+ uint16_t rq_barctl;
+ uint16_t rq_barseqctl;
+};
+
+/*
+ * 802.11n HT Capability IE
+ * NB: these reflect D1.10
+ */
+struct ieee80211_ie_htcap {
+ uint8_t hc_id; /* element ID */
+ uint8_t hc_len; /* length in bytes */
+ uint16_t hc_cap; /* HT caps (see below) */
+ uint8_t hc_param; /* HT params (see below) */
+ uint8_t hc_mcsset[16]; /* supported MCS set */
+ uint16_t hc_extcap; /* extended HT capabilities */
+ uint32_t hc_txbf; /* txbf capabilities */
+ uint8_t hc_antenna; /* antenna capabilities */
+};
+
+/*
+ * 802.11n HT Information IE
+ */
+struct ieee80211_ie_htinfo {
+ uint8_t hi_id; /* element ID */
+ uint8_t hi_len; /* length in bytes */
+ uint8_t hi_ctrlchannel; /* primary channel */
+ uint8_t hi_byte1; /* ht ie byte 1 */
+ uint8_t hi_byte2; /* ht ie byte 2 */
+ uint8_t hi_byte3; /* ht ie byte 3 */
+ uint16_t hi_byte45; /* ht ie bytes 4+5 */
+ uint8_t hi_basicmcsset[16]; /* basic MCS set */
+};
#pragma pack()
#define IEEE80211_FC0_VERSION_MASK 0x03
@@ -278,7 +445,9 @@ struct ieee80211_wme_param {
#define IEEE80211_FC0_SUBTYPE_DISASSOC 0xa0
#define IEEE80211_FC0_SUBTYPE_AUTH 0xb0
#define IEEE80211_FC0_SUBTYPE_DEAUTH 0xc0
+#define IEEE80211_FC0_SUBTYPE_ACTION 0xd0
/* for TYPE_CTL */
+#define IEEE80211_FC0_SUBTYPE_BAR 0x80
#define IEEE80211_FC0_SUBTYPE_PS_POLL 0xa0
#define IEEE80211_FC0_SUBTYPE_RTS 0xb0
#define IEEE80211_FC0_SUBTYPE_CTS 0xc0
@@ -302,6 +471,7 @@ struct ieee80211_wme_param {
#define IEEE80211_FC1_DIR_TODS 0x01 /* STA->AP */
#define IEEE80211_FC1_DIR_FROMDS 0x02 /* AP ->STA */
#define IEEE80211_FC1_DIR_DSTODS 0x03 /* AP ->AP */
+
#define IEEE80211_FC1_MORE_FRAG 0x04
#define IEEE80211_FC1_RETRY 0x08
#define IEEE80211_FC1_PWR_MGT 0x10
@@ -313,6 +483,17 @@ struct ieee80211_wme_param {
#define IEEE80211_SEQ_FRAG_SHIFT 0
#define IEEE80211_SEQ_SEQ_MASK 0xfff0
#define IEEE80211_SEQ_SEQ_SHIFT 4 /* 4bit frag number */
+#define IEEE80211_SEQ_RANGE 4096
+
+#define IEEE80211_SEQ_ADD(seq, incr) \
+ (((seq) + (incr)) & (IEEE80211_SEQ_RANGE - 1))
+#define IEEE80211_SEQ_INC(seq) IEEE80211_SEQ_ADD(seq, 1)
+#define IEEE80211_SEQ_SUB(a, b) \
+ (((a) + IEEE80211_SEQ_RANGE - (b)) & (IEEE80211_SEQ_RANGE - 1))
+
+#define IEEE80211_SEQ_BA_RANGE 2048 /* 2^11 */
+#define IEEE80211_SEQ_BA_BEFORE(a, b) \
+ (IEEE80211_SEQ_SUB(b, a+1) < IEEE80211_SEQ_BA_RANGE-1)
/* Length of management frame variable-length components in bytes */
#define IEEE80211_NWID_LEN 32 /* SSID */
@@ -321,6 +502,210 @@ struct ieee80211_wme_param {
#define IEEE80211_IBSS_LEN 4 /* IBSS parameters */
#define IEEE80211_ERP_LEN 1 /* ERP information */
+#define IEEE80211_QOS_TXOP 0x00ff
+/* bit 8 is reserved */
+#define IEEE80211_QOS_AMSDU 0x80
+#define IEEE80211_QOS_AMSDU_S 7
+#define IEEE80211_QOS_ACKPOLICY 0x60
+#define IEEE80211_QOS_ACKPOLICY_S 5
+#define IEEE80211_QOS_ACKPOLICY_NOACK 0x20 /* No ACK required */
+#define IEEE80211_QOS_ACKPOLICY_BA 0x60 /* Block ACK */
+#define IEEE80211_QOS_ESOP 0x10
+#define IEEE80211_QOS_ESOP_S 4
+#define IEEE80211_QOS_TID 0x0f
+
+/* does frame have QoS sequence control data */
+#define IEEE80211_QOS_HAS_SEQ(wh) \
+ (((wh)->i_fc[0] & \
+ (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_QOS)) == \
+ (IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_QOS))
+
+#define WME_NUM_AC 4 /* 4 AC categories */
+#define WME_NUM_TID 16 /* 16 tids */
+
+#define WME_PARAM_ACI 0x60 /* Mask for ACI field */
+#define WME_PARAM_ACI_S 5 /* Shift for ACI field */
+#define WME_PARAM_ACM 0x10 /* Mask for ACM bit */
+#define WME_PARAM_ACM_S 4 /* Shift for ACM bit */
+#define WME_PARAM_AIFSN 0x0f /* Mask for aifsn field */
+#define WME_PARAM_AIFSN_S 0 /* Shift for aifsn field */
+#define WME_PARAM_LOGCWMIN 0x0f /* Mask for CwMin field (in log) */
+#define WME_PARAM_LOGCWMIN_S 0 /* Shift for CwMin field */
+#define WME_PARAM_LOGCWMAX 0xf0 /* Mask for CwMax field (in log) */
+#define WME_PARAM_LOGCWMAX_S 4 /* Shift for CwMax field */
+
+#define WME_AC_TO_TID(_ac) ( \
+ ((_ac) == WME_AC_VO) ? 6 : \
+ ((_ac) == WME_AC_VI) ? 5 : \
+ ((_ac) == WME_AC_BK) ? 1 : \
+ 0)
+
+#define TID_TO_WME_AC(_tid) ( \
+ ((_tid) == 0 || (_tid) == 3) ? WME_AC_BE : \
+ ((_tid) < 3) ? WME_AC_BK : \
+ ((_tid) < 6) ? WME_AC_VI : \
+ WME_AC_VO)
+
+#define IEEE80211_MNF_SETUP_REQ 0
+#define IEEE80211_MNF_SETUP_RESP 1
+#define IEEE80211_MNF_TEARDOWN 2
+
+#define IEEE80211_ACTION_CAT_QOS 0 /* QoS */
+#define IEEE80211_ACTION_CAT_BA 3 /* BA */
+#define IEEE80211_ACTION_CAT_HT 7 /* HT */
+
+#define IEEE80211_ACTION_HT_TXCHWIDTH 0 /* recommend xmit chan width */
+#define IEEE80211_ACTION_HT_MIMOPWRSAVE 1 /* MIMO power save */
+
+#define IEEE80211_A_HT_TXCHWIDTH_20 0
+#define IEEE80211_A_HT_TXCHWIDTH_2040 1
+
+#define IEEE80211_A_HT_MIMOPWRSAVE_ENA 0x01 /* PS enabled */
+#define IEEE80211_A_HT_MIMOPWRSAVE_MODE 0x02
+#define IEEE80211_A_HT_MIMOPWRSAVE_MODE_S 1
+#define IEEE80211_A_HT_MIMOPWRSAVE_DYNAMIC 0x02 /* Dynamic Mode */
+#define IEEE80211_A_HT_MIMOPWRSAVE_STATIC 0x00 /* no SM packets */
+/* bits 2-7 reserved */
+
+/* Block Ack actions */
+#define IEEE80211_ACTION_BA_ADDBA_REQUEST 0 /* ADDBA request */
+#define IEEE80211_ACTION_BA_ADDBA_RESPONSE 1 /* ADDBA response */
+#define IEEE80211_ACTION_BA_DELBA 2 /* DELBA */
+
+/* Block Ack Parameter Set */
+#define IEEE80211_BAPS_BUFSIZ 0xffc0 /* buffer size */
+#define IEEE80211_BAPS_BUFSIZ_S 6
+#define IEEE80211_BAPS_TID 0x003c /* TID */
+#define IEEE80211_BAPS_TID_S 2
+#define IEEE80211_BAPS_POLICY 0x0002 /* block ack policy */
+#define IEEE80211_BAPS_POLICY_S 1
+
+#define IEEE80211_BAPS_POLICY_DELAYED (0<<IEEE80211_BAPS_POLICY_S)
+#define IEEE80211_BAPS_POLICY_IMMEDIATE (1<<IEEE80211_BAPS_POLICY_S)
+
+/* Block Ack Sequence Control */
+#define IEEE80211_BASEQ_START 0xfff0 /* starting seqnum */
+#define IEEE80211_BASEQ_START_S 4
+#define IEEE80211_BASEQ_FRAG 0x000f /* fragment number */
+#define IEEE80211_BASEQ_FRAG_S 0
+
+/* Delayed Block Ack Parameter Set */
+#define IEEE80211_DELBAPS_TID 0xf000 /* TID */
+#define IEEE80211_DELBAPS_TID_S 12
+#define IEEE80211_DELBAPS_INIT 0x0800 /* initiator */
+#define IEEE80211_DELBAPS_INIT_S 11
+
+/* BAR Control */
+#define IEEE80211_BAR_TID 0xf000 /* TID */
+#define IEEE80211_BAR_TID_S 12
+#define IEEE80211_BAR_COMP 0x0004 /* compressed */
+#define IEEE80211_BAR_MTID 0x0002
+#define IEEE80211_BAR_NOACK 0x0001 /* no-ack policy */
+
+/* HT capability flags (ht_cap) */
+#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC supported */
+#define IEEE80211_HTCAP_CHWIDTH40 0x0002 /* 20/40 supported */
+#define IEEE80211_HTCAP_SMPS 0x000c /* SM Power Save mode */
+#define IEEE80211_HTCAP_SMPS_OFF 0x0000 /* none (static mode) */
+#define IEEE80211_HTCAP_SMPS_DYNAMIC 0x0004 /* send RTS first */
+/* NB: SMPS value 2 is reserved */
+#define IEEE80211_HTCAP_SMPS_ENA 0x000c /* enabled */
+#define IEEE80211_HTCAP_GREENFIELD 0x0010 /* Greenfield supported */
+#define IEEE80211_HTCAP_SHORTGI20 0x0020 /* Short GI in 20MHz */
+#define IEEE80211_HTCAP_SHORTGI40 0x0040 /* Short GI in 40MHz */
+#define IEEE80211_HTCAP_TXSTBC 0x0080 /* STBC tx ok */
+#define IEEE80211_HTCAP_RXSTBC 0x0300 /* STBC rx support */
+#define IEEE80211_HTCAP_RXSTBC_S 8
+#define IEEE80211_HTCAP_RXSTBC_1STREAM 0x0100 /* 1 spatial stream */
+#define IEEE80211_HTCAP_RXSTBC_2STREAM 0x0200 /* 1-2 spatial streams */
+#define IEEE80211_HTCAP_RXSTBC_3STREAM 0x0300 /* 1-3 spatial streams */
+#define IEEE80211_HTCAP_DELBA 0x0400 /* HT DELBA supported */
+#define IEEE80211_HTCAP_MAXAMSDU 0x0800 /* max A-MSDU length */
+#define IEEE80211_HTCAP_MAXAMSDU_7935 0x0800 /* 7935 octets */
+#define IEEE80211_HTCAP_MAXAMSDU_3839 0x0000 /* 3839 octets */
+#define IEEE80211_HTCAP_DSSSCCK40 0x1000 /* DSSS/CCK in 40MHz */
+#define IEEE80211_HTCAP_PSMP 0x2000 /* PSMP supported */
+#define IEEE80211_HTCAP_40INTOLERANT 0x4000 /* 40MHz intolerant */
+#define IEEE80211_HTCAP_LSIGTXOPPROT 0x8000 /* L-SIG TXOP prot */
+
+/* HT parameters (hc_param) */
+#define IEEE80211_HTCAP_MAXRXAMPDU 0x03 /* max rx A-MPDU factor */
+#define IEEE80211_HTCAP_MAXRXAMPDU_S 0
+#define IEEE80211_HTCAP_MAXRXAMPDU_8K 0
+#define IEEE80211_HTCAP_MAXRXAMPDU_16K 1
+#define IEEE80211_HTCAP_MAXRXAMPDU_32K 2
+#define IEEE80211_HTCAP_MAXRXAMPDU_64K 3
+#define IEEE80211_HTCAP_MPDUDENSITY 0x1c /* min MPDU start spacing */
+#define IEEE80211_HTCAP_MPDUDENSITY_S 2
+#define IEEE80211_HTCAP_MPDUDENSITY_NA 0 /* no time restriction */
+#define IEEE80211_HTCAP_MPDUDENSITY_025 1 /* 1/4 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_05 2 /* 1/2 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_1 3 /* 1 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_2 4 /* 2 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_4 5 /* 4 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_8 6 /* 8 us */
+#define IEEE80211_HTCAP_MPDUDENSITY_16 7 /* 16 us */
+
+/* HT extended capabilities (hc_extcap) */
+#define IEEE80211_HTCAP_PCO 0x0001 /* PCO capable */
+#define IEEE80211_HTCAP_PCOTRANS 0x0006 /* PCO transition time */
+#define IEEE80211_HTCAP_PCOTRANS_S 1
+#define IEEE80211_HTCAP_PCOTRANS_04 0x0002 /* 400 us */
+#define IEEE80211_HTCAP_PCOTRANS_15 0x0004 /* 1.5 ms */
+#define IEEE80211_HTCAP_PCOTRANS_5 0x0006 /* 5 ms */
+/* bits 3-7 reserved */
+#define IEEE80211_HTCAP_MCSFBACK 0x0300 /* MCS feedback */
+#define IEEE80211_HTCAP_MCSFBACK_S 8
+#define IEEE80211_HTCAP_MCSFBACK_NONE 0x0000 /* nothing provided */
+#define IEEE80211_HTCAP_MCSFBACK_UNSOL 0x0200 /* unsolicited feedback */
+#define IEEE80211_HTCAP_MCSFBACK_MRQ 0x0300 /* " "+respond to MRQ */
+#define IEEE80211_HTCAP_HTC 0x0400 /* +HTC support */
+#define IEEE80211_HTCAP_RDR 0x0800
+ /* reverse direction responder */
+/* bits 12-15 reserved */
+
+/* byte1 */
+#define IEEE80211_HTINFO_2NDCHAN 0x03 /* secondary/ext chan offset */
+#define IEEE80211_HTINFO_2NDCHAN_S 0
+#define IEEE80211_HTINFO_2NDCHAN_NONE 0x00 /* no secondary/ext channel */
+#define IEEE80211_HTINFO_2NDCHAN_ABOVE 0x01 /* above private channel */
+/* NB: 2 is reserved */
+#define IEEE80211_HTINFO_2NDCHAN_BELOW 0x03 /* below primary channel */
+#define IEEE80211_HTINFO_TXWIDTH 0x04 /* tx channel width */
+#define IEEE80211_HTINFO_TXWIDTH_20 0x00 /* 20MHz width */
+#define IEEE80211_HTINFO_TXWIDTH_2040 0x04 /* any supported width */
+#define IEEE80211_HTINFO_RIFSMODE 0x08 /* Reduced IFS (RIFS) use */
+#define IEEE80211_HTINFO_RIFSMODE_PROH 0x00 /* RIFS use prohibited */
+#define IEEE80211_HTINFO_RIFSMODE_PERM 0x08 /* RIFS use permitted */
+#define IEEE80211_HTINFO_PMSPONLY 0x10 /* PSMP required to associate */
+#define IEEE80211_HTINFO_SIGRAN 0xe0 /* shortest Service Interval */
+#define IEEE80211_HTINFO_SIGRAN_S 5
+#define IEEE80211_HTINFO_SIGRAN_5 0x00 /* 5 ms */
+/* XXX add rest */
+
+/* bytes 2+3 */
+#define IEEE80211_HTINFO_OPMODE 0x03 /* operating mode */
+#define IEEE80211_HTINFO_OPMODE_S 0
+#define IEEE80211_HTINFO_OPMODE_PURE 0x00 /* no protection */
+#define IEEE80211_HTINFO_OPMODE_PROTOPT 0x01 /* protection optional */
+#define IEEE80211_HTINFO_OPMODE_HT20PR 0x02 /* protection for HT20 sta's */
+#define IEEE80211_HTINFO_OPMODE_MIXED 0x03 /* protection for legacy sta */
+#define IEEE80211_HTINFO_NONGF_PRESENT 0x04 /* non-GF sta's present */
+#define IEEE80211_HTINFO_TXBL 0x08 /* transmit burst limit */
+#define IEEE80211_HTINFO_NONHT_PRESENT 0x10 /* non-HT sta's present */
+/* bits 5-15 reserved */
+
+/* bytes 4+5 */
+#define IEEE80211_HTINFO_2NDARYBEACON 0x01
+#define IEEE80211_HTINFO_LSIGTXOPPROT 0x02
+#define IEEE80211_HTINFO_PCO_ACTIVE 0x04
+#define IEEE80211_HTINFO_40MHZPHASE 0x08
+
+/* byte5 */
+#define IEEE80211_HTINFO_BASIC_STBCMCS 0x7f
+#define IEEE80211_HTINFO_BASIC_STBCMCS_S 0
+#define IEEE80211_HTINFO_DUALPROTECTED 0x80
+
/*
* Length of management frame information elements containing
* a variable-length component is:
@@ -352,6 +737,7 @@ struct ieee80211_wme_param {
#define IEEE80211_CAPINFO_SHORT_PREAMBLE 0x0020
#define IEEE80211_CAPINFO_PBCC 0x0040
#define IEEE80211_CAPINFO_CHNL_AGILITY 0x0080
+#define IEEE80211_CAPINFO_SPECTRUM_MGMT 0x0100
/* bits 8-9 are reserved */
#define IEEE80211_CAPINFO_SHORT_SLOTTIME 0x0400
#define IEEE80211_CAPINFO_RSN 0x0800
@@ -374,9 +760,21 @@ enum {
IEEE80211_ELEMID_COUNTRY = 7,
IEEE80211_ELEMID_CHALLENGE = 16,
/* 17-31 reserved for challenge text extension */
+ IEEE80211_ELEMID_PWRCNSTR = 32,
+ IEEE80211_ELEMID_PWRCAP = 33,
+ IEEE80211_ELEMID_TPCREQ = 34,
+ IEEE80211_ELEMID_TPCREP = 35,
+ IEEE80211_ELEMID_SUPPCHAN = 36,
+ IEEE80211_ELEMID_CHANSWITCHANN = 37,
+ IEEE80211_ELEMID_MEASREQ = 38,
+ IEEE80211_ELEMID_MEASREP = 39,
+ IEEE80211_ELEMID_QUIET = 40,
+ IEEE80211_ELEMID_IBSSDFS = 41,
IEEE80211_ELEMID_ERP = 42,
+ IEEE80211_ELEMID_HTCAP = 45,
IEEE80211_ELEMID_RSN = 48,
IEEE80211_ELEMID_XRATES = 50,
+ IEEE80211_ELEMID_HTINFO = 61,
/* 128-129 proprietary elements used by Agere chipsets */
IEEE80211_ELEMID_AGERE1 = 128,
IEEE80211_ELEMID_AGERE2 = 129,
@@ -385,6 +783,10 @@ enum {
IEEE80211_ELEMID_VENDOR = 221 /* vendor private */
};
+#define BCM_OUI 0x4c9000 /* Broadcom OUI */
+#define BCM_OUI_HTCAP 51 /* pre-draft HTCAP ie */
+#define BCM_OUI_HTINFO 52 /* pre-draft HTINFO ie */
+
#define WPA_OUI 0xf25000
#define WPA_OUI_TYPE 0x01
#define WPA_VERSION 1 /* current supported version */
@@ -431,11 +833,20 @@ enum {
IEEE80211_REASON_NOT_ASSOCED = 7,
IEEE80211_REASON_ASSOC_LEAVE = 8,
IEEE80211_REASON_ASSOC_NOT_AUTHED = 9,
- IEEE80211_REASON_INVALID_POWER = 10,
- IEEE80211_REASON_RSN_REQUIRED = 11,
- IEEE80211_REASON_RSN_INCONSISTENT = 12,
- IEEE80211_REASON_IE_INVALID = 13,
- IEEE80211_REASON_MIC_FAILURE = 14
+ IEEE80211_REASON_DISASSOC_PWRCAP_BAD = 10, /* 11h */
+ IEEE80211_REASON_DISASSOC_SUPCHAN_BAD = 11, /* 11h */
+ IEEE80211_REASON_IE_INVALID = 13, /* 11i */
+ IEEE80211_REASON_MIC_FAILURE = 14, /* 11i */
+ IEEE80211_REASON_4WAY_HANDSHAKE_TIMEOUT = 15, /* 11i */
+ IEEE80211_REASON_GROUP_KEY_UPDATE_TIMEOUT = 16, /* 11i */
+ IEEE80211_REASON_IE_IN_4WAY_DIFFERS = 17, /* 11i */
+ IEEE80211_REASON_GROUP_CIPHER_INVALID = 18, /* 11i */
+ IEEE80211_REASON_PAIRWISE_CIPHER_INVALID = 19, /* 11i */
+ IEEE80211_REASON_AKMP_INVALID = 20, /* 11i */
+ IEEE80211_REASON_UNSUPP_RSN_IE_VERSION = 21, /* 11i */
+ IEEE80211_REASON_INVALID_RSN_IE_CAP = 22, /* 11i */
+ IEEE80211_REASON_802_1X_AUTH_FAILED = 23, /* 11i */
+ IEEE80211_REASON_CIPHER_SUITE_REJECTED = 24 /* 11i */
};
/*
@@ -455,13 +866,21 @@ enum {
IEEE80211_STATUS_TIMEOUT = 16,
IEEE80211_STATUS_TOOMANY = 17,
IEEE80211_STATUS_BASIC_RATE = 18,
- IEEE80211_STATUS_SP_REQUIRED = 19,
- IEEE80211_STATUS_PBCC_REQUIRED = 20,
- IEEE80211_STATUS_CA_REQUIRED = 21,
- IEEE80211_STATUS_TOO_MANY_STATIONS = 22,
- IEEE80211_STATUS_RATES = 23,
- IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25,
- IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26
+ IEEE80211_STATUS_SP_REQUIRED = 19, /* 11b */
+ IEEE80211_STATUS_PBCC_REQUIRED = 20, /* 11b */
+ IEEE80211_STATUS_CA_REQUIRED = 21, /* 11b */
+ IEEE80211_STATUS_SPECMGMT_REQUIRED = 22, /* 11h */
+ IEEE80211_STATUS_PWRCAP_REQUIRED = 23, /* 11h */
+ IEEE80211_STATUS_SUPCHAN_REQUIRED = 24, /* 11h */
+ IEEE80211_STATUS_SHORTSLOT_REQUIRED = 25, /* 11g */
+ IEEE80211_STATUS_DSSSOFDM_REQUIRED = 26, /* 11g */
+ IEEE80211_STATUS_INVALID_IE = 40, /* 11i */
+ IEEE80211_STATUS_GROUP_CIPHER_INVALID = 41, /* 11i */
+ IEEE80211_STATUS_PAIRWISE_CIPHER_INVALID = 42, /* 11i */
+ IEEE80211_STATUS_AKMP_INVALID = 43, /* 11i */
+ IEEE80211_STATUS_UNSUPP_RSN_IE_VERSION = 44, /* 11i */
+ IEEE80211_STATUS_INVALID_RSN_IE_CAP = 45, /* 11i */
+ IEEE80211_STATUS_CIPHER_SUITE_REJECTED = 46 /* 11i */
};
#define IEEE80211_WEP_KEYLEN 5 /* 40bit */
@@ -533,6 +952,8 @@ enum {
#define IEEE80211_F_DOFRATE 0x00000002 /* use fixed rate */
#define IEEE80211_F_DONEGO 0x00000004 /* calc negotiated rate */
#define IEEE80211_F_DODEL 0x00000008 /* delete ignore rate */
+#define IEEE80211_F_DOBRS 0x00000010 /* check basic rate set */
+#define IEEE80211_F_JOIN 0x00000020 /* sta joining our bss */
/*
* Beacon frames constructed by ieee80211_beacon_alloc
@@ -547,6 +968,7 @@ struct ieee80211_beacon_offsets {
uint16_t bo_tim_len; /* atim/dtim length in bytes */
uint16_t bo_trailer_len; /* trailer length in bytes */
uint8_t *bo_erp; /* start of ERP element */
+ uint8_t *bo_htinfo; /* start of HT info element */
};
#ifdef __cplusplus