]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/wireless/ath5k/base.c
Merge branch 'x86-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-omap-h63xx.git] / drivers / net / wireless / ath5k / base.c
index 2d14255eb103d60096256eee89c78bbfe68f8550..8ef87356e083f28241db4298c1a0a0384732f340 100644 (file)
@@ -60,6 +60,9 @@
 #include "debug.h"
 
 static int ath5k_calinterval = 10; /* Calibrate PHY every 10 secs (TODO: Fixme) */
+static int modparam_nohwcrypt;
+module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444);
+MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption.");
 
 
 /******************\
@@ -197,7 +200,7 @@ static int          ath5k_pci_resume(struct pci_dev *pdev);
 #endif /* CONFIG_PM */
 
 static struct pci_driver ath5k_pci_driver = {
-       .name           = "ath5k_pci",
+       .name           = KBUILD_MODNAME,
        .id_table       = ath5k_pci_id_table,
        .probe          = ath5k_pci_probe,
        .remove         = __devexit_p(ath5k_pci_remove),
@@ -219,8 +222,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
                struct ieee80211_if_init_conf *conf);
 static void ath5k_remove_interface(struct ieee80211_hw *hw,
                struct ieee80211_if_init_conf *conf);
-static int ath5k_config(struct ieee80211_hw *hw,
-               struct ieee80211_conf *conf);
+static int ath5k_config(struct ieee80211_hw *hw, u32 changed);
 static int ath5k_config_interface(struct ieee80211_hw *hw,
                struct ieee80211_vif *vif,
                struct ieee80211_if_conf *conf);
@@ -238,7 +240,7 @@ static int ath5k_get_tx_stats(struct ieee80211_hw *hw,
                struct ieee80211_tx_queue_stats *stats);
 static u64 ath5k_get_tsf(struct ieee80211_hw *hw);
 static void ath5k_reset_tsf(struct ieee80211_hw *hw);
