|
@@ -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;
|