Browse Source

ath9k: Re-order functions in main.c

Some of the functions in main.c are re-ordered in such
a way that all local functions are defined before mac80211
and pci callbacks.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Vasanthakumar Thiagarajan 16 years ago
parent
commit
8feceb6792
1 changed files with 861 additions and 861 deletions
  1. 861 861
      drivers/net/wireless/ath9k/main.c

+ 861 - 861
drivers/net/wireless/ath9k/main.c

@@ -325,1087 +325,1087 @@ static u8 parse_mpdudensity(u8 mpdudensity)
 	}
 }
 
-static int ath9k_start(struct ieee80211_hw *hw)
+static void ath9k_ht_conf(struct ath_softc *sc,
+			  struct ieee80211_bss_conf *bss_conf)
 {
-	struct ath_softc *sc = hw->priv;
-	struct ieee80211_channel *curchan = hw->conf.channel;
-	int error = 0, pos;
-
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
-		"initial channel: %d MHz\n", __func__, curchan->center_freq);
+#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14)
+	struct ath_ht_info *ht_info = &sc->sc_ht_info;
 
-	/* setup initial channel */
+	if (bss_conf->assoc_ht) {
+		ht_info->ext_chan_offset =
+			bss_conf->ht_bss_conf->bss_cap &
+				IEEE80211_HT_IE_CHA_SEC_OFFSET;
 
-	pos = ath_get_channel(sc, curchan);
-	if (pos == -1) {
-		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
-		return -EINVAL;
-	}
+		if (!(bss_conf->ht_conf->cap &
+			IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
+			    (bss_conf->ht_bss_conf->bss_cap &
+				IEEE80211_HT_IE_CHA_WIDTH))
+			ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
+		else
+			ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
 
-	sc->sc_ah->ah_channels[pos].chanmode =
-		(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
+		ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
+		ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
+					bss_conf->ht_conf->ampdu_factor);
+		ht_info->mpdudensity =
+			parse_mpdudensity(bss_conf->ht_conf->ampdu_density);
 
-	/* open ath_dev */
-	error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
-	if (error) {
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Unable to complete ath_open\n", __func__);
-		return error;
 	}
 
-	ieee80211_wake_queues(hw);
-	return 0;
+#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT
 }
 
-static int ath9k_tx(struct ieee80211_hw *hw,
-		    struct sk_buff *skb)
+static void ath9k_bss_assoc_info(struct ath_softc *sc,
+				 struct ieee80211_bss_conf *bss_conf)
 {
-	struct ath_softc *sc = hw->priv;
-	int hdrlen, padsize;
-	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
-	/*
-	 * As a temporary workaround, assign seq# here; this will likely need
-	 * to be cleaned up to work better with Beacon transmission and virtual
-	 * BSSes.
-	 */
-	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
-		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
-			sc->seq_no += 0x10;
-		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
-		hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
-	}
+	struct ieee80211_hw *hw = sc->hw;
+	struct ieee80211_channel *curchan = hw->conf.channel;
+	struct ath_vap *avp;
+	int pos;
+	DECLARE_MAC_BUF(mac);
 
-	/* Add the padding after the header if this is not already done */
-	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-	if (hdrlen & 3) {
-		padsize = hdrlen % 4;
-		if (skb_headroom(skb) < padsize)
-			return -1;
-		skb_push(skb, padsize);
-		memmove(skb->data, skb->data + padsize, hdrlen);
-	}
+	if (bss_conf->assoc) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
+			__func__,
+			bss_conf->aid);
 
-	DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
-		__func__,
-		skb);
+		avp = sc->sc_vaps[0];
+		if (avp == NULL) {
+			DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
+				__func__);
+			return;
+		}
 
-	if (ath_tx_start(sc, skb) != 0) {
-		DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
-		dev_kfree_skb_any(skb);
-		/* FIXME: Check for proper return value from ATH_DEV */
-		return 0;
-	}
+		/* New association, store aid */
+		if (avp->av_opmode == ATH9K_M_STA) {
+			sc->sc_curaid = bss_conf->aid;
+			ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
+					       sc->sc_curaid);
+		}
 
-	return 0;
-}
+		/* Configure the beacon */
+		ath_beacon_config(sc, 0);
+		sc->sc_flags |= SC_OP_BEACONS;
 
-static void ath9k_stop(struct ieee80211_hw *hw)
-{
-	struct ath_softc *sc = hw->priv;
-	int error;
+		/* Reset rssi stats */
+		sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
+		sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
+		sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
+		sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
+		/* Update chainmask */
+		ath_update_chainmask(sc, bss_conf->assoc_ht);
 
-	error = ath_suspend(sc);
-	if (error)
 		DPRINTF(sc, ATH_DBG_CONFIG,
-			"%s: Device is no longer present\n", __func__);
+			"%s: bssid %s aid 0x%x\n",
+			__func__,
+			print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
 
-	ieee80211_stop_queues(hw);
-}
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
+			__func__,
+			curchan->center_freq);
 
-static int ath9k_add_interface(struct ieee80211_hw *hw,
-			       struct ieee80211_if_init_conf *conf)
-{
-	struct ath_softc *sc = hw->priv;
-	int error, ic_opmode = 0;
+		pos = ath_get_channel(sc, curchan);
+		if (pos == -1) {
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Invalid channel\n", __func__);
+			return;
+		}
 
-	/* Support only vap for now */
+		if (hw->conf.ht_conf.ht_supported)
+			sc->sc_ah->ah_channels[pos].chanmode =
+				ath_get_extchanmode(sc, curchan);
+		else
+			sc->sc_ah->ah_channels[pos].chanmode =
+				(curchan->band == IEEE80211_BAND_2GHZ) ?
+				CHANNEL_G : CHANNEL_A;
 
-	if (sc->sc_nvaps)
-		return -ENOBUFS;
+		/* set h/w channel */
+		if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Unable to set channel\n",
+				__func__);
 
-	switch (conf->type) {
-	case IEEE80211_IF_TYPE_STA:
-		ic_opmode = ATH9K_M_STA;
-		break;
-	case IEEE80211_IF_TYPE_IBSS:
-		ic_opmode = ATH9K_M_IBSS;
-		break;
-	case IEEE80211_IF_TYPE_AP:
-		ic_opmode = ATH9K_M_HOSTAP;
-		break;
-	default:
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Interface type %d not yet supported\n",
-			__func__, conf->type);
-		return -EOPNOTSUPP;
+		ath_rate_newstate(sc, avp);
+		/* Update ratectrl about the new state */
+		ath_rc_node_update(hw, avp->rc_node);
+	} else {
+		DPRINTF(sc, ATH_DBG_CONFIG,
+		"%s: Bss Info DISSOC\n", __func__);
+		sc->sc_curaid = 0;
 	}
+}
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n",
-		__func__,
-		ic_opmode);
+void ath_get_beaconconfig(struct ath_softc *sc,
+			  int if_id,
+			  struct ath_beacon_config *conf)
+{
+	struct ieee80211_hw *hw = sc->hw;
 
-	error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
-	if (error) {
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Unable to attach vap, error: %d\n",
-			__func__, error);
-		return error;
-	}
+	/* fill in beacon config data */
 
-	return 0;
+	conf->beacon_interval = hw->conf.beacon_int;
+	conf->listen_interval = 100;
+	conf->dtim_count = 1;
+	conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
 }
 
-static void ath9k_remove_interface(struct ieee80211_hw *hw,
-				   struct ieee80211_if_init_conf *conf)
+void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
+		     struct ath_xmit_status *tx_status, struct ath_node *an)
 {
-	struct ath_softc *sc = hw->priv;
-	struct ath_vap *avp;
-	int error;
+	struct ieee80211_hw *hw = sc->hw;
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__);
+	DPRINTF(sc, ATH_DBG_XMIT,
+		"%s: TX complete: skb: %p\n", __func__, skb);
 
-	avp = sc->sc_vaps[0];
-	if (avp == NULL) {
-		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
-			__func__);
-		return;
+	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
+		tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
+		/* free driver's private data area of tx_info */
+		if (tx_info->driver_data[0] != NULL)
+			kfree(tx_info->driver_data[0]);
+			tx_info->driver_data[0] = NULL;
 	}
 
