changeset 8594:8ce911903616

6774031 iwk driver needs to support Ad-Hoc networking
author fei feng - Sun Microsystems - Beijing China <Fei.Feng@Sun.COM>
date Wed, 21 Jan 2009 16:12:41 +0800
parents 09567ba33f63
children ee750373956d
files usr/src/uts/common/io/iwk/iwk2.c usr/src/uts/common/io/iwk/iwk2_var.h usr/src/uts/common/io/net80211/net80211_input.c usr/src/uts/common/io/net80211/net80211_ioctl.c usr/src/uts/common/io/net80211/net80211_node.c usr/src/uts/common/io/net80211/net80211_output.c usr/src/uts/common/io/net80211/net80211_proto.c
diffstat 7 files changed, 965 insertions(+), 148 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/io/iwk/iwk2.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/iwk/iwk2.c	Wed Jan 21 16:12:41 2009 +0800
@@ -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.
  */
 
@@ -308,6 +308,8 @@
 static int	iwk_rx_sens(iwk_sc_t *sc);
 static int	iwk_cck_sens(iwk_sc_t *sc, uint32_t actual_rx_time);
 static int	iwk_ofdm_sens(iwk_sc_t *sc, uint32_t actual_rx_time);
+static void	iwk_recv_mgmt(struct ieee80211com *ic, mblk_t *mp,
+    struct ieee80211_node *in, int subtype, int rssi, uint32_t rstamp);
 
 static void	iwk_write_event_log(iwk_sc_t *);
 static void	iwk_write_error_log(iwk_sc_t *);
@@ -335,6 +337,11 @@
 static void	iwk_destroy_locks(iwk_sc_t *sc);
 static int	iwk_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type);
 static void	iwk_thread(iwk_sc_t *sc);
+static int	iwk_run_state_config_ibss(ieee80211com_t *ic);
+static int	iwk_run_state_config_sta(ieee80211com_t *ic);
+static int	iwk_start_tx_beacon(ieee80211com_t *ic);
+static int	iwk_clean_add_node_ibss(struct ieee80211com *ic,
+    uint8_t addr[IEEE80211_ADDR_LEN], uint8_t *index2);
 
 /*
  * Supported rates for 802.11b/g modes (in 500Kbps unit).
@@ -561,6 +568,8 @@
 	    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_ibss.node_tb_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);
@@ -651,6 +660,10 @@
 	 * Support WPA/WPA2
 	 */
 	ic->ic_caps |= IEEE80211_C_WPA;
+	/*
+	 * support Adhoc mode
+	 */
+	ic->ic_caps |= IEEE80211_C_IBSS;
 
 	/* set supported .11b and .11g rates */
 	ic->ic_sup_rates[IEEE80211_MODE_11B] = iwk_rateset_11b;
@@ -665,6 +678,7 @@
 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ |
 		    IEEE80211_CHAN_PASSIVE;
 	}
+	ic->ic_ibss_chan = &ic->ic_sup_channels[0];
 
 	ic->ic_xmit = iwk_send;
 	/*
@@ -685,6 +699,7 @@
 	sc->sc_newstate = ic->ic_newstate;
 	ic->ic_newstate = iwk_newstate;
 	sc->sc_recv_mgmt = ic->ic_recv_mgmt;
+	ic->ic_recv_mgmt = iwk_recv_mgmt;
 	ic->ic_node_alloc = iwk_node_alloc;
 	ic->ic_node_free = iwk_node_free;
 	ic->ic_crypto.cs_key_set = iwk_key_set;
@@ -727,7 +742,7 @@
 	IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr);
 
 	macp = mac_alloc(MAC_VERSION);
-	if (err != DDI_SUCCESS) {
+	if (macp == NULL) {
 		cmn_err(CE_WARN,
 		    "iwk_attach(): failed to do mac_alloc()\n");
 		goto attach_fail9;
@@ -1620,48 +1635,30 @@
 		}
 		IWK_DBG((IWK_DEBUG_80211, "iwk: associated."));
 
+		/* IBSS mode */
+		if (ic->ic_opmode == IEEE80211_M_IBSS) {
+			/*
+			 * clean all nodes in ibss node table
+			 * in order to be consistent with hardware
+			 */
+			err = iwk_run_state_config_ibss(ic);
+			if (err != IWK_SUCCESS) {
+				cmn_err(CE_WARN, "iwk_newstate(): "
+				    "failed to update configuration "
+				    "in IBSS mode\n");
+				mutex_exit(&sc->sc_glock);
+				return (err);
+			}
+		}
+
 		/* none IBSS mode */
 		if (ic->ic_opmode != IEEE80211_M_IBSS) {
 			/* 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;
-			/*
-			 * short preamble/slot time are
-			 * negotiated when associating
-			 */
-			sc->sc_config.flags &=
-			    ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK |
-			    RXON_FLG_SHORT_SLOT_MSK);
-
-			if (ic->ic_flags & IEEE80211_F_SHSLOT)
-				sc->sc_config.flags |=
-				    LE_32(RXON_FLG_SHORT_SLOT_MSK);
-
-			if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
-				sc->sc_config.flags |=
-				    LE_32(RXON_FLG_SHORT_PREAMBLE_MSK);
-
-			sc->sc_config.filter_flags |=
-			    LE_32(RXON_FILTER_ASSOC_MSK);
-
-			if (ic->ic_opmode != IEEE80211_M_STA)
-				sc->sc_config.filter_flags |=
-				    LE_32(RXON_FILTER_BCON_AWARE_MSK);
-
-			IWK_DBG((IWK_DEBUG_80211, "config chan %d flags %x"
-			    " filter_flags %x\n",
-			    sc->sc_config.chan, sc->sc_config.flags,
-			    sc->sc_config.filter_flags));
-			err = iwk_cmd(sc, REPLY_RXON, &sc->sc_config,
-			    sizeof (iwk_rxon_cmd_t), 1);
+			err = iwk_run_state_config_sta(ic);
 			if (err != IWK_SUCCESS) {
-				cmn_err(CE_WARN, "could not update "
-				    "configuration\n");
+				cmn_err(CE_WARN, "iwk_newstate(): "
+				    "failed to update configuration "
+				    "in none IBSS mode\n");
 				mutex_exit(&sc->sc_glock);
 				return (err);
 			}
@@ -1678,9 +1675,24 @@
 		if (err) {
 			cmn_err(CE_WARN, "iwk_newstate(): "
 			    "failed to set tx power table\n");
+			mutex_exit(&sc->sc_glock);
 			return (err);
 		}
 
