diff options
author | Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> | 2011-04-26 09:08:12 +0200 |
---|---|---|
committer | Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com> | 2011-04-26 22:17:13 +0200 |
commit | 112c3d0f87e56405f3b76bb651a19cf1ce9f9b12 (patch) | |
tree | 310cde568766cf952d18f2cd54088b9a6059f69a | |
parent | 8354275662192a79ee8fafc966797dee3106c622 (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.c | 78 | ||||
-rw-r--r-- | drivers/staging/cw1200/ap.h | 4 | ||||
-rw-r--r-- | drivers/staging/cw1200/cw1200.h | 7 | ||||
-rw-r--r-- | drivers/staging/cw1200/main.c | 4 | ||||
-rw-r--r-- | drivers/staging/cw1200/txrx.c | 5 | ||||
-rw-r--r-- | drivers/staging/cw1200/wsm.c | 44 |
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' : ' '); |