-static int ath5k_beacon_update(struct ieee80211_hw *hw,
+static int ath5k_beacon_update(struct ath5k_softc *sc,
                struct sk_buff *skb);
 static void ath5k_bss_info_changed(struct ieee80211_hw *hw,
                struct ieee80211_vif *vif,
@@ -548,8 +550,8 @@ ath5k_pci_probe(struct pci_dev *pdev,
 
        /* set up multi-rate retry capabilities */
        if (sc->ah->ah_version == AR5K_AR5212) {
-               hw->max_altrates = 3;
-               hw->max_altrate_tries = 11;
+               hw->max_rates = 4;
+               hw->max_rate_tries = 11;
        }
 
        /* Finish private driver data initialization */
@@ -711,7 +713,7 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
 {
        struct ath5k_softc *sc = hw->priv;
        struct ath5k_hw *ah = sc->ah;
-       u8 mac[ETH_ALEN];
+       u8 mac[ETH_ALEN] = {};
        int ret;
 
        ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
@@ -781,7 +783,13 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
        tasklet_init(&sc->restq, ath5k_tasklet_reset, (unsigned long)sc);
        setup_timer(&sc->calib_tim, ath5k_calibrate, (unsigned long)sc);
 
-       ath5k_hw_get_lladdr(ah, mac);
+       ret = ath5k_eeprom_read_mac(ah, mac);
+       if (ret) {
+               ATH5K_ERR(sc, "unable to read address from EEPROM: 0x%04x\n",
+                       sc->pdev->device);
+               goto err_queues;
+       }
+
        SET_IEEE80211_PERM_ADDR(hw, mac);
        /* All MAC address bits matter for ACKs */
        memset(sc->bssidmask, 0xff, ETH_ALEN);
@@ -1188,7 +1196,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
                ieee80211_get_hdrlen_from_skb(skb), AR5K_PKT_TYPE_NORMAL,
                (sc->power_level * 2),
                ieee80211_get_tx_rate(sc->hw, info)->hw_value,
-               info->control.retry_limit, keyidx, 0, flags, 0, 0);
+               info->control.rates[0].count, keyidx, 0, flags, 0, 0);
        if (ret)
                goto err_unmap;
 
@@ -1200,7 +1208,7 @@ ath5k_txbuf_setup(struct ath5k_softc *sc, struct ath5k_buf *bf)
                        break;
 
                mrr_rate[i] = rate->hw_value;
-               mrr_tries[i] = info->control.retries[i].limit;
+               mrr_tries[i] = info->control.rates[i + 1].count;
        }
 
        ah->ah_setup_mrr_tx_desc(ah, ds,
@@ -1660,7 +1668,7 @@ ath5k_tasklet_rx(unsigned long data)
        struct ath5k_desc *ds;
        int ret;
        int hdrlen;
-       int pad;
+       int padsize;
 
        spin_lock(&sc->rxbuflock);
        if (list_empty(&sc->rxbuf)) {
@@ -1745,16 +1753,19 @@ accept:
 
                skb_put(skb, rs.rs_datalen);
 
-               /*
-                * the hardware adds a padding to 4 byte boundaries between
-                * the header and the payload data if the header length is
-                * not multiples of 4 - remove it
-                */
+               /* The MAC header is padded to have 32-bit boundary if the
+                * packet payload is non-zero. The general calculation for
+                * padsize would take into account odd header lengths:
+                * padsize = (4 - hdrlen % 4) % 4; However, since only
+                * even-length headers are used, padding can only be 0 or 2
+                * bytes and we can optimize this a bit. In addition, we must
+                * not try to remove padding from short control frames that do
+                * not have payload. */
                hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-               if (hdrlen & 3) {
-                       pad = hdrlen % 4;
-                       memmove(skb->data + pad, skb->data, hdrlen);
-                       skb_pull(skb, pad);
+               padsize = ath5k_pad_size(hdrlen);
+               if (padsize) {
+                       memmove(skb->data + padsize, skb->data, hdrlen);
+                       skb_pull(skb, padsize);
                }
 
                /*
@@ -1785,7 +1796,17 @@ accept:
 
                rxs.noise = sc->ah->ah_noise_floor;
                rxs.signal = rxs.noise + rs.rs_rssi;
-               rxs.qual = rs.rs_rssi * 100 / 64;
+
+               /* An rssi of 35 indicates you should be able use
+                * 54 Mbps reliably. A more elaborate scheme can be used
+                * here but it requires a map of SNR/throughput for each
+                * possible mode used */
+               rxs.qual = rs.rs_rssi * 100 / 35;
+
+               /* rssi can be more than 35 though, anything above that
+                * should be considered at 100% */
+               if (rxs.qual > 100)
+                       rxs.qual = 100;
 
                rxs.antenna = rs.rs_antenna;
                rxs.rate_idx = ath5k_hw_to_driver_rix(sc, rs.rs_rate);
@@ -1846,30 +1867,26 @@ ath5k_tx_processq(struct ath5k_softc *sc, struct ath5k_txq *txq)
                pci_unmap_single(sc->pdev, bf->skbaddr, skb->len,
                                PCI_DMA_TODEVICE);
 
-               memset(&info->status, 0, sizeof(info->status));
-               info->tx_rate_idx = ath5k_hw_to_driver_rix(sc,
-                               ts.ts_rate[ts.ts_final_idx]);
-               info->status.retry_count = ts.ts_longretry;
-
+               ieee80211_tx_info_clear_status(info);
                for (i = 0; i < 4; i++) {
-                       struct ieee80211_tx_altrate *r =
-                               &info->status.retries[i];
+                       struct ieee80211_tx_rate *r =
+                               &info->status.rates[i];
 
                        if (ts.ts_rate[i]) {
-                               r->rate_idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
-                               r->limit = ts.ts_retry[i];
+                               r->idx = ath5k_hw_to_driver_rix(sc, ts.ts_rate[i]);
+                               r->count = ts.ts_retry[i];
                        } else {
-                               r->rate_idx = -1;
-                               r->limit = 0;
+                               r->idx = -1;
+                               r->count = 0;
                        }
                }
 
-               info->status.excessive_retries = 0;
+               /* count the successful attempt as well */
+               info->status.rates[ts.ts_final_idx].count++;
+
                if (unlikely(ts.ts_status)) {
                        sc->ll_stats.dot11ACKFailureCount++;
-                       if (ts.ts_status & AR5K_TXERR_XRETRY)
-                               info->status.excessive_retries = 1;
-                       else if (ts.ts_status & AR5K_TXERR_FILT)
+                       if (ts.ts_status & AR5K_TXERR_FILT)
                                info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
                } else {
                        info->flags |= IEEE80211_TX_STAT_ACK;
@@ -2143,8 +2160,6 @@ ath5k_beacon_update_timers(struct ath5k_softc *sc, u64 bc_tsf)
  *
  * In IBSS mode we use a self-linked tx descriptor if possible. We enable SWBA
  * interrupts to detect TSF updates only.
- *
- * AP mode is missing.
  */
 static void
 ath5k_beacon_config(struct ath5k_softc *sc)
@@ -2157,7 +2172,9 @@ ath5k_beacon_config(struct ath5k_softc *sc)
 
        if (sc->opmode == NL80211_IFTYPE_STATION) {
                sc->imask |= AR5K_INT_BMISS;
-       } else if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+       } else if (sc->opmode == NL80211_IFTYPE_ADHOC ||
+                       sc->opmode == NL80211_IFTYPE_MESH_POINT ||
+                       sc->opmode == NL80211_IFTYPE_AP) {
                /*
                 * In IBSS mode we use a self-linked tx descriptor and let the
                 * hardware send the beacons automatically. We have to load it
@@ -2169,13 +2186,15 @@ ath5k_beacon_config(struct ath5k_softc *sc)
 
                sc->imask |= AR5K_INT_SWBA;
 
-               if (ath5k_hw_hasveol(ah)) {
-                       spin_lock(&sc->block);
-                       ath5k_beacon_send(sc);
-                       spin_unlock(&sc->block);
-               }
+               if (sc->opmode == NL80211_IFTYPE_ADHOC) {
+                       if (ath5k_hw_hasveol(ah)) {
+                               spin_lock(&sc->block);
+                               ath5k_beacon_send(sc);
+                               spin_unlock(&sc->block);
+                       }
+               } else
+                       ath5k_beacon_update_timers(sc, -1);
        }
-       /* TODO else AP */
 
        ath5k_hw_set_imr(ah, sc->imask);
 }
@@ -2215,9 +2234,9 @@ ath5k_init(struct ath5k_softc *sc, bool is_resume)
         */
        sc->curchan = sc->hw->conf.channel;
        sc->curband = &sc->sbands[sc->curchan->band];
-       sc->imask = AR5K_INT_RX | AR5K_INT_TX | AR5K_INT_RXEOL |
-               AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL |
-               AR5K_INT_MIB;
+       sc->imask = AR5K_INT_RXOK | AR5K_INT_RXERR | AR5K_INT_RXEOL |
+               AR5K_INT_RXORN | AR5K_INT_TXDESC | AR5K_INT_TXEOL |
+               AR5K_INT_FATAL | AR5K_INT_GLOBAL | AR5K_INT_MIB;
        ret = ath5k_reset(sc, false, false);
        if (ret)
                goto done;
@@ -2409,9 +2428,10 @@ ath5k_intr(int irq, void *dev_id)
                                /* bump tx trigger level */
                                ath5k_hw_update_tx_triglevel(ah, true);
                        }
-                       if (status & AR5K_INT_RX)
+                       if (status & (AR5K_INT_RXOK | AR5K_INT_RXERR))
                                tasklet_schedule(&sc->rxtq);
-                       if (status & AR5K_INT_TX)
+                       if (status & (AR5K_INT_TXOK | AR5K_INT_TXDESC
+                                       | AR5K_INT_TXERR | AR5K_INT_TXEOL))
                                tasklet_schedule(&sc->txtq);
                        if (status & AR5K_INT_BMISS) {
                        }
@@ -2527,8 +2547,7 @@ ath5k_register_led(struct ath5k_softc *sc, struct ath5k_led *led,
        led->led_dev.brightness_set = ath5k_led_brightness_set;
 
        err = led_classdev_register(&sc->pdev->dev, &led->led_dev);
-       if (err)
-       {
+       if (err) {
                ATH5K_WARN(sc, "could not register LED %s\n", name);
                led->sc = NULL;
        }
@@ -2607,7 +2626,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
        struct ath5k_buf *bf;
        unsigned long flags;
        int hdrlen;
-       int pad;
+       int padsize;
 
        ath5k_debug_dump_skb(sc, skb, "TX  ", 1);
 
@@ -2619,15 +2638,16 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
         * if this is not the case we add the padding after the header
         */
        hdrlen = ieee80211_get_hdrlen_from_skb(skb);
-       if (hdrlen & 3) {
-               pad = hdrlen % 4;
-               if (skb_headroom(skb) < pad) {
+       padsize = ath5k_pad_size(hdrlen);
+       if (padsize) {
+
+               if (skb_headroom(skb) < padsize) {
                        ATH5K_ERR(sc, "tx hdrlen not %%4: %d not enough"
-                               " headroom to pad %d\n", hdrlen, pad);
-                       return -1;
+                                 " headroom to pad %d\n", hdrlen, padsize);
+                       return NETDEV_TX_BUSY;
                }
-               skb_push(skb, pad);
-               memmove(skb->data, skb->data+pad, hdrlen);
+               skb_push(skb, padsize);
+               memmove(skb->data, skb->data+padsize, hdrlen);
        }
 
        spin_lock_irqsave(&sc->txbuflock, flags);
@@ -2635,7 +2655,7 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                ATH5K_ERR(sc, "no further txbuf available, dropping packet\n");
                spin_unlock_irqrestore(&sc->txbuflock, flags);
                ieee80211_stop_queue(hw, skb_get_queue_mapping(skb));
-               return -1;
+               return NETDEV_TX_BUSY;
        }
        bf = list_first_entry(&sc->txbuf, struct ath5k_buf, list);
        list_del(&bf->list);
@@ -2653,10 +2673,10 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
                sc->txbuf_len++;
                spin_unlock_irqrestore(&sc->txbuflock, flags);
                dev_kfree_skb_any(skb);
-               return 0;
+               return NETDEV_TX_OK;
        }
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static int
@@ -2746,8 +2766,10 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        sc->vif = conf->vif;
 
        switch (conf->type) {
+       case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_STATION:
        case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_MONITOR:
                sc->opmode = conf->type;
                break;
@@ -2759,6 +2781,7 @@ static int ath5k_add_interface(struct ieee80211_hw *hw,
        /* Set to a reasonable value. Note that this will
         * be set to mac80211's value at ath5k_config(). */
        sc->bintval = 1000;
+       ath5k_hw_set_lladdr(sc->ah, conf->mac_addr);
 
        ret = 0;
 end:
@@ -2771,11 +2794,13 @@ ath5k_remove_interface(struct ieee80211_hw *hw,
                        struct ieee80211_if_init_conf *conf)
 {
        struct ath5k_softc *sc = hw->priv;
+       u8 mac[ETH_ALEN] = {};
 
        mutex_lock(&sc->lock);
        if (sc->vif != conf->vif)
                goto end;
 
+       ath5k_hw_set_lladdr(sc->ah, mac);
        sc->vif = NULL;
 end:
        mutex_unlock(&sc->lock);
@@ -2785,10 +2810,10 @@ end:
  * TODO: Phy disable/diversity etc
  */
 static int
-ath5k_config(struct ieee80211_hw *hw,
-                       struct ieee80211_conf *conf)
+ath5k_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct ath5k_softc *sc = hw->priv;
+       struct ieee80211_conf *conf = &hw->conf;
 
        sc->bintval = conf->beacon_int;
        sc->power_level = conf->power_level;
@@ -2809,7 +2834,7 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                ret = -EIO;
                goto unlock;
        }
-       if (conf->bssid) {
+       if (conf->changed & IEEE80211_IFCC_BSSID && conf->bssid) {
                /* Cache for later use during resets */
                memcpy(ah->ah_bssid, conf->bssid, ETH_ALEN);
                /* XXX: assoc id is set to 0 for now, mac80211 doesn't have
@@ -2817,18 +2842,17 @@ ath5k_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                ath5k_hw_set_associd(ah, ah->ah_bssid, 0);
                mmiowb();
        }
-
        if (conf->changed & IEEE80211_IFCC_BEACON &&
-           vif->type == NL80211_IFTYPE_ADHOC) {
+                       (vif->type == NL80211_IFTYPE_ADHOC ||
+                        vif->type == NL80211_IFTYPE_MESH_POINT ||
+                        vif->type == NL80211_IFTYPE_AP)) {
                struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
                if (!beacon) {
                        ret = -ENOMEM;
                        goto unlock;
                }
-               /* call old handler for now */
-               ath5k_beacon_update(hw, beacon);
+               ath5k_beacon_update(sc, beacon);
        }
-
        mutex_unlock(&sc->lock);
 
        return ath5k_reset_wake(sc);
@@ -2888,9 +2912,9 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
                if (*new_flags & FIF_PROMISC_IN_BSS) {
                        rfilt |= AR5K_RX_FILTER_PROM;
                        __set_bit(ATH_STAT_PROMISC, sc->status);
-               }
-               else
+               } else {
                        __clear_bit(ATH_STAT_PROMISC, sc->status);
+               }
        }
 
        /* Note, AR5K_RX_FILTER_MCAST is already enabled */
@@ -2948,12 +2972,15 @@ static void ath5k_configure_filter(struct ieee80211_hw *hw,
                test_bit(ATH_STAT_PROMISC, sc->status))
                rfilt |= AR5K_RX_FILTER_PROM;
        if ((sc->opmode == NL80211_IFTYPE_STATION && sc->assoc) ||
-               sc->opmode == NL80211_IFTYPE_ADHOC) {
+               sc->opmode == NL80211_IFTYPE_ADHOC ||
+               sc->opmode == NL80211_IFTYPE_AP)
                rfilt |= AR5K_RX_FILTER_BEACON;
-       }
+       if (sc->opmode == NL80211_IFTYPE_MESH_POINT)
+               rfilt |= AR5K_RX_FILTER_CONTROL | AR5K_RX_FILTER_BEACON |
+                       AR5K_RX_FILTER_PROBEREQ | AR5K_RX_FILTER_PROM;
 
        /* Set filters */
-       ath5k_hw_set_rx_filter(ah,rfilt);
+       ath5k_hw_set_rx_filter(ah, rfilt);
 
        /* Set multicast bits */
        ath5k_hw_set_mcast_filter(ah, mfilt[0], mfilt[1]);
@@ -2970,12 +2997,13 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
        struct ath5k_softc *sc = hw->priv;
        int ret = 0;
 
-       switch(key->alg) {
+       if (modparam_nohwcrypt)
+               return -EOPNOTSUPP;
+
+       switch (key->alg) {
        case ALG_WEP:
-       /* XXX: fix hardware encryption, its not working. For now
-        * allow software encryption */
-               /* break; */
        case ALG_TKIP:
+               break;
        case ALG_CCMP:
                return -EOPNOTSUPP;
        default:
@@ -2994,6 +3022,8 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                }
                __set_bit(key->keyidx, sc->keymap);
                key->hw_key_idx = key->keyidx;
+               key->flags |= (IEEE80211_KEY_FLAG_GENERATE_IV |
+                              IEEE80211_KEY_FLAG_GENERATE_MMIC);
                break;
        case DISABLE_KEY:
                ath5k_hw_reset_key(sc->ah, key->keyidx);
@@ -3060,19 +3090,13 @@ ath5k_reset_tsf(struct ieee80211_hw *hw)
 }
 
 static int
-ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
+ath5k_beacon_update(struct ath5k_softc *sc, struct sk_buff *skb)
 {
-       struct ath5k_softc *sc = hw->priv;
        unsigned long flags;
        int ret;
 
        ath5k_debug_dump_skb(sc, skb, "BC  ", 1);
 
-       if (sc->opmode != NL80211_IFTYPE_ADHOC) {
-               ret = -EIO;
-               goto end;
-       }
-
        spin_lock_irqsave(&sc->block, flags);
        ath5k_txbuf_free(sc, sc->bbuf);
        sc->bbuf->skb = skb;
@@ -3085,7 +3109,6 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
                mmiowb();
        }
 
-end:
        return ret;
 }
 static void