+		if (ic->ic_opmode == IEEE80211_M_IBSS) {
+
+			/*
+			 * allocate and transmit beacon frames
+			 */
+			err = iwk_start_tx_beacon(ic);
+			if (err != IWK_SUCCESS) {
+				cmn_err(CE_WARN, "iwk_newstate(): "
+				    "can't transmit beacon frames\n");
+				mutex_exit(&sc->sc_glock);
+				return (err);
+			}
+		}
+
 		/* start automatic rate control */
 		mutex_enter(&sc->sc_mt_lock);
 		if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) {
@@ -1758,6 +1770,7 @@
 	iwk_sc_t *sc = (iwk_sc_t *)ic;
 	iwk_add_sta_t node;
 	int err;
+	uint8_t index1;
 
 	switch (k->wk_cipher->ic_cipher) {
 	case IEEE80211_CIPHER_WEP:
@@ -1778,6 +1791,66 @@
 	if (IEEE80211_IS_MULTICAST(mac)) {
 		(void) memset(node.bssid, 0xff, 6);
 		node.id = IWK_BROADCAST_ID;
+	} else if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		mutex_exit(&sc->sc_glock);
+		mutex_enter(&sc->sc_ibss.node_tb_lock);
+
+		/*
+		 * search for node in ibss node table
+		 */
+		for (index1 = IWK_STA_ID; index1 < IWK_STATION_COUNT;
+		    index1++) {
+			if (sc->sc_ibss.ibss_node_tb[index1].used &&
+			    IEEE80211_ADDR_EQ(sc->sc_ibss.
+			    ibss_node_tb[index1].node.bssid,
+			    mac)) {
+				break;
+			}
+		}
+		if (index1 >= IWK_BROADCAST_ID) {
+			cmn_err(CE_WARN, "iwk_key_set(): "
+			    "have no this node in hardware node table\n");
+			mutex_exit(&sc->sc_ibss.node_tb_lock);
+			return (0);
+		} else {
+			/*
+			 * configure key for given node in hardware
+			 */
+			if (k->wk_flags & IEEE80211_KEY_XMIT) {
+				sc->sc_ibss.ibss_node_tb[index1].
+				    node.key_flags = 0;
+				sc->sc_ibss.ibss_node_tb[index1].
+				    node.keyp = k->wk_keyix;
+			} else {
+				sc->sc_ibss.ibss_node_tb[index1].
+				    node.key_flags = (1 << 14);
+				sc->sc_ibss.ibss_node_tb[index1].
+				    node.keyp = k->wk_keyix + 4;
+			}
+
+			(void) memcpy(sc->sc_ibss.ibss_node_tb[index1].node.key,
+			    k->wk_key, k->wk_keylen);
+			sc->sc_ibss.ibss_node_tb[index1].node.key_flags |=
+			    (STA_KEY_FLG_CCMP | (1 << 3) | (k->wk_keyix << 8));
+			sc->sc_ibss.ibss_node_tb[index1].node.sta_mask =
+			    STA_MODIFY_KEY_MASK;
+			sc->sc_ibss.ibss_node_tb[index1].node.control = 1;
+
+			mutex_enter(&sc->sc_glock);
+			err = iwk_cmd(sc, REPLY_ADD_STA,
+			    &sc->sc_ibss.ibss_node_tb[index1].node,
+			    sizeof (iwk_add_sta_t), 1);
+			if (err != IWK_SUCCESS) {
+				cmn_err(CE_WARN, "iwk_key_set(): "
+				    "failed to update IBSS node in hardware\n");
+				mutex_exit(&sc->sc_glock);
+				mutex_exit(&sc->sc_ibss.node_tb_lock);
+				return (0);
+			}
+			mutex_exit(&sc->sc_glock);
+		}
+		mutex_exit(&sc->sc_ibss.node_tb_lock);
+		return (1);
 	} else {
 		IEEE80211_ADDR_COPY(node.bssid, ic->ic_bss->in_bssid);
 		node.id = IWK_AP_ID;
@@ -2497,6 +2570,7 @@
 	mblk_t *m, *m0;
 	int rate, hdrlen, len, len0, mblen, off, err = IWK_SUCCESS;
 	uint16_t masks = 0;
+	uint8_t index, index1, index2;
 
 	ring = &sc->sc_txq[0];
 	data = &ring->data[ring->cur];
@@ -2550,6 +2624,49 @@
 
 	wh = (struct ieee80211_frame *)m->b_rptr;
 
+	if (ic->ic_opmode == IEEE80211_M_IBSS &&
+	    (!(IEEE80211_IS_MULTICAST(wh->i_addr1)))) {
+		mutex_enter(&sc->sc_glock);
+		mutex_enter(&sc->sc_ibss.node_tb_lock);
+
+		/*
+		 * search for node in ibss node table
+		 */
+		for (index1 = IWK_STA_ID;
+		    index1 < IWK_STATION_COUNT; index1++) {
+			if (sc->sc_ibss.ibss_node_tb[index1].used &&
+			    IEEE80211_ADDR_EQ(sc->sc_ibss.
+			    ibss_node_tb[index1].node.bssid,
+			    wh->i_addr1)) {
+				break;
+			}
+		}
+
+		/*
+		 * if don't find in ibss node table
+		 */
+		if (index1 >= IWK_BROADCAST_ID) {
+			err = iwk_clean_add_node_ibss(ic,
+			    wh->i_addr1, &index2);
+			if (err != IWK_SUCCESS) {
+				cmn_err(CE_WARN, "iwk_send(): "
+				    "failed to clean all nodes "
+				    "and add one node\n");
+				mutex_exit(&sc->sc_ibss.node_tb_lock);
+				mutex_exit(&sc->sc_glock);
+				freemsg(m);
+				sc->sc_tx_err++;
+				err = IWK_SUCCESS;
+				goto exit;
+			}
+			index = index2;
+		} else {
+			index = index1;
+		}
+		mutex_exit(&sc->sc_ibss.node_tb_lock);
+		mutex_exit(&sc->sc_glock);
+	}
+
 	in = ieee80211_find_txnode(ic, wh->i_addr1);
 	if (in == NULL) {
 		cmn_err(CE_WARN, "iwk_send(): failed to find tx node\n");
@@ -2640,7 +2757,9 @@
 	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
 		tx->sta_id = IWK_BROADCAST_ID;
 	} else {
-		if (ic->ic_opmode != IEEE80211_M_IBSS)
+		if (ic->ic_opmode == IEEE80211_M_IBSS)
+			tx->sta_id = index;
+		else
 			tx->sta_id = IWK_AP_ID;
 	}
 
@@ -2740,7 +2859,15 @@
 {
 	iwk_sc_t	*sc  = (iwk_sc_t *)arg;
 	ieee80211com_t	*ic = &sc->sc_ic;
-	int		err;
+
+	enum ieee80211_opmode		oldmod;
+	iwk_tx_power_table_cmd_t	txpower;
+	iwk_add_sta_t			node;
+	iwk_link_quality_cmd_t		link_quality;
+	uint16_t			masks = 0;
+	int				i, err, err1;
+
+	oldmod = ic->ic_opmode;
 
 	mutex_enter(&sc->sc_glock);
 	if (sc->sc_flags & (IWK_F_SUSPEND | IWK_F_HW_ERR_RECOVER)) {
@@ -2752,6 +2879,125 @@
 
 	err = ieee80211_ioctl(ic, wq, mp);
 
+	/*
+	 * return to STA mode
+	 */
+	if ((0 == err || ENETRESET == err) && (oldmod != ic->ic_opmode) &&
+	    (ic->ic_opmode == IEEE80211_M_STA)) {
+		/* configure rxon */
+		(void) memset(&sc->sc_config, 0, sizeof (iwk_rxon_cmd_t));
+		IEEE80211_ADDR_COPY(sc->sc_config.node_addr, ic->ic_macaddr);
+		IEEE80211_ADDR_COPY(sc->sc_config.wlap_bssid, ic->ic_macaddr);
+		sc->sc_config.chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
+		sc->sc_config.flags = (RXON_FLG_TSF2HOST_MSK |
+		    RXON_FLG_AUTO_DETECT_MSK |
+		    RXON_FLG_BAND_24G_MSK);
+		sc->sc_config.flags &= (~RXON_FLG_CCK_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_ACCEPT_GRP_MSK |
+			    RXON_FILTER_DIS_DECRYPT_MSK |
+			    RXON_FILTER_DIS_GRP_DECRYPT_MSK);
+			break;
+		case IEEE80211_M_IBSS:
+		case IEEE80211_M_AHDEMO:
+			sc->sc_config.dev_type = RXON_DEV_TYPE_IBSS;
+			sc->sc_config.flags |= RXON_FLG_SHORT_PREAMBLE_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;
+		case IEEE80211_M_HOSTAP:
+			sc->sc_config.dev_type = RXON_DEV_TYPE_AP;
+			break;
+		case IEEE80211_M_MONITOR:
+			sc->sc_config.dev_type = RXON_DEV_TYPE_SNIFFER;
+			sc->sc_config.filter_flags |=
+			    LE_32(RXON_FILTER_ACCEPT_GRP_MSK |
+			    RXON_FILTER_CTL2HOST_MSK |
+			    RXON_FILTER_PROMISC_MSK);
+			break;
+		}
+		sc->sc_config.cck_basic_rates  = 0x0f;
+		sc->sc_config.ofdm_basic_rates = 0xff;
+		sc->sc_config.ofdm_ht_single_stream_basic_rates = 0xff;
+		sc->sc_config.ofdm_ht_dual_stream_basic_rates = 0xff;
+		/* set antenna */
+		mutex_enter(&sc->sc_glock);
+		sc->sc_config.rx_chain = RXON_RX_CHAIN_DRIVER_FORCE_MSK |
+		    LE_16((0x7 << RXON_RX_CHAIN_VALID_POS) |
+		    (0x6 << RXON_RX_CHAIN_FORCE_SEL_POS) |
+		    (0x7 << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS));
+		err1 = iwk_cmd(sc, REPLY_RXON, &sc->sc_config,
+		    sizeof (iwk_rxon_cmd_t), 1);
+		if (err1 != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_m_ioctl(): "
+			    "failed to set configure command"
+			    " please run (ifconfig unplumb and"
+			    " ifconfig plumb)\n");
+		}
+		/*
+		 * set Tx power for 2.4GHz channels
+		 * (need further investigation. fix tx power at present)
+		 */
+		(void) memset(&txpower, 0, sizeof (txpower));
+		txpower.band = 1; /* for 2.4G */
+		txpower.channel = sc->sc_config.chan;
+		txpower.channel_normal_width = 0;
+		for (i = 0; i < POWER_TABLE_NUM_HT_OFDM_ENTRIES; i++) {
+			txpower.tx_power.ht_ofdm_power[i].
+			    s.ramon_tx_gain = 0x3f3f;
+			txpower.tx_power.ht_ofdm_power[i].
+			    s.dsp_predis_atten = 110 | (110 << 8);
+		}
+		txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES].
+		    s.ramon_tx_gain = 0x3f3f;
+		txpower.tx_power.ht_ofdm_power[POWER_TABLE_NUM_HT_OFDM_ENTRIES].
+		    s.dsp_predis_atten = 110 | (110 << 8);
+		err1 = iwk_cmd(sc, REPLY_TX_PWR_TABLE_CMD, &txpower,
+		    sizeof (txpower), 1);
+		if (err1 != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_m_ioctl(): failed to set txpower"
+			    " please run (ifconfig unplumb "
+			    "and ifconfig plumb)\n");
+		}
+		/* add broadcast node so that we can send broadcast frame */
+		(void) memset(&node, 0, sizeof (node));
+		(void) memset(node.bssid, 0xff, 6);
+		node.id = IWK_BROADCAST_ID;
+		err1 = iwk_cmd(sc, REPLY_ADD_STA, &node, sizeof (node), 1);
+		if (err1 != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_m_ioctl(): "
+			    "failed to add broadcast node\n");
+		}
+
+		/* TX_LINK_QUALITY cmd */
+		(void) memset(&link_quality, 0, sizeof (link_quality));
+		for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+			masks |= RATE_MCS_CCK_MSK;
+			masks |= RATE_MCS_ANT_B_MSK;
+			masks &= ~RATE_MCS_ANT_A_MSK;
+			link_quality.rate_n_flags[i] =
+			    iwk_rate_to_plcp(2) | 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 = IWK_BROADCAST_ID;
+		err1 = iwk_cmd(sc, REPLY_TX_LINK_QUALITY_CMD, &link_quality,
+		    sizeof (link_quality), 1);
+		if (err1 != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_m_ioctl(): "
+			    "failed to config link quality table\n");
+		}
+		mutex_exit(&sc->sc_glock);
+		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+	}
+
 	if (err == ENETRESET) {
 		/*
 		 * This is special for the hidden AP connection.
@@ -3252,7 +3498,7 @@
 		return (err);
 	}
 
-	/* TX_LINK_QUALITY cmd ? */
+	/* TX_LINK_QUALITY cmd */
 	(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++) {
@@ -3505,6 +3751,7 @@
 		    RXON_FILTER_DIS_DECRYPT_MSK |
 		    RXON_FILTER_DIS_GRP_DECRYPT_MSK);
 		break;
+	case IEEE80211_M_IBSS:
 	case IEEE80211_M_AHDEMO:
 		sc->sc_config.dev_type = RXON_DEV_TYPE_IBSS;
 		sc->sc_config.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
@@ -5488,6 +5735,79 @@
 }
 
 /*
+ * additional process to management frames
+ */
+static void iwk_recv_mgmt(struct ieee80211com *ic, mblk_t *mp,
+    struct ieee80211_node *in,
+    int subtype, int rssi, uint32_t rstamp)
+{
+	iwk_sc_t *sc = (iwk_sc_t *)ic;
+	struct ieee80211_frame *wh;
+	uint8_t index1, index2;
+	int err;
+
+	sc->sc_recv_mgmt(ic, mp, in, subtype, rssi, rstamp);
+
+	mutex_enter(&sc->sc_glock);
+	switch (subtype) {
+	case IEEE80211_FC0_SUBTYPE_BEACON:
+		if (sc->sc_ibss.ibss_beacon.syncbeacon && in == ic->ic_bss &&
+		    ic->ic_state == IEEE80211_S_RUN) {
+			if (ieee80211_beacon_update(ic, in,
+			    &sc->sc_ibss.ibss_beacon.iwk_boff,
+			    sc->sc_ibss.ibss_beacon.mp, 0)) {
+				bcopy(sc->sc_ibss.ibss_beacon.mp->b_rptr,
+				    sc->sc_ibss.ibss_beacon.beacon_cmd.
+				    bcon_frame,
+				    MBLKL(sc->sc_ibss.ibss_beacon.mp));
+			}
+			err = iwk_cmd(sc, REPLY_TX_BEACON,
+			    &sc->sc_ibss.ibss_beacon.beacon_cmd,
+			    sc->sc_ibss.ibss_beacon.beacon_cmd_len, 1);
+			if (err != IWK_SUCCESS) {
+				cmn_err(CE_WARN, "iwk_recv_mgmt(): "
+				    "failed to TX beacon.\n");
+			}
+			sc->sc_ibss.ibss_beacon.syncbeacon = 0;
+		}
+		if (ic->ic_opmode == IEEE80211_M_IBSS &&
+		    ic->ic_state == IEEE80211_S_RUN) {
+			wh = (struct ieee80211_frame *)mp->b_rptr;
+			mutex_enter(&sc->sc_ibss.node_tb_lock);
+			/*
+			 * search for node in ibss node table
+			 */
+			for (index1 = IWK_STA_ID; index1 < IWK_STATION_COUNT;
+			    index1++) {
+				if (sc->sc_ibss.ibss_node_tb[index1].used &&
+				    IEEE80211_ADDR_EQ(sc->sc_ibss.
+				    ibss_node_tb[index1].node.bssid,
+				    wh->i_addr2)) {
+					break;
+				}
+			}
+			/*
+			 * if don't find in ibss node table
+			 */
+			if (index1 >= IWK_BROADCAST_ID) {
+				err = iwk_clean_add_node_ibss(ic,
+				    wh->i_addr2, &index2);
+				if (err != IWK_SUCCESS) {
+					cmn_err(CE_WARN, "iwk_recv_mgmt(): "
+					    "failed to clean all nodes "
+					    "and add one node\n");
+				}
+			}
+			mutex_exit(&sc->sc_ibss.node_tb_lock);
+		}
+		break;
+	case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+		break;
+	}
+	mutex_exit(&sc->sc_glock);
+}
+
+/*
  * 1)  log_event_table_ptr indicates base of the event log.  This traces
  *     a 256-entry history of uCode execution within a circular buffer.
  *     Its header format is:
@@ -5673,3 +5993,405 @@
 
 	iwk_mac_access_exit(sc);
 }
+
+static int
+iwk_run_state_config_ibss(ieee80211com_t *ic)
+{
+	iwk_sc_t *sc = (iwk_sc_t *)ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	int i, err = IWK_SUCCESS;
+
+	mutex_enter(&sc->sc_ibss.node_tb_lock);
+
+	/*
+	 * clean all nodes in ibss node table assure be
+	 * consistent with hardware
+	 */
+	for (i = IWK_STA_ID; i < IWK_STATION_COUNT; i++) {
+		sc->sc_ibss.ibss_node_tb[i].used = 0;
+		(void) memset(&sc->sc_ibss.ibss_node_tb[i].node,
+		    0,
+		    sizeof (iwk_add_sta_t));
+	}
+
+	sc->sc_ibss.node_number = 0;
+
+	mutex_exit(&sc->sc_ibss.node_tb_lock);
+
+	/*
+	 * configure RX and TX
+	 */
+	sc->sc_config.dev_type = RXON_DEV_TYPE_IBSS;
+
+	sc->sc_config.flags |= RXON_FLG_SHORT_PREAMBLE_MSK;
+	sc->sc_config.filter_flags =
+	    LE_32(RXON_FILTER_ACCEPT_GRP_MSK |
+	    RXON_FILTER_DIS_DECRYPT_MSK |
+	    RXON_FILTER_DIS_GRP_DECRYPT_MSK);
+
+	sc->sc_config.assoc_id = 0;
+
+	IEEE80211_ADDR_COPY(sc->sc_config.bssid, in->in_bssid);
+	sc->sc_config.chan = ieee80211_chan2ieee(ic,
+	    in->in_chan);
+
+	if (ic->ic_curmode == IEEE80211_MODE_11B) {
+		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 {
+		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 |
+	    RXON_FLG_SHORT_SLOT_MSK);
+
+	if (ic->ic_flags & IEEE80211_F_SHSLOT) {
+		sc->sc_config.flags |=
+		    LE_32(RXON_FLG_SHORT_SLOT_MSK);
+	}
+
+	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) {
+		sc->sc_config.flags |=
+		    LE_32(RXON_FLG_SHORT_PREAMBLE_MSK);
+	}
+
+	sc->sc_config.filter_flags |=
+	    LE_32(RXON_FILTER_ASSOC_MSK);
+
+	err = iwk_cmd(sc, REPLY_RXON, &sc->sc_config,
+	    sizeof (iwk_rxon_cmd_t), 1);
+	if (err != IWK_SUCCESS) {
+		cmn_err(CE_WARN, "iwk_run_state_config_ibss(): "
+		    "failed to update configuration.\n");
+		return (err);
+	}
+
+	return (err);
+
+}
+
+static int
+iwk_run_state_config_sta(ieee80211com_t *ic)
+{
+	iwk_sc_t *sc = (iwk_sc_t *)ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	int err = IWK_SUCCESS;
+
+	/* update adapter's configuration */
+	if (sc->sc_assoc_id != in->in_associd) {
+		cmn_err(CE_WARN, "iwk_run_state_config_sta(): "
+		    "associate ID mismatch: expected %d, "
+		    "got %d\n",
+		    in->in_associd, sc->sc_assoc_id);
+	}
+	sc->sc_config.assoc_id = in->in_associd & 0x3fff;
+
+	/*
+	 * short preamble/slot time are
+	 * negotiated when associating
+	 */
+	sc->sc_config.flags &=
+	    ~LE_32(RXON_FLG_SHORT_PREAMBLE_MSK |
+	    RXON_FLG_SHORT_SLOT_MSK);
+
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		sc->sc_config.flags |=
+		    LE_32(RXON_FLG_SHORT_SLOT_MSK);
+
+	if (ic->ic_flags & IEEE80211_F_SHPREAMBLE)
+		sc->sc_config.flags |=
+		    LE_32(RXON_FLG_SHORT_PREAMBLE_MSK);
+
+	sc->sc_config.filter_flags |=
+	    LE_32(RXON_FILTER_ASSOC_MSK);
+
+	if (ic->ic_opmode != IEEE80211_M_STA)
+		sc->sc_config.filter_flags |=
+		    LE_32(RXON_FILTER_BCON_AWARE_MSK);
+
+	IWK_DBG((IWK_DEBUG_80211, "config chan %d flags %x"
+	    " filter_flags %x\n",
+	    sc->sc_config.chan, sc->sc_config.flags,
+	    sc->sc_config.filter_flags));
+
+	err = iwk_cmd(sc, REPLY_RXON, &sc->sc_config,
+	    sizeof (iwk_rxon_cmd_t), 1);
+	if (err != IWK_SUCCESS) {
+		cmn_err(CE_WARN, "iwk_run_state_config_sta(): "
+		    "failed to update configuration\n");
+		return (err);
+	}
+
+	return (err);
+}
+
+static int
+iwk_start_tx_beacon(ieee80211com_t *ic)
+{
+	iwk_sc_t *sc = (iwk_sc_t *)ic;
+	ieee80211_node_t *in = ic->ic_bss;
+	int err = IWK_SUCCESS;
+	iwk_tx_beacon_cmd_t  *tx_beacon_p;
+	uint16_t  masks = 0;
+	mblk_t *mp;
+	int rate;
+
+	/*
+	 * allocate and transmit beacon frames
+	 */
+	tx_beacon_p = &sc->sc_ibss.ibss_beacon.beacon_cmd;
+
+	(void) memset(tx_beacon_p, 0,
+	    sizeof (iwk_tx_beacon_cmd_t));
+	rate = 0;
+	masks = 0;
+
+	tx_beacon_p->config.sta_id = IWK_BROADCAST_ID;
+	tx_beacon_p->config.stop_time.life_time =
+	    LE_32(0xffffffff);
+
+	if (sc->sc_ibss.ibss_beacon.mp != NULL) {
+		freemsg(sc->sc_ibss.ibss_beacon.mp);
+		sc->sc_ibss.ibss_beacon.mp = NULL;
+	}
+
+	sc->sc_ibss.ibss_beacon.mp =
+	    ieee80211_beacon_alloc(ic, in,
+	    &sc->sc_ibss.ibss_beacon.iwk_boff);
+	if (sc->sc_ibss.ibss_beacon.mp == NULL) {
+		cmn_err(CE_WARN, "iwk_start_tx_beacon(): "
+		    "failed to get beacon frame.\n");
+		return (IWK_FAIL);
+	}
+
+	mp = sc->sc_ibss.ibss_beacon.mp;
+
+	ASSERT(mp->b_cont == NULL);
+
+	bcopy(mp->b_rptr, tx_beacon_p->bcon_frame, MBLKL(mp));
+
+	tx_beacon_p->config.len = (uint16_t)(MBLKL(mp));
+	sc->sc_ibss.ibss_beacon.beacon_cmd_len =
+	    sizeof (iwk_tx_cmd_t) +
+	    4 + tx_beacon_p->config.len;
+
+	/*
+	 * beacons are sent at 1M
+	 */
+	rate = in->in_rates.ir_rates[0];
+	rate &= IEEE80211_RATE_VAL;
+
+	if (2 == rate || 4 == rate || 11 == rate ||
+	    22 == rate) {
+		masks |= RATE_MCS_CCK_MSK;
+	}
+
+	masks |= RATE_MCS_ANT_B_MSK;
+
+	tx_beacon_p->config.rate.r.rate_n_flags =
+	    (iwk_rate_to_plcp(rate) | masks);
+
+
+	tx_beacon_p->config.tx_flags =
+	    (TX_CMD_FLG_SEQ_CTL_MSK | TX_CMD_FLG_TSF_MSK);
+
+	if (ic->ic_bss->in_tstamp.tsf != 0) {
+		sc->sc_ibss.ibss_beacon.syncbeacon = 1;
+	} else {
+		if (ieee80211_beacon_update(ic, in,
+		    &sc->sc_ibss.ibss_beacon.iwk_boff,
+		    mp, 0)) {
+			bcopy(mp->b_rptr,
+			    tx_beacon_p->bcon_frame,
+			    MBLKL(mp));
+		}
+
+		err = iwk_cmd(sc, REPLY_TX_BEACON,
+		    tx_beacon_p,
+		    sc->sc_ibss.ibss_beacon.beacon_cmd_len,
+		    1);
+		if (err != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_start_tx_beacon(): "
+			    "failed to TX beacon.\n");
+			return (err);
+		}
+
+		sc->sc_ibss.ibss_beacon.syncbeacon = 0;
+	}
+
+	return (err);
+}
+
+static int
+iwk_clean_add_node_ibss(struct ieee80211com *ic,
+    uint8_t addr[IEEE80211_ADDR_LEN], uint8_t *index2)
+{
+	iwk_sc_t *sc = (iwk_sc_t *)ic;
+	uint8_t	index;
+	iwk_add_sta_t bc_node;
+	iwk_link_quality_cmd_t bc_link_quality;
+	iwk_link_quality_cmd_t link_quality;
+	uint16_t  bc_masks = 0;
+	uint16_t  masks = 0;
+	int i, rate;
+	struct ieee80211_rateset rs;
+	iwk_ibss_node_t *ibss_node_p;
+	int err = IWK_SUCCESS;
+
+	/*
+	 * find a location that is not
+	 * used in ibss node table
+	 */
+	for (index = IWK_STA_ID;
+	    index < IWK_STATION_COUNT; index++) {
+		if (!sc->sc_ibss.ibss_node_tb[index].used) {
+			break;
+		}
+	}
+
+	/*
+	 * if have too many nodes in hardware, clean up
+	 */
+	if (index < IWK_BROADCAST_ID &&
+	    sc->sc_ibss.node_number >= 25) {
+		if (iwk_cmd(sc, REPLY_REMOVE_ALL_STA,
+		    NULL, 0, 1) != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+			    "failed to remove all nodes in hardware\n");
+			return (IWK_FAIL);
+		}
+
+		for (i = IWK_STA_ID; i < IWK_STATION_COUNT; i++) {
+			sc->sc_ibss.ibss_node_tb[i].used = 0;
+			(void) memset(&sc->sc_ibss.ibss_node_tb[i].node,
+			    0, sizeof (iwk_add_sta_t));
+		}
+
+		sc->sc_ibss.node_number = 0;
+
+		/*
+		 * add broadcast node so that we
+		 * can send broadcast frame
+		 */
+		(void) memset(&bc_node, 0, sizeof (bc_node));
+		(void) memset(bc_node.bssid, 0xff, 6);
+		bc_node.id = IWK_BROADCAST_ID;
+
+		err = iwk_cmd(sc, REPLY_ADD_STA, &bc_node, sizeof (bc_node), 1);
+		if (err != IWK_SUCCESS) {
+		cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+		    "failed to add broadcast node\n");
+		return (err);
+		}
+
+		/* TX_LINK_QUALITY cmd */
+		(void) memset(&bc_link_quality, 0, sizeof (bc_link_quality));
+		for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
+			bc_masks |= RATE_MCS_CCK_MSK;
+			bc_masks |= RATE_MCS_ANT_B_MSK;
+			bc_masks &= ~RATE_MCS_ANT_A_MSK;
+			bc_link_quality.rate_n_flags[i] =
+			    iwk_rate_to_plcp(2) | bc_masks;
+		}
+
+		bc_link_quality.general_params.single_stream_ant_msk = 2;
+		bc_link_quality.general_params.dual_stream_ant_msk = 3;
+		bc_link_quality.agg_params.agg_dis_start_th = 3;
+		bc_link_quality.agg_params.agg_time_limit = LE_16(4000);
+		bc_link_quality.sta_id = IWK_BROADCAST_ID;
+
+		err = iwk_cmd(sc, REPLY_TX_LINK_QUALITY_CMD,
+		    &bc_link_quality, sizeof (bc_link_quality), 1);
+		if (err != IWK_SUCCESS) {
+			cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+			    "failed to config link quality table\n");
+			return (err);
+		}
+	}
+
+	if (index >= IWK_BROADCAST_ID) {
+		cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+		    "the count of node in hardware is too much\n");
+		return (IWK_FAIL);
+	}
+
+	/*
+	 * add a node into hardware
+	 */
+	ibss_node_p = &sc->sc_ibss.ibss_node_tb[index];
+
+	ibss_node_p->used = 1;
+
+	(void) memset(&ibss_node_p->node, 0,
+	    sizeof (iwk_add_sta_t));
+
+	IEEE80211_ADDR_COPY(ibss_node_p->node.bssid, addr);
+	ibss_node_p->node.id = index;
+	ibss_node_p->node.control = 0;
+	ibss_node_p->node.flags = 0;
+
+	err = iwk_cmd(sc, REPLY_ADD_STA, &ibss_node_p->node,
+	    sizeof (iwk_add_sta_t), 1);
+	if (err != IWK_SUCCESS) {
+		cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+		    "failed to add IBSS node\n");
+		ibss_node_p->used = 0;
+		(void) memset(&ibss_node_p->node, 0,
+		    sizeof (iwk_add_sta_t));
+		return (err);
+	}
+
+	sc->sc_ibss.node_number++;
+
+	(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] =
+		    iwk_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 = ibss_node_p->node.id;
+
+	err = iwk_cmd(sc, REPLY_TX_LINK_QUALITY_CMD,
+	    &link_quality, sizeof (link_quality), 1);
+	if (err != IWK_SUCCESS) {
+		cmn_err(CE_WARN, "iwk_clean_add_node_ibss(): "
+		    "failed to set up TX link quality\n");
+		ibss_node_p->used = 0;
+		(void) memset(ibss_node_p->node.bssid, 0, 6);
+		return (err);
+	}
+
+	*index2 = index;
+
+	return (err);
+}
--- a/usr/src/uts/common/io/iwk/iwk2_var.h	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/iwk/iwk2_var.h	Wed Jan 21 16:12:41 2009 +0800
@@ -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.
  */
 
