diff options
author | Quaker Fang <Quaker.Fang@Sun.COM> | 2009-08-06 13:05:38 +0800 |
---|---|---|
committer | Quaker Fang <Quaker.Fang@Sun.COM> | 2009-08-06 13:05:38 +0800 |
commit | e2cf88ac9d753a00c17aa235f6afdc76574fe3a6 (patch) | |
tree | 894b2df5cf690a33664c4bcd17481540914b8698 /usr/src | |
parent | 26fd77009b17f8c8fb32eb362584cfd635e87ad9 (diff) | |
download | illumos-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')
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, ¶m, 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 |