2012-05-18 04:46:45

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 0/5] TKIP/GEM support for 18xx

TKIP extra padding is not required for 18xx cards. Turn this into a
quirk for 12xx cards only.
18xx cards must have their Tx spare-blocks updated before any vif can
transmit GEM or TKIP encrypted packets. When spare blocks are updated,
we must make sure no Tx frame reaches the FW. Otherwise our Tx block
accounting will be out of sync.
Introduce new internal Tx queue wake/stop APIs to allow us to safely
stop the queues for multiple reasons (similar to mac80211). Use the
new APIs to prevent all Tx during the change of spare blocks.

Arik Nemtsov (5):
wlcore/wl12xx/wl18xx: introduce quirk to remove TKIP header space
wlcore/wl12xx/wl18xx: handle spare blocks spacial cases per arch
wlcore: add stop reason bitmap for waking/starting queues
wlcore: stop queues on Tx flush
wlcore/wl12xx/wl18xx: implement op_set_key per HW arch

drivers/net/wireless/ti/wl12xx/main.c | 27 ++++-
drivers/net/wireless/ti/wl18xx/acx.c | 4 +
drivers/net/wireless/ti/wl18xx/main.c | 101 ++++++++++++++++---
drivers/net/wireless/ti/wl18xx/tx.c | 3 +-
drivers/net/wireless/ti/wl18xx/tx.h | 3 +-
drivers/net/wireless/ti/wl18xx/wl18xx.h | 3 +
drivers/net/wireless/ti/wlcore/cmd.c | 5 +-
drivers/net/wireless/ti/wlcore/debugfs.c | 1 -
drivers/net/wireless/ti/wlcore/hw_ops.h | 21 ++++
drivers/net/wireless/ti/wlcore/main.c | 55 ++++++-----
drivers/net/wireless/ti/wlcore/tx.c | 152 ++++++++++++++++++++++++-----
drivers/net/wireless/ti/wlcore/tx.h | 23 ++++-
drivers/net/wireless/ti/wlcore/wlcore.h | 21 +++-
drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 -
14 files changed, 343 insertions(+), 79 deletions(-)

--
1.7.9.5



2012-05-18 04:46:48

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 1/5] wlcore/wl12xx/wl18xx: introduce quirk to remove TKIP header space

18xx chips do not require extra space in the TKIP header. Introduce a
new HW quirk to allow us to make this feature arch-specific. 12xx chip
will now have this quirk.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl12xx/main.c | 7 +++++--
drivers/net/wireless/ti/wl18xx/tx.c | 3 ++-
drivers/net/wireless/ti/wlcore/cmd.c | 5 +++--
drivers/net/wireless/ti/wlcore/main.c | 8 +++++---
drivers/net/wireless/ti/wlcore/tx.c | 9 ++++++---
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++
6 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index 522c898..982cb20 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -632,7 +632,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* clear the alignment quirk, since we don't support it */
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;

- wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+ wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+ WLCORE_QUIRK_TKIP_HEADER_SPACE;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
memcpy(&wl->conf.mem, &wl12xx_default_priv_conf.mem_wl127x,
@@ -650,7 +651,8 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
/* clear the alignment quirk, since we don't support it */
wl->quirks &= ~WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN;

- wl->quirks |= WLCORE_QUIRK_LEGACY_NVS;
+ wl->quirks |= WLCORE_QUIRK_LEGACY_NVS |
+ WLCORE_QUIRK_TKIP_HEADER_SPACE;
wl->plt_fw_name = WL127X_PLT_FW_NAME;
wl->sr_fw_name = WL127X_FW_NAME_SINGLE;
wl->mr_fw_name = WL127X_FW_NAME_MULTI;
@@ -665,6 +667,7 @@ static int wl12xx_identify_chip(struct wl1271 *wl)
case CHIP_ID_1283_PG20:
wl1271_debug(DEBUG_BOOT, "chip id 0x%x (1283 PG20)",
wl->chip.id);
+ wl->quirks |= WLCORE_QUIRK_TKIP_HEADER_SPACE;
wl->plt_fw_name = WL128X_PLT_FW_NAME;
wl->sr_fw_name = WL128X_FW_NAME_SINGLE;
wl->mr_fw_name = WL128X_FW_NAME_MULTI;
diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c
index 4de00b9..5b1fb10 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.c
+++ b/drivers/net/wireless/ti/wl18xx/tx.c
@@ -75,7 +75,8 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte)
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));

/* remove TKIP header space if present */
- if (info->control.hw_key &&
+ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data, hdrlen);
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index df8d672..885364c 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -1046,7 +1046,7 @@ out:

int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{
- int ret, extra;
+ int ret, extra = 0;
u16 fc;
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
@@ -1085,7 +1085,8 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif)
/* encryption space */
switch (wlvif->encryption_type) {
case KEY_TKIP:
- extra = WL1271_EXTRA_SPACE_TKIP;
+ if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+ extra = WL1271_EXTRA_SPACE_TKIP;
break;
case KEY_AES:
extra = WL1271_EXTRA_SPACE_AES;
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 0dc3ae3..d6e6509 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -4978,9 +4978,11 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
WL1271_CIPHER_SUITE_GEM,
};