@@ -90,6 +90,28 @@
 	int	recovery;
 } iwk_amrr_t;
 
+typedef struct iwk_ibss_node {
+	iwk_add_sta_t	node;
+	int8_t		used;
+} iwk_ibss_node_t;
+
+typedef struct iwk_ibss_beacon {
+	/* for update beacon frame dynamically */
+	struct			ieee80211_beacon_offsets iwk_boff;
+	uint32_t		beacon_cmd_len;
+	iwk_tx_beacon_cmd_t	beacon_cmd;
+	uint8_t			syncbeacon;
+	/* beacon frame allocated from net80211 module */
+	mblk_t			*mp;
+} iwk_ibss_beacon_t;
+
+typedef struct iwk_ibss {
+	iwk_ibss_node_t		ibss_node_tb[IWK_STATION_COUNT];
+	uint32_t		node_number;
+	kmutex_t		node_tb_lock;
+	iwk_ibss_beacon_t	ibss_beacon;
+} iwk_ibss_t;
+
 typedef struct iwk_softc {
 	struct ieee80211com	sc_ic;
 	dev_info_t		*sc_dip;
@@ -174,6 +196,7 @@
 	uint32_t		sc_tx_err;
 	uint32_t		sc_rx_err;
 	uint32_t		sc_tx_retries;
+	iwk_ibss_t		sc_ibss;
 } iwk_sc_t;
 
 #define	IWK_F_ATTACHED		(1 << 0)
