aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2011-04-26 09:08:12 +0200
committerDmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>2011-04-26 22:17:13 +0200
commit112c3d0f87e56405f3b76bb651a19cf1ce9f9b12 (patch)
tree310cde568766cf952d18f2cd54088b9a6059f69a
parent8354275662192a79ee8fafc966797dee3106c622 (diff)
WLAN: AP mode fine-tuning.2.6.38-wlan_driver-rc2
+ Explicitly set broadcast bit in TIM when broadcasts are available. + Split handling of management frames (link ID = 0) and buffered multicasts (link ID = CW1200_LINK_ID_AFTER_DTIM) Signed-off-by: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
-rw-r--r--drivers/staging/cw1200/ap.c78
-rw-r--r--drivers/staging/cw1200/ap.h4
-rw-r--r--drivers/staging/cw1200/cw1200.h7
-rw-r--r--drivers/staging/cw1200/main.c4
-rw-r--r--drivers/staging/cw1200/txrx.c5
-rw-r--r--drivers/staging/cw1200/wsm.c44
6 files changed, 118 insertions, 24 deletions
diff --git a/drivers/staging/cw1200/ap.c b/drivers/staging/cw1200/ap.c
index 05802647457..93aee42ab6c 100644
--- a/drivers/staging/cw1200/ap.c
+++ b/drivers/staging/cw1200/ap.c
@@ -105,19 +105,31 @@ void cw1200_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
}
}
-int cw1200_set_tim_work(struct work_struct *work)
+static int cw1200_set_tim_impl(struct cw1200_common *priv, bool multicast)
{
- struct cw1200_common *priv =
- container_of(work, struct cw1200_common, set_tim_work);
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_BEACON,
};
+ u16 tim_offset, tim_length;
ap_printk(KERN_DEBUG "[AP] %s.\n", __func__);
- frame.skb = ieee80211_beacon_get(priv->hw, priv->vif);
+ frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
+ &tim_offset, &tim_length);
if (WARN_ON(!frame.skb))
- return 0;
+ return -ENOMEM;
+
+ if (tim_offset && tim_length >= 6) {
+ /* Ignore DTIM count from mac80211:
+ * firmware handles DTIM internally. */
+ frame.skb->data[tim_offset + 2] = 0;
+
+ /* Set/reset aid0 bit */
+ if (multicast)
+ frame.skb->data[tim_offset + 4] |= 1;
+ else
+ frame.skb->data[tim_offset + 4] &= ~1;
+ }
WARN_ON(wsm_set_template_frame(priv, &frame));
@@ -126,6 +138,13 @@ int cw1200_set_tim_work(struct work_struct *work)
return 0;
}
+void cw1200_set_tim_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, set_tim_work);
+ (void)cw1200_set_tim_impl(priv, !priv->suspend_multicast);
+}
+
int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
bool set)
{
@@ -378,6 +397,31 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev,
mutex_unlock(&priv->conf_mutex);
}
+void cw1200_multicast_start_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_start_work);
+
+ (void)cw1200_set_tim_impl(priv, true);
+ wsm_lock_tx(priv);
+ priv->suspend_multicast = false;
+ wsm_unlock_tx(priv);
+ cw1200_bh_wakeup(priv);
+}
+
+void cw1200_multicast_stop_work(struct work_struct *work)
+{
+ struct cw1200_common *priv =
+ container_of(work, struct cw1200_common, multicast_stop_work);
+
+ /* Lock flushes send queue in device. Just to make sure DTIM beacom
+ * and frames are sent. */
+ wsm_lock_tx(priv);
+ priv->suspend_multicast = true;
+ (void)cw1200_set_tim_impl(priv, false);
+ wsm_unlock_tx(priv);
+}
+
/* ******************************************************************** */
/* WSM callback */
void cw1200_suspend_resume(struct cw1200_common *priv,
@@ -385,6 +429,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv,
{
int queue = 1 << wsm_queue_id_to_linux(arg->queue);
u32 unicast = 1 << arg->link_id;
+ u32 after_dtim = 1 << CW1200_LINK_ID_AFTER_DTIM;
u32 wakeup_required = 0;
u32 set = 0;
u32 clear;
@@ -399,8 +444,21 @@ void cw1200_suspend_resume(struct cw1200_common *priv,
arg->multicast ? "broadcast" : "unicast");
if (arg->multicast) {
- priv->suspend_multicast = arg->stop;
- wakeup_required = !arg->stop;
+ if (arg->stop)
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ else {
+ /* Handle only if there is data to be sent */
+ for (i = 0; i < 4; ++i) {
+ if (cw1200_queue_get_num_queued(
+ &priv->tx_queue[i],
+ after_dtim)) {
+ queue_work(priv->workqueue,
+ &priv->multicast_start_work);
+ break;
+ }
+ }
+ }
} else {
if (arg->stop)
set = unicast;
@@ -419,7 +477,11 @@ void cw1200_suspend_resume(struct cw1200_common *priv,
tx_suspend_mask = priv->tx_suspend_mask[i];
priv->tx_suspend_mask[i] =
(tx_suspend_mask & ~clear) | set;
- wakeup_required |= tx_suspend_mask & clear;
+
+ wakeup_required = wakeup_required ||
+ cw1200_queue_get_num_queued(
+ &priv->tx_queue[i],
+ tx_suspend_mask & clear);
}
}
if (wakeup_required)
diff --git a/drivers/staging/cw1200/ap.h b/drivers/staging/cw1200/ap.h
index 13e9654dc05..77c364dfc2d 100644
--- a/drivers/staging/cw1200/ap.h
+++ b/drivers/staging/cw1200/ap.h
@@ -28,7 +28,9 @@ void cw1200_bss_info_changed(struct ieee80211_hw *dev,
void cw1200_suspend_resume(struct cw1200_common *priv,
struct wsm_suspend_resume *arg);
-int cw1200_set_tim_work(struct work_struct *work);
+void cw1200_set_tim_work(struct work_struct *work);
+void cw1200_multicast_start_work(struct work_struct *work);
+void cw1200_multicast_stop_work(struct work_struct *work);
#endif
diff --git a/drivers/staging/cw1200/cw1200.h b/drivers/staging/cw1200/cw1200.h
index fa8df5ae192..2251f1ca756 100644
--- a/drivers/staging/cw1200/cw1200.h
+++ b/drivers/staging/cw1200/cw1200.h
@@ -53,9 +53,10 @@
#define txrx_printk(...)
#endif
-#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
+#define CW1200_MAX_CTRL_FRAME_LEN (0x1000)
-#define CW1200_MAX_STA_IN_AP_MODE (5)
+#define CW1200_MAX_STA_IN_AP_MODE (5)
+#define CW1200_LINK_ID_AFTER_DTIM (CW1200_MAX_STA_IN_AP_MODE + 1)
enum cw1200_join_status {
CW1200_JOIN_STATUS_MONITOR = 0,
@@ -164,6 +165,8 @@ struct cw1200_common {
u32 sta_asleep_mask;
bool suspend_multicast;
struct work_struct set_tim_work;
+ struct work_struct multicast_start_work;
+ struct work_struct multicast_stop_work;
/* WSM events and CQM implementation */
diff --git a/drivers/staging/cw1200/main.c b/drivers/staging/cw1200/main.c
index db6bc4b04d8..e1251b4e4e4 100644
--- a/drivers/staging/cw1200/main.c
+++ b/drivers/staging/cw1200/main.c
@@ -301,10 +301,12 @@ struct ieee80211_hw *cw1200_init_common(size_t priv_data_len)
#endif
INIT_WORK(&priv->tx_failure_work, cw1200_tx_failure_work);
INIT_WORK(&priv->set_tim_work, cw1200_set_tim_work);
+ INIT_WORK(&priv->multicast_start_work, cw1200_multicast_start_work);
+ INIT_WORK(&priv->multicast_stop_work, cw1200_multicast_stop_work);
for (i = 0; i < 4; ++i) {
if (unlikely(cw1200_queue_init(&priv->tx_queue[i], i,
- 16, CW1200_MAX_STA_IN_AP_MODE + 1))) {
+ 16, CW1200_LINK_ID_AFTER_DTIM + 1))) {
for (; i > 0; i--)
cw1200_queue_deinit(&priv->tx_queue[i - 1]);
ieee80211_free_hw(hw);
diff --git a/drivers/staging/cw1200/txrx.c b/drivers/staging/cw1200/txrx.c
index 91f35a77f98..5c677556bd9 100644
--- a/drivers/staging/cw1200/txrx.c
+++ b/drivers/staging/cw1200/txrx.c
@@ -390,9 +390,10 @@ int cw1200_tx(struct ieee80211_hw *dev, struct sk_buff *skb)
int link_id = 0;
int ret;
- if (tx_info->control.sta) {
+ if (tx_info->flags | IEEE80211_TX_CTL_SEND_AFTER_DTIM)
+ link_id = CW1200_LINK_ID_AFTER_DTIM;
+ else if (tx_info->control.sta)
link_id = sta_priv->link_id;
- }
txrx_printk(KERN_DEBUG "[TX] TX %d bytes (queue: %d, link_id: %d).\n",
skb->len, queue, link_id);
diff --git a/drivers/staging/cw1200/wsm.c b/drivers/staging/cw1200/wsm.c
index 9c441dc1a1f..37723cc88a1 100644
--- a/drivers/staging/cw1200/wsm.c
+++ b/drivers/staging/cw1200/wsm.c
@@ -1341,23 +1341,27 @@ static bool wsm_handle_tx_data(struct cw1200_common *priv,
static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
struct cw1200_queue **queue_p,
- u32* tx_allowed_mask_p)
+ u32* tx_allowed_mask_p,
+ bool *more)
{
int i;
struct cw1200_queue *queue = NULL;
u32 tx_allowed_mask;
+ int mcasts = 0;
/* Search for a queue with multicast frames buffered */
if (priv->sta_asleep_mask && !priv->suspend_multicast) {
- tx_allowed_mask = 1;
+ tx_allowed_mask = 1 << CW1200_LINK_ID_AFTER_DTIM;
for (i = 0; i < 4; ++i) {
- queue = &priv->tx_queue[i];
- if (cw1200_queue_get_num_queued(
- queue, tx_allowed_mask))
- goto found;
+ mcasts += cw1200_queue_get_num_queued(
+ &priv->tx_queue[i], tx_allowed_mask);
+ if (!queue && mcasts)
+ queue = &priv->tx_queue[i];
+ if (mcasts > 1)
+ break;
}
- /* If not found, suspend multicast */
- priv->suspend_multicast = true;
+ if (mcasts)
+ goto found;
}
/* Search for unicast traffic */
@@ -1366,7 +1370,8 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
tx_allowed_mask = ~priv->sta_asleep_mask;
if (priv->sta_asleep_mask) {
tx_allowed_mask |= ~priv->tx_suspend_mask[i];
- tx_allowed_mask &= ~1;
+ } else {
+ tx_allowed_mask |= 1 << CW1200_LINK_ID_AFTER_DTIM;
}
if (cw1200_queue_get_num_queued(
queue, tx_allowed_mask))
@@ -1377,6 +1382,7 @@ static int wsm_get_tx_queue_and_mask(struct cw1200_common *priv,
found:
*queue_p = queue;
*tx_allowed_mask_p = tx_allowed_mask;
+ *more = mcasts > 1;
return 0;
}
@@ -1397,6 +1403,9 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data,
*/
int count = 0;
+ /* More is used only for broadcasts. */
+ bool more;
+
if (priv->wsm_cmd.ptr) {
++count;
spin_lock_irqsave(&priv->wsm_cmd.lock, flags);
@@ -1410,7 +1419,7 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data,
break;
if (wsm_get_tx_queue_and_mask(priv, &queue,
- &tx_allowed_mask))
+ &tx_allowed_mask, &more))
break;
if (cw1200_queue_get(queue,
@@ -1432,6 +1441,21 @@ int wsm_get_tx(struct cw1200_common *priv, u8 **data,
*data = (u8 *)wsm;
*tx_len = __le16_to_cpu(wsm->hdr.len);
+ if (more) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *) &wsm[1];
+ /* more buffered multicast/broadcast frames
+ * ==> set MoreData flag in IEEE 802.11 header
+ * to inform PS STAs */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ } else if (priv->mode == NL80211_IFTYPE_AP &&
+ !priv->suspend_multicast) {
+ priv->suspend_multicast = true;
+ queue_work(priv->workqueue,
+ &priv->multicast_stop_work);
+ }
+
wsm_printk(KERN_DEBUG "[WSM] >>> 0x%.4X (%d) %p %c\n",
0x0004, *tx_len, *data,
wsm->more ? 'M' : ' ');