-#ifdef CONFIG_SLOW_ANT_DIV
-	ath_slow_ant_div_stop(&sc->sc_antdiv);
-#endif
-
-	/* Update ratectrl */
-	ath_rate_newstate(sc, avp);
+	if (tx_status->flags & ATH_TX_BAR) {
+		tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
+		tx_status->flags &= ~ATH_TX_BAR;
+	}
 
-	/* Reclaim beacon resources */
-	if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
-	    sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
-		ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
-		ath_beacon_return(sc, avp);
+	if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) {
+		if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
+			/* Frame was not ACKed, but an ACK was expected */
+			tx_info->status.excessive_retries = 1;
+		}
+	} else {
+		/* Frame was ACKed */
+		tx_info->flags |= IEEE80211_TX_STAT_ACK;
 	}
 
-	/* Set interrupt mask */
-	sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
-	ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL);
-	sc->sc_flags &= ~SC_OP_BEACONS;
+	tx_info->status.retry_count = tx_status->retries;
 
-	error = ath_vap_detach(sc, 0);
-	if (error)
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Unable to detach vap, error: %d\n",
-			__func__, error);
+	ieee80211_tx_status(hw, skb);
+	if (an)
+		ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
 }
 
-static int ath9k_config(struct ieee80211_hw *hw,
-			struct ieee80211_conf *conf)
+int _ath_rx_indicate(struct ath_softc *sc,
+		     struct sk_buff *skb,
+		     struct ath_recv_status *status,
+		     u16 keyix)
 {
-	struct ath_softc *sc = hw->priv;
-	struct ieee80211_channel *curchan = hw->conf.channel;
-	int pos;
-
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
-		__func__,
-		curchan->center_freq);
+	struct ieee80211_hw *hw = sc->hw;
+	struct ath_node *an = NULL;
+	struct ieee80211_rx_status rx_status;
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+	int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+	int padsize;
+	enum ATH_RX_TYPE st;
 
-	pos = ath_get_channel(sc, curchan);
-	if (pos == -1) {
-		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
-		return -EINVAL;
+	/* see if any padding is done by the hw and remove it */
+	if (hdrlen & 3) {
+		padsize = hdrlen % 4;
+		memmove(skb->data + padsize, skb->data, hdrlen);
+		skb_pull(skb, padsize);
 	}
 
-	sc->sc_ah->ah_channels[pos].chanmode =
-		(curchan->band == IEEE80211_BAND_2GHZ) ?
-		CHANNEL_G : CHANNEL_A;
+	/* Prepare rx status */
+	ath9k_rx_prepare(sc, skb, status, &rx_status);
 
-	if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
-		sc->sc_ah->ah_channels[pos].chanmode =
-			ath_get_extchanmode(sc, curchan);
+	if (!(keyix == ATH9K_RXKEYIX_INVALID) &&
+	    !(status->flags & ATH_RX_DECRYPT_ERROR)) {
+		rx_status.flag |= RX_FLAG_DECRYPTED;
+	} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
+		   && !(status->flags & ATH_RX_DECRYPT_ERROR)
+		   && skb->len >= hdrlen + 4) {
+		keyix = skb->data[hdrlen + 3] >> 6;
 
-	sc->sc_config.txpowlimit = 2 * conf->power_level;
+		if (test_bit(keyix, sc->sc_keymap))
+			rx_status.flag |= RX_FLAG_DECRYPTED;
+	}
 
-	/* set h/w channel */
-	if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
-		DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n",
-			__func__);
+	spin_lock_bh(&sc->node_lock);
+	an = ath_node_find(sc, hdr->addr2);
+	spin_unlock_bh(&sc->node_lock);
+
+	if (an) {
+		ath_rx_input(sc, an,
+			     hw->conf.ht_conf.ht_supported,
+			     skb, status, &st);
+	}
+	if (!an || (st != ATH_RX_CONSUMED))
+		__ieee80211_rx(hw, skb, &rx_status);
 
 	return 0;
 }
 
-static int ath9k_config_interface(struct ieee80211_hw *hw,
-				  struct ieee80211_vif *vif,
-				  struct ieee80211_if_conf *conf)
+int ath_rx_subframe(struct ath_node *an,
+		    struct sk_buff *skb,
+		    struct ath_recv_status *status)
 {
-	struct ath_softc *sc = hw->priv;
-	struct ath_hal *ah = sc->sc_ah;
-	struct ath_vap *avp;
-	u32 rfilt = 0;
-	int error, i;
-	DECLARE_MAC_BUF(mac);
-
-	avp = sc->sc_vaps[0];
-	if (avp == NULL) {
-		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
-			__func__);
-		return -EINVAL;
-	}
-
-	/* TODO: Need to decide which hw opmode to use for multi-interface
-	 * cases */
-	if (vif->type == IEEE80211_IF_TYPE_AP &&
-	    ah->ah_opmode != ATH9K_M_HOSTAP) {
-		ah->ah_opmode = ATH9K_M_HOSTAP;
-		ath9k_hw_setopmode(ah);
-		ath9k_hw_write_associd(ah, sc->sc_myaddr, 0);
-		/* Request full reset to get hw opmode changed properly */
-		sc->sc_flags |= SC_OP_FULL_RESET;
-	}
-
-	if ((conf->changed & IEEE80211_IFCC_BSSID) &&
-	    !is_zero_ether_addr(conf->bssid)) {
-		switch (vif->type) {
-		case IEEE80211_IF_TYPE_STA:
-		case IEEE80211_IF_TYPE_IBSS:
-			/* Update ratectrl about the new state */
-			ath_rate_newstate(sc, avp);
-
-			/* Set BSSID */
-			memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN);
-			sc->sc_curaid = 0;
-			ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
-					       sc->sc_curaid);
+	struct ath_softc *sc = an->an_sc;
+	struct ieee80211_hw *hw = sc->hw;
+	struct ieee80211_rx_status rx_status;
 
-			/* Set aggregation protection mode parameters */
-			sc->sc_config.ath_aggr_prot = 0;
+	/* Prepare rx status */
+	ath9k_rx_prepare(sc, skb, status, &rx_status);
+	if (!(status->flags & ATH_RX_DECRYPT_ERROR))
+		rx_status.flag |= RX_FLAG_DECRYPTED;
 
-			/*
-			 * Reset our TSF so that its value is lower than the
-			 * beacon that we are trying to catch.
-			 * Only then hw will update its TSF register with the
-			 * new beacon. Reset the TSF before setting the BSSID
-			 * to avoid allowing in any frames that would update
-			 * our TSF only to have us clear it
-			 * immediately thereafter.
-			 */
-			ath9k_hw_reset_tsf(sc->sc_ah);
+	__ieee80211_rx(hw, skb, &rx_status);
 
-			/* Disable BMISS interrupt when we're not associated */
-			ath9k_hw_set_interrupts(sc->sc_ah,
-					sc->sc_imask &
-					~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
-			sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+	return 0;
+}
 
-			DPRINTF(sc, ATH_DBG_CONFIG,
-				"%s: RX filter 0x%x bssid %s aid 0x%x\n",
-				__func__, rfilt,
-				print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
+/********************************/
+/*	 LED functions		*/
+/********************************/
 
-			/* need to reconfigure the beacon */
-			sc->sc_flags &= ~SC_OP_BEACONS ;
+static void ath_led_brightness(struct led_classdev *led_cdev,
+			       enum led_brightness brightness)
+{
+	struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
+	struct ath_softc *sc = led->sc;
 
-			break;
-		default:
-			break;
-		}
+	switch (brightness) {
+	case LED_OFF:
+		if (led->led_type == ATH_LED_ASSOC ||
+		    led->led_type == ATH_LED_RADIO)
+			sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
+		ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
+				(led->led_type == ATH_LED_RADIO) ? 1 :
+				!!(sc->sc_flags & SC_OP_LED_ASSOCIATED));
+		break;
+	case LED_FULL:
+		if (led->led_type == ATH_LED_ASSOC)
+			sc->sc_flags |= SC_OP_LED_ASSOCIATED;
+		ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
+		break;
+	default:
+		break;
 	}
+}
 