--- a/usr/src/uts/common/io/net80211/net80211_input.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/net80211/net80211_input.c	Wed Jan 21 16:12:41 2009 +0800
@@ -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
@@ -73,6 +73,7 @@
 	uint8_t tid;
 
 	ASSERT(in != NULL);
+	in->in_inact = in->in_inact_reload;
 	type = (uint8_t)-1;		/* undefined */
 	len = MBLKL(mp);
 	if (len < sizeof (struct ieee80211_frame_min)) {
@@ -179,7 +180,6 @@
 			}
 			in->in_rxseqs[tid] = rxseq;
 		}
-		in->in_inact = 0;
 	}
 
 	switch (type) {
@@ -879,24 +879,21 @@
 		return;
 	}
 
-	if (scan.capinfo & IEEE80211_CAPINFO_IBSS) {
+	if (ic->ic_opmode == IEEE80211_M_IBSS &&
+	    scan.capinfo & IEEE80211_CAPINFO_IBSS) {
 		if (!IEEE80211_ADDR_EQ(wh->i_addr2, in->in_macaddr)) {
 			/*
 			 * Create a new entry in the neighbor table.
 			 */
 			in = ieee80211_add_neighbor(ic, wh, &scan);
-		} else if (in->in_capinfo == 0) {
+		} else {
 			/*
-			 * Update faked node created on transmit.
-			 * Note this also updates the tsf.
+			 * Copy data from beacon to neighbor table.
+			 * Some of this information might change after
+			 * ieee80211_add_neighbor(), so we just copy
+			 * everything over to be safe.
 			 */
 			ieee80211_init_neighbor(in, wh, &scan);
-		} else {
-			/*
-			 * Record tsf for potential resync.
-			 */
-			bcopy(scan.tstamp, in->in_tstamp.data,
-			    sizeof (in->in_tstamp));
 		}
 		if (in != NULL) {
 			in->in_rssi = (uint8_t)rssi;
@@ -969,14 +966,20 @@
 			frm += frm[1] + 2;
 		}
 		IEEE80211_VERIFY_ELEMENT(rates, IEEE80211_RATE_MAXSIZE, break);
+		if (xrates != NULL) {
+			IEEE80211_VERIFY_ELEMENT(xrates,
+			    IEEE80211_RATE_MAXSIZE - rates[1], break);
+		}
 		IEEE80211_VERIFY_ELEMENT(ssid, IEEE80211_NWID_LEN, break);
 		IEEE80211_VERIFY_SSID(ic->ic_bss, ssid, break);
-		if ((ic->ic_flags & IEEE80211_F_HIDESSID) && ssid[1] == 0) {
-			ieee80211_dbg(IEEE80211_MSG_INPUT,
-			    "ieee80211_recv_mgmt: ignore %s, "
-			    "no ssid with ssid suppression enabled",
-			    IEEE80211_SUBTYPE_NAME(subtype));
-			break;
+		if (ic->ic_flags & IEEE80211_F_HIDESSID) {
+			if (ssid == NULL || ssid[1] == 0) {
+				ieee80211_dbg(IEEE80211_MSG_INPUT,
+				    "ieee80211_recv_mgmt: ignore %s, "
+				    "no ssid with ssid suppression enabled",
+				    IEEE80211_SUBTYPE_NAME(subtype));
+				break;
+			}
 		}
 
 		if (in == ic->ic_bss) {
--- a/usr/src/uts/common/io/net80211/net80211_ioctl.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/net80211/net80211_ioctl.c	Wed Jan 21 16:12:41 2009 +0800
@@ -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.
  */
 
@@ -462,6 +462,9 @@
 		wl_get_bsstype(ic, ow_opmode);
 		break;
 	case  WLAN_SET_PARAM:
+		if (*iw_opmode == ic->ic_opmode)
+			break;
+
 		err = wl_set_bsstype(ic, iw_opmode);
 		break;
 	default:
@@ -477,6 +480,64 @@
 }
 
 static int
+wifi_cfg_createibss(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
+{
+	mblk_t *omp;
+	wldp_t *inp = (wldp_t *)(*mp)->b_rptr;
+	wldp_t *outp;
+	wl_create_ibss_t *iw_ibss = (wl_create_ibss_t *)inp->wldp_buf;
+	wl_create_ibss_t *ow_ibss;
+	int err = 0;
+
+	if ((omp = wifi_getoutmsg(*mp, cmd, sizeof (wl_create_ibss_t))) == NULL)
+		return (ENOMEM);
+	outp = (wldp_t *)omp->b_rptr;
+	ow_ibss = (wl_create_ibss_t *)outp->wldp_buf;
+
+	switch (cmd) {
+	case WLAN_GET_PARAM:
+		*ow_ibss = (ic->ic_flags & IEEE80211_F_IBSSON) ? 1 : 0;
+		break;
+	case  WLAN_SET_PARAM:
+		ieee80211_dbg(IEEE80211_MSG_CONFIG, "wifi_cfg_createibss: "
+		    "set createibss=%u\n", *iw_ibss);
+		if ((ic->ic_caps & IEEE80211_C_IBSS) == 0) {
+			outp->wldp_result = WL_LACK_FEATURE;
+			err = ENOTSUP;
+			break;
+		}
+		if (*iw_ibss) { /* create ibss */
+			if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
+				ic->ic_flags |= IEEE80211_F_IBSSON;
+				ic->ic_opmode = IEEE80211_M_IBSS;
+				/*
+				 * Yech, slot time may change depending on the
+				 * operating mode so reset it to be sure
+				 * everything is setup appropriately.
+				 */
+				ieee80211_reset_erp(ic);
+				err = ENETRESET;
+			}
+		} else {
+			if (ic->ic_flags & IEEE80211_F_IBSSON) {
+				ic->ic_flags &= ~IEEE80211_F_IBSSON;
+				err = ENETRESET;
+			}
+		}
+		break;
+	default:
+		ieee80211_err("wifi_cfg_bsstype: unknown command %x\n", cmd);
+		outp->wldp_result = WL_NOTSUPPORTED;
+		err = EINVAL;
+		break;
+	}
+
+	freemsg(*mp);
+	*mp = omp;
+	return (err);
+}
+
+static int
 wifi_cfg_linkstatus(struct ieee80211com *ic, uint32_t cmd, mblk_t **mp)
 {
 	mblk_t *omp;
@@ -682,6 +743,8 @@
 	if ((uint8_t *)conf > end)
 		return;
 
+	conf->wl_ess_conf_length = sizeof (struct wl_ess_conf);
+
 	/* skip newly allocated NULL bss node */
 	if (IEEE80211_ADDR_EQ(in->in_macaddr, ic->ic_macaddr))
 		return;
@@ -875,6 +938,8 @@
 		bzero(ic->ic_nw_keys[i].wk_key, IEEE80211_KEYBUF_SIZE);
 	}
 	ic->ic_curmode = IEEE80211_MODE_AUTO;
+	ic->ic_flags &= ~IEEE80211_F_IBSSON;
+	ic->ic_opmode = IEEE80211_M_STA;
 }
 
 static int
@@ -1244,6 +1309,9 @@
 	case WL_BSS_TYPE:
 		err = wifi_cfg_bsstype(ic, cmd, mp);
 		break;
+	case WL_CREATE_IBSS:
+		err = wifi_cfg_createibss(ic, cmd, mp);
+		break;
 	case WL_DESIRED_RATES:
 		err = wifi_cfg_desrates(ic, cmd, mp);
 		break;
@@ -1492,11 +1560,9 @@
 			err = ENOTSUP;
 			break;
 		}
-		if ((ic->ic_flags & IEEE80211_F_IBSSON) == 0) {
-			ic->ic_flags |= IEEE80211_F_IBSSON;
-			ic->ic_opmode = IEEE80211_M_IBSS;
-			err = ENETRESET;
-		}
+
+		ic->ic_opmode = IEEE80211_M_IBSS;
+		err = ENETRESET;
 		break;
 	default:
 		ieee80211_err("wl_set_bsstype: "
--- a/usr/src/uts/common/io/net80211/net80211_node.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/net80211/net80211_node.c	Wed Jan 21 16:12:41 2009 +0800
@@ -1,11 +1,11 @@
 /*
- * Copyright 2007 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"
-
 /*
  * Node management routines
  */
@@ -162,6 +160,7 @@
 
 	in->in_flags |= IEEE80211_NODE_AUTH;
 	in->in_inact_reload = im->im_inact_run;
+	in->in_inact = in->in_inact_reload;
 }
 
 /*
@@ -392,6 +391,9 @@
 	ieee80211_node_t *in;
 	ieee80211_node_t *obss;
 
+	ieee80211_node_table_reset(&ic->ic_sta);
+	ieee80211_reset_erp(ic);
+
 	in = ieee80211_alloc_node(ic, &ic->ic_scan, ic->ic_macaddr);
 	ASSERT(in != NULL);
 	obss = ic->ic_bss;
--- a/usr/src/uts/common/io/net80211/net80211_output.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/net80211/net80211_output.c	Wed Jan 21 16:12:41 2009 +0800
@@ -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,9 +35,6 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
-
 /*
  * Send out 802.11 frames
  */
@@ -402,18 +399,21 @@
 		if (mp == NULL)
 			return (ENOMEM);
 
-		bzero(frm, 8);	/* timestamp is set by hardware/driver */
+		bzero(frm, 8);	/* timestamp should be filled later */
 		frm += 8;
-		*(uint16_t *)frm = LE_16(in->in_intval);
+		*(uint16_t *)frm = LE_16(ic->ic_bss->in_intval);
 		frm += 2;
 		capinfo = ieee80211_get_capinfo(ic);
 		*(uint16_t *)frm = LE_16(capinfo);
 		frm += 2;
 
-		frm = ieee80211_add_ssid(frm, in->in_essid, in->in_esslen);
+		/* ssid */
+		frm = ieee80211_add_ssid(frm, ic->ic_bss->in_essid,
+		    ic->ic_bss->in_esslen);
+		/* supported rates */
 		frm = ieee80211_add_rates(frm, &in->in_rates);
 
-		if (ic->ic_phytype == IEEE80211_T_FH) {
+		if (IEEE80211_IS_CHAN_FHSS(ic->ic_curchan)) {
 			*frm++ = IEEE80211_ELEMID_FHPARMS;
 			*frm++ = IEEE80211_FH_LEN;
 			*frm++ = in->in_fhdwell & 0x00ff;
@@ -434,7 +434,12 @@
 			*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;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_AUTH:
@@ -593,6 +598,7 @@
 
 		frm = ieee80211_add_rates(frm, &in->in_rates);
 		frm = ieee80211_add_xrates(frm, &in->in_rates);
+		mp->b_wptr = frm;
 		break;
 
 	case IEEE80211_FC0_SUBTYPE_DISASSOC:
--- a/usr/src/uts/common/io/net80211/net80211_proto.c	Tue Jan 20 23:15:29 2009 -0800
+++ b/usr/src/uts/common/io/net80211/net80211_proto.c	Wed Jan 21 16:12:41 2009 +0800
@@ -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 protocol support
  */
@@ -106,49 +104,49 @@
 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
 	case IEEE80211_FC1_DIR_NODS:
 		(void) snprintf(buf2, sizeof (buf2), "NODS %s",
-			ieee80211_macaddr_sprintf(wh->i_addr2));
+		    ieee80211_macaddr_sprintf(wh->i_addr2));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "->%s",
-			ieee80211_macaddr_sprintf(wh->i_addr1));
+		    ieee80211_macaddr_sprintf(wh->i_addr1));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
-			ieee80211_macaddr_sprintf(wh->i_addr3));
+		    ieee80211_macaddr_sprintf(wh->i_addr3));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		break;
 	case IEEE80211_FC1_DIR_TODS:
 		(void) snprintf(buf2, sizeof (buf2), "TODS %s",
-			ieee80211_macaddr_sprintf(wh->i_addr2));
+		    ieee80211_macaddr_sprintf(wh->i_addr2));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "->%s",
-			ieee80211_macaddr_sprintf(wh->i_addr3));
+		    ieee80211_macaddr_sprintf(wh->i_addr3));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
-			ieee80211_macaddr_sprintf(wh->i_addr1));
+		    ieee80211_macaddr_sprintf(wh->i_addr1));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		break;
 	case IEEE80211_FC1_DIR_FROMDS:
 		(void) snprintf(buf2, sizeof (buf2), "FRDS %s",
-			ieee80211_macaddr_sprintf(wh->i_addr3));
+		    ieee80211_macaddr_sprintf(wh->i_addr3));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "->%s",
-			ieee80211_macaddr_sprintf(wh->i_addr1));
+		    ieee80211_macaddr_sprintf(wh->i_addr1));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "(%s)",
-			ieee80211_macaddr_sprintf(wh->i_addr2));
+		    ieee80211_macaddr_sprintf(wh->i_addr2));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		break;
 	case IEEE80211_FC1_DIR_DSTODS:
 		(void) snprintf(buf2, sizeof (buf2), "DSDS %s",
-			ieee80211_macaddr_sprintf((uint8_t *)&wh[1]));
+		    ieee80211_macaddr_sprintf((uint8_t *)&wh[1]));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "->%s  ",
-			ieee80211_macaddr_sprintf(wh->i_addr3));
+		    ieee80211_macaddr_sprintf(wh->i_addr3));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "%s",
-			ieee80211_macaddr_sprintf(wh->i_addr2));
+		    ieee80211_macaddr_sprintf(wh->i_addr2));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		(void) snprintf(buf2, sizeof (buf2), "->%s",
-			ieee80211_macaddr_sprintf(wh->i_addr1));
+		    ieee80211_macaddr_sprintf(wh->i_addr1));
 		(void) strncat(buf1, buf2, sizeof (buf2));
 		break;
 	}
