diff options
Diffstat (limited to 'drivers/net/wireless/ti/wlcore')
-rw-r--r-- | drivers/net/wireless/ti/wlcore/Kconfig | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/acx.c | 80 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/acx.h | 30 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/boot.c | 3 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/cmd.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/event.c | 29 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/main.c | 323 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/rx.c | 36 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/rx.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/wl12xx.h | 41 | ||||
-rw-r--r-- | drivers/net/wireless/ti/wlcore/wlcore.h | 6 |
11 files changed, 532 insertions, 30 deletions
diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig index 9d04c38938b..54156b0b5c2 100644 --- a/drivers/net/wireless/ti/wlcore/Kconfig +++ b/drivers/net/wireless/ti/wlcore/Kconfig @@ -1,6 +1,6 @@ config WLCORE tristate "TI wlcore support" - depends on WL_TI && GENERIC_HARDIRQS + depends on WL_TI && GENERIC_HARDIRQS && MAC80211 depends on INET select FW_LOADER ---help--- diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 5912541a925..509aa881d79 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1714,3 +1714,83 @@ out: return ret; } + +/* Set the global behaviour of RX filters - On/Off + default action */ +int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable, + enum rx_filter_action action) +{ + struct acx_default_rx_filter *acx; + int ret; + + wl1271_debug(DEBUG_ACX, "acx default rx filter en: %d act: %d", + enable, action); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + acx->enable = enable; + acx->default_action = action; + + ret = wl1271_cmd_configure(wl, ACX_ENABLE_RX_DATA_FILTER, acx, + sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx default rx filter enable failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +/* Configure or disable a specific RX filter pattern */ +int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable, + struct wl12xx_rx_filter *filter) +{ + struct acx_rx_filter_cfg *acx; + int fields_size = 0; + int acx_size; + int ret; + + WARN_ON(enable && !filter); + WARN_ON(index >= WL1271_MAX_RX_FILTERS); + + wl1271_debug(DEBUG_ACX, + "acx set rx filter idx: %d enable: %d filter: %p", + index, enable, filter); + + if (enable) { + fields_size = wl1271_rx_filter_get_fields_size(filter); + + wl1271_debug(DEBUG_ACX, "act: %d num_fields: %d field_size: %d", + filter->action, filter->num_fields, fields_size); + } + + acx_size = ALIGN(sizeof(*acx) + fields_size, 4); + acx = kzalloc(acx_size, GFP_KERNEL); + + if (!acx) + return -ENOMEM; + + acx->enable = enable; + acx->index = index; + + if (enable) { + acx->num_fields = filter->num_fields; + acx->action = filter->action; + wl1271_rx_filter_flatten_fields(filter, acx->fields); + } + + wl1271_dump(DEBUG_ACX, "RX_FILTER: ", acx, acx_size); + + ret = wl1271_cmd_configure(wl, ACX_SET_RX_DATA_FILTER, acx, acx_size); + if (ret < 0) { + wl1271_warning("setting rx filter failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index b2f88831b7a..8106b2ebfe6 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1147,6 +1147,32 @@ struct wl12xx_acx_config_hangover { u8 padding[2]; } __packed; + +struct acx_default_rx_filter { + struct acx_header header; + u8 enable; + + /* action of type FILTER_XXX */ + u8 default_action; + + u8 pad[2]; +} __packed; + + +struct acx_rx_filter_cfg { + struct acx_header header; + + u8 enable; + + /* 0 - WL1271_MAX_RX_FILTERS-1 */ + u8 index; + + u8 action; + + u8 num_fields; + u8 fields[0]; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0000, ACX_MEM_CFG = 0x0001, @@ -1304,5 +1330,9 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); int wl1271_acx_fm_coex(struct wl1271 *wl); int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl); int wl12xx_acx_config_hangover(struct wl1271 *wl); +int wl1271_acx_default_rx_filter_enable(struct wl1271 *wl, bool enable, + enum rx_filter_action action); +int wl1271_acx_set_rx_filter(struct wl1271 *wl, u8 index, bool enable, + struct wl12xx_rx_filter *filter); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wlcore/boot.c b/drivers/net/wireless/ti/wlcore/boot.c index 3a2207db540..9b98230f84c 100644 --- a/drivers/net/wireless/ti/wlcore/boot.c +++ b/drivers/net/wireless/ti/wlcore/boot.c @@ -72,7 +72,7 @@ static int wlcore_boot_fw_version(struct wl1271 *wl) struct wl1271_static_data *static_data; int ret; - static_data = kmalloc(sizeof(*static_data), GFP_DMA); + static_data = kmalloc(sizeof(*static_data), GFP_KERNEL | GFP_DMA); if (!static_data) { wl1271_error("Couldn't allocate memory for static data!"); return -ENOMEM; @@ -413,6 +413,7 @@ int wlcore_boot_run_firmware(struct wl1271 *wl) /* unmask required mbox events */ wl->event_mask = BSS_LOSE_EVENT_ID | + REGAINED_BSS_EVENT_ID | SCAN_COMPLETE_EVENT_ID | ROLE_STOP_COMPLETE_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 5c4716c6f04..5b128a97144 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -123,7 +123,9 @@ static int wl1271_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask) unsigned long timeout; int ret = 0; - events_vector = kmalloc(sizeof(*events_vector), GFP_DMA); + events_vector = kmalloc(sizeof(*events_vector), GFP_KERNEL | GFP_DMA); + if (!events_vector) + return -ENOMEM; timeout = jiffies + msecs_to_jiffies(WL1271_EVENT_TIMEOUT); @@ -1034,7 +1036,7 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) skb_reserve(skb, sizeof(*hdr) + WL1271_EXTRA_SPACE_MAX); tmpl = (struct wl12xx_arp_rsp_template *)skb_put(skb, sizeof(*tmpl)); - memset(tmpl, 0, sizeof(tmpl)); + memset(tmpl, 0, sizeof(*tmpl)); /* llc layer */ memcpy(tmpl->llc_hdr, rfc1042_header, sizeof(rfc1042_header)); @@ -1083,7 +1085,7 @@ int wl1271_cmd_build_arp_rsp(struct wl1271 *wl, struct wl12xx_vif *wlvif) /* mac80211 header */ hdr = (struct ieee80211_hdr_3addr *)skb_push(skb, sizeof(*hdr)); - memset(hdr, 0, sizeof(hdr)); + memset(hdr, 0, sizeof(*hdr)); fc = IEEE80211_FTYPE_DATA | IEEE80211_FCTL_TODS; if (wlvif->sta.qos) fc |= IEEE80211_STYPE_QOS_DATA; diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 292632ddf89..28e2a633c3b 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -103,7 +103,6 @@ static int wl1271_event_process(struct wl1271 *wl) struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; u32 vector; - bool beacon_loss = false; bool disconnect_sta = false; unsigned long sta_bitmap = 0; @@ -141,20 +140,23 @@ static int wl1271_event_process(struct wl1271 *wl) mbox->soft_gemini_sense_info); /* - * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon - * filtering) is enabled. Without PSM, the stack will receive all - * beacons and can detect beacon loss by itself. - * - * As there's possibility that the driver disables PSM before receiving - * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. - * + * We are HW_MONITOR device. On beacon loss - queue + * connection loss work. Cancel it on REGAINED event. */ if (vector & BSS_LOSE_EVENT_ID) { /* TODO: check for multi-role */ + int delay = wl->conf.conn.synch_fail_thold * + wl->conf.conn.bss_lose_timeout; wl1271_info("Beacon loss detected."); + cancel_delayed_work_sync(&wl->connection_loss_work); + ieee80211_queue_delayed_work(wl->hw, &wl->connection_loss_work, + msecs_to_jiffies(delay)); + } - /* indicate to the stack, that beacons have been lost */ - beacon_loss = true; + if (vector & REGAINED_BSS_EVENT_ID) { + /* TODO: check for multi-role */ + wl1271_info("Beacon regained."); + cancel_delayed_work_sync(&wl->connection_loss_work); } if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { @@ -257,13 +259,6 @@ static int wl1271_event_process(struct wl1271 *wl) rcu_read_unlock(); } } - - if (beacon_loss) - wl12xx_for_each_wlvif_sta(wl, wlvif) { - vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_connection_loss(vif); - } - return 0; } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2b0f987660c..acef93390d3 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -1120,6 +1120,7 @@ int wl1271_plt_stop(struct wl1271 *wl) cancel_work_sync(&wl->recovery_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); + cancel_delayed_work_sync(&wl->connection_loss_work); mutex_lock(&wl->mutex); wl1271_power_off(wl); @@ -1261,8 +1262,270 @@ static struct sk_buff *wl12xx_alloc_dummy_packet(struct wl1271 *wl) #ifdef CONFIG_PM +static 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); +} + +void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter) +{ + int i; + + if (filter == NULL) + return; + + for (i = 0; i < filter->num_fields; i++) + kfree(filter->fields[i].pattern); + + kfree(filter); +} + +int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, + u16 offset, u8 flags, + u8 *pattern, u8 len) +{ + struct wl12xx_rx_filter_field *field; + + if (filter->num_fields == WL1271_RX_FILTER_MAX_FIELDS) { + wl1271_warning("Max fields per RX filter. can't alloc another"); + return -EINVAL; + } + + field = &filter->fields[filter->num_fields]; + + field->pattern = kzalloc(len, GFP_KERNEL); + if (!field->pattern) { + wl1271_warning("Failed to allocate RX filter pattern"); + return -ENOMEM; + } + + filter->num_fields++; + + field->offset = cpu_to_le16(offset); + field->flags = flags; + field->len = len; + memcpy(field->pattern, pattern, len); + + return 0; +} + +int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter) +{ + int i, fields_size = 0; + + for (i = 0; i < filter->num_fields; i++) + fields_size += filter->fields[i].len + + sizeof(struct wl12xx_rx_filter_field) - + sizeof(u8 *); + + return fields_size; +} + +void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, + u8 *buf) +{ + int i; + struct wl12xx_rx_filter_field *field; + + for (i = 0; i < filter->num_fields; i++) { + field = (struct wl12xx_rx_filter_field *)buf; + + field->offset = filter->fields[i].offset; + field->flags = filter->fields[i].flags; + field->len = filter->fields[i].len; + + memcpy(&field->pattern, filter->fields[i].pattern, field->len); + buf += sizeof(struct wl12xx_rx_filter_field) - + sizeof(u8 *) + field->len; + } +} + +/* + * Allocates an RX filter returned through f + * which needs to be freed using rx_filter_free() + */ +static 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; @@ -1273,6 +1536,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); @@ -1308,10 +1572,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; @@ -1332,6 +1597,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); @@ -1355,15 +1622,16 @@ 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) { + mutex_unlock(&wl->mutex); wl1271_warning("couldn't prepare device to suspend"); return ret; } @@ -1487,6 +1755,7 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) cancel_work_sync(&wl->tx_work); cancel_delayed_work_sync(&wl->elp_work); cancel_delayed_work_sync(&wl->tx_watchdog_work); + cancel_delayed_work_sync(&wl->connection_loss_work); /* let's notify MAC80211 about the remaining pending TX frames */ wl12xx_tx_reset(wl, true); @@ -3439,6 +3708,9 @@ sta_not_found: do_join = true; set_assoc = true; + /* Cancel connection_loss_work */ + cancel_delayed_work_sync(&wl->connection_loss_work); + /* * use basic rates from AP, and determine lowest rate * to use with control frames. @@ -4549,6 +4821,34 @@ static struct bin_attribute fwlog_attr = { .read = wl1271_sysfs_read_fwlog, }; +static void wl1271_connection_loss_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct ieee80211_vif *vif; + struct wl12xx_vif *wlvif; + + dwork = container_of(work, struct delayed_work, work); + wl = container_of(dwork, struct wl1271, connection_loss_work); + + wl1271_info("Connection loss work."); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state == WL1271_STATE_OFF)) + goto out; + + /* Call mac80211 connection loss */ + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + goto out; + vif = wl12xx_wlvif_to_vif(wlvif); + ieee80211_connection_loss(vif); + } +out: + mutex_unlock(&wl->mutex); +} + static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic, int n) { @@ -4804,6 +5104,8 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) INIT_WORK(&wl->recovery_work, wl1271_recovery_work); INIT_DELAYED_WORK(&wl->scan_complete_work, wl1271_scan_complete_work); INIT_DELAYED_WORK(&wl->tx_watchdog_work, wl12xx_tx_watchdog_work); + INIT_DELAYED_WORK(&wl->connection_loss_work, + wl1271_connection_loss_work); wl->freezable_wq = create_freezable_workqueue("wl12xx_wq"); if (!wl->freezable_wq) { @@ -4861,7 +5163,7 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size) goto err_dummy_packet; } - wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_DMA); + wl->mbox = kmalloc(sizeof(*wl->mbox), GFP_KERNEL | GFP_DMA); if (!wl->mbox) { ret = -ENOMEM; goto err_fwlog; @@ -5003,9 +5305,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); diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 89bd9385e90..1f1d9488dfb 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -278,3 +278,39 @@ void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status) wl12xx_rearm_rx_streaming(wl, active_hlids); } + +int wl1271_rx_filter_enable(struct wl1271 *wl, + int index, bool enable, + struct wl12xx_rx_filter *filter) +{ + int ret; + + if (wl->rx_filter_enabled[index] == enable) { + wl1271_warning("Request to enable an already " + "enabled rx filter %d", index); + return 0; + } + + ret = wl1271_acx_set_rx_filter(wl, index, enable, filter); + + if (ret) { + wl1271_error("Failed to %s rx data filter %d (err=%d)", + enable ? "enable" : "disable", index, ret); + return ret; + } + + wl->rx_filter_enabled[index] = enable; + + return 0; +} + +void wl1271_rx_filter_clear_all(struct wl1271 *wl) +{ + int i; + + for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) { + if (!wl->rx_filter_enabled[i]) + continue; + wl1271_rx_filter_enable(wl, i, 0, NULL); + } +} diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 6e129e2a854..e9a162a864c 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -138,5 +138,9 @@ struct wl1271_rx_descriptor { void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); +int wl1271_rx_filter_enable(struct wl1271 *wl, + int index, bool enable, + struct wl12xx_rx_filter *filter); +void wl1271_rx_filter_clear_all(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/ti/wlcore/wl12xx.h b/drivers/net/wireless/ti/wlcore/wl12xx.h index a9b220c43e5..f12bdf74518 100644 --- a/drivers/net/wireless/ti/wlcore/wl12xx.h +++ b/drivers/net/wireless/ti/wlcore/wl12xx.h @@ -279,6 +279,39 @@ struct wl1271_link { u8 ba_bitmap; }; +#define WL1271_MAX_RX_FILTERS 5 +#define WL1271_RX_FILTER_MAX_FIELDS 8 + +#define WL1271_RX_FILTER_ETH_HEADER_SIZE 14 +#define WL1271_RX_FILTER_MAX_FIELDS_SIZE 95 +#define RX_FILTER_FIELD_OVERHEAD \ + (sizeof(struct wl12xx_rx_filter_field) - sizeof(u8 *)) +#define WL1271_RX_FILTER_MAX_PATTERN_SIZE \ + (WL1271_RX_FILTER_MAX_FIELDS_SIZE - RX_FILTER_FIELD_OVERHEAD) + +#define WL1271_RX_FILTER_FLAG_MASK BIT(0) +#define WL1271_RX_FILTER_FLAG_IP_HEADER 0 +#define WL1271_RX_FILTER_FLAG_ETHERNET_HEADER BIT(1) + +enum rx_filter_action { + FILTER_DROP = 0, + FILTER_SIGNAL = 1, + FILTER_FW_HANDLE = 2 +}; + +struct wl12xx_rx_filter_field { + __le16 offset; + u8 len; + u8 flags; + u8 *pattern; +} __packed; + +struct wl12xx_rx_filter { + u8 action; + int num_fields; + struct wl12xx_rx_filter_field fields[WL1271_RX_FILTER_MAX_FIELDS]; +}; + struct wl1271_station { u8 hlid; }; @@ -439,6 +472,14 @@ int wl1271_plt_stop(struct wl1271 *wl); int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_queue_recovery_work(struct wl1271 *wl); size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); +int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, + u16 offset, u8 flags, + u8 *pattern, u8 len); +void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); +struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); +int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); +void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, + u8 *buf); #define JOIN_TIMEOUT 5000 /* 5000 milliseconds to join */ diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 39f9fadfebd..0b3f0b586f4 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -243,6 +243,9 @@ struct wl1271 { struct wl1271_scan scan; struct delayed_work scan_complete_work; + /* Connection loss work */ + struct delayed_work connection_loss_work; + bool sched_scanning; /* The current band */ @@ -349,6 +352,9 @@ struct wl1271 { /* size of the private FW status data */ size_t fw_status_priv_len; + + /* RX Data filter rule state - enabled/disabled */ + bool rx_filter_enabled[WL1271_MAX_RX_FILTERS]; }; int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); |