-	if ((conf->changed & IEEE80211_IFCC_BEACON) &&
-	    ((vif->type == IEEE80211_IF_TYPE_IBSS) ||
-	     (vif->type == IEEE80211_IF_TYPE_AP))) {
-		/*
-		 * Allocate and setup the beacon frame.
-		 *
-		 * Stop any previous beacon DMA.  This may be
-		 * necessary, for example, when an ibss merge
-		 * causes reconfiguration; we may be called
-		 * with beacon transmission active.
-		 */
-		ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
+static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
+			    char *trigger)
+{
+	int ret;
 
-		error = ath_beacon_alloc(sc, 0);
-		if (error != 0)
-			return error;
+	led->sc = sc;
+	led->led_cdev.name = led->name;
+	led->led_cdev.default_trigger = trigger;
+	led->led_cdev.brightness_set = ath_led_brightness;
 
-		ath_beacon_sync(sc, 0);
-	}
+	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
+	if (ret)
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"Failed to register led:%s", led->name);
+	else
+		led->registered = 1;
+	return ret;
+}
 
-	/* Check for WLAN_CAPABILITY_PRIVACY ? */
-	if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) {
-		for (i = 0; i < IEEE80211_WEP_NKID; i++)
-			if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
-				ath9k_hw_keysetmac(sc->sc_ah,
-						   (u16)i,
-						   sc->sc_curbssid);
+static void ath_unregister_led(struct ath_led *led)
+{
+	if (led->registered) {
+		led_classdev_unregister(&led->led_cdev);
+		led->registered = 0;
 	}
-
-	/* Only legacy IBSS for now */
-	if (vif->type == IEEE80211_IF_TYPE_IBSS)
-		ath_update_chainmask(sc, 0);
-
-	return 0;
 }
 
-#define SUPPORTED_FILTERS			\
-	(FIF_PROMISC_IN_BSS |			\
-	FIF_ALLMULTI |				\
-	FIF_CONTROL |				\
-	FIF_OTHER_BSS |				\
-	FIF_BCN_PRBRESP_PROMISC |		\
-	FIF_FCSFAIL)
-
-/* FIXME: sc->sc_full_reset ? */
-static void ath9k_configure_filter(struct ieee80211_hw *hw,
-				   unsigned int changed_flags,
-				   unsigned int *total_flags,
-				   int mc_count,
-				   struct dev_mc_list *mclist)
+static void ath_deinit_leds(struct ath_softc *sc)
 {
-	struct ath_softc *sc = hw->priv;
-	u32 rfilt;
+	ath_unregister_led(&sc->assoc_led);
+	sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
+	ath_unregister_led(&sc->tx_led);
+	ath_unregister_led(&sc->rx_led);
+	ath_unregister_led(&sc->radio_led);
+	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+}
 
-	changed_flags &= SUPPORTED_FILTERS;
-	*total_flags &= SUPPORTED_FILTERS;
+static void ath_init_leds(struct ath_softc *sc)
+{
+	char *trigger;
+	int ret;
 
-	sc->rx_filter = *total_flags;
-	rfilt = ath_calcrxfilter(sc);
-	ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
+	/* Configure gpio 1 for output */
+	ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
+			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+	/* LED off, active low */
+	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
 
-	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
-		if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
-			ath9k_hw_write_associd(sc->sc_ah, ath_bcast_mac, 0);
-	}
+	trigger = ieee80211_get_radio_led_name(sc->hw);
+	snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
+		"ath9k-%s:radio", wiphy_name(sc->hw->wiphy));
+	ret = ath_register_led(sc, &sc->radio_led, trigger);
+	sc->radio_led.led_type = ATH_LED_RADIO;
+	if (ret)
+		goto fail;
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n",
-		__func__, sc->rx_filter);
-}
+	trigger = ieee80211_get_assoc_led_name(sc->hw);
+	snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
+		"ath9k-%s:assoc", wiphy_name(sc->hw->wiphy));
+	ret = ath_register_led(sc, &sc->assoc_led, trigger);
+	sc->assoc_led.led_type = ATH_LED_ASSOC;
+	if (ret)
+		goto fail;
 
-static void ath9k_sta_notify(struct ieee80211_hw *hw,
-			     struct ieee80211_vif *vif,
-			     enum sta_notify_cmd cmd,
-			     const u8 *addr)
-{
-	struct ath_softc *sc = hw->priv;
-	struct ath_node *an;
-	unsigned long flags;
-	DECLARE_MAC_BUF(mac);
+	trigger = ieee80211_get_tx_led_name(sc->hw);
+	snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
+		"ath9k-%s:tx", wiphy_name(sc->hw->wiphy));
+	ret = ath_register_led(sc, &sc->tx_led, trigger);
+	sc->tx_led.led_type = ATH_LED_TX;
+	if (ret)
+		goto fail;
 
-	spin_lock_irqsave(&sc->node_lock, flags);
-	an = ath_node_find(sc, (u8 *) addr);
-	spin_unlock_irqrestore(&sc->node_lock, flags);
+	trigger = ieee80211_get_rx_led_name(sc->hw);
+	snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
+		"ath9k-%s:rx", wiphy_name(sc->hw->wiphy));
+	ret = ath_register_led(sc, &sc->rx_led, trigger);
+	sc->rx_led.led_type = ATH_LED_RX;
+	if (ret)
+		goto fail;
 
-	switch (cmd) {
-	case STA_NOTIFY_ADD:
-		spin_lock_irqsave(&sc->node_lock, flags);
-		if (!an) {
-			ath_node_attach(sc, (u8 *)addr, 0);
-			DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
-				__func__,
-				print_mac(mac, addr));
-		} else {
-			ath_node_get(sc, (u8 *)addr);
-		}
-		spin_unlock_irqrestore(&sc->node_lock, flags);
-		break;
-	case STA_NOTIFY_REMOVE:
-		if (!an)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Removal of a non-existent node\n",
-				__func__);
-		else {
-			ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
-			DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
-				__func__,
-				print_mac(mac, addr));
-		}
-		break;
-	default:
-		break;
-	}
+	return;
+
+fail:
+	ath_deinit_leds(sc);
 }
 
-static int ath9k_conf_tx(struct ieee80211_hw *hw,
-			 u16 queue,
-			 const struct ieee80211_tx_queue_params *params)
+static int ath_detach(struct ath_softc *sc)
 {
-	struct ath_softc *sc = hw->priv;
-	struct ath9k_tx_queue_info qi;
-	int ret = 0, qnum;
+	struct ieee80211_hw *hw = sc->hw;
 
-	if (queue >= WME_NUM_AC)
-		return 0;
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
 
-	qi.tqi_aifs = params->aifs;
-	qi.tqi_cwmin = params->cw_min;
-	qi.tqi_cwmax = params->cw_max;
-	qi.tqi_burstTime = params->txop;
-	qnum = ath_get_hal_qnum(queue, sc);
+	/* Deinit LED control */
+	ath_deinit_leds(sc);
 
-	DPRINTF(sc, ATH_DBG_CONFIG,
-		"%s: Configure tx [queue/halq] [%d/%d],  "
-		"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
-		__func__,
-		queue,
-		qnum,
-		params->aifs,
-		params->cw_min,
-		params->cw_max,
-		params->txop);
+	/* Unregister hw */
 
-	ret = ath_txq_update(sc, qnum, &qi);
-	if (ret)
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: TXQ Update failed\n", __func__);
+	ieee80211_unregister_hw(hw);
 
-	return ret;
-}
+	/* unregister Rate control */
+	ath_rate_control_unregister();
 
-static int ath9k_set_key(struct ieee80211_hw *hw,
-			 enum set_key_cmd cmd,
-			 const u8 *local_addr,
-			 const u8 *addr,
-			 struct ieee80211_key_conf *key)
-{
-	struct ath_softc *sc = hw->priv;
-	int ret = 0;
+	/* tx/rx cleanup */
 
-	DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
+	ath_rx_cleanup(sc);
+	ath_tx_cleanup(sc);
 
-	switch (cmd) {
-	case SET_KEY:
-		ret = ath_key_config(sc, addr, key);
-		if (!ret) {
-			set_bit(key->keyidx, sc->sc_keymap);
-			key->hw_key_idx = key->keyidx;
-			/* push IV and Michael MIC generation to stack */
-			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
-			if (key->alg == ALG_TKIP)
-				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
-		}
-		break;
-	case DISABLE_KEY:
-		ath_key_delete(sc, key);
-		clear_bit(key->keyidx, sc->sc_keymap);
-		sc->sc_keytype = ATH9K_CIPHER_CLR;
-		break;
-	default:
-		ret = -EINVAL;
-	}
+	/* Deinit */
 
-	return ret;
+	ath_deinit(sc);
+
+	return 0;
 }
 