- /* The tx descriptor buffer and the TKIP space. */
- wl->hw->extra_tx_headroom = WL1271_EXTRA_SPACE_TKIP +
- sizeof(struct wl1271_tx_hw_descr);
+ /* The tx descriptor buffer */
+ wl->hw->extra_tx_headroom = sizeof(struct wl1271_tx_hw_descr);
+
+ if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
+ wl->hw->extra_tx_headroom += WL1271_EXTRA_SPACE_TKIP;

/* unit us */
/* FIXME: find a proper value */
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index e8a2998..6b68e29 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -358,7 +358,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* TODO: handle dummy packets on multi-vifs */
is_dummy = wl12xx_is_dummy_packet(wl, skb);

- if (info->control.hw_key &&
+ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
extra = WL1271_EXTRA_SPACE_TKIP;

@@ -852,7 +853,8 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));

/* remove TKIP header space if present */
- if (info->control.hw_key &&
+ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
memmove(skb->data + WL1271_EXTRA_SPACE_TKIP, skb->data,
@@ -1001,7 +1003,8 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
*/
info = IEEE80211_SKB_CB(skb);
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
- if (info->control.hw_key &&
+ if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
+ info->control.hw_key &&
info->control.hw_key->cipher ==
WLAN_CIPHER_SUITE_TKIP) {
int hdrlen = ieee80211_get_hdrlen_from_skb(skb);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 001777a..5a35a33 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -412,6 +412,9 @@ int wlcore_free_hw(struct wl1271 *wl);
/* Some firmwares may not support ELP */
#define WLCORE_QUIRK_NO_ELP BIT(6)

+/* extra header space is required for TKIP */
+#define WLCORE_QUIRK_TKIP_HEADER_SPACE BIT(8)
+
/* TODO: move to the lower drivers when all usages are abstracted */
#define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111)
--
1.7.9.5


2012-05-18 04:46:52

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 3/5] wlcore: add stop reason bitmap for waking/starting queues

Allow the driver to wake/stop the queues for multiple reasons. A queue
is started when no stop-reasons exist.

Convert all wake/stop queue calls to use the new API.

Before, a stopped queue was almost synonymous a high-watermark on Tx.
Remove a bit of code in wl12xx_tx_reset() that relied on it.

Internal packets arriving from mac80211 are also discarded when a queue
is stopped. A notable exception to this is the watermark reason, which
is a "soft"-stop reason. We allow traffic to gradually come to a halt,
but we don't mind spurious packets here and there. This is merely a flow
regulation mechanism.

Based on a similar patch by Eliad Peller <eliadWizery.com>.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 21 ++++--
drivers/net/wireless/ti/wlcore/tx.c | 109 +++++++++++++++++++++++++++----
drivers/net/wireless/ti/wlcore/tx.h | 21 +++++-
drivers/net/wireless/ti/wlcore/wlcore.h | 2 +-
4 files changed, 132 insertions(+), 21 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index efa3b55..6e6926b 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -868,7 +868,7 @@ static void wl1271_recovery_work(struct work_struct *work)
}

/* Prevent spurious TX during FW restart */
- ieee80211_stop_queues(wl->hw);
+ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);

if (wl->sched_scanning) {
ieee80211_sched_scan_stopped(wl->hw);
@@ -894,7 +894,7 @@ static void wl1271_recovery_work(struct work_struct *work)
* Its safe to enable TX now - the queues are stopped after a request
* to restart the HW.
*/
- ieee80211_wake_queues(wl->hw);
+ wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FW_RESTART);
return;
out_unlock:
wl->watchdog_recovery = false;
@@ -1109,9 +1109,16 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)

spin_lock_irqsave(&wl->wl_lock, flags);

- /* queue the packet */
+ /*
+ * drop the packet if the link is invalid or the queue is stopped
+ * for any reason but watermark. Watermark is a "soft"-stop so we
+ * allow these packets through.
+ */
if (hlid == WL12XX_INVALID_LINK_ID ||
- (wlvif && !test_bit(hlid, wlvif->links_map))) {
+ (wlvif && !test_bit(hlid, wlvif->links_map)) ||
+ (wlcore_is_queue_stopped(wl, q) &&
+ !wlcore_is_queue_stopped_by_reason(wl, q,
+ WLCORE_QUEUE_STOP_REASON_WATERMARK))) {
wl1271_debug(DEBUG_TX, "DROP skb hlid %d q %d", hlid, q);
ieee80211_free_txskb(hw, skb);
goto out;
@@ -1129,8 +1136,8 @@ static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
*/
if (wl->tx_queue_count[q] >= WL1271_TX_QUEUE_HIGH_WATERMARK) {
wl1271_debug(DEBUG_TX, "op_tx: stopping queues for q %d", q);
- ieee80211_stop_queue(wl->hw, mapping);
- set_bit(q, &wl->stopped_queues_map);
+ wlcore_stop_queue_locked(wl, q,
+ WLCORE_QUEUE_STOP_REASON_WATERMARK);
}

/*
@@ -1713,7 +1720,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
cancel_delayed_work_sync(&wl->connection_loss_work);

/* let's notify MAC80211 about the remaining pending TX frames */
- wl12xx_tx_reset(wl, true);
+ wl12xx_tx_reset(wl);
mutex_lock(&wl->mutex);

wl1271_power_off(wl);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 0949ab1..f68567b 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -443,18 +443,15 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,

void wl1271_handle_tx_low_watermark(struct wl1271 *wl)
{
- unsigned long flags;
int i;

for (i = 0; i < NUM_TX_QUEUES; i++) {
- if (test_bit(i, &wl->stopped_queues_map) &&
+ if (wlcore_is_queue_stopped_by_reason(wl, i,
+ WLCORE_QUEUE_STOP_REASON_WATERMARK) &&
wl->tx_queue_count[i] <= WL1271_TX_QUEUE_LOW_WATERMARK) {
/* firmware buffer has space, restart queues */
- spin_lock_irqsave(&wl->wl_lock, flags);
- ieee80211_wake_queue(wl->hw,
- wl1271_tx_get_mac80211_queue(i));
- clear_bit(i, &wl->stopped_queues_map);
- spin_unlock_irqrestore(&wl->wl_lock, flags);
+ wlcore_wake_queue(wl, i,
+ WLCORE_QUEUE_STOP_REASON_WATERMARK);
}
}
}
@@ -963,7 +960,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif)

}
/* caller must hold wl->mutex and TX must be stopped */
-void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
+void wl12xx_tx_reset(struct wl1271 *wl)
{
int i;
struct sk_buff *skb;
@@ -978,15 +975,12 @@ void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues)
wl->tx_queue_count[i] = 0;
}

