return 0;
}
-int ath_rx_subframe(struct ath_node *an,
- struct sk_buff *skb,
- struct ath_recv_status *status)
+#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)
{
- struct ath_softc *sc = an->an_sc;
- struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_rx_status rx_status;
+ struct ath_softc *sc = hw->priv;
+ u32 rfilt;
- /* Prepare rx status */
- ath9k_rx_prepare(sc, skb, status, &rx_status);
- if (!(status->flags & ATH_RX_DECRYPT_ERROR))
- rx_status.flag |= RX_FLAG_DECRYPTED;
+ changed_flags &= SUPPORTED_FILTERS;
+ *total_flags &= SUPPORTED_FILTERS;
- __ieee80211_rx(hw, skb, &rx_status);
+ sc->rx_filter = *total_flags;
+ rfilt = ath_calcrxfilter(sc);
+ ath9k_hw_setrxfilter(sc->sc_ah, rfilt);
- return 0;
-}
+ 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);
+ }
-enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc)
-{
- return sc->sc_ht_info.tx_chan_width;
+ DPRINTF(sc, ATH_DBG_CONFIG, "%s: Set HW RX filter: 0x%x\n",
+ __func__, sc->rx_filter);
}
-static int ath_detach(struct ath_softc *sc)
+static void ath9k_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta)
{
- struct ieee80211_hw *hw = sc->hw;
-
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__);
+ struct ath_softc *sc = hw->priv;
+ struct ath_node *an;
+ unsigned long flags;
+ DECLARE_MAC_BUF(mac);
- /* Unregister hw */
+ spin_lock_irqsave(&sc->node_lock, flags);
+ an = ath_node_find(sc, sta->addr);
+ spin_unlock_irqrestore(&sc->node_lock, flags);
- ieee80211_unregister_hw(hw);
+ switch (cmd) {
+ case STA_NOTIFY_ADD:
+ spin_lock_irqsave(&sc->node_lock, flags);
+ if (!an) {
+ ath_node_attach(sc, sta->addr, 0);
+ DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %s\n",
+ __func__, print_mac(mac, sta->addr));
+ } else {
+ ath_node_get(sc, sta->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, sta->addr));
+ }
+ break;
+ default:
+ break;
+ }
+}
- /* unregister Rate control */
- ath_rate_control_unregister();
+static int ath9k_conf_tx(struct ieee80211_hw *hw,
+ u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath9k_tx_queue_info qi;
+ int ret = 0, qnum;
- /* tx/rx cleanup */
+ if (queue >= WME_NUM_AC)
+ return 0;
- ath_rx_cleanup(sc);
- ath_tx_cleanup(sc);
+ 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 */
+ 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);
- ath_deinit(sc);
+ ret = ath_txq_update(sc, qnum, &qi);
+ if (ret)
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "%s: TXQ Update failed\n", __func__);
- return 0;
+ return ret;
}
-static int ath_attach(u16 devid,
- struct ath_softc *sc)
+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 ieee80211_hw *hw = sc->hw;
- int error = 0;
+ struct ath_softc *sc = hw->priv;
+ int ret = 0;
- DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach ATH hw\n", __func__);
+ DPRINTF(sc, ATH_DBG_KEYCACHE, " %s: Set HW Key\n", __func__);
- error = ath_init(devid, sc);
- if (error != 0)
- return error;
+ 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;
+ }
- /* Init nodes */
+ return ret;
+}
- INIT_LIST_HEAD(&sc->node_list);
- spin_lock_init(&sc->node_lock);
+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;
- /* get mac address from hardware and set in mac80211 */
+ 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;
+ }
- SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr);
+ 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 channels and rates */
+ 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);
+ }
- 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;
+ 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);
+ }
+}
- 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);
+static u64 ath9k_get_tsf(struct ieee80211_hw *hw)
+{
+ u64 tsf;
+ struct ath_softc *sc = hw->priv;
+ struct ath_hal *ah = sc->sc_ah;
- hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
- &sc->sbands[IEEE80211_BAND_2GHZ];
+ tsf = ath9k_hw_gettsf64(ah);
- 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;
+ return tsf;
+}
- 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);
+static void ath9k_reset_tsf(struct ieee80211_hw *hw)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_hal *ah = sc->sc_ah;
- hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
- &sc->sbands[IEEE80211_BAND_5GHZ];
- }
+ ath9k_hw_reset_tsf(ah);
+}
- /* FIXME: Have to figure out proper hw init values later */
+static int ath9k_ampdu_action(struct ieee80211_hw *hw,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta,
+ u16 tid, u16 *ssn)
+{
+ struct ath_softc *sc = hw->priv;
+ int ret = 0;
- hw->queues = 4;
- hw->ampdu_queues = 1;
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ ret = ath_rx_aggr_start(sc, sta->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, sta->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, sta->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, sta->addr, tid);
+ break;
+ case IEEE80211_AMPDU_TX_STOP:
+ ret = ath_tx_aggr_stop(sc, sta->addr, tid);
+ if (ret < 0)
+ DPRINTF(sc, ATH_DBG_FATAL,
+ "%s: Unable to stop TX aggregation\n",
+ __func__);
- /* Register rate control */
- hw->rate_control_algorithm = "ath9k_rate_control";
- error = ath_rate_control_register();
- if (error != 0) {
+ ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid);
+ break;
+ default:
DPRINTF(sc, ATH_DBG_FATAL,
- "%s: Unable to register rate control "
- "algorithm:%d\n", __func__, error);
- ath_rate_control_unregister();
- goto bad;
- }
-
- error = ieee80211_register_hw(hw);
- if (error != 0) {
- ath_rate_control_unregister();
- goto bad;
+ "%s: Unknown AMPDU action\n", __func__);
}
- /* initialize tx/rx engine */
-
- error = ath_tx_init(sc, ATH_TXBUF);
- if (error != 0)
- goto bad1;
-
- error = ath_rx_init(sc, ATH_RXBUF);
- if (error != 0)
- goto bad1;
-
- return 0;
-bad1:
- ath_detach(sc);
-bad:
- return error;
+ 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;
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath_softc *sc = hw->priv;
+ enum ath9k_int status;
- if (pdev->irq)
+ if (pdev->irq) {
+ ath9k_hw_set_interrupts(sc->sc_ah, 0);
+ /* clear the ISR */
+ ath9k_hw_getisr(sc->sc_ah, &status);
- sc->sc_invalid = 1;
++ sc->sc_flags |= SC_OP_INVALID;
free_irq(pdev->irq, sc);
+ }
ath_detach(sc);
+
pci_iounmap(pdev, sc->mem);
pci_release_region(pdev, 0);
pci_disable_device(pdev);