-static void ath9k_ht_conf(struct ath_softc *sc,
-			  struct ieee80211_bss_conf *bss_conf)
+static int ath_attach(u16 devid,
+		      struct ath_softc *sc)
 {
-#define IEEE80211_HT_CAP_40MHZ_INTOLERANT BIT(14)
-	struct ath_ht_info *ht_info = &sc->sc_ht_info;
-
-	if (bss_conf->assoc_ht) {
-		ht_info->ext_chan_offset =
-			bss_conf->ht_bss_conf->bss_cap &
-				IEEE80211_HT_IE_CHA_SEC_OFFSET;
+	struct ieee80211_hw *hw = sc->hw;
+	int error = 0;
 
-		if (!(bss_conf->ht_conf->cap &
-			IEEE80211_HT_CAP_40MHZ_INTOLERANT) &&
-			    (bss_conf->ht_bss_conf->bss_cap &
-				IEEE80211_HT_IE_CHA_WIDTH))
-			ht_info->tx_chan_width = ATH9K_HT_MACMODE_2040;
-		else
-			ht_info->tx_chan_width = ATH9K_HT_MACMODE_20;
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
 
-		ath9k_hw_set11nmac2040(sc->sc_ah, ht_info->tx_chan_width);
-		ht_info->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR +
-					bss_conf->ht_conf->ampdu_factor);
-		ht_info->mpdudensity =
-			parse_mpdudensity(bss_conf->ht_conf->ampdu_density);
+	error = ath_init(devid, sc);
+	if (error != 0)
+		return error;
 
-	}
+	/* Init nodes */
 
-#undef IEEE80211_HT_CAP_40MHZ_INTOLERANT
-}
+	INIT_LIST_HEAD(&sc->node_list);
+	spin_lock_init(&sc->node_lock);
 
-static void ath9k_bss_assoc_info(struct ath_softc *sc,
-				 struct ieee80211_bss_conf *bss_conf)
-{
-	struct ieee80211_hw *hw = sc->hw;
-	struct ieee80211_channel *curchan = hw->conf.channel;
-	struct ath_vap *avp;
-	int pos;
-	DECLARE_MAC_BUF(mac);
+	/* get mac address from hardware and set in mac80211 */
 
-	if (bss_conf->assoc) {
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: Bss Info ASSOC %d\n",
-			__func__,
-			bss_conf->aid);
+	SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
 
-		avp = sc->sc_vaps[0];
-		if (avp == NULL) {
-			DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
-				__func__);
-			return;
-		}
+	/* setup channels and rates */
 
-		/* New association, store aid */
-		if (avp->av_opmode == ATH9K_M_STA) {
-			sc->sc_curaid = bss_conf->aid;
-			ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
-					       sc->sc_curaid);
-		}
+	sc->sbands[IEEE80211_BAND_2GHZ].channels =
+		sc->channels[IEEE80211_BAND_2GHZ];
+	sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
+		sc->rates[IEEE80211_BAND_2GHZ];
+	sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
 
-		/* Configure the beacon */
-		ath_beacon_config(sc, 0);
-		sc->sc_flags |= SC_OP_BEACONS;
+	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
+		/* Setup HT capabilities for 2.4Ghz*/
+		setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
 
-		/* Reset rssi stats */
-		sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER;
-		sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER;
-		sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER;
-		sc->sc_halstats.ns_avgtxrate = ATH_RATE_DUMMY_MARKER;
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
+		&sc->sbands[IEEE80211_BAND_2GHZ];
 
-		/* Update chainmask */
-		ath_update_chainmask(sc, bss_conf->assoc_ht);
+	if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
+		sc->sbands[IEEE80211_BAND_5GHZ].channels =
+			sc->channels[IEEE80211_BAND_5GHZ];
+		sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
+			sc->rates[IEEE80211_BAND_5GHZ];
+		sc->sbands[IEEE80211_BAND_5GHZ].band =
+			IEEE80211_BAND_5GHZ;
 
-		DPRINTF(sc, ATH_DBG_CONFIG,
-			"%s: bssid %s aid 0x%x\n",
-			__func__,
-			print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
+		if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
+			/* Setup HT capabilities for 5Ghz*/
+			setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info);
 
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
-			__func__,
-			curchan->center_freq);
+		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+			&sc->sbands[IEEE80211_BAND_5GHZ];
+	}
 
-		pos = ath_get_channel(sc, curchan);
-		if (pos == -1) {
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Invalid channel\n", __func__);
-			return;
-		}
+	/* FIXME: Have to figure out proper hw init values later */
 
-		if (hw->conf.ht_conf.ht_supported)
-			sc->sc_ah->ah_channels[pos].chanmode =
-				ath_get_extchanmode(sc, curchan);
-		else
-			sc->sc_ah->ah_channels[pos].chanmode =
-				(curchan->band == IEEE80211_BAND_2GHZ) ?
-				CHANNEL_G : CHANNEL_A;
+	hw->queues = 4;
+	hw->ampdu_queues = 1;
 
-		/* set h/w channel */
-		if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Unable to set channel\n",
-				__func__);
+	/* Register rate control */
+	hw->rate_control_algorithm = "ath9k_rate_control";
+	error = ath_rate_control_register();
+	if (error != 0) {
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: Unable to register rate control "
+			"algorithm:%d\n", __func__, error);
+		ath_rate_control_unregister();
+		goto bad;
+	}
 
-		ath_rate_newstate(sc, avp);
-		/* Update ratectrl about the new state */
-		ath_rc_node_update(hw, avp->rc_node);
-	} else {
-		DPRINTF(sc, ATH_DBG_CONFIG,
-		"%s: Bss Info DISSOC\n", __func__);
-		sc->sc_curaid = 0;
+	error = ieee80211_register_hw(hw);
+	if (error != 0) {
+		ath_rate_control_unregister();
+		goto bad;
 	}
+
+	/* Initialize LED control */
+	ath_init_leds(sc);
+
+	/* initialize tx/rx engine */
+
+	error = ath_tx_init(sc, ATH_TXBUF);
+	if (error != 0)
+		goto detach;
+
+	error = ath_rx_init(sc, ATH_RXBUF);
+	if (error != 0)
+		goto detach;
+
+	return 0;
+detach:
+	ath_detach(sc);
+bad:
+	return error;
 }
 
-static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
-				   struct ieee80211_vif *vif,
-				   struct ieee80211_bss_conf *bss_conf,
-				   u32 changed)
+static int ath9k_start(struct ieee80211_hw *hw)
 {
 	struct ath_softc *sc = hw->priv;
+	struct ieee80211_channel *curchan = hw->conf.channel;
+	int error = 0, pos;
 
-	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
-			__func__,
-			bss_conf->use_short_preamble);
-		if (bss_conf->use_short_preamble)
-			sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
-		else
-			sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
-	}
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with "
+		"initial channel: %d MHz\n", __func__, curchan->center_freq);
 
-	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n",
-			__func__,
-			bss_conf->use_cts_prot);
-		if (bss_conf->use_cts_prot &&
-		    hw->conf.channel->band != IEEE80211_BAND_5GHZ)
-			sc->sc_flags |= SC_OP_PROTECT_ENABLE;
-		else
-			sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
-	}
+	/* setup initial channel */
 
-	if (changed & BSS_CHANGED_HT) {
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
-			__func__,
-			bss_conf->assoc_ht);
-		ath9k_ht_conf(sc, bss_conf);
+	pos = ath_get_channel(sc, curchan);
+	if (pos == -1) {
+		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
+		return -EINVAL;
 	}
 
