From 90852f7aed0f90d443efd7e0f9b82d8ac8186848 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Sat, 2 Jan 2010 10:31:42 +0100 Subject: mwl8k: fix configure_filter() memory leak on error If there was an error acquiring the firmware lock in mwl8k_configure_filter(), we would end up leaking the multicast command packet prepared by mwl8k_prepare_multicast(). Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 59d49159cf2..59f92105b0c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3157,8 +3157,10 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, /* Clear unsupported feature flags */ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; - if (mwl8k_fw_lock(hw)) + if (mwl8k_fw_lock(hw)) { + kfree(cmd); return; + } if (priv->sniffer_enabled) { mwl8k_enable_sniffer(hw, 0); -- cgit v1.2.3 From 6976b665fc2b19900659b964bba3b55de08f264f Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Sat, 2 Jan 2010 10:31:50 +0100 Subject: mwl8k: update version number to 0.11 Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index c1c6ecd0c5b..9545ff8d642 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -26,7 +26,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.10" +#define MWL8K_VERSION "0.11" /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 -- cgit v1.2.3 From 91942230689c1758685499e82e53769d5e7f32eb Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:53:54 +0100 Subject: mwl8k: bail out if there is no AP firmware image support for this chip Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 9545ff8d642..bfaa7911b33 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3361,10 +3361,17 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, mwl8k_release_firmware(priv); - if (priv->ap_fw) + if (priv->ap_fw) { priv->rxd_ops = priv->device_info->ap_rxd_ops; - else + if (priv->rxd_ops == NULL) { + printk(KERN_ERR "%s: Driver does not have AP " + "firmware image support for this hardware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + } else { priv->rxd_ops = &rxd_sta_ops; + } priv->sniffer_enabled = false; priv->wmm_enabled = false; -- cgit v1.2.3 From 153458ff7ed5949cd5b07a2355a34aad9891ad98 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:54:08 +0100 Subject: mwl8k: prevent freeing free IRQ if ieee80211_register_hw() fails Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index bfaa7911b33..2c286d18bae 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3501,7 +3501,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot register device\n", wiphy_name(hw->wiphy)); - goto err_free_irq; + goto err_free_queues; } printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n", -- cgit v1.2.3 From 9a2303b93039d0f3dd578cd75fe20ebaf075f1cb Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:54:25 +0100 Subject: mwl8k: make the tx ring drain status messages somewhat more friendly Old: > phy0: timeout waiting for tx rings to drain (9 -> 5 pkts), retrying > phy0: timeout waiting for tx rings to drain (5 -> 2 pkts), retrying > phy0: tx rings drained New: > phy0: waiting for tx rings to drain (9 -> 5 pkts) > phy0: waiting for tx rings to drain (5 -> 2 pkts) > phy0: tx rings drained Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 2c286d18bae..46a5cf214a2 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1269,8 +1269,8 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) } if (priv->pending_tx_pkts < oldcount) { - printk(KERN_NOTICE "%s: timeout waiting for tx " - "rings to drain (%d -> %d pkts), retrying\n", + printk(KERN_NOTICE "%s: waiting for tx rings " + "to drain (%d -> %d pkts)\n", wiphy_name(hw->wiphy), oldcount, priv->pending_tx_pkts); retry = 1; -- cgit v1.2.3 From 25d81b1e1a0cca41a71a08468a7d3a4c751c8565 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:54:41 +0100 Subject: mwl8k: move struct peer_capability_info to its only user Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 91 +++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 48 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 46a5cf214a2..3dbdea90483 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -598,54 +598,6 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) } -/* - * Defines shared between transmission and reception. - */ -/* HT control fields for firmware */ -struct ewc_ht_info { - __le16 control1; - __le16 control2; - __le16 control3; -} __attribute__((packed)); - -/* Firmware Station database operations */ -#define MWL8K_STA_DB_ADD_ENTRY 0 -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 -#define MWL8K_STA_DB_FLUSH 3 - -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 - -struct peer_capability_info { - /* Peer type - AP vs. STA. */ - __u8 peer_type; - - /* Basic 802.11 capabilities from assoc resp. */ - __le16 basic_caps; - - /* Set if peer supports 802.11n high throughput (HT). */ - __u8 ht_support; - - /* Valid if HT is supported. */ - __le16 ht_caps; - __u8 extended_ht_caps; - struct ewc_ht_info ewc_info; - - /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[12]; - - /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[16]; - __u8 pad[16]; - - /* If set, interoperability mode, no proprietary extensions. */ - __u8 interop; - __u8 pad2; - __u8 station_id; - __le16 amsdu_enabled; -} __attribute__((packed)); - /* DMA header used by firmware and hardware. */ struct mwl8k_dma_data { __le16 fwlen; @@ -2630,6 +2582,49 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) /* * CMD_UPDATE_STADB. */ +#define MWL8K_STA_DB_ADD_ENTRY 0 +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 +#define MWL8K_STA_DB_FLUSH 3 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +struct ewc_ht_info { + __le16 control1; + __le16 control2; + __le16 control3; +} __attribute__((packed)); + +struct peer_capability_info { + /* Peer type - AP vs. STA. */ + __u8 peer_type; + + /* Basic 802.11 capabilities from assoc resp. */ + __le16 basic_caps; + + /* Set if peer supports 802.11n high throughput (HT). */ + __u8 ht_support; + + /* Valid if HT is supported. */ + __le16 ht_caps; + __u8 extended_ht_caps; + struct ewc_ht_info ewc_info; + + /* Legacy rate table. Intersection of our rates and peer rates. */ + __u8 legacy_rates[12]; + + /* HT rate table. Intersection of our rates and peer rates. */ + __u8 ht_rates[16]; + __u8 pad[16]; + + /* If set, interoperability mode, no proprietary extensions. */ + __u8 interop; + __u8 pad2; + __u8 station_id; + __le16 amsdu_enabled; +} __attribute__((packed)); + struct mwl8k_cmd_update_stadb { struct mwl8k_cmd_pkt header; -- cgit v1.2.3 From bbfd9128d3b4a80bea017ebdd47b31a80dc7eadf Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:55:12 +0100 Subject: mwl8k: handle station database update for AP's sta entry via ->sta_notify() Inserting and removing a hardware station database entry for the AP when we are in managed mode is currently done in ->bss_info_changed(). To prepare for adding AP mode support, implement the ->sta_notify() driver method, and let that handle inserting and removing the hardware station database entry for our AP instead. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 82 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 3dbdea90483..df0596ca710 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -185,6 +185,10 @@ struct mwl8k_priv { bool sniffer_enabled; bool wmm_enabled; + struct work_struct sta_notify_worker; + spinlock_t sta_notify_list_lock; + struct list_head sta_notify_list; + /* XXX need to convert this to handle multiple interfaces */ bool capture_beacon; u8 capture_bssid[ETH_ALEN]; @@ -2641,7 +2645,7 @@ struct mwl8k_cmd_update_stadb { } __attribute__((packed)); static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, __u32 action) + struct ieee80211_vif *vif, __u32 action, u8 *addr) { struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_stadb *cmd; @@ -2657,7 +2661,7 @@ static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, cmd->action = cpu_to_le32(action); peer_info = &cmd->peer_info; - memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN); + memcpy(cmd->peer_addr, addr, ETH_ALEN); switch (action) { case MWL8K_STA_DB_ADD_ENTRY: @@ -2978,12 +2982,6 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, if (rc) goto out; - /* Update peer rate info */ - rc = mwl8k_cmd_update_stadb(hw, vif, - MWL8K_STA_DB_MODIFY_ENTRY); - if (rc) - goto out; - /* Set AID */ rc = mwl8k_cmd_set_aid(hw, vif); if (rc) @@ -2996,7 +2994,6 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); priv->capture_beacon = true; } else { - rc = mwl8k_cmd_update_stadb(hw, vif, MWL8K_STA_DB_DEL_ENTRY); memset(mwl8k_vif->bssid, 0, ETH_ALEN); } @@ -3142,6 +3139,67 @@ static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return mwl8k_cmd_set_rts_threshold(hw, MWL8K_CMD_SET, value); } +struct mwl8k_sta_notify_item +{ + struct list_head list; + struct ieee80211_vif *vif; + enum sta_notify_cmd cmd; + u8 addr[ETH_ALEN]; +}; + +static void mwl8k_sta_notify_worker(struct work_struct *work) +{ + struct mwl8k_priv *priv = + container_of(work, struct mwl8k_priv, sta_notify_worker); + + spin_lock_bh(&priv->sta_notify_list_lock); + while (!list_empty(&priv->sta_notify_list)) { + struct mwl8k_sta_notify_item *s; + int action; + + s = list_entry(priv->sta_notify_list.next, + struct mwl8k_sta_notify_item, list); + list_del(&s->list); + + spin_unlock_bh(&priv->sta_notify_list_lock); + + if (s->cmd == STA_NOTIFY_ADD) + action = MWL8K_STA_DB_MODIFY_ENTRY; + else + action = MWL8K_STA_DB_DEL_ENTRY; + mwl8k_cmd_update_stadb(priv->hw, s->vif, action, s->addr); + + kfree(s); + + spin_lock_bh(&priv->sta_notify_list_lock); + } + spin_unlock_bh(&priv->sta_notify_list_lock); +} + +static void +mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_sta_notify_item *s; + + if (cmd != STA_NOTIFY_ADD && cmd != STA_NOTIFY_REMOVE) + return; + + s = kmalloc(sizeof(*s), GFP_ATOMIC); + if (s != NULL) { + s->vif = vif; + s->cmd = cmd; + memcpy(s->addr, sta->addr, ETH_ALEN); + + spin_lock(&priv->sta_notify_list_lock); + list_add_tail(&s->list, &priv->sta_notify_list); + spin_unlock(&priv->sta_notify_list_lock); + + ieee80211_queue_work(hw, &priv->sta_notify_worker); + } +} + static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { @@ -3201,6 +3259,7 @@ static const struct ieee80211_ops mwl8k_ops = { .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, + .sta_notify = mwl8k_sta_notify, .conf_tx = mwl8k_conf_tx, .get_tx_stats = mwl8k_get_tx_stats, .get_stats = mwl8k_get_stats, @@ -3404,6 +3463,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, priv->radio_on = 0; priv->radio_short_preamble = 0; + /* Station database handling */ + INIT_WORK(&priv->sta_notify_worker, mwl8k_sta_notify_worker); + spin_lock_init(&priv->sta_notify_list_lock); + INIT_LIST_HEAD(&priv->sta_notify_list); + /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); -- cgit v1.2.3 From 0a11dfc36604d9b24deda17461b7ea69851846aa Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:55:21 +0100 Subject: mwl8k: remove mwl8k_vif::bssid, which is now useless Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index df0596ca710..ed666b7e7e4 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -211,9 +211,6 @@ struct mwl8k_vif { /* Local MAC address. */ u8 mac_addr[ETH_ALEN]; - /* BSSID of AP. */ - u8 bssid[ETH_ALEN]; - /* Index into station database. Returned by UPDATE_STADB. */ u8 peer_id; @@ -2001,7 +1998,7 @@ struct mwl8k_cmd_set_post_scan { } __attribute__((packed)); static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac) +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; @@ -2077,7 +2074,6 @@ struct mwl8k_cmd_update_set_aid { static int mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_set_aid *cmd; u16 prot_mode; int rc; @@ -2090,7 +2086,7 @@ mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(vif->bss_conf.aid); - memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN); + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); if (vif->bss_conf.use_cts_prot) { prot_mode = MWL8K_FRAME_PROT_11G; @@ -2945,7 +2941,6 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, u32 changed) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); int rc; if ((changed & BSS_CHANGED_ASSOC) == 0) @@ -2958,8 +2953,6 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, return; if (vif->bss_conf.assoc) { - memcpy(mwl8k_vif->bssid, vif->bss_conf.bssid, ETH_ALEN); - /* Install rates */ rc = mwl8k_cmd_set_rate(hw, vif); if (rc) @@ -2991,10 +2984,8 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ - memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); + memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); priv->capture_beacon = true; - } else { - memset(mwl8k_vif->bssid, 0, ETH_ALEN); } out: @@ -3097,7 +3088,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ mwl8k_cmd_set_pre_scan(hw); } else { - u8 *bssid; + const u8 *bssid; /* * Enable the BSS filter. @@ -3109,7 +3100,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ bssid = "\x01\x00\x00\x00\x00\x00"; if (priv->vif != NULL) - bssid = MWL8K_VIF(priv->vif)->bssid; + bssid = priv->vif->bss_conf.bssid; mwl8k_cmd_set_post_scan(hw, bssid); } -- cgit v1.2.3 From a680400e8ac32adda81b5e2d7f23dfac63e064fe Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:55:42 +0100 Subject: mwl8k: move ->peer_id from mwl8k_vif to mwl8k_sta For STA firmware, move the per-peer hardware station ID to the driver-private part of struct ieee80211_sta, where it belongs. (Since issuing a hardware station database maintenance command sleeps, we can't hold a reference to the ieee80211_sta * across the command, and since we won't know the station ID until after the command completes, we need to re-lookup the sta when the command is done to write the returned station ID back to its driver-private part.) Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 119 ++++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 52 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ed666b7e7e4..5b5cf9357db 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -211,15 +211,17 @@ struct mwl8k_vif { /* Local MAC address. */ u8 mac_addr[ETH_ALEN]; - /* Index into station database. Returned by UPDATE_STADB. */ - u8 peer_id; - /* Non AMPDU sequence number assigned by driver */ - u16 seqno; + u16 seqno; }; - #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) +struct mwl8k_sta { + /* Index into station database. Returned by UPDATE_STADB. */ + u8 peer_id; +}; +#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) + static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, @@ -1402,7 +1404,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; - tx->peer_id = mwl8k_vif->peer_id; + if (!priv->ap_fw && tx_info->control.sta != NULL) + tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; + else + tx->peer_id = 0; wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); @@ -2582,14 +2587,6 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) /* * CMD_UPDATE_STADB. */ -#define MWL8K_STA_DB_ADD_ENTRY 0 -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 -#define MWL8K_STA_DB_FLUSH 3 - -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 - struct ewc_ht_info { __le16 control1; __le16 control2; @@ -2640,12 +2637,17 @@ struct mwl8k_cmd_update_stadb { struct peer_capability_info peer_info; } __attribute__((packed)); -static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, __u32 action, u8 *addr) +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_stadb *cmd; - struct peer_capability_info *peer_info; + struct peer_capability_info *p; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2654,37 +2656,38 @@ static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); + memcpy(cmd->peer_addr, addr, ETH_ALEN); - cmd->action = cpu_to_le32(action); - peer_info = &cmd->peer_info; + p = &cmd->peer_info; + p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; + p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); + memcpy(p->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + p->interop = 1; + p->amsdu_enabled = 0; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc ? rc : p->station_id; +} + +static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_update_stadb *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); memcpy(cmd->peer_addr, addr, ETH_ALEN); - switch (action) { - case MWL8K_STA_DB_ADD_ENTRY: - case MWL8K_STA_DB_MODIFY_ENTRY: - /* Build peer_info block */ - peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; - peer_info->basic_caps = - cpu_to_le16(vif->bss_conf.assoc_capability); - memcpy(peer_info->legacy_rates, mwl8k_rateids, - sizeof(mwl8k_rateids)); - peer_info->interop = 1; - peer_info->amsdu_enabled = 0; - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = peer_info->station_id; - - break; - - case MWL8K_STA_DB_DEL_ENTRY: - case MWL8K_STA_DB_FLUSH: - default: - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = 0; - break; - } + rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; @@ -3142,11 +3145,11 @@ static void mwl8k_sta_notify_worker(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, sta_notify_worker); + struct ieee80211_hw *hw = priv->hw; spin_lock_bh(&priv->sta_notify_list_lock); while (!list_empty(&priv->sta_notify_list)) { struct mwl8k_sta_notify_item *s; - int action; s = list_entry(priv->sta_notify_list.next, struct mwl8k_sta_notify_item, list); @@ -3154,11 +3157,22 @@ static void mwl8k_sta_notify_worker(struct work_struct *work) spin_unlock_bh(&priv->sta_notify_list_lock); - if (s->cmd == STA_NOTIFY_ADD) - action = MWL8K_STA_DB_MODIFY_ENTRY; - else - action = MWL8K_STA_DB_DEL_ENTRY; - mwl8k_cmd_update_stadb(priv->hw, s->vif, action, s->addr); + if (s->cmd == STA_NOTIFY_ADD) { + int rc; + + rc = mwl8k_cmd_update_stadb_add(hw, s->vif, s->addr); + if (rc >= 0) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + sta = ieee80211_find_sta(s->vif, s->addr); + if (sta != NULL) + MWL8K_STA(sta)->peer_id = rc; + rcu_read_unlock(); + } + } else { + mwl8k_cmd_update_stadb_del(hw, s->vif, s->addr); + } kfree(s); @@ -3448,6 +3462,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* Set rssi and noise values to dBm */ hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->vif_data_size = sizeof(struct mwl8k_vif); + hw->sta_data_size = sizeof(struct mwl8k_sta); priv->vif = NULL; /* Set default radio state and preamble */ -- cgit v1.2.3 From c6e9601071173fed2a77f9c435c41f3b33d1018f Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:55:52 +0100 Subject: mwl8k: honor peer rate set When calling SET_RATE, SET_AID, or when creating a station database entry for our AP, pass in the AP's rate set instead of just blindly enabling all legacy rates, so as to end up doing the right thing when talking to 11b-only APs. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 57 +++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 5b5cf9357db..d8eff68978f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -256,10 +256,6 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 720, .hw_value = 144, }, }; -static const u8 mwl8k_rateids[12] = { - 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, -}; - /* Set or get info from Firmware */ #define MWL8K_CMD_SET 0x0001 #define MWL8K_CMD_GET 0x0000 @@ -2076,8 +2072,25 @@ struct mwl8k_cmd_update_set_aid { __u8 supp_rates[14]; } __attribute__((packed)); +static void legacy_rate_mask_to_array(u8 *rates, u32 mask) +{ + int i; + int j; + + /* + * Clear nonstandard rates 4 and 13. + */ + mask &= 0x1fef; + + for (i = 0, j = 0; i < 14; i++) { + if (mask & (1 << i)) + rates[j++] = mwl8k_rates[i].hw_value; + } +} + static int -mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +mwl8k_cmd_set_aid(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 legacy_rate_mask) { struct mwl8k_cmd_update_set_aid *cmd; u16 prot_mode; @@ -2090,7 +2103,6 @@ mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(vif->bss_conf.aid); - memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); if (vif->bss_conf.use_cts_prot) { @@ -2111,7 +2123,7 @@ mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } cmd->protection_mode = cpu_to_le16(prot_mode); - memcpy(cmd->supp_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2132,7 +2144,8 @@ struct mwl8k_cmd_set_rate { } __attribute__((packed)); static int -mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 legacy_rate_mask) { struct mwl8k_cmd_set_rate *cmd; int rc; @@ -2143,7 +2156,7 @@ mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2644,7 +2657,8 @@ struct mwl8k_cmd_update_stadb { #define MWL8K_PEER_TYPE_ACCESSPOINT 2 static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u8 *addr) + struct ieee80211_vif *vif, + u8 *addr, u32 legacy_rate_mask) { struct mwl8k_cmd_update_stadb *cmd; struct peer_capability_info *p; @@ -2662,7 +2676,7 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, p = &cmd->peer_info; p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); - memcpy(p->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + legacy_rate_mask_to_array(p->legacy_rates, legacy_rate_mask); p->interop = 1; p->amsdu_enabled = 0; @@ -2956,8 +2970,20 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, return; if (vif->bss_conf.assoc) { + struct ieee80211_sta *ap; + u32 legacy_rate_mask; + + rcu_read_lock(); + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap != NULL) + legacy_rate_mask = ap->supp_rates[IEEE80211_BAND_2GHZ]; + rcu_read_unlock(); + + if (ap == NULL) + goto out; + /* Install rates */ - rc = mwl8k_cmd_set_rate(hw, vif); + rc = mwl8k_cmd_set_rate(hw, vif, legacy_rate_mask); if (rc) goto out; @@ -2979,7 +3005,7 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, goto out; /* Set AID */ - rc = mwl8k_cmd_set_aid(hw, vif); + rc = mwl8k_cmd_set_aid(hw, vif, legacy_rate_mask); if (rc) goto out; @@ -3139,6 +3165,7 @@ struct mwl8k_sta_notify_item struct ieee80211_vif *vif; enum sta_notify_cmd cmd; u8 addr[ETH_ALEN]; + u32 legacy_rate_mask; }; static void mwl8k_sta_notify_worker(struct work_struct *work) @@ -3160,7 +3187,8 @@ static void mwl8k_sta_notify_worker(struct work_struct *work) if (s->cmd == STA_NOTIFY_ADD) { int rc; - rc = mwl8k_cmd_update_stadb_add(hw, s->vif, s->addr); + rc = mwl8k_cmd_update_stadb_add(hw, s->vif, + s->addr, s->legacy_rate_mask); if (rc >= 0) { struct ieee80211_sta *sta; @@ -3196,6 +3224,7 @@ mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, s->vif = vif; s->cmd = cmd; memcpy(s->addr, sta->addr, ETH_ALEN); + s->legacy_rate_mask = sta->supp_rates[IEEE80211_BAND_2GHZ]; spin_lock(&priv->sta_notify_list_lock); list_add_tail(&s->list, &priv->sta_notify_list); -- cgit v1.2.3 From c3cbbe8a5cd2886e13e9c93e059d9761f3715665 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:56:07 +0100 Subject: mwl8k: fix changed flags handling in mwl8k_bss_info_changed() Previously, mwl8k_bss_info_changed() would refuse to do anything if the 'changed' argument indicated that the association status hadn't changed. Fix this up so that it will allow changing things like the preamble type, the slot time and the CTS-to-self protection method without having to reassociate. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 52 ++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index d8eff68978f..fa7b4fe074f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2958,57 +2958,71 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, u32 changed) { struct mwl8k_priv *priv = hw->priv; + u32 ap_legacy_rates; int rc; - if ((changed & BSS_CHANGED_ASSOC) == 0) + if (mwl8k_fw_lock(hw)) return; - priv->capture_beacon = false; - - rc = mwl8k_fw_lock(hw); - if (rc) - return; + /* + * No need to capture a beacon if we're no longer associated. + */ + if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) + priv->capture_beacon = false; + /* + * Get the AP's legacy rates. + */ + ap_legacy_rates = 0; if (vif->bss_conf.assoc) { struct ieee80211_sta *ap; - u32 legacy_rate_mask; rcu_read_lock(); - ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); - if (ap != NULL) - legacy_rate_mask = ap->supp_rates[IEEE80211_BAND_2GHZ]; - rcu_read_unlock(); - if (ap == NULL) + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap == NULL) { + rcu_read_unlock(); goto out; + } - /* Install rates */ - rc = mwl8k_cmd_set_rate(hw, vif, legacy_rate_mask); + ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + + rcu_read_unlock(); + } + + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { + rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates); if (rc) goto out; - /* Turn on rate adaptation */ rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, MWL8K_UCAST_RATE, NULL); if (rc) goto out; + } - /* Set radio preamble */ + if (changed & BSS_CHANGED_ERP_PREAMBLE) { rc = mwl8k_set_radio_preamble(hw, vif->bss_conf.use_short_preamble); if (rc) goto out; + } - /* Set slot time */ + if (changed & BSS_CHANGED_ERP_SLOT) { rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; + } - /* Set AID */ - rc = mwl8k_cmd_set_aid(hw, vif, legacy_rate_mask); + if (((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) || + (changed & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))) { + rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; + } + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. -- cgit v1.2.3 From 9e1b17ead81e72d3db37b4cf15cde1f613603822 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:56:19 +0100 Subject: mwl8k: add support for 88w8363 in STA mode Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index fa7b4fe074f..de74c111d96 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3344,11 +3344,17 @@ static void mwl8k_finalize_join_worker(struct work_struct *work) } enum { - MWL8687 = 0, + MWL8363 = 0, + MWL8687, MWL8366, }; static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { + [MWL8363] = { + .part_name = "88w8363", + .helper_image = "mwl8k/helper_8363.fw", + .fw_image = "mwl8k/fmimage_8363.fw", + }, [MWL8687] = { .part_name = "88w8687", .helper_image = "mwl8k/helper_8687.fw", @@ -3363,6 +3369,8 @@ static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { }; static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { + { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, -- cgit v1.2.3 From 610677d2f0415570a7590790e8be376a946cd08b Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:56:46 +0100 Subject: mwl8k: allow setting HT channels Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index de74c111d96..2ae6c363f37 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2030,8 +2030,9 @@ struct mwl8k_cmd_set_rf_channel { } __attribute__((packed)); static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel) + struct ieee80211_conf *conf) { + struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_set_rf_channel *cmd; int rc; @@ -2043,10 +2044,17 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->current_channel = channel->hw_value; + if (channel->band == IEEE80211_BAND_2GHZ) - cmd->channel_flags = cpu_to_le32(0x00000081); - else - cmd->channel_flags = cpu_to_le32(0x00000000); + cmd->channel_flags |= cpu_to_le32(0x00000001); + + if (conf->channel_type == NL80211_CHAN_NO_HT || + conf->channel_type == NL80211_CHAN_HT20) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (conf->channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (conf->channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2926,7 +2934,7 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - rc = mwl8k_cmd_set_rf_channel(hw, conf->channel); + rc = mwl8k_cmd_set_rf_channel(hw, conf); if (rc) goto out; -- cgit v1.2.3 From 13935e2cf39b124c9a2ff0349b294e0b1e2e3aef Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:57:59 +0100 Subject: mwl8k: pass in HT capabilities and rates when associating Pass the AP's MCS rate mask to SET_RATE when associating, and make UPDATE_STADB pass in the peer's HT caps and rates when adding a new hardware station database entry. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 2ae6c363f37..e0301b6310f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2153,7 +2153,7 @@ struct mwl8k_cmd_set_rate { static int mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u32 legacy_rate_mask) + u32 legacy_rate_mask, u8 *mcs_rates) { struct mwl8k_cmd_set_rate *cmd; int rc; @@ -2165,6 +2165,7 @@ mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); + memcpy(cmd->mcs_set, mcs_rates, 16); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2666,7 +2667,7 @@ struct mwl8k_cmd_update_stadb { static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - u8 *addr, u32 legacy_rate_mask) + struct ieee80211_sta *sta) { struct mwl8k_cmd_update_stadb *cmd; struct peer_capability_info *p; @@ -2679,12 +2680,18 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); - memcpy(cmd->peer_addr, addr, ETH_ALEN); + memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); p = &cmd->peer_info; p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); - legacy_rate_mask_to_array(p->legacy_rates, legacy_rate_mask); + p->ht_support = sta->ht_cap.ht_supported; + p->ht_caps = sta->ht_cap.cap; + p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + legacy_rate_mask_to_array(p->legacy_rates, + sta->supp_rates[IEEE80211_BAND_2GHZ]); + memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); p->interop = 1; p->amsdu_enabled = 0; @@ -2967,6 +2974,7 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, { struct mwl8k_priv *priv = hw->priv; u32 ap_legacy_rates; + u8 ap_mcs_rates[16]; int rc; if (mwl8k_fw_lock(hw)) @@ -2979,12 +2987,11 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, priv->capture_beacon = false; /* - * Get the AP's legacy rates. + * Get the AP's legacy and MCS rates. */ ap_legacy_rates = 0; if (vif->bss_conf.assoc) { struct ieee80211_sta *ap; - rcu_read_lock(); ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); @@ -2994,12 +3001,13 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, } ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); rcu_read_unlock(); } if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { - rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates); + rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; @@ -3186,8 +3194,7 @@ struct mwl8k_sta_notify_item struct list_head list; struct ieee80211_vif *vif; enum sta_notify_cmd cmd; - u8 addr[ETH_ALEN]; - u32 legacy_rate_mask; + struct ieee80211_sta sta; }; static void mwl8k_sta_notify_worker(struct work_struct *work) @@ -3209,19 +3216,18 @@ static void mwl8k_sta_notify_worker(struct work_struct *work) if (s->cmd == STA_NOTIFY_ADD) { int rc; - rc = mwl8k_cmd_update_stadb_add(hw, s->vif, - s->addr, s->legacy_rate_mask); + rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta); if (rc >= 0) { struct ieee80211_sta *sta; rcu_read_lock(); - sta = ieee80211_find_sta(s->vif, s->addr); + sta = ieee80211_find_sta(s->vif, s->sta.addr); if (sta != NULL) MWL8K_STA(sta)->peer_id = rc; rcu_read_unlock(); } } else { - mwl8k_cmd_update_stadb_del(hw, s->vif, s->addr); + mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr); } kfree(s); @@ -3245,8 +3251,7 @@ mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (s != NULL) { s->vif = vif; s->cmd = cmd; - memcpy(s->addr, sta->addr, ETH_ALEN); - s->legacy_rate_mask = sta->supp_rates[IEEE80211_BAND_2GHZ]; + s->sta = *sta; spin_lock(&priv->sta_notify_list_lock); list_add_tail(&s->list, &priv->sta_notify_list); -- cgit v1.2.3 From a2292d83b5dcb7f378956a124854d2b17fa53aa3 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:58:12 +0100 Subject: mwl8k: trivial rx-only ampdu implementation AMPDU receive doesn't need any special handling, so let's enable this before tackling the transmit side. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index e0301b6310f..57ced0db910 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3309,6 +3309,22 @@ static int mwl8k_get_stats(struct ieee80211_hw *hw, return mwl8k_cmd_get_stat(hw, stats); } +static int +mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } +} + static const struct ieee80211_ops mwl8k_ops = { .tx = mwl8k_tx, .start = mwl8k_start, @@ -3324,6 +3340,7 @@ static const struct ieee80211_ops mwl8k_ops = { .conf_tx = mwl8k_conf_tx, .get_tx_stats = mwl8k_get_tx_stats, .get_stats = mwl8k_get_stats, + .ampdu_action = mwl8k_ampdu_action, }; static void mwl8k_tx_reclaim_handler(unsigned long data) -- cgit v1.2.3 From 341c97918ab7e84a155ea8b18759425304d213b6 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Mon, 4 Jan 2010 21:58:40 +0100 Subject: mwl8k: pass GET_HW_SPEC capability bitmask up the stack This enables HT association and AMPDU in the receive direction for STA firmware images on hardware that supports it. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 57ced0db910..23a5a344262 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1578,6 +1578,68 @@ struct mwl8k_cmd_get_hw_spec_sta { __le32 total_rxd; } __attribute__((packed)); +#define MWL8K_CAP_MAX_AMSDU 0x20000000 +#define MWL8K_CAP_GREENFIELD 0x08000000 +#define MWL8K_CAP_AMPDU 0x04000000 +#define MWL8K_CAP_RX_STBC 0x01000000 +#define MWL8K_CAP_TX_STBC 0x00800000 +#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 +#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 +#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 +#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 +#define MWL8K_CAP_DELAY_BA 0x00003000 +#define MWL8K_CAP_MIMO 0x00000200 +#define MWL8K_CAP_40MHZ 0x00000100 + +static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap) +{ + struct mwl8k_priv *priv = hw->priv; + int rx_streams; + int tx_streams; + + priv->band.ht_cap.ht_supported = 1; + + if (cap & MWL8K_CAP_MAX_AMSDU) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + if (cap & MWL8K_CAP_GREENFIELD) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + if (cap & MWL8K_CAP_AMPDU) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + priv->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + priv->band.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_NONE; + } + if (cap & MWL8K_CAP_RX_STBC) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + if (cap & MWL8K_CAP_TX_STBC) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + if (cap & MWL8K_CAP_SHORTGI_40MHZ) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + if (cap & MWL8K_CAP_SHORTGI_20MHZ) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + if (cap & MWL8K_CAP_DELAY_BA) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + if (cap & MWL8K_CAP_40MHZ) + priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); + tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); + + priv->band.ht_cap.mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + priv->band.ht_cap.mcs.rx_mask[1] = 0xff; + if (rx_streams >= 3) + priv->band.ht_cap.mcs.rx_mask[2] = 0xff; + priv->band.ht_cap.mcs.rx_mask[4] = 0x01; + priv->band.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + if (rx_streams != tx_streams) { + priv->band.ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + priv->band.ht_cap.mcs.tx_params |= (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } +} + static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; @@ -1608,6 +1670,8 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + if (cmd->caps & cpu_to_le32(MWL8K_CAP_MIMO)) + mwl8k_set_ht_caps(hw, le32_to_cpu(cmd->caps)); } kfree(cmd); -- cgit v1.2.3 From c2c2b12a8b6cd23d4abbc086642647c656bf406c Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:27:59 +0100 Subject: mwl8k: minor cleanups Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a04863633d1..cc160418e6c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -208,10 +208,7 @@ struct mwl8k_priv { /* Per interface specific private data */ struct mwl8k_vif { - /* Local MAC address. */ - u8 mac_addr[ETH_ALEN]; - - /* Non AMPDU sequence number assigned by driver */ + /* Non AMPDU sequence number assigned by driver. */ u16 seqno; }; #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) @@ -2287,8 +2284,8 @@ struct mwl8k_cmd_set_rts_threshold { __le16 threshold; } __attribute__((packed)); -static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, - u16 action, u16 threshold) +static int +mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) { struct mwl8k_cmd_set_rts_threshold *cmd; int rc; @@ -2299,8 +2296,8 @@ static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(action); - cmd->threshold = cpu_to_le16(threshold); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->threshold = cpu_to_le16(rts_thresh); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2955,14 +2952,13 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } + /* Set the mac address. */ + mwl8k_cmd_set_mac_addr(hw, vif->addr); + /* Clean out driver private area */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - /* Set and save the mac address */ - mwl8k_cmd_set_mac_addr(hw, vif->addr); - memcpy(mwl8k_vif->mac_addr, vif->addr, ETH_ALEN); - /* Set Initial sequence number to zero */ mwl8k_vif->seqno = 0; @@ -2977,9 +2973,6 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, { struct mwl8k_priv *priv = hw->priv; - if (priv->vif == NULL) - return; - mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); priv->vif = NULL; @@ -3252,7 +3245,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - return mwl8k_cmd_set_rts_threshold(hw, MWL8K_CMD_SET, value); + return mwl8k_cmd_set_rts_threshold(hw, value); } struct mwl8k_sta_notify_item @@ -3669,7 +3662,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* * Temporarily enable interrupts. Initial firmware host - * commands use interrupts and avoids polling. Disable + * commands use interrupts and avoid polling. Disable * interrupts when done. */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); -- cgit v1.2.3 From b569e924a9ea7c6f03dcf9b8a98d78d341925b87 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:28:14 +0100 Subject: mwl8k: undo transmit queue 0/1 swapping in mwl8k_cmd_set_edca_params() The comment and code in mwl8k_cmd_set_edca_params() suggest that the mapping between SET_EDCA_PARAMS queue numbers and transmit rings isn't actually 1:1, while tests show that the mapping is in fact 1:1. So, get rid of the transmit queue 0/1 swapping. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index cc160418e6c..3f5fdb69a2b 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2397,12 +2397,6 @@ mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, if (cmd == NULL) return -ENOMEM; - /* - * Queues 0 (BE) and 1 (BK) are swapped in hardware for - * this call. - */ - qnum ^= !(qnum >> 1); - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); -- cgit v1.2.3 From 62abd3cfb2f1a0ab1963ac4c4087c477da6b1f2a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:28:34 +0100 Subject: mwl8k: bump the transmit wait timeout to 5 seconds While it is reasonable to expect that at least one transmit ring entry will be processed per second while we are waiting for the transmit rings to drain, the firmware can end up doing batching of transmit ring status writeback, which means that the transmit rings can appear stuck for more than a second at a time. Bump the TX drain wait timeout up from 1 to 5 seconds to account for this. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 3f5fdb69a2b..8f5f7c9f75f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1173,7 +1173,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) /* * Must be called with priv->fw_mutex held and tx queues stopped. */ -#define MWL8K_TX_WAIT_TIMEOUT_MS 1000 +#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { -- cgit v1.2.3 From b71ed2c6ce8b5c3782ed70d67dc9adbd7ed07684 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:30:16 +0100 Subject: mwl8k: simplify mwl8k_cmd_use_fixed_rate() As we always use the auto rate adaptation feature and never pass in a rate table, USE_FIXED_RATE can be simplified somewhat. While we're at it, rename it to *_sta, as this is the STA version of the command. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 88 +++++++++++--------------------------------- 1 file changed, 22 insertions(+), 66 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 8f5f7c9f75f..20e7cf2e266 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2482,49 +2482,30 @@ static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) } /* - * CMD_USE_FIXED_RATE. + * CMD_USE_FIXED_RATE (STA version). */ -#define MWL8K_RATE_TABLE_SIZE 8 -#define MWL8K_UCAST_RATE 0 -#define MWL8K_USE_AUTO_RATE 0x0002 - -struct mwl8k_rate_entry { - /* Set to 1 if HT rate, 0 if legacy. */ - __le32 is_ht_rate; - - /* Set to 1 to use retry_count field. */ - __le32 enable_retry; - - /* Specified legacy rate or MCS. */ - __le32 rate; - - /* Number of allowed retries. */ - __le32 retry_count; -} __attribute__((packed)); - -struct mwl8k_rate_table { - /* 1 to allow specified rate and below */ - __le32 allow_rate_drop; - __le32 num_rates; - struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE]; +struct mwl8k_cmd_use_fixed_rate_sta { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[8]; + __le32 rate_type; + __le32 reserved1; + __le32 reserved2; } __attribute__((packed)); -struct mwl8k_cmd_use_fixed_rate { - struct mwl8k_cmd_pkt header; - __le32 action; - struct mwl8k_rate_table rate_table; - - /* Unicast, Broadcast or Multicast */ - __le32 rate_type; - __le32 reserved1; - __le32 reserved2; -} __attribute__((packed)); +#define MWL8K_USE_AUTO_RATE 0x0002 +#define MWL8K_UCAST_RATE 0 -static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, - u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table) +static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) { - struct mwl8k_cmd_use_fixed_rate *cmd; - int count; + struct mwl8k_cmd_use_fixed_rate_sta *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2533,32 +2514,8 @@ static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - cmd->action = cpu_to_le32(action); - cmd->rate_type = cpu_to_le32(rate_type); - - if (rate_table != NULL) { - /* - * Copy over each field manually so that endian - * conversion can be done. - */ - cmd->rate_table.allow_rate_drop = - cpu_to_le32(rate_table->allow_rate_drop); - cmd->rate_table.num_rates = - cpu_to_le32(rate_table->num_rates); - - for (count = 0; count < rate_table->num_rates; count++) { - struct mwl8k_rate_entry *dst = - &cmd->rate_table.rate_entry[count]; - struct mwl8k_rate_entry *src = - &rate_table->rate_entry[count]; - - dst->is_ht_rate = cpu_to_le32(src->is_ht_rate); - dst->enable_retry = cpu_to_le32(src->enable_retry); - dst->rate = cpu_to_le32(src->rate); - dst->retry_count = cpu_to_le32(src->retry_count); - } - } + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -3062,8 +3019,7 @@ static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, if (rc) goto out; - rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, - MWL8K_UCAST_RATE, NULL); + rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; } -- cgit v1.2.3 From 088aab8b62666a002907c912cd346ae6dc9f42b7 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:30:36 +0100 Subject: mwl8k: add the AP version of USE_FIXED_RATE As with the STA version, unicast will use auto rate adaptation, but the AP version allows setting the rates to be used for management and multicast transmissions, which can be set based on the BSS basic rate set. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 20e7cf2e266..ec79033801a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2523,6 +2523,47 @@ static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) return rc; } +/* + * CMD_USE_FIXED_RATE (AP version). + */ +struct mwl8k_cmd_use_fixed_rate_ap { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct mwl8k_rate_entry_ap { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[4]; + u8 multicast_rate; + u8 multicast_rate_type; + u8 management_rate; +} __attribute__((packed)); + +static int +mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) +{ + struct mwl8k_cmd_use_fixed_rate_ap *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->multicast_rate = mcast; + cmd->management_rate = mgmt; + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + /* * CMD_ENABLE_SNIFFER. */ -- cgit v1.2.3 From 3f5610ff560aeaccf051a6f93f25535c219599a0 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:30:58 +0100 Subject: mwl8k: implement AP firmware station database maintenance STA firmware uses UPDATE_STADB to manipulate the hardware station database, whereas AP firmware uses SET_NEW_STN -- this implements the latter, and hooks it into mwl8k_sta_notify(), to be used if we're running on AP firmware. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 134 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 117 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ec79033801a..f0026f33232 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -281,6 +281,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 #define MWL8K_CMD_SET_MAC_ADDR 0x0202 #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 +#define MWL8K_CMD_SET_NEW_STN 0x1111 #define MWL8K_CMD_UPDATE_STADB 0x1123 static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) @@ -313,6 +314,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); + MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(UPDATE_STADB); default: snprintf(buf, bufsize, "0x%x", cmd); @@ -2659,6 +2661,90 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) return rc; } +/* + * CMD_SET_NEW_STN. + */ +struct mwl8k_cmd_set_new_stn { + struct mwl8k_cmd_pkt header; + __le16 aid; + __u8 mac_addr[6]; + __le16 stn_id; + __le16 action; + __le16 rsvd; + __le32 legacy_rates; + __u8 ht_rates[4]; + __le16 cap_info; + __le16 ht_capabilities_info; + __u8 mac_ht_param_info; + __u8 rev; + __u8 control_channel; + __u8 add_channel; + __le16 op_mode; + __le16 stbc; + __u8 add_qos_info; + __u8 is_qos_sta; + __le32 fw_sta_ptr; +} __attribute__((packed)); + +#define MWL8K_STA_ACTION_ADD 0 +#define MWL8K_STA_ACTION_REMOVE 2 + +static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->aid = cpu_to_le16(sta->aid); + memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); + cmd->stn_id = cpu_to_le16(sta->aid); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); + cmd->legacy_rates = cpu_to_le32(sta->supp_rates[IEEE80211_BAND_2GHZ]); + if (sta->ht_cap.ht_supported) { + cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; + cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; + cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; + cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; + cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); + cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + cmd->is_qos_sta = 1; + } + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + /* * CMD_UPDATE_STADB. */ @@ -3247,6 +3333,36 @@ struct mwl8k_sta_notify_item struct ieee80211_sta sta; }; +static void +mwl8k_do_sta_notify(struct ieee80211_hw *hw, struct mwl8k_sta_notify_item *s) +{ + struct mwl8k_priv *priv = hw->priv; + + /* + * STA firmware uses UPDATE_STADB, AP firmware uses SET_NEW_STN. + */ + if (!priv->ap_fw && s->cmd == STA_NOTIFY_ADD) { + int rc; + + rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta); + if (rc >= 0) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + sta = ieee80211_find_sta(s->vif, s->sta.addr); + if (sta != NULL) + MWL8K_STA(sta)->peer_id = rc; + rcu_read_unlock(); + } + } else if (!priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) { + mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr); + } else if (priv->ap_fw && s->cmd == STA_NOTIFY_ADD) { + mwl8k_cmd_set_new_stn_add(hw, s->vif, &s->sta); + } else if (priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) { + mwl8k_cmd_set_new_stn_del(hw, s->vif, s->sta.addr); + } +} + static void mwl8k_sta_notify_worker(struct work_struct *work) { struct mwl8k_priv *priv = @@ -3263,23 +3379,7 @@ static void mwl8k_sta_notify_worker(struct work_struct *work) spin_unlock_bh(&priv->sta_notify_list_lock); - if (s->cmd == STA_NOTIFY_ADD) { - int rc; - - rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta); - if (rc >= 0) { - struct ieee80211_sta *sta; - - rcu_read_lock(); - sta = ieee80211_find_sta(s->vif, s->sta.addr); - if (sta != NULL) - MWL8K_STA(sta)->peer_id = rc; - rcu_read_unlock(); - } - } else { - mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr); - } - + mwl8k_do_sta_notify(hw, s); kfree(s); spin_lock_bh(&priv->sta_notify_list_lock); -- cgit v1.2.3 From a9e00b151ec2121b7ae09d84a2b5a68b6461e98a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:31:30 +0100 Subject: mwl8k: correctly set the mac_type field for AP SET_MAC_ADDR Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f0026f33232..428575beb8d 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2607,6 +2607,9 @@ struct mwl8k_cmd_set_mac_addr { }; } __attribute__((packed)); +#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 +#define MWL8K_MAC_TYPE_PRIMARY_AP 2 + static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) { struct mwl8k_priv *priv = hw->priv; @@ -2620,7 +2623,7 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); cmd->header.length = cpu_to_le16(sizeof(*cmd)); if (priv->ap_fw) { - cmd->mbss.mac_type = 0; + cmd->mbss.mac_type = cpu_to_le16(MWL8K_MAC_TYPE_PRIMARY_AP); memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); } else { memcpy(cmd->mac_addr, mac, ETH_ALEN); -- cgit v1.2.3 From b64fe619e371fc17d8d686d6d44aef1b41317880 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:31:39 +0100 Subject: mwl8k: basic AP interface support Add support for creating AP interfaces, and enabling beaconing. This allows running a basic AP (11b/g mode only for now). Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 171 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 428575beb8d..759c94fb8e7 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -266,6 +266,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_RF_ANTENNA 0x0020 +#define MWL8K_CMD_SET_BEACON 0x0100 #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -281,6 +282,7 @@ static const struct ieee80211_rate mwl8k_rates[] = { #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 #define MWL8K_CMD_SET_MAC_ADDR 0x0202 #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 +#define MWL8K_CMD_BSS_START 0x1100 #define MWL8K_CMD_SET_NEW_STN 0x1111 #define MWL8K_CMD_UPDATE_STADB 0x1123 @@ -299,6 +301,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RF_TX_POWER); MWL8K_CMDNAME(RF_ANTENNA); + MWL8K_CMDNAME(SET_BEACON); MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); @@ -314,6 +317,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); + MWL8K_CMDNAME(BSS_START); MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(UPDATE_STADB); default: @@ -1769,7 +1773,9 @@ struct mwl8k_cmd_set_hw_spec { __le32 total_rxd; } __attribute__((packed)); -#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) { @@ -1790,7 +1796,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); for (i = 0; i < MWL8K_TX_QUEUES; i++) cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); - cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); @@ -2027,6 +2035,35 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) return rc; } +/* + * CMD_SET_BEACON. + */ +struct mwl8k_cmd_set_beacon { + struct mwl8k_cmd_pkt header; + __le16 beacon_len; + __u8 beacon[0]; +}; + +static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len) +{ + struct mwl8k_cmd_set_beacon *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); + cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); + cmd->beacon_len = cpu_to_le16(len); + memcpy(cmd->beacon, beacon, len); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + /* * CMD_SET_PRE_SCAN. */ @@ -2664,6 +2701,33 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) return rc; } +/* + * CMD_BSS_START. + */ +struct mwl8k_cmd_bss_start { + struct mwl8k_cmd_pkt header; + __le32 enable; +} __attribute__((packed)); + +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable) +{ + struct mwl8k_cmd_bss_start *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->enable = cpu_to_le32(enable); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + /* * CMD_SET_NEW_STN. */ @@ -2727,6 +2791,26 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, return rc; } +static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); + + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); + + return rc; +} + static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *addr) { @@ -3015,16 +3099,10 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, if (priv->vif != NULL) return -EBUSY; - /* - * We only support managed interfaces for now. - */ - if (vif->type != NL80211_IFTYPE_STATION) - return -EINVAL; - /* * Reject interface creation if sniffer mode is active, as * STA operation is mutually exclusive with hardware sniffer - * mode. + * mode. (Sniffer mode is only used on STA firmware.) */ if (priv->sniffer_enabled) { printk(KERN_INFO "%s: unable to create STA " @@ -3036,6 +3114,9 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, /* Set the mac address. */ mwl8k_cmd_set_mac_addr(hw, vif->addr); + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_add_self(hw, vif); + /* Clean out driver private area */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); @@ -3054,6 +3135,9 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, { struct mwl8k_priv *priv = hw->priv; + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); + mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); priv->vif = NULL; @@ -3105,10 +3189,9 @@ out: return rc; } -static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) +static void +mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) { struct mwl8k_priv *priv = hw->priv; u32 ap_legacy_rates; @@ -3188,6 +3271,66 @@ out: mwl8k_fw_unlock(hw); } +static void +mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + int rc; + + if (mwl8k_fw_lock(hw)) + return; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); + if (rc) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + int idx; + int rate; + + /* + * Use lowest supported basic rate for multicasts + * and management frames (such as probe responses -- + * beacons will always go out at 1 Mb/s). + */ + idx = ffs(vif->bss_conf.basic_rates); + rate = idx ? mwl8k_rates[idx - 1].hw_value : 2; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } + + if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { + struct sk_buff *skb; + + skb = ieee80211_beacon_get(hw, vif); + if (skb != NULL) { + mwl8k_cmd_set_beacon(hw, skb->data, skb->len); + kfree_skb(skb); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mwl8k_cmd_bss_start(hw, info->enable_beacon); + +out: + mwl8k_fw_unlock(hw); +} + +static void +mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mwl8k_priv *priv = hw->priv; + + if (!priv->ap_fw) + mwl8k_bss_info_changed_sta(hw, vif, info, changed); + else + mwl8k_bss_info_changed_ap(hw, vif, info, changed); +} + static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mclist) { @@ -3766,6 +3909,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, rc = mwl8k_cmd_get_hw_spec_ap(hw); if (!rc) rc = mwl8k_cmd_set_hw_spec(hw); + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP); } else { rc = mwl8k_cmd_get_hw_spec_sta(hw); -- cgit v1.2.3 From efb7c49a68cf206f35793d7799608e1d69a209f9 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:31:47 +0100 Subject: mwl8k: allow limiting the amount of transmit reclaim done Add a limit argument to mwl8k_txq_reclaim(), to allow limiting the number of packets that it will reclaim, and make it return the number of packets that it reclaimed. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 759c94fb8e7..6598efcda5c 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1249,13 +1249,15 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) MWL8K_TXD_STATUS_OK_RETRY | \ MWL8K_TXD_STATUS_OK_MORE_RETRY)) -static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) +static int +mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - int wake = 0; + int processed; - while (txq->stats.len > 0) { + processed = 0; + while (txq->stats.len > 0 && limit--) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; @@ -1302,11 +1304,13 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) ieee80211_tx_status_irqsafe(hw, skb); - wake = 1; + processed++; } - if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) + if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) ieee80211_wake_queue(hw, index); + + return processed; } /* must be called only when the card's transmit is completely halted */ @@ -1315,7 +1319,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - mwl8k_txq_reclaim(hw, index, 1); + mwl8k_txq_reclaim(hw, index, INT_MAX, 1); kfree(txq->skb); txq->skb = NULL; @@ -3084,7 +3088,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw) /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); } static int mwl8k_add_interface(struct ieee80211_hw *hw, @@ -3647,7 +3651,7 @@ static void mwl8k_tx_reclaim_handler(unsigned long data) spin_lock_bh(&priv->tx_lock); for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 0); + mwl8k_txq_reclaim(hw, i, INT_MAX, 0); if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { complete(priv->tx_wait); @@ -4021,7 +4025,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_deinit(hw, i); -- cgit v1.2.3 From 1e9f9de3b17db3aa358f39d6932662324178350d Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:32:01 +0100 Subject: mwl8k: keep TX_DONE interrupt masked while transmit reclaim is running By making use of the CLEAR_SEL feature of the mwl8k host interface interrupt controller, we can keep the TX_DONE interrupt source masked while the transmit reclaim tasklet is running (NAPI style) without having to touch the interrupt controller's interrupt mask register when entering or exiting polling mode, and without having to do any more register reads/writes than we do now. When CLEAR_SEL is enabled on the TX_DONE interrupt source, reading the interrupt status register will clear the TX_DONE status bit if it was set, allowing it to be set again if a new TX_DONE event arrives while we are running the TX reclaim tasklet, but such a new event will then not trigger another PCI interrupt until a zero is written to the TX_DONE interrupt status register bit. I.e., if we write a zero to the TX_DONE interrupt source bit in the interrupt status register when the TX reclaim tasklet thinks it's done, a PCI interrupt will be triggered if a new TX_DONE event arrived from the hardware between us deciding that there is no more work to do and re-enabling the TX_DONE interrupt source, thereby avoiding the classic NAPI poll mode exit race that would otherwise occur. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 77 ++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 31 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 6598efcda5c..3f55aa0c8db 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -202,8 +202,8 @@ struct mwl8k_priv { */ struct work_struct finalize_join_worker; - /* Tasklet to reclaim TX descriptors and buffers after tx */ - struct tasklet_struct tx_reclaim_task; + /* Tasklet to perform TX reclaim. */ + struct tasklet_struct poll_tx_task; }; /* Per interface specific private data */ @@ -2963,13 +2963,16 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) u32 status; status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (!status) return IRQ_NONE; - if (status & MWL8K_A2H_INT_TX_DONE) - tasklet_schedule(&priv->tx_reclaim_task); + if (status & MWL8K_A2H_INT_TX_DONE) { + status &= ~MWL8K_A2H_INT_TX_DONE; + tasklet_schedule(&priv->poll_tx_task); + } + + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); if (status & MWL8K_A2H_INT_RX_READY) { while (rxq_process(hw, 0, 1)) @@ -2990,6 +2993,35 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void mwl8k_tx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + int i; + + limit = 32; + + spin_lock_bh(&priv->tx_lock); + + for (i = 0; i < MWL8K_TX_QUEUES; i++) + limit -= mwl8k_txq_reclaim(hw, i, limit, 0); + + if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { + complete(priv->tx_wait); + priv->tx_wait = NULL; + } + + spin_unlock_bh(&priv->tx_lock); + + if (limit) { + writel(~MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_tx_task); + } +} + /* * Core driver operations. @@ -3026,7 +3058,7 @@ static int mwl8k_start(struct ieee80211_hw *hw) } /* Enable tx reclaim tasklet */ - tasklet_enable(&priv->tx_reclaim_task); + tasklet_enable(&priv->poll_tx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -3059,7 +3091,7 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (rc) { iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); - tasklet_disable(&priv->tx_reclaim_task); + tasklet_disable(&priv->poll_tx_task); } return rc; @@ -3084,7 +3116,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw) dev_kfree_skb(priv->beacon_skb); /* Stop tx reclaim tasklet */ - tasklet_disable(&priv->tx_reclaim_task); + tasklet_disable(&priv->poll_tx_task); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) @@ -3643,23 +3675,6 @@ static const struct ieee80211_ops mwl8k_ops = { .ampdu_action = mwl8k_ampdu_action, }; -static void mwl8k_tx_reclaim_handler(unsigned long data) -{ - int i; - struct ieee80211_hw *hw = (struct ieee80211_hw *) data; - struct mwl8k_priv *priv = hw->priv; - - spin_lock_bh(&priv->tx_lock); - for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, INT_MAX, 0); - - if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } - spin_unlock_bh(&priv->tx_lock); -} - static void mwl8k_finalize_join_worker(struct work_struct *work) { struct mwl8k_priv *priv = @@ -3859,9 +3874,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); /* TX reclaim tasklet */ - tasklet_init(&priv->tx_reclaim_task, - mwl8k_tx_reclaim_handler, (unsigned long)hw); - tasklet_disable(&priv->tx_reclaim_task); + tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_tx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); @@ -3890,7 +3904,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); + iowrite32(MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); rc = request_irq(priv->pdev->irq, mwl8k_interrupt, @@ -4018,7 +4033,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_unregister_hw(hw); /* Remove tx reclaim tasklet */ - tasklet_kill(&priv->tx_reclaim_task); + tasklet_kill(&priv->poll_tx_task); /* Stop hardware */ mwl8k_hw_reset(priv); -- cgit v1.2.3 From 67e2eb27958cae758ccbc86443c360ec285acc3e Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Fri, 8 Jan 2010 18:32:18 +0100 Subject: mwl8k: move receive processing to tasklet Like how TX reclaim is done in a tasklet, move receive processing to tasklet context as well. This can have nice benefits for CPU utilisation and throughput, especially at 3-stream rates. (Use the same CLEAR_SEL trick as the TX reclaim tasklet does, to avoid having to touch the interrupt mask registers.) Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 47 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 3f55aa0c8db..13b0e32b051 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -204,6 +204,9 @@ struct mwl8k_priv { /* Tasklet to perform TX reclaim. */ struct tasklet_struct poll_tx_task; + + /* Tasklet to perform RX. */ + struct tasklet_struct poll_rx_task; }; /* Per interface specific private data */ @@ -2971,14 +2974,14 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) tasklet_schedule(&priv->poll_tx_task); } - if (status) - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (status & MWL8K_A2H_INT_RX_READY) { - while (rxq_process(hw, 0, 1)) - rxq_refill(hw, 0, 1); + status &= ~MWL8K_A2H_INT_RX_READY; + tasklet_schedule(&priv->poll_rx_task); } + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + if (status & MWL8K_A2H_INT_OPC_DONE) { if (priv->hostcmd_wait != NULL) complete(priv->hostcmd_wait); @@ -3022,6 +3025,24 @@ static void mwl8k_tx_poll(unsigned long data) } } +static void mwl8k_rx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + + limit = 32; + limit -= rxq_process(hw, 0, limit); + limit -= rxq_refill(hw, 0, limit); + + if (limit) { + writel(~MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_rx_task); + } +} + /* * Core driver operations. @@ -3057,8 +3078,9 @@ static int mwl8k_start(struct ieee80211_hw *hw) return -EIO; } - /* Enable tx reclaim tasklet */ + /* Enable TX reclaim and RX tasklets. */ tasklet_enable(&priv->poll_tx_task); + tasklet_enable(&priv->poll_rx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -3092,6 +3114,7 @@ static int mwl8k_start(struct ieee80211_hw *hw) iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); } return rc; @@ -3115,8 +3138,9 @@ static void mwl8k_stop(struct ieee80211_hw *hw) if (priv->beacon_skb != NULL) dev_kfree_skb(priv->beacon_skb); - /* Stop tx reclaim tasklet */ + /* Stop TX reclaim and RX tasklets. */ tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) @@ -3873,9 +3897,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); - /* TX reclaim tasklet */ + /* TX reclaim and RX tasklets. */ tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); tasklet_disable(&priv->poll_tx_task); + tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_rx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); @@ -3904,7 +3930,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(MWL8K_A2H_INT_TX_DONE, + iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); @@ -4032,8 +4058,9 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_unregister_hw(hw); - /* Remove tx reclaim tasklet */ + /* Remove TX reclaim and RX tasklets. */ tasklet_kill(&priv->poll_tx_task); + tasklet_kill(&priv->poll_rx_task); /* Stop hardware */ mwl8k_hw_reset(priv); -- cgit v1.2.3 From c92d4edecf489dbcbb2e5dd3c513790e57e2ea0e Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:47:22 +0100 Subject: mwl8k: update MODULE_FIRMWARE tags Add MODULE_FIRMWARE tags for the mwl8k firmware images that don't have them yet, and move them to where the firmware image names are declared. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 13b0e32b051..01cababfefa 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -394,9 +394,6 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) return 0; } -MODULE_FIRMWARE("mwl8k/helper_8687.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); - struct mwl8k_cmd_pkt { __le16 code; __le16 length; @@ -3737,6 +3734,13 @@ static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { }, }; +MODULE_FIRMWARE("mwl8k/helper_8363.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); +MODULE_FIRMWARE("mwl8k/helper_8366.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); + static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, -- cgit v1.2.3 From 9189c10087a738c764046fa27651d332594cd8e6 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:47:32 +0100 Subject: mwl8k: remove (mostly) write-only variable priv->current_channel Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 01cababfefa..f2c4524888a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -156,8 +156,6 @@ struct mwl8k_priv { struct ieee80211_vif *vif; - struct ieee80211_channel *current_channel; - /* power management status cookie from firmware */ u32 *cookie; dma_addr_t cookie_dma; @@ -3050,7 +3048,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) int index = skb_get_queue_mapping(skb); int rc; - if (priv->current_channel == NULL) { + if (!priv->radio_on) { printk(KERN_DEBUG "%s: dropped TX frame since radio " "disabled\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); @@ -3182,7 +3180,6 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, mwl8k_vif->seqno = 0; priv->vif = vif; - priv->current_channel = NULL; return 0; } @@ -3208,7 +3205,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (conf->flags & IEEE80211_CONF_IDLE) { mwl8k_cmd_radio_disable(hw); - priv->current_channel = NULL; return 0; } @@ -3224,8 +3220,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - priv->current_channel = conf->channel; - if (conf->power_level > 18) conf->power_level = 18; rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); -- cgit v1.2.3 From c97470dd253831e880c72ea5d022ed7f3aee45c3 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:47:37 +0100 Subject: mwl8k: don't call SET_AID if we're not associated Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f2c4524888a..67ee3da3326 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3261,9 +3261,9 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* * Get the AP's legacy and MCS rates. */ - ap_legacy_rates = 0; if (vif->bss_conf.assoc) { struct ieee80211_sta *ap; + rcu_read_lock(); ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); @@ -3301,8 +3301,9 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; } - if (((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) || - (changed & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT))) { + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT))) { rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; -- cgit v1.2.3 From 657232b625890f202867ede0ed67ecf15827ff80 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:47:47 +0100 Subject: mwl8k: simplify sequence number assignment By storing the sequence counter in << 4 format, like other drivers do. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 67ee3da3326..7d165befb0d 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1355,11 +1355,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - u16 seqno = mwl8k_vif->seqno; - wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - wh->seq_ctrl |= cpu_to_le16(seqno << 4); - mwl8k_vif->seqno = seqno++ % 4096; + wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); + mwl8k_vif->seqno += 0x10; } /* Setup firmware control bit fields for each frame type. */ -- cgit v1.2.3 From ca66527c60385dcec878ebd90749d1fdc43bc870 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:47:53 +0100 Subject: mwl8k: add another 88w8366 PCI ID 0x2a43 is a single-band (2.4GHz only) 88w8366 mini-PCIe card. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 7d165befb0d..76da583df32 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3740,6 +3740,7 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, { }, }; MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); -- cgit v1.2.3 From 777ad375d5960e0d2a945a34032b182eb2952d45 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:48:04 +0100 Subject: mwl8k: rename 2.4 GHz band/channels/rates related variables from FOO to FOO_24 To prepare for adding 5 GHz band/channels/rates. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 76 +++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 39 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 76da583df32..37cbfbf2156 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -141,6 +141,9 @@ struct mwl8k_priv { /* hardware/firmware parameters */ bool ap_fw; struct rxd_ops *rxd_ops; + struct ieee80211_supported_band band_24; + struct ieee80211_channel channels_24[14]; + struct ieee80211_rate rates_24[14]; /* firmware access */ struct mutex fw_mutex; @@ -173,11 +176,6 @@ struct mwl8k_priv { struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES]; - /* PHY parameters */ - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[14]; - bool radio_on; bool radio_short_preamble; bool sniffer_enabled; @@ -220,7 +218,7 @@ struct mwl8k_sta { }; #define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) -static const struct ieee80211_channel mwl8k_channels[] = { +static const struct ieee80211_channel mwl8k_channels_24[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2422, .hw_value = 3, }, @@ -237,7 +235,7 @@ static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2484, .hw_value = 14, }, }; -static const struct ieee80211_rate mwl8k_rates[] = { +static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, @@ -731,8 +729,8 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, } else { int i; - for (i = 0; i < ARRAY_SIZE(mwl8k_rates); i++) { - if (mwl8k_rates[i].hw_value == rxd->rate) { + for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { + if (mwl8k_rates_24[i].hw_value == rxd->rate) { status->rate_idx = i; break; } @@ -1597,48 +1595,48 @@ struct mwl8k_cmd_get_hw_spec_sta { static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap) { struct mwl8k_priv *priv = hw->priv; + struct ieee80211_supported_band *band = &priv->band_24; int rx_streams; int tx_streams; - priv->band.ht_cap.ht_supported = 1; + band->ht_cap.ht_supported = 1; if (cap & MWL8K_CAP_MAX_AMSDU) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; if (cap & MWL8K_CAP_GREENFIELD) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; if (cap & MWL8K_CAP_AMPDU) { hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; - priv->band.ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - priv->band.ht_cap.ampdu_density = - IEEE80211_HT_MPDU_DENSITY_NONE; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; } if (cap & MWL8K_CAP_RX_STBC) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; if (cap & MWL8K_CAP_TX_STBC) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; if (cap & MWL8K_CAP_SHORTGI_40MHZ) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; if (cap & MWL8K_CAP_SHORTGI_20MHZ) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; if (cap & MWL8K_CAP_DELAY_BA) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; if (cap & MWL8K_CAP_40MHZ) - priv->band.ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); - priv->band.ht_cap.mcs.rx_mask[0] = 0xff; + band->ht_cap.mcs.rx_mask[0] = 0xff; if (rx_streams >= 2) - priv->band.ht_cap.mcs.rx_mask[1] = 0xff; + band->ht_cap.mcs.rx_mask[1] = 0xff; if (rx_streams >= 3) - priv->band.ht_cap.mcs.rx_mask[2] = 0xff; - priv->band.ht_cap.mcs.rx_mask[4] = 0x01; - priv->band.ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + band->ht_cap.mcs.rx_mask[2] = 0xff; + band->ht_cap.mcs.rx_mask[4] = 0x01; + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; if (rx_streams != tx_streams) { - priv->band.ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - priv->band.ht_cap.mcs.tx_params |= (tx_streams - 1) << + band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= (tx_streams - 1) << IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; } } @@ -2192,7 +2190,7 @@ static void legacy_rate_mask_to_array(u8 *rates, u32 mask) for (i = 0, j = 0; i < 14; i++) { if (mask & (1 << i)) - rates[j++] = mwl8k_rates[i].hw_value; + rates[j++] = mwl8k_rates_24[i].hw_value; } } @@ -3347,7 +3345,7 @@ mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * beacons will always go out at 1 Mb/s). */ idx = ffs(vif->bss_conf.basic_rates); - rate = idx ? mwl8k_rates[idx - 1].hw_value : 2; + rate = idx ? mwl8k_rates_24[idx - 1].hw_value : 2; mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); } @@ -3855,16 +3853,16 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, priv->pending_tx_pkts = 0; - memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels)); - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(mwl8k_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(mwl8k_rates); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; + memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); + priv->band_24.band = IEEE80211_BAND_2GHZ; + priv->band_24.channels = priv->channels_24; + priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); + priv->band_24.bitrates = priv->rates_24; + priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(mwl8k_rates)); - memcpy(priv->rates, mwl8k_rates, sizeof(mwl8k_rates)); + BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); + memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); /* * Extra headroom is the size of the required DMA header -- cgit v1.2.3 From 1349ad2f06f86f41415cf7ffa9e068fd4f89be87 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:48:17 +0100 Subject: mwl8k: move responsibility for initialising wiphy bands to GET_HW_SPEC So that we can make 2.4 GHz and 5 GHz band registration conditional on the capability bitmask returned by the firmware. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 37cbfbf2156..a17111f4026 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1557,6 +1557,28 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) return rc; } +/* + * Setup code shared between STA and AP firmware images. + */ +static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); + memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); + + BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); + memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); + + priv->band_24.band = IEEE80211_BAND_2GHZ; + priv->band_24.channels = priv->channels_24; + priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); + priv->band_24.bitrates = priv->rates_24; + priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; +} + /* * CMD_GET_HW_SPEC (STA version). */ @@ -1671,6 +1693,7 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_setup_2ghz_band(hw); if (cmd->caps & cpu_to_le32(MWL8K_CAP_MIMO)) mwl8k_set_ht_caps(hw, le32_to_cpu(cmd->caps)); } @@ -1726,6 +1749,7 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_setup_2ghz_band(hw); off = le32_to_cpu(cmd->wcbbase0) & 0xffff; iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off); @@ -3853,17 +3877,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, priv->pending_tx_pkts = 0; - memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); - priv->band_24.band = IEEE80211_BAND_2GHZ; - priv->band_24.channels = priv->channels_24; - priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); - priv->band_24.bitrates = priv->rates_24; - priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; - - BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); - memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); - /* * Extra headroom is the size of the required DMA header * minus the size of the smallest 802.11 frame (CTS frame). -- cgit v1.2.3 From 4eae9edd38c0a9ce34e39100ccc69ff520bc1224 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:48:32 +0100 Subject: mwl8k: add 5 GHz band channels and rates Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a17111f4026..13dded49323 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -144,6 +144,9 @@ struct mwl8k_priv { struct ieee80211_supported_band band_24; struct ieee80211_channel channels_24[14]; struct ieee80211_rate rates_24[14]; + struct ieee80211_supported_band band_50; + struct ieee80211_channel channels_50[4]; + struct ieee80211_rate rates_50[9]; /* firmware access */ struct mutex fw_mutex; @@ -252,6 +255,25 @@ static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 720, .hw_value = 144, }, }; +static const struct ieee80211_channel mwl8k_channels_50[] = { + { .center_freq = 5180, .hw_value = 36, }, + { .center_freq = 5200, .hw_value = 40, }, + { .center_freq = 5220, .hw_value = 44, }, + { .center_freq = 5240, .hw_value = 48, }, +}; + +static const struct ieee80211_rate mwl8k_rates_50[] = { + { .bitrate = 60, .hw_value = 12, }, + { .bitrate = 90, .hw_value = 18, }, + { .bitrate = 120, .hw_value = 24, }, + { .bitrate = 180, .hw_value = 36, }, + { .bitrate = 240, .hw_value = 48, }, + { .bitrate = 360, .hw_value = 72, }, + { .bitrate = 480, .hw_value = 96, }, + { .bitrate = 540, .hw_value = 108, }, + { .bitrate = 720, .hw_value = 144, }, +}; + /* Set or get info from Firmware */ #define MWL8K_CMD_SET 0x0001 #define MWL8K_CMD_GET 0x0000 @@ -1579,6 +1601,25 @@ static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; } +static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); + memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); + + BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); + memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); + + priv->band_50.band = IEEE80211_BAND_5GHZ; + priv->band_50.channels = priv->channels_50; + priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); + priv->band_50.bitrates = priv->rates_50; + priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; +} + /* * CMD_GET_HW_SPEC (STA version). */ -- cgit v1.2.3 From 854783444bab0024556c0aefdb0a860f2f1da286 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:48:56 +0100 Subject: mwl8k: properly set receive status rate index on 5 GHz receive The mwl8k firmware uses indices into the 2.4 GHz band rate table for the receive descriptor channel field even if the packet was received on a 5 GHz channel, while mac80211 expects an index into the 5 GHz band rate table when packets are received on the 5 GHz band, which presents a mismatch as the 5 GHz band rate table lacks the five non-OFDM rates that the 2.4 GHz rate table starts with. To handle this properly, we need to substract 5 from the rate index field if the packet was received on a 5 GHz channel (and was not received at an MCS rate). Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 13dded49323..cc2ce61b799 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -759,7 +759,13 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, } } - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; @@ -850,7 +856,13 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) status->flag |= RX_FLAG_HT; - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; -- cgit v1.2.3 From 8707d0262585423cdc053bf8db0912e53915e5e4 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:49:15 +0100 Subject: mwl8k: handle 5 GHz legacy rate bitmaps in firmware commands Whenever mac80211 gives us a legacy rate bitmap in the context of the 5 GHz band, we need to remember to shift the bitmap left by 5 positions before giving it to the firmware, as the firmware follows the bitmap bit assignment of the 2.4 GHz rate table even if we're on the 5 GHz band, and the 2.4 GHz rate table includes five non-OFDM rates at the start that are not valid in the 5 GHz band. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index cc2ce61b799..e370245b390 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2836,6 +2836,7 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct mwl8k_cmd_set_new_stn *cmd; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2848,7 +2849,11 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); cmd->stn_id = cpu_to_le16(sta->aid); cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); - cmd->legacy_rates = cpu_to_le32(sta->supp_rates[IEEE80211_BAND_2GHZ]); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + cmd->legacy_rates = cpu_to_le32(rates); if (sta->ht_cap.ht_supported) { cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; @@ -2972,6 +2977,7 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, { struct mwl8k_cmd_update_stadb *cmd; struct peer_capability_info *p; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2990,8 +2996,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, p->ht_caps = sta->ht_cap.cap; p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | ((sta->ht_cap.ampdu_density & 7) << 2); - legacy_rate_mask_to_array(p->legacy_rates, - sta->supp_rates[IEEE80211_BAND_2GHZ]); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + legacy_rate_mask_to_array(p->legacy_rates, rates); memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); p->interop = 1; p->amsdu_enabled = 0; @@ -3345,7 +3354,12 @@ mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto out; } - ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + } else { + ap_legacy_rates = + ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; + } memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); rcu_read_unlock(); @@ -3422,7 +3436,13 @@ mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * beacons will always go out at 1 Mb/s). */ idx = ffs(vif->bss_conf.basic_rates); - rate = idx ? mwl8k_rates_24[idx - 1].hw_value : 2; + if (idx) + idx--; + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); } -- cgit v1.2.3 From 42574ea2274ec0a2a9c58ab01be91b65e60a2291 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:49:18 +0100 Subject: mwl8k: allow selecting 5 GHz channels Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index e370245b390..6e8c126aa5e 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2222,6 +2222,8 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, if (channel->band == IEEE80211_BAND_2GHZ) cmd->channel_flags |= cpu_to_le32(0x00000001); + else if (channel->band == IEEE80211_BAND_5GHZ) + cmd->channel_flags |= cpu_to_le32(0x00000004); if (conf->channel_type == NL80211_CHAN_NO_HT || conf->channel_type == NL80211_CHAN_HT20) -- cgit v1.2.3 From 06953235f48c696b22c5ed45570680fb070f7277 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:49:41 +0100 Subject: mwl8k: use firmware capability field to decide which bands to register Make the decision about whether to register the 2.4 and 5 GHz bands with mac80211 by looking at the capability field in GET_HW_SPEC (STA firmware only for now). This enables 5 GHz STA operation. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 6e8c126aa5e..382ef4305a3 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1666,11 +1666,14 @@ struct mwl8k_cmd_get_hw_spec_sta { #define MWL8K_CAP_DELAY_BA 0x00003000 #define MWL8K_CAP_MIMO 0x00000200 #define MWL8K_CAP_40MHZ 0x00000100 +#define MWL8K_CAP_BAND_MASK 0x00000007 +#define MWL8K_CAP_5GHZ 0x00000004 +#define MWL8K_CAP_2GHZ4 0x00000001 -static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap) +static void +mwl8k_set_ht_caps(struct ieee80211_hw *hw, + struct ieee80211_supported_band *band, u32 cap) { - struct mwl8k_priv *priv = hw->priv; - struct ieee80211_supported_band *band = &priv->band_24; int rx_streams; int tx_streams; @@ -1716,6 +1719,24 @@ static void mwl8k_set_ht_caps(struct ieee80211_hw *hw, u32 cap) } } +static void +mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) +{ + struct mwl8k_priv *priv = hw->priv; + + if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { + mwl8k_setup_2ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_24, caps); + } + + if (caps & MWL8K_CAP_5GHZ) { + mwl8k_setup_5ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_50, caps); + } +} + static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; @@ -1746,9 +1767,7 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; - mwl8k_setup_2ghz_band(hw); - if (cmd->caps & cpu_to_le32(MWL8K_CAP_MIMO)) - mwl8k_set_ht_caps(hw, le32_to_cpu(cmd->caps)); + mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); } kfree(cmd); -- cgit v1.2.3 From f5bb87cfba5ae9dd3724b846ac2fa7e033425c1c Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:49:51 +0100 Subject: mwl8k: convert the priv->vif pointer to a list of vifs To prepare for adding multi-BSS support. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 51 ++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 382ef4305a3..ee7d8a6329f 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -160,7 +160,8 @@ struct mwl8k_priv { /* TX quiesce completion, protected by fw_mutex and tx_lock */ struct completion *tx_wait; - struct ieee80211_vif *vif; + /* List of interfaces. */ + struct list_head vif_list; /* power management status cookie from firmware */ u32 *cookie; @@ -210,6 +211,9 @@ struct mwl8k_priv { /* Per interface specific private data */ struct mwl8k_vif { + struct list_head list; + struct ieee80211_vif *vif; + /* Non AMPDU sequence number assigned by driver. */ u16 seqno; }; @@ -3246,7 +3250,7 @@ static void mwl8k_stop(struct ieee80211_hw *hw) } static int mwl8k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif; @@ -3254,7 +3258,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, /* * We only support one active interface at a time. */ - if (priv->vif != NULL) + if (!list_empty(&priv->vif_list)) return -EBUSY; /* @@ -3275,14 +3279,13 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, if (priv->ap_fw) mwl8k_cmd_set_new_stn_add_self(hw, vif); - /* Clean out driver private area */ + /* Setup driver private area. */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); - - /* Set Initial sequence number to zero */ + mwl8k_vif->vif = vif; mwl8k_vif->seqno = 0; - priv->vif = vif; + list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; } @@ -3291,13 +3294,14 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); if (priv->ap_fw) mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); - priv->vif = NULL; + list_del(&mwl8k_vif->list); } static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) @@ -3526,7 +3530,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, * operation, so refuse to enable sniffer mode if a STA * interface is active. */ - if (priv->vif != NULL) { + if (!list_empty(&priv->vif_list)) { if (net_ratelimit()) printk(KERN_INFO "%s: not enabling sniffer " "mode because STA interface is active\n", @@ -3547,6 +3551,14 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, return 1; } +static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) +{ + if (!list_empty(&priv->vif_list)) + return list_entry(priv->vif_list.next, struct mwl8k_vif, list); + + return NULL; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -3595,6 +3607,7 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ mwl8k_cmd_set_pre_scan(hw); } else { + struct mwl8k_vif *mwl8k_vif; const u8 *bssid; /* @@ -3605,9 +3618,11 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, * (where the OUI part needs to be nonzero for * the BSSID to be accepted by POST_SCAN). */ - bssid = "\x01\x00\x00\x00\x00\x00"; - if (priv->vif != NULL) - bssid = priv->vif->bss_conf.bssid; + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + bssid = mwl8k_vif->vif->bss_conf.bssid; + else + bssid = "\x01\x00\x00\x00\x00\x00"; mwl8k_cmd_set_post_scan(hw, bssid); } @@ -3810,11 +3825,14 @@ static void mwl8k_finalize_join_worker(struct work_struct *work) struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; + struct mwl8k_vif *mwl8k_vif; - mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, - priv->vif->bss_conf.dtim_period); - dev_kfree_skb(skb); + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, + mwl8k_vif->vif->bss_conf.dtim_period); + dev_kfree_skb(skb); priv->beacon_skb = NULL; } @@ -3986,7 +4004,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->vif_data_size = sizeof(struct mwl8k_vif); hw->sta_data_size = sizeof(struct mwl8k_sta); - priv->vif = NULL; + + INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ priv->radio_on = 0; -- cgit v1.2.3 From f57ca9c1af3c1e30a40ad99d75940176d8c3ff3a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:50:14 +0100 Subject: mwl8k: prepare for posting per-vif firmware commands One of the bytes in the mwl8k firmware command header is the 'macid' byte, which for per-vif commands indicates which of the BSSes this command is intended for. (For commands that are not per-vif commands, this byte can just be 0.) This patch adds mwl8k_post_pervif_cmd(), which will take the macid assigned to this interface (to be done in ->add_interface()), copy it into the command packet macid field, and post the command as usual. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ee7d8a6329f..ea39ef64def 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -214,6 +214,9 @@ struct mwl8k_vif { struct list_head list; struct ieee80211_vif *vif; + /* Firmware macid for this vif. */ + int macid; + /* Non AMPDU sequence number assigned by driver. */ u16 seqno; }; @@ -419,7 +422,8 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) struct mwl8k_cmd_pkt { __le16 code; __le16 length; - __le16 seq_num; + __u8 seq_num; + __u8 macid; __le16 result; char payload[0]; } __attribute__((packed)); @@ -477,6 +481,7 @@ static int mwl8k_load_fw_image(struct mwl8k_priv *priv, cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); cmd->seq_num = 0; + cmd->macid = 0; cmd->result = 0; done = 0; @@ -1595,6 +1600,15 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) return rc; } +static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct mwl8k_cmd_pkt *cmd) +{ + if (vif != NULL) + cmd->macid = MWL8K_VIF(vif)->macid; + return mwl8k_post_cmd(hw, cmd); +} + /* * Setup code shared between STA and AP firmware images. */ @@ -3283,6 +3297,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); mwl8k_vif->vif = vif; + mwl8k_vif->macid = 0; mwl8k_vif->seqno = 0; list_add_tail(&mwl8k_vif->list, &priv->vif_list); -- cgit v1.2.3 From aa21d0f69a5ca28d33f584b8952cca154115fd26 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:50:36 +0100 Subject: mwl8k: post per-vif firmware commands as per-vif commands SET_BEACON, SET_MAC_ADDR, BSS_START and SET_NEW_STN are the currently supported firmware commands that are actually per-vif commands. Use mwl8k_post_pervif_cmd() for these commands, so that the macid of the vif they operate on gets passed down into the firmware. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 49 +++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ea39ef64def..d8cf43853de 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -294,7 +294,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = { #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_RF_ANTENNA 0x0020 -#define MWL8K_CMD_SET_BEACON 0x0100 +#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -308,10 +308,10 @@ static const struct ieee80211_rate mwl8k_rates_50[] = { #define MWL8K_CMD_MIMO_CONFIG 0x0125 #define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 -#define MWL8K_CMD_SET_MAC_ADDR 0x0202 +#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 -#define MWL8K_CMD_BSS_START 0x1100 -#define MWL8K_CMD_SET_NEW_STN 0x1111 +#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ +#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ #define MWL8K_CMD_UPDATE_STADB 0x1123 static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) @@ -2156,7 +2156,8 @@ struct mwl8k_cmd_set_beacon { __u8 beacon[0]; }; -static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len) +static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *beacon, int len) { struct mwl8k_cmd_set_beacon *cmd; int rc; @@ -2170,7 +2171,7 @@ static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, u8 *beacon, int len) cmd->beacon_len = cpu_to_le16(len); memcpy(cmd->beacon, beacon, len); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2761,7 +2762,8 @@ struct mwl8k_cmd_set_mac_addr { #define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 #define MWL8K_MAC_TYPE_PRIMARY_AP 2 -static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) +static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_cmd_set_mac_addr *cmd; @@ -2780,7 +2782,7 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) memcpy(cmd->mac_addr, mac, ETH_ALEN); } - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2823,7 +2825,8 @@ struct mwl8k_cmd_bss_start { __le32 enable; } __attribute__((packed)); -static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable) +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int enable) { struct mwl8k_cmd_bss_start *cmd; int rc; @@ -2836,7 +2839,7 @@ static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, int enable) cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->enable = cpu_to_le32(enable); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2904,7 +2907,7 @@ static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, cmd->is_qos_sta = 1; } - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2924,7 +2927,7 @@ static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2945,7 +2948,7 @@ static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, memcpy(cmd->mac_addr, addr, ETH_ALEN); cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -3287,12 +3290,6 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } - /* Set the mac address. */ - mwl8k_cmd_set_mac_addr(hw, vif->addr); - - if (priv->ap_fw) - mwl8k_cmd_set_new_stn_add_self(hw, vif); - /* Setup driver private area. */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); @@ -3300,6 +3297,12 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, mwl8k_vif->macid = 0; mwl8k_vif->seqno = 0; + /* Set the mac address. */ + mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); + + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_add_self(hw, vif); + list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; @@ -3314,7 +3317,7 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, if (priv->ap_fw) mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); - mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); list_del(&mwl8k_vif->list); } @@ -3492,13 +3495,13 @@ mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, skb = ieee80211_beacon_get(hw, vif); if (skb != NULL) { - mwl8k_cmd_set_beacon(hw, skb->data, skb->len); + mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); kfree_skb(skb); } } if (changed & BSS_CHANGED_BEACON_ENABLED) - mwl8k_cmd_bss_start(hw, info->enable_beacon); + mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); out: mwl8k_fw_unlock(hw); @@ -4112,7 +4115,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } /* Clear MAC address */ - rc = mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); if (rc) { printk(KERN_ERR "%s: Cannot clear MAC address\n", wiphy_name(hw->wiphy)); -- cgit v1.2.3 From ee0ddf1865954f44ee929d963e2c968eb377f447 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:51:30 +0100 Subject: mwl8k: enable multi-BSS AP operation As follows: - GET_HW_SPEC is now responsible for setting priv->{ap,sta}_macids_supported, which are bitmasks of supported macids for AP and STA mode. (Typically, STA firmware images will support only one macid, #0, in STA mode, and AP firmware images will support macids #0-7, in AP mode.) - Our wiphy ->interfaces_modes is now set based on the non-zero-ness of these two bitmasks. - We main priv->macids_used, a bitmask of which macids are currently in use. ->add_interface() will assign the lowest free macid for this interface type as it is created, or bail out if there are no more free macids to assign. ->delete_interface() will mark the macid as being free again. This enables the multi-BSS code added in the previous commits. Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 70 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index d8cf43853de..06dc7a0978a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -147,6 +147,8 @@ struct mwl8k_priv { struct ieee80211_supported_band band_50; struct ieee80211_channel channels_50[4]; struct ieee80211_rate rates_50[9]; + u32 ap_macids_supported; + u32 sta_macids_supported; /* firmware access */ struct mutex fw_mutex; @@ -161,6 +163,7 @@ struct mwl8k_priv { struct completion *tx_wait; /* List of interfaces. */ + u32 macids_used; struct list_head vif_list; /* power management status cookie from firmware */ @@ -1786,6 +1789,8 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000001; } kfree(cmd); @@ -1840,6 +1845,8 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; mwl8k_setup_2ghz_band(hw); + priv->ap_macids_supported = 0x000000ff; + priv->sta_macids_supported = 0x00000000; off = le32_to_cpu(cmd->wcbbase0) & 0xffff; iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off); @@ -2759,16 +2766,33 @@ struct mwl8k_cmd_set_mac_addr { }; } __attribute__((packed)); -#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 -#define MWL8K_MAC_TYPE_PRIMARY_AP 2 +#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 +#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 +#define MWL8K_MAC_TYPE_PRIMARY_AP 2 +#define MWL8K_MAC_TYPE_SECONDARY_AP 3 static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *mac) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); struct mwl8k_cmd_set_mac_addr *cmd; + int mac_type; int rc; + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { + if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { + if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; + } + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -2776,7 +2800,7 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); cmd->header.length = cpu_to_le16(sizeof(*cmd)); if (priv->ap_fw) { - cmd->mbss.mac_type = cpu_to_le16(MWL8K_MAC_TYPE_PRIMARY_AP); + cmd->mbss.mac_type = cpu_to_le16(mac_type); memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); } else { memcpy(cmd->mac_addr, mac, ETH_ALEN); @@ -3271,12 +3295,8 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif; - - /* - * We only support one active interface at a time. - */ - if (!list_empty(&priv->vif_list)) - return -EBUSY; + u32 macids_supported; + int macid; /* * Reject interface creation if sniffer mode is active, as @@ -3290,11 +3310,27 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } + + switch (vif->type) { + case NL80211_IFTYPE_AP: + macids_supported = priv->ap_macids_supported; + break; + case NL80211_IFTYPE_STATION: + macids_supported = priv->sta_macids_supported; + break; + default: + return -EINVAL; + } + + macid = ffs(macids_supported & ~priv->macids_used); + if (!macid--) + return -EBUSY; + /* Setup driver private area. */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); mwl8k_vif->vif = vif; - mwl8k_vif->macid = 0; + mwl8k_vif->macid = macid; mwl8k_vif->seqno = 0; /* Set the mac address. */ @@ -3303,6 +3339,7 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, if (priv->ap_fw) mwl8k_cmd_set_new_stn_add_self(hw, vif); + priv->macids_used |= 1 << mwl8k_vif->macid; list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; @@ -3319,6 +3356,7 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); + priv->macids_used &= ~(1 << mwl8k_vif->macid); list_del(&mwl8k_vif->list); } @@ -4023,6 +4061,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, hw->vif_data_size = sizeof(struct mwl8k_vif); hw->sta_data_size = sizeof(struct mwl8k_sta); + priv->macids_used = 0; INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ @@ -4094,12 +4133,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, rc = mwl8k_cmd_get_hw_spec_ap(hw); if (!rc) rc = mwl8k_cmd_set_hw_spec(hw); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP); } else { rc = mwl8k_cmd_get_hw_spec_sta(hw); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); } if (rc) { printk(KERN_ERR "%s: Cannot initialise firmware\n", @@ -4107,6 +4142,13 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_irq; } + hw->wiphy->interface_modes = 0; + if (priv->ap_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + if (priv->sta_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + + /* Turn radio off */ rc = mwl8k_cmd_radio_disable(hw); if (rc) { -- cgit v1.2.3 From a5fb297d634ba20bd53a7d6fecd611bbfd342e78 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Tue, 12 Jan 2010 13:51:38 +0100 Subject: mwl8k: update version number (to 0.12) and copyright Signed-off-by: Lennert Buytenhek Signed-off-by: John W. Linville --- drivers/net/wireless/mwl8k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/mwl8k.c') diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 06dc7a0978a..68546ca0ba3 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2,7 +2,7 @@ * drivers/net/wireless/mwl8k.c * Driver for Marvell TOPDOG 802.11 Wireless cards * - * Copyright (C) 2008-2009 Marvell Semiconductor Inc. + * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -26,7 +26,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.11" +#define MWL8K_VERSION "0.12" /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 -- cgit v1.2.3