@@ -161,13 +159,13 @@
 		break;
 	case IEEE80211_FC0_TYPE_MGT:
 		(void) snprintf(buf2, sizeof (buf2), "%s",
-			ieee80211_mgt_subtype_name[
-				(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
-				>> IEEE80211_FC0_SUBTYPE_SHIFT]);
+		    ieee80211_mgt_subtype_name[
+		    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK)
+		    >> IEEE80211_FC0_SUBTYPE_SHIFT]);
 		break;
 	default:
 		(void) snprintf(buf2, sizeof (buf2), "type#%d",
-			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
+		    wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK);
 		break;
 	}
 	(void) strncat(buf1, buf2, sizeof (buf2));
@@ -194,7 +192,7 @@
 			(void) strncat(buf1, buf2, 3);
 		}
 		ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_dump_pkt(): %s",
-			buf1);
+		    buf1);
 	}
 }
 
@@ -327,7 +325,7 @@
 	 * the driver is capable of doing it.
 	 */
 	ieee80211_set_shortslottime(ic,
-		ic->ic_curmode == IEEE80211_MODE_11A);
+	    ic->ic_curmode == IEEE80211_MODE_11A);
 	/*
 	 * Set short preamble and ERP barker-preamble flags.
 	 */
@@ -424,7 +422,7 @@
 	if (ic->ic_flags & IEEE80211_F_SCAN)
 		return;
 	ieee80211_dbg(IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
-		"%s\n", "beacon miss");
+	    "%s\n", "beacon miss");
 
 	/*
 	 * Our handling is only meaningful for stations that are
@@ -446,9 +444,9 @@
 		 */
 		IEEE80211_UNLOCK(ic);
 		(void) ieee80211_send_probereq(ic->ic_bss, ic->ic_macaddr,
-			ic->ic_bss->in_bssid, ic->ic_bss->in_bssid,
-			ic->ic_bss->in_essid, ic->ic_bss->in_esslen,
-			ic->ic_opt_ie, ic->ic_opt_ie_len);
+		    ic->ic_bss->in_bssid, ic->ic_bss->in_bssid,
+		    ic->ic_bss->in_essid, ic->ic_bss->in_esslen,
+		    ic->ic_opt_ie, ic->ic_opt_ie_len);
 		return;
 	}
 	im->im_bmiss_count = 0;