-	if (changed & BSS_CHANGED_ASSOC) {
-		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n",
-			__func__,
-			bss_conf->assoc);
-		ath9k_bss_assoc_info(sc, bss_conf);
+	sc->sc_ah->ah_channels[pos].chanmode =
+		(curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A;
+
+	/* open ath_dev */
+	error = ath_open(sc, &sc->sc_ah->ah_channels[pos]);
+	if (error) {
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: Unable to complete ath_open\n", __func__);
+		return error;
 	}
+
+	ieee80211_wake_queues(hw);
+	return 0;
 }
 
-static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
+static int ath9k_tx(struct ieee80211_hw *hw,
+		    struct sk_buff *skb)
 {
-	u64 tsf;
 	struct ath_softc *sc = hw->priv;
-	struct ath_hal *ah = sc->sc_ah;
+	int hdrlen, padsize;
+	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
 
-	tsf = ath9k_hw_gettsf64(ah);
+	/*
+	 * As a temporary workaround, assign seq# here; this will likely need
+	 * to be cleaned up to work better with Beacon transmission and virtual
+	 * BSSes.
+	 */
+	if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+		struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+		if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)
+			sc->seq_no += 0x10;
+		hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG);
+		hdr->seq_ctrl |= cpu_to_le16(sc->seq_no);
+	}
 
-	return tsf;
+	/* Add the padding after the header if this is not already done */
+	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
+	if (hdrlen & 3) {
+		padsize = hdrlen % 4;
+		if (skb_headroom(skb) < padsize)
+			return -1;
+		skb_push(skb, padsize);
+		memmove(skb->data, skb->data + padsize, hdrlen);
+	}
+
+	DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n",
+		__func__,
+		skb);
+
+	if (ath_tx_start(sc, skb) != 0) {
+		DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__);
+		dev_kfree_skb_any(skb);
+		/* FIXME: Check for proper return value from ATH_DEV */
+		return 0;
+	}
+
+	return 0;
 }
 
-static void ath9k_reset_tsf(struct ieee80211_hw *hw)
+static void ath9k_stop(struct ieee80211_hw *hw)
 {
 	struct ath_softc *sc = hw->priv;
-	struct ath_hal *ah = sc->sc_ah;
+	int error;
 
-	ath9k_hw_reset_tsf(ah);
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__);
+
+	error = ath_suspend(sc);
+	if (error)
+		DPRINTF(sc, ATH_DBG_CONFIG,
+			"%s: Device is no longer present\n", __func__);
+
+	ieee80211_stop_queues(hw);
 }
 
-static int ath9k_ampdu_action(struct ieee80211_hw *hw,
-		       enum ieee80211_ampdu_mlme_action action,
-		       const u8 *addr,
-		       u16 tid,
-		       u16 *ssn)
+static int ath9k_add_interface(struct ieee80211_hw *hw,
+			       struct ieee80211_if_init_conf *conf)
 {
 	struct ath_softc *sc = hw->priv;
-	int ret = 0;
+	int error, ic_opmode = 0;
 
-	switch (action) {
-	case IEEE80211_AMPDU_RX_START:
-		ret = ath_rx_aggr_start(sc, addr, tid, ssn);
-		if (ret < 0)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Unable to start RX aggregation\n",
-				__func__);
-		break;
-	case IEEE80211_AMPDU_RX_STOP:
-		ret = ath_rx_aggr_stop(sc, addr, tid);
-		if (ret < 0)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Unable to stop RX aggregation\n",
-				__func__);
+	/* Support only vap for now */
+
+	if (sc->sc_nvaps)
+		return -ENOBUFS;
+
+	switch (conf->type) {
+	case IEEE80211_IF_TYPE_STA:
+		ic_opmode = ATH9K_M_STA;
 		break;
-	case IEEE80211_AMPDU_TX_START:
-		ret = ath_tx_aggr_start(sc, addr, tid, ssn);
-		if (ret < 0)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Unable to start TX aggregation\n",
-				__func__);
-		else
-			ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
+	case IEEE80211_IF_TYPE_IBSS:
+		ic_opmode = ATH9K_M_IBSS;
 		break;
-	case IEEE80211_AMPDU_TX_STOP:
-		ret = ath_tx_aggr_stop(sc, addr, tid);
-		if (ret < 0)
-			DPRINTF(sc, ATH_DBG_FATAL,
-				"%s: Unable to stop TX aggregation\n",
-				__func__);
-
-		ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
+	case IEEE80211_IF_TYPE_AP:
+		ic_opmode = ATH9K_M_HOSTAP;
 		break;
 	default:
 		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Unknown AMPDU action\n", __func__);
+			"%s: Interface type %d not yet supported\n",
+			__func__, conf->type);
+		return -EOPNOTSUPP;
 	}
 
-	return ret;
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a VAP of type: %d\n",
+		__func__,
+		ic_opmode);
+
+	error = ath_vap_attach(sc, 0, conf->vif, ic_opmode);
+	if (error) {
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: Unable to attach vap, error: %d\n",
+			__func__, error);
+		return error;
+	}
+
+	return 0;
 }
 
-static struct ieee80211_ops ath9k_ops = {
-	.tx 		    = ath9k_tx,
-	.start 		    = ath9k_start,
-	.stop 		    = ath9k_stop,
-	.add_interface 	    = ath9k_add_interface,
-	.remove_interface   = ath9k_remove_interface,
-	.config 	    = ath9k_config,
-	.config_interface   = ath9k_config_interface,
-	.configure_filter   = ath9k_configure_filter,
-	.get_stats          = NULL,
-	.sta_notify         = ath9k_sta_notify,
-	.conf_tx 	    = ath9k_conf_tx,
-	.get_tx_stats 	    = NULL,
-	.bss_info_changed   = ath9k_bss_info_changed,
-	.set_tim            = NULL,
-	.set_key            = ath9k_set_key,
-	.hw_scan            = NULL,
-	.get_tkip_seq       = NULL,
-	.set_rts_threshold  = NULL,
-	.set_frag_threshold = NULL,
-	.set_retry_limit    = NULL,
-	.get_tsf 	    = ath9k_get_tsf,
-	.reset_tsf 	    = ath9k_reset_tsf,
-	.tx_last_beacon     = NULL,
-	.ampdu_action       = ath9k_ampdu_action
-};
-
-void ath_get_beaconconfig(struct ath_softc *sc,
-			  int if_id,
-			  struct ath_beacon_config *conf)
-{
-	struct ieee80211_hw *hw = sc->hw;
-
-	/* fill in beacon config data */
-
-	conf->beacon_interval = hw->conf.beacon_int;
-	conf->listen_interval = 100;
-	conf->dtim_count = 1;
-	conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * conf->listen_interval;
-}
-
-void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb,
-		     struct ath_xmit_status *tx_status, struct ath_node *an)
+static void ath9k_remove_interface(struct ieee80211_hw *hw,
+				   struct ieee80211_if_init_conf *conf)
 {
-	struct ieee80211_hw *hw = sc->hw;
-	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
+	struct ath_softc *sc = hw->priv;
+	struct ath_vap *avp;
+	int error;
 
-	DPRINTF(sc, ATH_DBG_XMIT,
-		"%s: TX complete: skb: %p\n", __func__, skb);
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__);
 
-	if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK ||
-		tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) {
-		/* free driver's private data area of tx_info */
-		if (tx_info->driver_data[0] != NULL)
-			kfree(tx_info->driver_data[0]);
-			tx_info->driver_data[0] = NULL;
+	avp = sc->sc_vaps[0];
+	if (avp == NULL) {
+		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
+			__func__);
+		return;
 	}
 
-	if (tx_status->flags & ATH_TX_BAR) {
-		tx_info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK;
-		tx_status->flags &= ~ATH_TX_BAR;
-	}
+#ifdef CONFIG_SLOW_ANT_DIV
+	ath_slow_ant_div_stop(&sc->sc_antdiv);
+#endif
 
-	if (tx_status->flags & (ATH_TX_ERROR | ATH_TX_XRETRY)) {
-		if (!(tx_info->flags & IEEE80211_TX_CTL_NO_ACK)) {
-			/* Frame was not ACKed, but an ACK was expected */
-			tx_info->status.excessive_retries = 1;
-		}
-	} else {
-		/* Frame was ACKed */
-		tx_info->flags |= IEEE80211_TX_STAT_ACK;
+	/* Update ratectrl */
+	ath_rate_newstate(sc, avp);
+
+	/* Reclaim beacon resources */
+	if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP ||
+	    sc->sc_ah->ah_opmode == ATH9K_M_IBSS) {
+		ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
+		ath_beacon_return(sc, avp);
 	}
 