- wl->stopped_queues_map = 0;
-
/*
* Make sure the driver is at a consistent state, in case this
* function is called from a context other than interface removal.
* This call will always wake the TX queues.
*/
- if (reset_tx_queues)
- wl1271_handle_tx_low_watermark(wl);
+ wl1271_handle_tx_low_watermark(wl);

for (i = 0; i < wl->num_tx_desc; i++) {
if (wl->tx_frames[i] == NULL)
@@ -1060,3 +1054,94 @@ u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)

return BIT(__ffs(rate_set));
}
+
+void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason)
+{
+ bool stopped = !!wl->queue_stop_reasons[queue];
+
+ /* queue should not be stopped for this reason */
+ WARN_ON(test_and_set_bit(reason, &wl->queue_stop_reasons[queue]));
+
+ if (stopped)
+ return;
+
+ ieee80211_stop_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+}
+
+void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ wlcore_stop_queue_locked(wl, queue, reason);
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+
+ /* queue should not be clear for this reason */
+ WARN_ON(!test_and_clear_bit(reason, &wl->queue_stop_reasons[queue]));
+
+ if (wl->queue_stop_reasons[queue])
+ goto out;
+
+ ieee80211_wake_queue(wl->hw, wl1271_tx_get_mac80211_queue(queue));
+
+out:
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+void wlcore_stop_queues(struct wl1271 *wl,
+ enum wlcore_queue_stop_reason reason)
+{
+ int i;
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wlcore_stop_queue(wl, i, reason);
+}
+
+void wlcore_wake_queues(struct wl1271 *wl,
+ enum wlcore_queue_stop_reason reason)
+{
+ int i;
+
+ for (i = 0; i < NUM_TX_QUEUES; i++)
+ wlcore_wake_queue(wl, i, reason);
+}
+
+void wlcore_reset_stopped_queues(struct wl1271 *wl)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wl->wl_lock, flags);
+
+ for (i = 0; i < NUM_TX_QUEUES; i++) {
+ if (!wl->queue_stop_reasons[i])
+ continue;
+
+ wl->queue_stop_reasons[i] = 0;
+ ieee80211_wake_queue(wl->hw,
+ wl1271_tx_get_mac80211_queue(i));
+ }
+
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+}
+
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason)
+{
+ return test_bit(reason, &wl->queue_stop_reasons[queue]);
+}
+
+bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue)
+{
+ return !!wl->queue_stop_reasons[queue];
+}
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index e24c436..6bf6956 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -184,6 +184,11 @@ struct wl1271_tx_hw_res_if {
struct wl1271_tx_hw_res_descr tx_results_queue[TX_HW_RESULT_QUEUE_LEN];
} __packed;