@@ -470,8 +468,8 @@
 	IEEE80211_LOCK(ic);
 	ostate = ic->ic_state;
 	ieee80211_dbg(IEEE80211_MSG_STATE, "ieee80211_newstate(): "
-		"%s -> %s\n",
-		ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
+	    "%s -> %s\n",
+	    ieee80211_state_name[ostate], ieee80211_state_name[nstate]);
 	ic->ic_state = nstate;
 	in = ic->ic_bss;
 	im->im_swbmiss_period = 0;	/* Reset software beacon miss period */
@@ -490,16 +488,23 @@
 		case IEEE80211_S_ASSOC:
 			if (ic->ic_opmode == IEEE80211_M_STA) {
 				IEEE80211_SEND_MGMT(ic, in,
-					IEEE80211_FC0_SUBTYPE_DEAUTH,
-					IEEE80211_REASON_AUTH_LEAVE);
+				    IEEE80211_FC0_SUBTYPE_DEAUTH,
+				    IEEE80211_REASON_AUTH_LEAVE);
 			}
 			break;
 		case IEEE80211_S_RUN:
-			if (ic->ic_opmode == IEEE80211_M_STA) {
+			switch (ic->ic_opmode) {
+			case IEEE80211_M_STA:
 				IEEE80211_SEND_MGMT(ic, in,
-					IEEE80211_FC0_SUBTYPE_DISASSOC,
-					IEEE80211_REASON_ASSOC_LEAVE);
+				    IEEE80211_FC0_SUBTYPE_DISASSOC,
+				    IEEE80211_REASON_ASSOC_LEAVE);
 				ieee80211_sta_leave(ic, in);
+				break;
+			case IEEE80211_M_IBSS:
+				ieee80211_notify_node_leave(ic, in);
+				break;
+			default:
+				break;
 			}
 			break;
 		}
