return p54_update_beacon_tim(priv, sta->aid, set);
}
-static int p54_beacon_format_ie_tim(struct sk_buff *skb)
+u8 *p54_find_ie(struct sk_buff *skb, u8 ie)
{
- /*
- * the good excuse for this mess is ... the firmware.
- * The dummy TIM MUST be at the end of the beacon frame,
- * because it'll be overwritten!
- */
-
struct ieee80211_mgmt *mgmt = (void *)skb->data;
u8 *pos, *end;
if (skb->len <= sizeof(mgmt))
- return -EINVAL;
+ return NULL;
pos = (u8 *)mgmt->u.beacon.variable;
end = skb->data + skb->len;
while (pos < end) {
if (pos + 2 + pos[1] > end)
- return -EINVAL;
+ return NULL;
- if (pos[0] == WLAN_EID_TIM) {
- u8 dtim_len = pos[1];
- u8 dtim_period = pos[3];
- u8 *next = pos + 2 + dtim_len;
+ if (pos[0] == ie)
+ return pos;
- if (dtim_len < 3)
- return -EINVAL;
+ pos += 2 + pos[1];
+ }
+ return NULL;
+}
- memmove(pos, next, end - next);
+static int p54_beacon_format_ie_tim(struct sk_buff *skb)
+{
+ /*
+ * the good excuse for this mess is ... the firmware.
+ * The dummy TIM MUST be at the end of the beacon frame,
+ * because it'll be overwritten!
+ */
+ u8 *tim;
+ u8 dtim_len;
+ u8 dtim_period;
+ u8 *next;
- if (dtim_len > 3)
- skb_trim(skb, skb->len - (dtim_len - 3));
+ tim = p54_find_ie(skb, WLAN_EID_TIM);
+ if (!tim)
+ return 0;
- pos = end - (dtim_len + 2);
+ dtim_len = tim[1];
+ dtim_period = tim[3];
+ next = tim + 2 + dtim_len;
+
+ if (dtim_len < 3)
+ return -EINVAL;
+
+ memmove(tim, next, skb_tail_pointer(skb) - next);
+ tim = skb_tail_pointer(skb) - (dtim_len + 2);
+
+ /* add the dummy at the end */
+ tim[0] = WLAN_EID_TIM;
+ tim[1] = 3;
+ tim[2] = 0;
+ tim[3] = dtim_period;
+ tim[4] = 0;
+
+ if (dtim_len > 3)
+ skb_trim(skb, skb->len - (dtim_len - 3));
- /* add the dummy at the end */
- pos[0] = WLAN_EID_TIM;
- pos[1] = 3;
- pos[2] = 0;
- pos[3] = dtim_period;
- pos[4] = 0;
- return 0;
- }
- pos += 2 + pos[1];
- }
return 0;
}
struct ieee80211_vif *vif)
{
struct sk_buff *beacon;
- __le32 old_beacon_req_id;
int ret;
beacon = ieee80211_beacon_get(priv->hw, vif);
if (ret)
return ret;
- old_beacon_req_id = priv->beacon_req_id;
- priv->beacon_req_id = GET_REQ_ID(beacon);
-
- ret = p54_tx_80211(priv->hw, beacon);
- if (ret) {
- priv->beacon_req_id = old_beacon_req_id;
- return -ENOSPC;
- }
-
+ /*
+ * During operation, the firmware takes care of beaconing.
+ * The driver only needs to upload a new beacon template, once
+ * the template was changed by the stack or userspace.
+ *
+ * LMAC API 3.2.2 also specifies that the driver does not need
+ * to cancel the old beacon template by hand, instead the firmware
+ * will release the previous one through the feedback mechanism.
+ */
+ WARN_ON(p54_tx_80211(priv->hw, beacon));
priv->tsf_high32 = 0;
priv->tsf_low32 = 0;
goto out;
}
- queue_delayed_work(dev->workqueue, &priv->work, 0);
+ ieee80211_queue_delayed_work(dev, &priv->work, 0);
priv->softled_state = 0;
err = p54_set_leds(priv);
mutex_lock(&priv->conf_mutex);
priv->vif = NULL;
- if (priv->beacon_req_id) {
+
+ /*
+ * LMAC API 3.2.2 states that any active beacon template must be
+ * canceled by the driver before attempting a mode transition.
+ */
+ if (le32_to_cpu(priv->beacon_req_id) != 0) {
p54_tx_cancel(priv, priv->beacon_req_id);
- priv->beacon_req_id = cpu_to_le32(0);
+ wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ);
}
priv->mode = NL80211_IFTYPE_MONITOR;
memset(priv->mac_addr, 0, ETH_ALEN);
if (ret)
goto out;
}
+ if (changed & IEEE80211_CONF_CHANGE_IDLE) {
+ ret = p54_setup_mac(priv);
+ if (ret)
+ goto out;
+ }
out:
mutex_unlock(&priv->conf_mutex);
int ret;
mutex_lock(&priv->conf_mutex);
- if ((params) && !(queue > 4)) {
+ if (queue < dev->queues) {
P54_SET_QUEUE(priv->qos_params[queue], params->aifs,
params->cw_min, params->cw_max, params->txop);
ret = p54_set_edcf(priv);
priv->wakeup_timer = info->beacon_int *
info->dtim_period * 5;
p54_setup_mac(priv);
+ } else {
+ priv->wakeup_timer = 500;
+ priv->aid = 0;
}
}
skb_queue_head_init(&priv->tx_pending);
dev->flags = IEEE80211_HW_RX_INCLUDES_FCS |
IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_PS_NULLFUNC_STACK |
+ IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_NOISE_DBM;
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_MESH_POINT);
dev->channel_change_time = 1000; /* TODO: find actual value */
+ priv->beacon_req_id = cpu_to_le32(0);
priv->tx_stats[P54_QUEUE_BEACON].limit = 1;
priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1;
priv->tx_stats[P54_QUEUE_MGMT].limit = 3;
dev->extra_tx_headroom = sizeof(struct p54_hdr) + 4 +
sizeof(struct p54_tx_data);
+ /*
+ * For now, disable PS by default because it affects
+ * link stability significantly.
+ */
+ dev->wiphy->ps_default = false;
+
mutex_init(&priv->conf_mutex);
mutex_init(&priv->eeprom_mutex);
init_completion(&priv->eeprom_comp);
+ init_completion(&priv->beacon_comp);
INIT_DELAYED_WORK(&priv->work, p54_work);
return dev;
void p54_free_common(struct ieee80211_hw *dev)
{
struct p54_common *priv = dev->priv;
+ unsigned int i;
+
+ for (i = 0; i < IEEE80211_NUM_BANDS; i++)
+ kfree(priv->band_table[i]);
kfree(priv->iq_autocal);
kfree(priv->output_limit);