+enum wlcore_queue_stop_reason {
+ WLCORE_QUEUE_STOP_REASON_WATERMARK,
+ WLCORE_QUEUE_STOP_REASON_FW_RESTART,
+};
+
static inline int wl1271_tx_get_queue(int queue)
{
switch (queue) {
@@ -230,7 +235,7 @@ void wl1271_tx_work(struct work_struct *work);
void wl1271_tx_work_locked(struct wl1271 *wl);
void wl1271_tx_complete(struct wl1271 *wl);
void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif);
-void wl12xx_tx_reset(struct wl1271 *wl, bool reset_tx_queues);
+void wl12xx_tx_reset(struct wl1271 *wl);
void wl1271_tx_flush(struct wl1271 *wl);
u8 wlcore_rate_to_idx(struct wl1271 *wl, u8 rate, enum ieee80211_band band);
u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set,
@@ -247,6 +252,20 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids);
unsigned int wlcore_calc_packet_alignment(struct wl1271 *wl,
unsigned int packet_length);
void wl1271_free_tx_id(struct wl1271 *wl, int id);
+void wlcore_stop_queue_locked(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queue(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queue(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason);
+void wlcore_stop_queues(struct wl1271 *wl,
+ enum wlcore_queue_stop_reason reason);
+void wlcore_wake_queues(struct wl1271 *wl,
+ enum wlcore_queue_stop_reason reason);
+void wlcore_reset_stopped_queues(struct wl1271 *wl);
+bool wlcore_is_queue_stopped_by_reason(struct wl1271 *wl, u8 queue,
+ enum wlcore_queue_stop_reason reason);
+bool wlcore_is_queue_stopped(struct wl1271 *wl, u8 queue);

/* from main.c */
void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 84a1b05..94b79d5 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -209,7 +209,7 @@ struct wl1271 {

/* Frames scheduled for transmission, not handled yet */
int tx_queue_count[NUM_TX_QUEUES];
- long stopped_queues_map;
+ unsigned long queue_stop_reasons[NUM_TX_QUEUES];

/* Frames received, not handled yet by mac80211 */
struct sk_buff_head deferred_rx_queue;
--
1.7.9.5


2012-05-18 04:46:50

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 2/5] wlcore/wl12xx/wl18xx: handle spare blocks spacial cases per arch

Add a HW op for getting spare blocks.

12xx cards require 2 spare blocks for GEM encrypted SKBs, regardless
of VIFs or keys programmed into the FW.

18xx cards require 2 spare blocks when there are any connected TKIP or
GEM VIFs. For now always return 2 spare blocks, as this works with all
networks. The special case TKIP/GEM functionality is added at a later
patch.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl12xx/main.c | 11 +++++++++--
drivers/net/wireless/ti/wl18xx/main.c | 11 ++++++++---
drivers/net/wireless/ti/wl18xx/tx.h | 3 ++-
drivers/net/wireless/ti/wlcore/debugfs.c | 1 -
drivers/net/wireless/ti/wlcore/hw_ops.h | 9 +++++++++
drivers/net/wireless/ti/wlcore/main.c | 11 -----------
drivers/net/wireless/ti/wlcore/tx.c | 20 ++++++++++----------
drivers/net/wireless/ti/wlcore/wlcore.h | 5 +----
drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 ---
9 files changed, 39 insertions(+), 35 deletions(-)

diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index 982cb20..94ca51a 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -1367,6 +1367,14 @@ out:
return ret;
}

+static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+ if (is_gem)
+ return WL12XX_TX_HW_BLOCK_GEM_SPARE;
+
+ return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
+}
+
static struct wlcore_ops wl12xx_ops = {
.identify_chip = wl12xx_identify_chip,
.identify_fw = wl12xx_identify_fw,
@@ -1390,6 +1398,7 @@ static struct wlcore_ops wl12xx_ops = {
.set_rx_csum = NULL,
.ap_get_mimo_wide_rate_mask = NULL,
.debugfs_init = wl12xx_debugfs_add_files,
+ .get_spare_blocks = wl12xx_get_spare_blocks,
};

static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
@@ -1425,8 +1434,6 @@ static int __devinit wl12xx_probe(struct platform_device *pdev)
wl->rtable = wl12xx_rtable;
wl->num_tx_desc = 16;
wl->num_rx_desc = 8;
- wl->normal_tx_spare = WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
- wl->gem_tx_spare = WL12XX_TX_HW_BLOCK_GEM_SPARE;
wl->band_rate_to_idx = wl12xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0;
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 80377aa..5b51410 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -877,7 +877,7 @@ static int wl18xx_hw_init(struct wl1271 *wl)

ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap,
sdio_align_size,
- WL18XX_TX_HW_BLOCK_SPARE,
+ WL18XX_TX_HW_EXTRA_BLOCK_SPARE,
WL18XX_HOST_IF_LEN_SIZE_FIELD);
if (ret < 0)
return ret;
@@ -1037,6 +1037,12 @@ static int wl18xx_handle_static_data(struct wl1271 *wl,
return 0;
}

+static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+ /* TODO: dynamically change to extra only when we have GEM or TKIP */
+ return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
+}
+
static struct wlcore_ops wl18xx_ops = {
.identify_chip = wl18xx_identify_chip,
.boot = wl18xx_boot,
@@ -1059,6 +1065,7 @@ static struct wlcore_ops wl18xx_ops = {
.get_mac = wl18xx_get_mac,
.debugfs_init = wl18xx_debugfs_add_files,
.handle_static_data = wl18xx_handle_static_data,
+ .get_spare_blocks = wl18xx_get_spare_blocks,
};

/* HT cap appropriate for wide channels */
@@ -1132,8 +1139,6 @@ static int __devinit wl18xx_probe(struct platform_device *pdev)
wl->rtable = wl18xx_rtable;
wl->num_tx_desc = 32;
wl->num_rx_desc = 16;
- wl->normal_tx_spare = WL18XX_TX_HW_BLOCK_SPARE;
- wl->gem_tx_spare = WL18XX_TX_HW_GEM_BLOCK_SPARE;
wl->band_rate_to_idx = wl18xx_band_rate_to_idx;
wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX;
wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0;
diff --git a/drivers/net/wireless/ti/wl18xx/tx.h b/drivers/net/wireless/ti/wl18xx/tx.h
index 2417262..8aecaf0 100644
--- a/drivers/net/wireless/ti/wl18xx/tx.h
+++ b/drivers/net/wireless/ti/wl18xx/tx.h
@@ -25,7 +25,8 @@
#include "../wlcore/wlcore.h"

#define WL18XX_TX_HW_BLOCK_SPARE 1
-#define WL18XX_TX_HW_GEM_BLOCK_SPARE 2
+/* for special cases - namely, TKIP and GEM */
+#define WL18XX_TX_HW_EXTRA_BLOCK_SPARE 2
#define WL18XX_TX_HW_BLOCK_SIZE 268

#define WL18XX_TX_STATUS_DESC_ID_MASK 0x7F
diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c
index fc44262..fcd6063 100644
--- a/drivers/net/wireless/ti/wlcore/debugfs.c
+++ b/drivers/net/wireless/ti/wlcore/debugfs.c
@@ -507,7 +507,6 @@ static ssize_t vifs_state_read(struct file *file, char __user *user_buf,
VIF_STATE_PRINT_INT(last_rssi_event);
VIF_STATE_PRINT_INT(ba_support);
VIF_STATE_PRINT_INT(ba_allowed);
- VIF_STATE_PRINT_INT(is_gem);
VIF_STATE_PRINT_LLHEX(tx_security_seq);
VIF_STATE_PRINT_INT(tx_security_last_seq_lsb);
}
diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h
index c590b6f..2cb3521 100644
--- a/drivers/net/wireless/ti/wlcore/hw_ops.h
+++ b/drivers/net/wireless/ti/wlcore/hw_ops.h
@@ -167,4 +167,13 @@ wlcore_handle_static_data(struct wl1271 *wl, void *static_data)
return 0;
}

