|
@@ -1261,6 +1261,67 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl)
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
+int wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
|
|
|
+{
|
|
|
+ int num_fields = 0, in_field = 0, fields_size = 0;
|
|
|
+ int i, pattern_len = 0;
|
|
|
+
|
|
|
+ if (!p->mask) {
|
|
|
+ wl1271_warning("No mask in WoWLAN pattern");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The pattern is broken up into segments of bytes at different offsets
|
|
|
+ * that need to be checked by the FW filter. Each segment is called
|
|
|
+ * a field in the FW API. We verify that the total number of fields
|
|
|
+ * required for this pattern won't exceed FW limits (8)
|
|
|
+ * as well as the total fields buffer won't exceed the FW limit.
|
|
|
+ * Note that if there's a pattern which crosses Ethernet/IP header
|
|
|
+ * boundary a new field is required.
|
|
|
+ */
|
|
|
+ for (i = 0; i < p->pattern_len; i++) {
|
|
|
+ if (test_bit(i, (unsigned long *)p->mask)) {
|
|
|
+ if (!in_field) {
|
|
|
+ in_field = 1;
|
|
|
+ pattern_len = 1;
|
|
|
+ } else {
|
|
|
+ if (i == WL1271_RX_FILTER_ETH_HEADER_SIZE) {
|
|
|
+ num_fields++;
|
|
|
+ fields_size += pattern_len +
|
|
|
+ RX_FILTER_FIELD_OVERHEAD;
|
|
|
+ pattern_len = 1;
|
|
|
+ } else
|
|
|
+ pattern_len++;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (in_field) {
|
|
|
+ in_field = 0;
|
|
|
+ fields_size += pattern_len +
|
|
|
+ RX_FILTER_FIELD_OVERHEAD;
|
|
|
+ num_fields++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (in_field) {
|
|
|
+ fields_size += pattern_len + RX_FILTER_FIELD_OVERHEAD;
|
|
|
+ num_fields++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (num_fields > WL1271_RX_FILTER_MAX_FIELDS) {
|
|
|
+ wl1271_warning("RX Filter too complex. Too many segments");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fields_size > WL1271_RX_FILTER_MAX_FIELDS_SIZE) {
|
|
|
+ wl1271_warning("RX filter pattern is too big");
|
|
|
+ return -E2BIG;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void)
|
|
|
{
|
|
|
return kzalloc(sizeof(struct wl12xx_rx_filter), GFP_KERNEL);
|
|
@@ -1339,8 +1400,130 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Allocates an RX filter returned through f
|
|
|
+ * which needs to be freed using rx_filter_free()
|
|
|
+ */
|
|
|
+int wl1271_convert_wowlan_pattern_to_rx_filter(
|
|
|
+ struct cfg80211_wowlan_trig_pkt_pattern *p,
|
|
|
+ struct wl12xx_rx_filter **f)
|
|
|
+{
|
|
|
+ int i, j, ret = 0;
|
|
|
+ struct wl12xx_rx_filter *filter;
|
|
|
+ u16 offset;
|
|
|
+ u8 flags, len;
|
|
|
+
|
|
|
+ filter = wl1271_rx_filter_alloc();
|
|
|
+ if (!filter) {
|
|
|
+ wl1271_warning("Failed to alloc rx filter");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ i = 0;
|
|
|
+ while (i < p->pattern_len) {
|
|
|
+ if (!test_bit(i, (unsigned long *)p->mask)) {
|
|
|
+ i++;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (j = i; j < p->pattern_len; j++) {
|
|
|
+ if (!test_bit(j, (unsigned long *)p->mask))
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE &&
|
|
|
+ j >= WL1271_RX_FILTER_ETH_HEADER_SIZE)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i < WL1271_RX_FILTER_ETH_HEADER_SIZE) {
|
|
|
+ offset = i;
|
|
|
+ flags = WL1271_RX_FILTER_FLAG_ETHERNET_HEADER;
|
|
|
+ } else {
|
|
|
+ offset = i - WL1271_RX_FILTER_ETH_HEADER_SIZE;
|
|
|
+ flags = WL1271_RX_FILTER_FLAG_IP_HEADER;
|
|
|
+ }
|
|
|
+
|
|
|
+ len = j - i;
|
|
|
+
|
|
|
+ ret = wl1271_rx_filter_alloc_field(filter,
|
|
|
+ offset,
|
|
|
+ flags,
|
|
|
+ &p->pattern[i], len);
|
|
|
+ if (ret)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ i = j;
|
|
|
+ }
|
|
|
+
|
|
|
+ filter->action = FILTER_SIGNAL;
|
|
|
+
|
|
|
+ *f = filter;
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ wl1271_rx_filter_free(filter);
|
|
|
+ *f = NULL;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int wl1271_configure_wowlan(struct wl1271 *wl,
|
|
|
+ struct cfg80211_wowlan *wow)
|
|
|
+{
|
|
|
+ int i, ret;
|
|
|
+
|
|
|
+ if (!wow || wow->any || !wow->n_patterns) {
|
|
|
+ wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
|
|
|
+ wl1271_rx_filter_clear_all(wl);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (WARN_ON(wow->n_patterns > WL1271_MAX_RX_FILTERS))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* Validate all incoming patterns before clearing current FW state */
|
|
|
+ for (i = 0; i < wow->n_patterns; i++) {
|
|
|
+ ret = wl1271_validate_wowlan_pattern(&wow->patterns[i]);
|
|
|
+ if (ret) {
|
|
|
+ wl1271_warning("Bad wowlan pattern %d", i);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ wl1271_acx_default_rx_filter_enable(wl, 0, FILTER_SIGNAL);
|
|
|
+ wl1271_rx_filter_clear_all(wl);
|
|
|
+
|
|
|
+ /* Translate WoWLAN patterns into filters */
|
|
|
+ for (i = 0; i < wow->n_patterns; i++) {
|
|
|
+ struct cfg80211_wowlan_trig_pkt_pattern *p;
|
|
|
+ struct wl12xx_rx_filter *filter = NULL;
|
|
|
+
|
|
|
+ p = &wow->patterns[i];
|
|
|
+
|
|
|
+ ret = wl1271_convert_wowlan_pattern_to_rx_filter(p, &filter);
|
|
|
+ if (ret) {
|
|
|
+ wl1271_warning("Failed to create an RX filter from "
|
|
|
+ "wowlan pattern %d", i);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_rx_filter_enable(wl, i, 1, filter);
|
|
|
+
|
|
|
+ wl1271_rx_filter_free(filter);
|
|
|
+ if (ret)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_acx_default_rx_filter_enable(wl, 1, FILTER_DROP);
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static int wl1271_configure_suspend_sta(struct wl1271 *wl,
|
|
|
- struct wl12xx_vif *wlvif)
|
|
|
+ struct wl12xx_vif *wlvif,
|
|
|
+ struct cfg80211_wowlan *wow)
|
|
|
{
|
|
|
int ret = 0;
|
|
|
|
|
@@ -1351,6 +1534,7 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
|
|
|
+ wl1271_configure_wowlan(wl, wow);
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
wl->conf.conn.suspend_wake_up_event,
|
|
|
wl->conf.conn.suspend_listen_interval);
|
|
@@ -1386,10 +1570,11 @@ out:
|
|
|
}
|
|
|
|
|
|
static int wl1271_configure_suspend(struct wl1271 *wl,
|
|
|
- struct wl12xx_vif *wlvif)
|
|
|
+ struct wl12xx_vif *wlvif,
|
|
|
+ struct cfg80211_wowlan *wow)
|
|
|
{
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS)
|
|
|
- return wl1271_configure_suspend_sta(wl, wlvif);
|
|
|
+ return wl1271_configure_suspend_sta(wl, wlvif, wow);
|
|
|
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
|
|
|
return wl1271_configure_suspend_ap(wl, wlvif);
|
|
|
return 0;
|
|
@@ -1410,6 +1595,8 @@ static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
return;
|
|
|
|
|
|
if (is_sta) {
|
|
|
+ wl1271_configure_wowlan(wl, NULL);
|
|
|
+
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
wl->conf.conn.wake_up_event,
|
|
|
wl->conf.conn.listen_interval);
|
|
@@ -1433,14 +1620,14 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw,
|
|
|
int ret;
|
|
|
|
|
|
wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow);
|
|
|
- WARN_ON(!wow || !wow->any);
|
|
|
+ WARN_ON(!wow);
|
|
|
|
|
|
wl1271_tx_flush(wl);
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
wl->wow_enabled = true;
|
|
|
wl12xx_for_each_wlvif(wl, wlvif) {
|
|
|
- ret = wl1271_configure_suspend(wl, wlvif);
|
|
|
+ ret = wl1271_configure_suspend(wl, wlvif, wow);
|
|
|
if (ret < 0) {
|
|
|
wl1271_warning("couldn't prepare device to suspend");
|
|
|
return ret;
|
|
@@ -5081,9 +5268,14 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
if (!ret) {
|
|
|
wl->irq_wake_enabled = true;
|
|
|
device_init_wakeup(wl->dev, 1);
|
|
|
- if (pdata->pwr_in_suspend)
|
|
|
+ if (pdata->pwr_in_suspend) {
|
|
|
wl->hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY;
|
|
|
-
|
|
|
+ wl->hw->wiphy->wowlan.n_patterns =
|
|
|
+ WL1271_MAX_RX_FILTERS;
|
|
|
+ wl->hw->wiphy->wowlan.pattern_min_len = 1;
|
|
|
+ wl->hw->wiphy->wowlan.pattern_max_len =
|
|
|
+ WL1271_RX_FILTER_MAX_PATTERN_SIZE;
|
|
|
+ }
|
|
|
}
|
|
|
disable_irq(wl->irq);
|
|
|
|