@@ -510,20 +515,9 @@
 	case IEEE80211_S_SCAN:
 		switch (ostate) {
 		case IEEE80211_S_INIT:
-			if (ic->ic_opmode == IEEE80211_M_IBSS &&
-			    ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
-				/*
-				 * AP operation and we already have a channel;
-				 * bypass the scan and startup immediately.
-				 */
-				ieee80211_create_ibss(ic, ic->ic_des_chan);
-			} else {
-				IEEE80211_UNLOCK(ic);
-				ieee80211_begin_scan(ic,
-					(arg == 0) ? B_FALSE : B_TRUE);
-				return (0);
-			}
-			break;
+			IEEE80211_UNLOCK(ic);
+			ieee80211_begin_scan(ic, (arg == 0) ? B_FALSE : B_TRUE);
+			return (0);
 		case IEEE80211_S_SCAN:
 			/*
 			 * Scan next. If doing an active scan and the
@@ -535,18 +529,18 @@
 			    !IEEE80211_IS_CHAN_PASSIVE(ic->ic_curchan)) {
 				IEEE80211_UNLOCK(ic);
 				(void) ieee80211_send_probereq(in,
-					ic->ic_macaddr, wifi_bcastaddr,
-					wifi_bcastaddr,
-					ic->ic_des_essid, ic->ic_des_esslen,
-					ic->ic_opt_ie, ic->ic_opt_ie_len);
+				    ic->ic_macaddr, wifi_bcastaddr,
+				    wifi_bcastaddr,
+				    ic->ic_des_essid, ic->ic_des_esslen,
+				    ic->ic_opt_ie, ic->ic_opt_ie_len);
 				return (0);
 			}
 			break;
 		case IEEE80211_S_RUN:
 			/* beacon miss */
 			ieee80211_dbg(IEEE80211_MSG_STATE,
-				"no recent beacons from %s, rescanning\n",
-				ieee80211_macaddr_sprintf(in->in_macaddr));
+			    "no recent beacons from %s, rescanning\n",
+			    ieee80211_macaddr_sprintf(in->in_macaddr));
 			IEEE80211_UNLOCK(ic);
 			ieee80211_sta_leave(ic, in);
 			IEEE80211_LOCK(ic);