+static inline int
+wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
+{
+ if (!wl->ops->get_spare_blocks)
+ BUG_ON(1);
+
+ return wl->ops->get_spare_blocks(wl, is_gem);
+}
+
#endif
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index d6e6509..efa3b55 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2801,17 +2801,6 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int ret;
bool is_ap = (wlvif->bss_type == BSS_TYPE_AP_BSS);

- /*
- * A role set to GEM cipher requires different Tx settings (namely
- * spare blocks). Note when we are in this mode so the HW can adjust.
- */
- if (key_type == KEY_GEM) {
- if (action == KEY_ADD_OR_REPLACE)
- wlvif->is_gem = true;
- else if (action == KEY_REMOVE)
- wlvif->is_gem = false;
- }
-
if (is_ap) {
struct wl1271_station *wl_sta;
u8 hlid;
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 6b68e29..0949ab1 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -187,28 +187,24 @@ EXPORT_SYMBOL(wlcore_calc_packet_alignment);

static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct sk_buff *skb, u32 extra, u32 buf_offset,
- u8 hlid)
+ u8 hlid, bool is_gem)
{
struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
u32 total_blocks;
int id, ret = -EBUSY, ac;
- u32 spare_blocks = wl->normal_tx_spare;
- bool is_dummy = false;
+ u32 spare_blocks;

if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
return -EAGAIN;

+ spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem);
+
/* allocate free identifier for the packet */
id = wl1271_alloc_tx_id(wl, skb);
if (id < 0)
return id;

- if (unlikely(wl12xx_is_dummy_packet(wl, skb)))
- is_dummy = true;
- else if (wlvif->is_gem)
- spare_blocks = wl->gem_tx_spare;
-
total_blocks = wlcore_hw_calc_tx_blocks(wl, total_len, spare_blocks);

if (total_blocks <= wl->tx_blocks_available) {
@@ -230,7 +226,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
wl->tx_allocated_pkts[ac]++;

- if (!is_dummy && wlvif &&
+ if (!wl12xx_is_dummy_packet(wl, skb) && wlvif &&
wlvif->bss_type == BSS_TYPE_AP_BSS &&
test_bit(hlid, wlvif->ap.sta_hlid_map))
wl->links[hlid].allocated_pkts++;
@@ -349,6 +345,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u32 total_len;
u8 hlid;
bool is_dummy;
+ bool is_gem = false;

if (!skb)
return -EINVAL;
@@ -377,6 +374,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return ret;
wlvif->default_key = idx;
}
+
+ is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
}
hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
if (hlid == WL12XX_INVALID_LINK_ID) {
@@ -384,7 +383,8 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return -EINVAL;
}

- ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid);
+ ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
+ is_gem);
if (ret < 0)
return ret;

diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 5a35a33..84a1b05 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -75,6 +75,7 @@ struct wlcore_ops {
int (*debugfs_init)(struct wl1271 *wl, struct dentry *rootdir);
int (*handle_static_data)(struct wl1271 *wl,
struct wl1271_static_data *static_data);
+ int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
};