-	tx_info->status.retry_count = tx_status->retries;
+	/* Set interrupt mask */
+	sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
+	ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL);
+	sc->sc_flags &= ~SC_OP_BEACONS;
 
-	ieee80211_tx_status(hw, skb);
-	if (an)
-		ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE);
+	error = ath_vap_detach(sc, 0);
+	if (error)
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: Unable to detach vap, error: %d\n",
+			__func__, error);
 }
 
-int _ath_rx_indicate(struct ath_softc *sc,
-		     struct sk_buff *skb,
-		     struct ath_recv_status *status,
-		     u16 keyix)
+static int ath9k_config(struct ieee80211_hw *hw,
+			struct ieee80211_conf *conf)
 {
-	struct ieee80211_hw *hw = sc->hw;
-	struct ath_node *an = NULL;
-	struct ieee80211_rx_status rx_status;
-	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
-	int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-	int padsize;
-	enum ATH_RX_TYPE st;
-
-	/* see if any padding is done by the hw and remove it */
-	if (hdrlen & 3) {
-		padsize = hdrlen % 4;
-		memmove(skb->data + padsize, skb->data, hdrlen);
-		skb_pull(skb, padsize);
-	}
-
-	/* Prepare rx status */
-	ath9k_rx_prepare(sc, skb, status, &rx_status);
-
-	if (!(keyix == ATH9K_RXKEYIX_INVALID) &&
-	    !(status->flags & ATH_RX_DECRYPT_ERROR)) {
-		rx_status.flag |= RX_FLAG_DECRYPTED;
-	} else if ((le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_PROTECTED)
-		   && !(status->flags & ATH_RX_DECRYPT_ERROR)
-		   && skb->len >= hdrlen + 4) {
-		keyix = skb->data[hdrlen + 3] >> 6;
-
-		if (test_bit(keyix, sc->sc_keymap))
-			rx_status.flag |= RX_FLAG_DECRYPTED;
-	}
+	struct ath_softc *sc = hw->priv;
+	struct ieee80211_channel *curchan = hw->conf.channel;
+	int pos;
 
-	spin_lock_bh(&sc->node_lock);
-	an = ath_node_find(sc, hdr->addr2);
-	spin_unlock_bh(&sc->node_lock);
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set channel: %d MHz\n",
+		__func__,
+		curchan->center_freq);
 
-	if (an) {
-		ath_rx_input(sc, an,
-			     hw->conf.ht_conf.ht_supported,
-			     skb, status, &st);
+	pos = ath_get_channel(sc, curchan);
+	if (pos == -1) {
+		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__);
+		return -EINVAL;
 	}
-	if (!an || (st != ATH_RX_CONSUMED))
-		__ieee80211_rx(hw, skb, &rx_status);
 
-	return 0;
-}
+	sc->sc_ah->ah_channels[pos].chanmode =
+		(curchan->band == IEEE80211_BAND_2GHZ) ?
+		CHANNEL_G : CHANNEL_A;
 
-int ath_rx_subframe(struct ath_node *an,
-		    struct sk_buff *skb,
-		    struct ath_recv_status *status)
-{
-	struct ath_softc *sc = an->an_sc;
-	struct ieee80211_hw *hw = sc->hw;
-	struct ieee80211_rx_status rx_status;
+	if (sc->sc_curaid && hw->conf.ht_conf.ht_supported)
+		sc->sc_ah->ah_channels[pos].chanmode =
+			ath_get_extchanmode(sc, curchan);
 
-	/* Prepare rx status */
-	ath9k_rx_prepare(sc, skb, status, &rx_status);
-	if (!(status->flags & ATH_RX_DECRYPT_ERROR))
-		rx_status.flag |= RX_FLAG_DECRYPTED;
+	sc->sc_config.txpowlimit = 2 * conf->power_level;
 
-	__ieee80211_rx(hw, skb, &rx_status);
+	/* set h/w channel */
+	if (ath_set_channel(sc, &sc->sc_ah->ah_channels[pos]) < 0)
+		DPRINTF(sc, ATH_DBG_FATAL, "%s: Unable to set channel\n",
+			__func__);
 
 	return 0;
 }
 
-/********************************/
-/*	 LED functions		*/
-/********************************/
-
-static void ath_led_brightness(struct led_classdev *led_cdev,
-			       enum led_brightness brightness)
+static int ath9k_config_interface(struct ieee80211_hw *hw,
+				  struct ieee80211_vif *vif,
+				  struct ieee80211_if_conf *conf)
 {
-	struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
-	struct ath_softc *sc = led->sc;
+	struct ath_softc *sc = hw->priv;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ath_vap *avp;
+	u32 rfilt = 0;
+	int error, i;
+	DECLARE_MAC_BUF(mac);
 
-	switch (brightness) {
-	case LED_OFF:
-		if (led->led_type == ATH_LED_ASSOC ||
-		    led->led_type == ATH_LED_RADIO)
-			sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
-		ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN,
-				(led->led_type == ATH_LED_RADIO) ? 1 :
-				!!(sc->sc_flags & SC_OP_LED_ASSOCIATED));
-		break;
-	case LED_FULL:
-		if (led->led_type == ATH_LED_ASSOC)
-			sc->sc_flags |= SC_OP_LED_ASSOCIATED;
-		ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 0);
-		break;
-	default:
-		break;
+	avp = sc->sc_vaps[0];
+	if (avp == NULL) {
+		DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n",
+			__func__);
+		return -EINVAL;
 	}
-}
 
-static int ath_register_led(struct ath_softc *sc, struct ath_led *led,
-			    char *trigger)
-{
-	int ret;
+	/* TODO: Need to decide which hw opmode to use for multi-interface
+	 * cases */
+	if (vif->type == IEEE80211_IF_TYPE_AP &&
+	    ah->ah_opmode != ATH9K_M_HOSTAP) {
+		ah->ah_opmode = ATH9K_M_HOSTAP;
+		ath9k_hw_setopmode(ah);
+		ath9k_hw_write_associd(ah, sc->sc_myaddr, 0);
+		/* Request full reset to get hw opmode changed properly */
+		sc->sc_flags |= SC_OP_FULL_RESET;
+	}
 
-	led->sc = sc;
-	led->led_cdev.name = led->name;
-	led->led_cdev.default_trigger = trigger;
-	led->led_cdev.brightness_set = ath_led_brightness;
+	if ((conf->changed & IEEE80211_IFCC_BSSID) &&
+	    !is_zero_ether_addr(conf->bssid)) {
+		switch (vif->type) {
+		case IEEE80211_IF_TYPE_STA:
+		case IEEE80211_IF_TYPE_IBSS:
+			/* Update ratectrl about the new state */
+			ath_rate_newstate(sc, avp);
 
-	ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev);
-	if (ret)
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"Failed to register led:%s", led->name);
-	else
-		led->registered = 1;
-	return ret;
-}
+			/* Set BSSID */
+			memcpy(sc->sc_curbssid, conf->bssid, ETH_ALEN);
+			sc->sc_curaid = 0;
+			ath9k_hw_write_associd(sc->sc_ah, sc->sc_curbssid,
+					       sc->sc_curaid);
 
-static void ath_unregister_led(struct ath_led *led)
-{
-	if (led->registered) {
-		led_classdev_unregister(&led->led_cdev);
-		led->registered = 0;
-	}
-}
+			/* Set aggregation protection mode parameters */
+			sc->sc_config.ath_aggr_prot = 0;
 
-static void ath_deinit_leds(struct ath_softc *sc)
-{
-	ath_unregister_led(&sc->assoc_led);
-	sc->sc_flags &= ~SC_OP_LED_ASSOCIATED;
-	ath_unregister_led(&sc->tx_led);
-	ath_unregister_led(&sc->rx_led);
-	ath_unregister_led(&sc->radio_led);
-	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
-}
+			/*
+			 * Reset our TSF so that its value is lower than the
+			 * beacon that we are trying to catch.
+			 * Only then hw will update its TSF register with the
+			 * new beacon. Reset the TSF before setting the BSSID
+			 * to avoid allowing in any frames that would update
+			 * our TSF only to have us clear it
+			 * immediately thereafter.
+			 */
+			ath9k_hw_reset_tsf(sc->sc_ah);
 