@@ -556,7 +550,7 @@
 		case IEEE80211_S_ASSOC:
 			/* timeout restart scan */
 			in = ieee80211_find_node(&ic->ic_scan,
-				ic->ic_bss->in_macaddr);
+			    ic->ic_bss->in_macaddr);
 			if (in != NULL) {
 				in->in_fails++;
 				ieee80211_unref_node(&in);
@@ -565,12 +559,13 @@
 		}
 		break;
 	case IEEE80211_S_AUTH:
+		ASSERT(ic->ic_opmode == IEEE80211_M_STA);
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
 			IEEE80211_UNLOCK(ic);
 			IEEE80211_SEND_MGMT(ic, in, IEEE80211_FC0_SUBTYPE_AUTH,
-				1);
+			    1);
 			return (0);
 		case IEEE80211_S_AUTH:
 		case IEEE80211_S_ASSOC:
@@ -578,7 +573,7 @@
 			case IEEE80211_FC0_SUBTYPE_AUTH:
 				IEEE80211_UNLOCK(ic);
 				IEEE80211_SEND_MGMT(ic, in,
-					IEEE80211_FC0_SUBTYPE_AUTH, 2);
+				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
 				return (0);
 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
 				/* ignore and retry scan on timeout */
@@ -591,37 +586,38 @@
 				ic->ic_state = ostate;	/* stay RUN */
 				IEEE80211_UNLOCK(ic);
 				IEEE80211_SEND_MGMT(ic, in,
-					IEEE80211_FC0_SUBTYPE_AUTH, 2);
+				    IEEE80211_FC0_SUBTYPE_AUTH, 2);
 				return (0);
 			case IEEE80211_FC0_SUBTYPE_DEAUTH:
 				IEEE80211_UNLOCK(ic);
 				ieee80211_sta_leave(ic, in);
 				/* try to re-auth */
 				IEEE80211_SEND_MGMT(ic, in,
-					IEEE80211_FC0_SUBTYPE_AUTH, 1);
+				    IEEE80211_FC0_SUBTYPE_AUTH, 1);
 				return (0);
 			}
 			break;
 		}
 		break;
 	case IEEE80211_S_ASSOC:
+		ASSERT(ic->ic_opmode == IEEE80211_M_STA);
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 		case IEEE80211_S_SCAN:
 		case IEEE80211_S_ASSOC:
 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_newstate: "
-				"invalid transition\n");
+			    "invalid transition\n");
 			break;
 		case IEEE80211_S_AUTH:
 			IEEE80211_UNLOCK(ic);
 			IEEE80211_SEND_MGMT(ic, in,
-				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
+			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0);
 			return (0);
 		case IEEE80211_S_RUN:
 			IEEE80211_UNLOCK(ic);
 			ieee80211_sta_leave(ic, in);
 			IEEE80211_SEND_MGMT(ic, in,
-				IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
+			    IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1);
 			return (0);
 		}
 		break;
@@ -629,18 +625,17 @@
 		switch (ostate) {
 		case IEEE80211_S_INIT:
 			ieee80211_err("ieee80211_newstate: "
-				"invalid transition\n");
+			    "invalid transition\n");
 			break;
 		case IEEE80211_S_AUTH:
 			ieee80211_err("ieee80211_newstate: "
-				"invalid transition\n");
+			    "invalid transition\n");
 			break;
 		case IEEE80211_S_SCAN:		/* adhoc/hostap mode */
 		case IEEE80211_S_ASSOC:		/* infra mode */
 			ASSERT(in->in_txrate < in->in_rates.ir_nrates);
 			im->im_mgt_timer = 0;
-			if (ic->ic_opmode == IEEE80211_M_STA)
-				ieee80211_notify_node_join(ic, in);
+			ieee80211_notify_node_join(ic, in);
 
 			/*
 			 * We can send data now; update the fastpath with our