enum wlcore_partitions {
@@ -355,10 +356,6 @@ struct wl1271 {
/* number of RX descriptors the HW supports. */
u32 num_rx_desc;

- /* spare Tx blocks for normal/GEM operating modes */
- u32 normal_tx_spare;
- u32 gem_tx_spare;
-
/* translate HW Tx rates to standard rate-indices */
const u8 **band_rate_to_idx;

diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h
index 83c9869..8260b1e 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore_i.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h
@@ -417,9 +417,6 @@ struct wl12xx_vif {
struct work_struct rx_streaming_disable_work;
struct timer_list rx_streaming_timer;

- /* does the current role use GEM for encryption (AP or STA) */
- bool is_gem;
-
/*
* This struct must be last!
* data that has to be saved acrossed reconfigs (e.g. recovery)
--
1.7.9.5


2012-05-18 04:46:56

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 5/5] wlcore/wl12xx/wl18xx: implement op_set_key per HW arch

The 12xx set_key just calls the common wlcore_set_key function, in order
to program the keys into the FW.

The 18xx variant changes the spare block count when a GEM or TKIP
key is set. Also modify the get_spare_blocks HW op for 18xx to return
the correct numbers of spare blocks, according to what is currently
set in FW.

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wl12xx/main.c | 9 +++
drivers/net/wireless/ti/wl18xx/acx.c | 4 ++
drivers/net/wireless/ti/wl18xx/main.c | 96 +++++++++++++++++++++++++++----
drivers/net/wireless/ti/wl18xx/wl18xx.h | 3 +
drivers/net/wireless/ti/wlcore/hw_ops.h | 12 ++++
drivers/net/wireless/ti/wlcore/main.c | 14 ++++-
drivers/net/wireless/ti/wlcore/tx.c | 3 +
drivers/net/wireless/ti/wlcore/tx.h | 1 +
drivers/net/wireless/ti/wlcore/wlcore.h | 8 +++
9 files changed, 136 insertions(+), 14 deletions(-)

diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c
index 94ca51a..4ed23aa 100644
--- a/drivers/net/wireless/ti/wl12xx/main.c
+++ b/drivers/net/wireless/ti/wl12xx/main.c
@@ -1375,6 +1375,14 @@ static int wl12xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
return WL12XX_TX_HW_BLOCK_SPARE_DEFAULT;
}

+static int wl12xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf)
+{
+ return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+}
+
static struct wlcore_ops wl12xx_ops = {
.identify_chip = wl12xx_identify_chip,
.identify_fw = wl12xx_identify_fw,
@@ -1399,6 +1407,7 @@ static struct wlcore_ops wl12xx_ops = {
.ap_get_mimo_wide_rate_mask = NULL,
.debugfs_init = wl12xx_debugfs_add_files,
.get_spare_blocks = wl12xx_get_spare_blocks,
+ .set_key = wl12xx_set_key,
};

static struct ieee80211_sta_ht_cap wl12xx_ht_cap = {
diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c
index 3379db2..01ba403 100644
--- a/drivers/net/wireless/ti/wl18xx/acx.c
+++ b/drivers/net/wireless/ti/wl18xx/acx.c
@@ -32,6 +32,10 @@ int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap,
struct wl18xx_acx_host_config_bitmap *bitmap_conf;
int ret;

+ wl1271_debug(DEBUG_ACX, "acx cfg bitmap %d blk %d spare %d field %d",
+ host_cfg_bitmap, sdio_blk_size, extra_mem_blks,
+ len_field_size);
+
bitmap_conf = kzalloc(sizeof(*bitmap_conf), GFP_KERNEL);
if (!bitmap_conf) {
ret = -ENOMEM;
diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c
index 5b51410..1e8f690 100644
--- a/drivers/net/wireless/ti/wl18xx/main.c
+++ b/drivers/net/wireless/ti/wl18xx/main.c
@@ -851,17 +851,12 @@ static void wl18xx_tx_immediate_completion(struct wl1271 *wl)
wl18xx_tx_immediate_complete(wl);
}

-static int wl18xx_hw_init(struct wl1271 *wl)
+static int wl18xx_set_host_cfg_bitmap(struct wl1271 *wl, u32 extra_mem_blk)
{
int ret;
- struct wl18xx_priv *priv = wl->priv;
- u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE |
- HOST_IF_CFG_ADD_RX_ALIGNMENT;
-
u32 sdio_align_size = 0;
-
- /* (re)init private structures. Relevant on recovery as well. */
- priv->last_fw_rls_idx = 0;
+ u32 host_cfg_bitmap = HOST_IF_CFG_RX_FIFO_ENABLE |
+ HOST_IF_CFG_ADD_RX_ALIGNMENT;

/* Enable Tx SDIO padding */
if (wl->quirks & WLCORE_QUIRK_TX_BLOCKSIZE_ALIGN) {
@@ -876,12 +871,28 @@ static int wl18xx_hw_init(struct wl1271 *wl)
}

ret = wl18xx_acx_host_if_cfg_bitmap(wl, host_cfg_bitmap,
- sdio_align_size,
- WL18XX_TX_HW_EXTRA_BLOCK_SPARE,
+ sdio_align_size, extra_mem_blk,
WL18XX_HOST_IF_LEN_SIZE_FIELD);
if (ret < 0)
return ret;

+ return 0;
+}
+
+static int wl18xx_hw_init(struct wl1271 *wl)
+{
+ int ret;
+ struct wl18xx_priv *priv = wl->priv;
+
+ /* (re)init private structures. Relevant on recovery as well. */
+ priv->last_fw_rls_idx = 0;
+ priv->extra_spare_vif_count = 0;
+
+ /* set the default amount of spare blocks in the bitmap */
+ ret = wl18xx_set_host_cfg_bitmap(wl, WL18XX_TX_HW_BLOCK_SPARE);
+ if (ret < 0)
+ return ret;
+
if (checksum_param) {
ret = wl18xx_acx_set_checksum_state(wl);
if (ret != 0)
@@ -1039,8 +1050,68 @@ static int wl18xx_handle_static_data(struct wl1271 *wl,

static int wl18xx_get_spare_blocks(struct wl1271 *wl, bool is_gem)
{
- /* TODO: dynamically change to extra only when we have GEM or TKIP */
- return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
+ struct wl18xx_priv *priv = wl->priv;
+
+ /* If we have VIFs requiring extra spare, indulge them */
+ if (priv->extra_spare_vif_count)
+ return WL18XX_TX_HW_EXTRA_BLOCK_SPARE;
+
+ return WL18XX_TX_HW_BLOCK_SPARE;
+}
+
+static int wl18xx_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf)
+{
+ struct wl18xx_priv *priv = wl->priv;
+ bool change_spare = false;
+ int ret;
+
+ /*
+ * when adding the first or removing the last GEM/TKIP interface,
+ * we have to adjust the number of spare blocks.
+ */
+ change_spare = (key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
+ key_conf->cipher == WLAN_CIPHER_SUITE_TKIP) &&
+ ((priv->extra_spare_vif_count == 0 && cmd == SET_KEY) ||
+ (priv->extra_spare_vif_count == 1 && cmd == DISABLE_KEY));
+
+ /* no need to change spare - just regular set_key */
+ if (!change_spare)
+ return wlcore_set_key(wl, cmd, vif, sta, key_conf);
+
+ /*
+ * stop the queues and flush to ensure the next packets are
+ * in sync with FW spare block accounting
+ */
+ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+ wl1271_tx_flush(wl);
+
+ ret = wlcore_set_key(wl, cmd, vif, sta, key_conf);
+ if (ret < 0)
+ goto out;
+
+ /* key is now set, change the spare blocks */
+ if (cmd == SET_KEY) {
+ ret = wl18xx_set_host_cfg_bitmap(wl,
+ WL18XX_TX_HW_EXTRA_BLOCK_SPARE);
+ if (ret < 0)
+ goto out;
+
+ priv->extra_spare_vif_count++;
+ } else {
+ ret = wl18xx_set_host_cfg_bitmap(wl,
+ WL18XX_TX_HW_BLOCK_SPARE);
+ if (ret < 0)
+ goto out;
+
+ priv->extra_spare_vif_count--;
+ }
+
+out:
+ wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
+ return ret;
}

static struct wlcore_ops wl18xx_ops = {
@@ -1066,6 +1137,7 @@ static struct wlcore_ops wl18xx_ops = {
.debugfs_init = wl18xx_debugfs_add_files,
.handle_static_data = wl18xx_handle_static_data,
.get_spare_blocks = wl18xx_get_spare_blocks,
+ .set_key = wl18xx_set_key,
};

/* HT cap appropriate for wide channels */
diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h
index 34e202b..b9c4309 100644
--- a/drivers/net/wireless/ti/wl18xx/wl18xx.h
+++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h
@@ -36,6 +36,9 @@ struct wl18xx_priv {
u8 last_fw_rls_idx;

u8 board_type;
+
+ /* number of VIFs requiring extra spare mem-blocks */
+ int extra_spare_vif_count;
};

#define WL18XX_FW_MAX_TX_STATUS_DESC 33
diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h
index 2cb3521..34e0498 100644
--- a/drivers/net/wireless/ti/wlcore/hw_ops.h
+++ b/drivers/net/wireless/ti/wlcore/hw_ops.h
@@ -176,4 +176,16 @@ wlcore_hw_get_spare_blocks(struct wl1271 *wl, bool is_gem)
return wl->ops->get_spare_blocks(wl, is_gem);
}

+static inline int
+wlcore_hw_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf)
+{
+ if (!wl->ops->set_key)
+ BUG_ON(1);
+
+ return wl->ops->set_key(wl, cmd, vif, sta, key_conf);
+}
+
#endif
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 1daaf1d..fe46a5b 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -2885,12 +2885,21 @@ static int wl1271_set_key(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return 0;
}

-static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key_conf)
{
struct wl1271 *wl = hw->priv;
+
+ return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
+}
+
+int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf)
+{
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret;
u32 tx_seq_32 = 0;
@@ -3001,6 +3010,7 @@ out_unlock:

return ret;
}
+EXPORT_SYMBOL_GPL(wlcore_set_key);

static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
@@ -4646,7 +4656,7 @@ static const struct ieee80211_ops wl1271_ops = {
.prepare_multicast = wl1271_op_prepare_multicast,
.configure_filter = wl1271_op_configure_filter,
.tx = wl1271_op_tx,
- .set_key = wl1271_op_set_key,
+ .set_key = wlcore_op_set_key,
.hw_scan = wl1271_op_hw_scan,
.cancel_hw_scan = wl1271_op_cancel_hw_scan,
.sched_scan_start = wl1271_op_sched_scan_start,
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index 78bf1b9..da9a07d 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1055,6 +1055,7 @@ out:
wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
mutex_unlock(&wl->flush_mutex);
}
+EXPORT_SYMBOL_GPL(wl1271_tx_flush);

u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
{
@@ -1115,6 +1116,7 @@ void wlcore_stop_queues(struct wl1271 *wl,
for (i = 0; i < NUM_TX_QUEUES; i++)
wlcore_stop_queue(wl, i, reason);
}
+EXPORT_SYMBOL_GPL(wlcore_stop_queues);

void wlcore_wake_queues(struct wl1271 *wl,
enum wlcore_queue_stop_reason reason)
@@ -1124,6 +1126,7 @@ void wlcore_wake_queues(struct wl1271 *wl,
for (i = 0; i < NUM_TX_QUEUES; i++)
wlcore_wake_queue(wl, i, reason);
}
+EXPORT_SYMBOL_GPL(wlcore_wake_queues);

void wlcore_reset_stopped_queues(struct wl1271 *wl)
{
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index e058a55..49e441f 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -188,6 +188,7 @@ enum wlcore_queue_stop_reason {
WLCORE_QUEUE_STOP_REASON_WATERMARK,
WLCORE_QUEUE_STOP_REASON_FW_RESTART,
WLCORE_QUEUE_STOP_REASON_FLUSH,
+ WLCORE_QUEUE_STOP_REASON_SPARE_BLK, /* 18xx specific */
};

static inline int wl1271_tx_get_queue(int queue)
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index c065ea6..e634500 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -76,6 +76,10 @@ struct wlcore_ops {
int (*handle_static_data)(struct wl1271 *wl,
struct wl1271_static_data *static_data);
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
+ int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf);
};

enum wlcore_partitions {
@@ -388,6 +392,10 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
int __devexit wlcore_remove(struct platform_device *pdev);
struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size);
int wlcore_free_hw(struct wl1271 *wl);
+int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key_conf);

/* Firmware image load chunk size */
#define CHUNK_SIZE 16384
--
1.7.9.5


2012-05-18 04:46:54

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 4/5] wlcore: stop queues on Tx flush