-static void ath_init_leds(struct ath_softc *sc)
-{
-	char *trigger;
-	int ret;
+			/* Disable BMISS interrupt when we're not associated */
+			ath9k_hw_set_interrupts(sc->sc_ah,
+					sc->sc_imask &
+					~(ATH9K_INT_SWBA | ATH9K_INT_BMISS));
+			sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS);
 
-	/* Configure gpio 1 for output */
-	ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN,
-			    AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-	/* LED off, active low */
-	ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1);
+			DPRINTF(sc, ATH_DBG_CONFIG,
+				"%s: RX filter 0x%x bssid %s aid 0x%x\n",
+				__func__, rfilt,
+				print_mac(mac, sc->sc_curbssid), sc->sc_curaid);
 
-	trigger = ieee80211_get_radio_led_name(sc->hw);
-	snprintf(sc->radio_led.name, sizeof(sc->radio_led.name),
-		"ath9k-%s:radio", wiphy_name(sc->hw->wiphy));
-	ret = ath_register_led(sc, &sc->radio_led, trigger);
-	sc->radio_led.led_type = ATH_LED_RADIO;
-	if (ret)
-		goto fail;
+			/* need to reconfigure the beacon */
+			sc->sc_flags &= ~SC_OP_BEACONS ;
 
-	trigger = ieee80211_get_assoc_led_name(sc->hw);
-	snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name),
-		"ath9k-%s:assoc", wiphy_name(sc->hw->wiphy));
-	ret = ath_register_led(sc, &sc->assoc_led, trigger);
-	sc->assoc_led.led_type = ATH_LED_ASSOC;
-	if (ret)
-		goto fail;
+			break;
+		default:
+			break;
+		}
+	}
 
-	trigger = ieee80211_get_tx_led_name(sc->hw);
-	snprintf(sc->tx_led.name, sizeof(sc->tx_led.name),
-		"ath9k-%s:tx", wiphy_name(sc->hw->wiphy));
-	ret = ath_register_led(sc, &sc->tx_led, trigger);
-	sc->tx_led.led_type = ATH_LED_TX;
-	if (ret)
-		goto fail;
+	if ((conf->changed & IEEE80211_IFCC_BEACON) &&
+	    ((vif->type == IEEE80211_IF_TYPE_IBSS) ||
+	     (vif->type == IEEE80211_IF_TYPE_AP))) {
+		/*
+		 * Allocate and setup the beacon frame.
+		 *
+		 * Stop any previous beacon DMA.  This may be
+		 * necessary, for example, when an ibss merge
+		 * causes reconfiguration; we may be called
+		 * with beacon transmission active.
+		 */
+		ath9k_hw_stoptxdma(sc->sc_ah, sc->sc_bhalq);
 
-	trigger = ieee80211_get_rx_led_name(sc->hw);
-	snprintf(sc->rx_led.name, sizeof(sc->rx_led.name),
-		"ath9k-%s:rx", wiphy_name(sc->hw->wiphy));
-	ret = ath_register_led(sc, &sc->rx_led, trigger);
-	sc->rx_led.led_type = ATH_LED_RX;
-	if (ret)
-		goto fail;
+		error = ath_beacon_alloc(sc, 0);
+		if (error != 0)
+			return error;
 
-	return;
+		ath_beacon_sync(sc, 0);
+	}
 
-fail:
-	ath_deinit_leds(sc);
-}
+	/* Check for WLAN_CAPABILITY_PRIVACY ? */
+	if ((avp->av_opmode != IEEE80211_IF_TYPE_STA)) {
+		for (i = 0; i < IEEE80211_WEP_NKID; i++)
+			if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i))
+				ath9k_hw_keysetmac(sc->sc_ah,
+						   (u16)i,
+						   sc->sc_curbssid);
+	}
 
-static int ath_detach(struct ath_softc *sc)
-{
-	struct ieee80211_hw *hw = sc->hw;
+	/* Only legacy IBSS for now */
+	if (vif->type == IEEE80211_IF_TYPE_IBSS)
+		ath_update_chainmask(sc, 0);
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
+	return 0;
+}
 
-	/* Deinit LED control */
-	ath_deinit_leds(sc);
+#define SUPPORTED_FILTERS			\
+	(FIF_PROMISC_IN_BSS |			\
+	FIF_ALLMULTI |				\
+	FIF_CONTROL |				\
+	FIF_OTHER_BSS |				\
+	FIF_BCN_PRBRESP_PROMISC |		\
+	FIF_FCSFAIL)
 
-	/* Unregister hw */
+/* FIXME: sc->sc_full_reset ? */
+static void ath9k_configure_filter(struct ieee80211_hw *hw,
+				   unsigned int changed_flags,
+				   unsigned int *total_flags,
+				   int mc_count,
+				   struct dev_mc_list *mclist)
+{
+	struct ath_softc *sc = hw->priv;
+	u32 rfilt;
 
-	ieee80211_unregister_hw(hw);
+	changed_flags &= SUPPORTED_FILTERS;
+	*total_flags &= SUPPORTED_FILTERS;
 
-	/* unregister Rate control */
-	ath_rate_control_unregister();
+	sc->rx_filter = *total_flags;
+	rfilt = ath_calcrxfilter(sc);
+	ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
 
-	/* tx/rx cleanup */
+	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+		if (*total_flags & FIF_BCN_PRBRESP_PROMISC)
+			ath9k_hw_write_associd(sc->sc_ah, ath_bcast_mac, 0);
+	}
 
-	ath_rx_cleanup(sc);
-	ath_tx_cleanup(sc);
+	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n",
+		__func__, sc->rx_filter);
+}
 
-	/* Deinit */
+static void ath9k_sta_notify(struct ieee80211_hw *hw,
+			     struct ieee80211_vif *vif,
+			     enum sta_notify_cmd cmd,
+			     const u8 *addr)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_node *an;
+	unsigned long flags;
+	DECLARE_MAC_BUF(mac);
 
-	ath_deinit(sc);
+	spin_lock_irqsave(&sc->node_lock, flags);
+	an = ath_node_find(sc, (u8 *) addr);
+	spin_unlock_irqrestore(&sc->node_lock, flags);
 
-	return 0;
+	switch (cmd) {
+	case STA_NOTIFY_ADD:
+		spin_lock_irqsave(&sc->node_lock, flags);
+		if (!an) {
+			ath_node_attach(sc, (u8 *)addr, 0);
+			DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
+				__func__,
+				print_mac(mac, addr));
+		} else {
+			ath_node_get(sc, (u8 *)addr);
+		}
+		spin_unlock_irqrestore(&sc->node_lock, flags);
+		break;
+	case STA_NOTIFY_REMOVE:
+		if (!an)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Removal of a non-existent node\n",
+				__func__);
+		else {
+			ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT);
+			DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %s\n",
+				__func__,
+				print_mac(mac, addr));
+		}
+		break;
+	default:
+		break;
+	}
 }
 
-static int ath_attach(u16 devid,
-		      struct ath_softc *sc)
+static int ath9k_conf_tx(struct ieee80211_hw *hw,
+			 u16 queue,
+			 const struct ieee80211_tx_queue_params *params)
 {
-	struct ieee80211_hw *hw = sc->hw;
-	int error = 0;
+	struct ath_softc *sc = hw->priv;
+	struct ath9k_tx_queue_info qi;
+	int ret = 0, qnum;
 
-	DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
+	if (queue >= WME_NUM_AC)
+		return 0;
 
-	error = ath_init(devid, sc);
-	if (error != 0)
-		return error;
+	qi.tqi_aifs = params->aifs;
+	qi.tqi_cwmin = params->cw_min;
+	qi.tqi_cwmax = params->cw_max;
+	qi.tqi_burstTime = params->txop;
+	qnum = ath_get_hal_qnum(queue, sc);
 
-	/* Init nodes */
+	DPRINTF(sc, ATH_DBG_CONFIG,
+		"%s: Configure tx [queue/halq] [%d/%d],  "
+		"aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
+		__func__,
+		queue,
+		qnum,
+		params->aifs,
+		params->cw_min,
+		params->cw_max,
+		params->txop);
 
-	INIT_LIST_HEAD(&sc->node_list);
-	spin_lock_init(&sc->node_lock);
+	ret = ath_txq_update(sc, qnum, &qi);
+	if (ret)
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: TXQ Update failed\n", __func__);
 
-	/* get mac address from hardware and set in mac80211 */
+	return ret;
+}
 