Stop network queues during Tx flush, and also drop other internal
mac80211 packets (mgmt) that may arrive when the queues are stopped.

When flush is done all driver queues are clear, forcefully if needed.

Protect the Tx flush operation with a new mutex, to prevent concurrency
that can mess us queue state.

Based on a patch by Eliad Peller <[email protected]>

Signed-off-by: Arik Nemtsov <[email protected]>
---
drivers/net/wireless/ti/wlcore/main.c | 1 +
drivers/net/wireless/ti/wlcore/tx.c | 11 ++++++++++-
drivers/net/wireless/ti/wlcore/tx.h | 1 +
drivers/net/wireless/ti/wlcore/wlcore.h | 3 +++
4 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 6e6926b..1daaf1d 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -5151,6 +5151,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
wl->state = WL1271_STATE_OFF;
wl->fw_type = WL12XX_FW_TYPE_NONE;
mutex_init(&wl->mutex);
+ mutex_init(&wl->flush_mutex);

order = get_order(WL1271_AGGR_BUFFER_SIZE);
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index f68567b..78bf1b9 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -1024,6 +1024,11 @@ void wl1271_tx_flush(struct wl1271 *wl)
int i;
timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);

+ /* only one flush should be in progress, for consistent queue state */
+ mutex_lock(&wl->flush_mutex);
+
+ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+
while (!time_after(jiffies, timeout)) {
mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d",
@@ -1032,7 +1037,7 @@ void wl1271_tx_flush(struct wl1271 *wl)
if ((wl->tx_frames_cnt == 0) &&
(wl1271_tx_total_queue_count(wl) == 0)) {
mutex_unlock(&wl->mutex);
- return;
+ goto out;
}
mutex_unlock(&wl->mutex);
msleep(1);
@@ -1045,6 +1050,10 @@ void wl1271_tx_flush(struct wl1271 *wl)
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);
mutex_unlock(&wl->mutex);
+
+out:
+ wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+ mutex_unlock(&wl->flush_mutex);
}

u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set)
diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h
index 6bf6956..e058a55 100644
--- a/drivers/net/wireless/ti/wlcore/tx.h
+++ b/drivers/net/wireless/ti/wlcore/tx.h
@@ -187,6 +187,7 @@ struct wl1271_tx_hw_res_if {
enum wlcore_queue_stop_reason {
WLCORE_QUEUE_STOP_REASON_WATERMARK,
WLCORE_QUEUE_STOP_REASON_FW_RESTART,
+ WLCORE_QUEUE_STOP_REASON_FLUSH,
};

static inline int wl1271_tx_get_queue(int queue)
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 94b79d5..c065ea6 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -379,6 +379,9 @@ struct wl1271 {

/* the current channel type */
enum nl80211_channel_type channel_type;
+
+ /* mutex for protecting the tx_flush function */
+ struct mutex flush_mutex;
};

int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev);
--
1.7.9.5


2012-06-06 17:07:44

by Luciano Coelho

[permalink] [raw]
Subject: Re: [PATCH 0/5] TKIP/GEM support for 18xx

On Fri, 2012-05-18 at 07:46 +0300, Arik Nemtsov wrote:
> TKIP extra padding is not required for 18xx cards. Turn this into a
> quirk for 12xx cards only.
> 18xx cards must have their Tx spare-blocks updated before any vif can
> transmit GEM or TKIP encrypted packets. When spare blocks are updated,
> we must make sure no Tx frame reaches the FW. Otherwise our Tx block
> accounting will be out of sync.
> Introduce new internal Tx queue wake/stop APIs to allow us to safely
> stop the queues for multiple reasons (similar to mac80211). Use the
> new APIs to prevent all Tx during the change of spare blocks.
>
> Arik Nemtsov (5):
> wlcore/wl12xx/wl18xx: introduce quirk to remove TKIP header space
> wlcore/wl12xx/wl18xx: handle spare blocks spacial cases per arch
> wlcore: add stop reason bitmap for waking/starting queues
> wlcore: stop queues on Tx flush
> wlcore/wl12xx/wl18xx: implement op_set_key per HW arch

Applied this set and pushed. Thanks, dude!

--
Luca.