-	SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
+static int ath9k_set_key(struct ieee80211_hw *hw,
+			 enum set_key_cmd cmd,
+			 const u8 *local_addr,
+			 const u8 *addr,
+			 struct ieee80211_key_conf *key)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret = 0;
 
-	/* setup channels and rates */
+	DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
 
-	sc->sbands[IEEE80211_BAND_2GHZ].channels =
-		sc->channels[IEEE80211_BAND_2GHZ];
-	sc->sbands[IEEE80211_BAND_2GHZ].bitrates =
-		sc->rates[IEEE80211_BAND_2GHZ];
-	sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
+	switch (cmd) {
+	case SET_KEY:
+		ret = ath_key_config(sc, addr, key);
+		if (!ret) {
+			set_bit(key->keyidx, sc->sc_keymap);
+			key->hw_key_idx = key->keyidx;
+			/* push IV and Michael MIC generation to stack */
+			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+			if (key->alg == ALG_TKIP)
+				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
+		}
+		break;
+	case DISABLE_KEY:
+		ath_key_delete(sc, key);
+		clear_bit(key->keyidx, sc->sc_keymap);
+		sc->sc_keytype = ATH9K_CIPHER_CLR;
+		break;
+	default:
+		ret = -EINVAL;
+	}
 
-	if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
-		/* Setup HT capabilities for 2.4Ghz*/
-		setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_info);
+	return ret;
+}
 
-	hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
-		&sc->sbands[IEEE80211_BAND_2GHZ];
+static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
+				   struct ieee80211_vif *vif,
+				   struct ieee80211_bss_conf *bss_conf,
+				   u32 changed)
+{
+	struct ath_softc *sc = hw->priv;
 
-	if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) {
-		sc->sbands[IEEE80211_BAND_5GHZ].channels =
-			sc->channels[IEEE80211_BAND_5GHZ];
-		sc->sbands[IEEE80211_BAND_5GHZ].bitrates =
-			sc->rates[IEEE80211_BAND_5GHZ];
-		sc->sbands[IEEE80211_BAND_5GHZ].band =
-			IEEE80211_BAND_5GHZ;
+	if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed PREAMBLE %d\n",
+			__func__,
+			bss_conf->use_short_preamble);
+		if (bss_conf->use_short_preamble)
+			sc->sc_flags |= SC_OP_PREAMBLE_SHORT;
+		else
+			sc->sc_flags &= ~SC_OP_PREAMBLE_SHORT;
+	}
 
-		if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT)
-			/* Setup HT capabilities for 5Ghz*/
-			setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_info);
+	if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed CTS PROT %d\n",
+			__func__,
+			bss_conf->use_cts_prot);
+		if (bss_conf->use_cts_prot &&
+		    hw->conf.channel->band != IEEE80211_BAND_5GHZ)
+			sc->sc_flags |= SC_OP_PROTECT_ENABLE;
+		else
+			sc->sc_flags &= ~SC_OP_PROTECT_ENABLE;
+	}
 
-		hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
-			&sc->sbands[IEEE80211_BAND_5GHZ];
+	if (changed & BSS_CHANGED_HT) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed HT %d\n",
+			__func__,
+			bss_conf->assoc_ht);
+		ath9k_ht_conf(sc, bss_conf);
 	}
 
-	/* FIXME: Have to figure out proper hw init values later */
+	if (changed & BSS_CHANGED_ASSOC) {
+		DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n",
+			__func__,
+			bss_conf->assoc);
+		ath9k_bss_assoc_info(sc, bss_conf);
+	}
+}
 
-	hw->queues = 4;
-	hw->ampdu_queues = 1;
+static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
+{
+	u64 tsf;
+	struct ath_softc *sc = hw->priv;
+	struct ath_hal *ah = sc->sc_ah;
 
-	/* Register rate control */
-	hw->rate_control_algorithm = "ath9k_rate_control";
-	error = ath_rate_control_register();
-	if (error != 0) {
-		DPRINTF(sc, ATH_DBG_FATAL,
-			"%s: Unable to register rate control "
-			"algorithm:%d\n", __func__, error);
-		ath_rate_control_unregister();
-		goto bad;
-	}
+	tsf = ath9k_hw_gettsf64(ah);
 
-	error = ieee80211_register_hw(hw);
-	if (error != 0) {
-		ath_rate_control_unregister();
-		goto bad;
-	}
+	return tsf;
+}
 
-	/* Initialize LED control */
-	ath_init_leds(sc);
+static void ath9k_reset_tsf(struct ieee80211_hw *hw)
+{
+	struct ath_softc *sc = hw->priv;
+	struct ath_hal *ah = sc->sc_ah;
 
-	/* initialize tx/rx engine */
+	ath9k_hw_reset_tsf(ah);
+}
 
-	error = ath_tx_init(sc, ATH_TXBUF);
-	if (error != 0)
-		goto detach;
+static int ath9k_ampdu_action(struct ieee80211_hw *hw,
+		       enum ieee80211_ampdu_mlme_action action,
+		       const u8 *addr,
+		       u16 tid,
+		       u16 *ssn)
+{
+	struct ath_softc *sc = hw->priv;
+	int ret = 0;
 
-	error = ath_rx_init(sc, ATH_RXBUF);
-	if (error != 0)
-		goto detach;
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+		ret = ath_rx_aggr_start(sc, addr, tid, ssn);
+		if (ret < 0)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Unable to start RX aggregation\n",
+				__func__);
+		break;
+	case IEEE80211_AMPDU_RX_STOP:
+		ret = ath_rx_aggr_stop(sc, addr, tid);
+		if (ret < 0)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Unable to stop RX aggregation\n",
+				__func__);
+		break;
+	case IEEE80211_AMPDU_TX_START:
+		ret = ath_tx_aggr_start(sc, addr, tid, ssn);
+		if (ret < 0)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Unable to start TX aggregation\n",
+				__func__);
+		else
+			ieee80211_start_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
+		break;
+	case IEEE80211_AMPDU_TX_STOP:
+		ret = ath_tx_aggr_stop(sc, addr, tid);
+		if (ret < 0)
+			DPRINTF(sc, ATH_DBG_FATAL,
+				"%s: Unable to stop TX aggregation\n",
+				__func__);
 
-	return 0;
-detach:
-	ath_detach(sc);
-bad:
-	return error;
+		ieee80211_stop_tx_ba_cb_irqsafe(hw, (u8 *)addr, tid);
+		break;
+	default:
+		DPRINTF(sc, ATH_DBG_FATAL,
+			"%s: Unknown AMPDU action\n", __func__);
+	}
+
+	return ret;
 }
 
+static struct ieee80211_ops ath9k_ops = {
+	.tx 		    = ath9k_tx,
+	.start 		    = ath9k_start,
+	.stop 		    = ath9k_stop,
+	.add_interface 	    = ath9k_add_interface,
+	.remove_interface   = ath9k_remove_interface,
+	.config 	    = ath9k_config,
+	.config_interface   = ath9k_config_interface,
+	.configure_filter   = ath9k_configure_filter,
+	.get_stats          = NULL,
+	.sta_notify         = ath9k_sta_notify,
+	.conf_tx 	    = ath9k_conf_tx,
+	.get_tx_stats 	    = NULL,
+	.bss_info_changed   = ath9k_bss_info_changed,
+	.set_tim            = NULL,
+	.set_key            = ath9k_set_key,
+	.hw_scan            = NULL,
+	.get_tkip_seq       = NULL,
+	.set_rts_threshold  = NULL,
+	.set_frag_threshold = NULL,
+	.set_retry_limit    = NULL,
+	.get_tsf 	    = ath9k_get_tsf,
+	.reset_tsf 	    = ath9k_reset_tsf,
+	.tx_last_beacon     = NULL,
+	.ampdu_action       = ath9k_ampdu_action
+};
+
 static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	void __iomem *mem;