|
@@ -248,7 +248,7 @@ static void wl12xx_tx_watchdog_work(struct work_struct *work)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
/* Tx went out in the meantime - everything is ok */
|
|
@@ -512,7 +512,7 @@ static int wlcore_irq_locked(struct wl1271 *wl)
|
|
|
|
|
|
wl1271_debug(DEBUG_IRQ, "IRQ work");
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -696,7 +696,7 @@ static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
|
|
|
* we can't call wl12xx_get_vif_count() here because
|
|
|
* wl->mutex is taken, so use the cached last_vif_count value
|
|
|
*/
|
|
|
- if (wl->last_vif_count > 1) {
|
|
|
+ if (wl->last_vif_count > 1 && wl->mr_fw_name) {
|
|
|
fw_type = WL12XX_FW_TYPE_MULTI;
|
|
|
fw_name = wl->mr_fw_name;
|
|
|
} else {
|
|
@@ -744,38 +744,14 @@ out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void wl1271_fetch_nvs(struct wl1271 *wl)
|
|
|
-{
|
|
|
- const struct firmware *fw;
|
|
|
- int ret;
|
|
|
-
|
|
|
- ret = request_firmware(&fw, WL12XX_NVS_NAME, wl->dev);
|
|
|
-
|
|
|
- if (ret < 0) {
|
|
|
- wl1271_debug(DEBUG_BOOT, "could not get nvs file %s: %d",
|
|
|
- WL12XX_NVS_NAME, ret);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
|
|
|
-
|
|
|
- if (!wl->nvs) {
|
|
|
- wl1271_error("could not allocate memory for the nvs file");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- wl->nvs_len = fw->size;
|
|
|
-
|
|
|
-out:
|
|
|
- release_firmware(fw);
|
|
|
-}
|
|
|
-
|
|
|
void wl12xx_queue_recovery_work(struct wl1271 *wl)
|
|
|
{
|
|
|
WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags));
|
|
|
|
|
|
/* Avoid a recursive recovery */
|
|
|
- if (!test_and_set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) {
|
|
|
+ if (wl->state == WLCORE_STATE_ON) {
|
|
|
+ wl->state = WLCORE_STATE_RESTARTING;
|
|
|
+ set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags);
|
|
|
wlcore_disable_interrupts_nosync(wl);
|
|
|
ieee80211_queue_work(wl->hw, &wl->recovery_work);
|
|
|
}
|
|
@@ -913,7 +889,7 @@ static void wl1271_recovery_work(struct work_struct *work)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state != WL1271_STATE_ON || wl->plt)
|
|
|
+ if (wl->state == WLCORE_STATE_OFF || wl->plt)
|
|
|
goto out_unlock;
|
|
|
|
|
|
if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) {
|
|
@@ -1081,7 +1057,7 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
|
|
|
|
|
|
wl1271_notice("power up");
|
|
|
|
|
|
- if (wl->state != WL1271_STATE_OFF) {
|
|
|
+ if (wl->state != WLCORE_STATE_OFF) {
|
|
|
wl1271_error("cannot go into PLT state because not "
|
|
|
"in off state: %d", wl->state);
|
|
|
ret = -EBUSY;
|
|
@@ -1102,7 +1078,7 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode)
|
|
|
if (ret < 0)
|
|
|
goto power_off;
|
|
|
|
|
|
- wl->state = WL1271_STATE_ON;
|
|
|
+ wl->state = WLCORE_STATE_ON;
|
|
|
wl1271_notice("firmware booted in PLT mode %s (%s)",
|
|
|
PLT_MODE[plt_mode],
|
|
|
wl->chip.fw_ver_str);
|
|
@@ -1171,7 +1147,7 @@ int wl1271_plt_stop(struct wl1271 *wl)
|
|
|
wl1271_power_off(wl);
|
|
|
wl->flags = 0;
|
|
|
wl->sleep_auth = WL1271_PSM_ILLEGAL;
|
|
|
- wl->state = WL1271_STATE_OFF;
|
|
|
+ wl->state = WLCORE_STATE_OFF;
|
|
|
wl->plt = false;
|
|
|
wl->plt_mode = PLT_OFF;
|
|
|
wl->rx_counter = 0;
|
|
@@ -1602,12 +1578,6 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
|
|
|
if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
|
|
goto out;
|
|
|
|
|
|
- if ((wl->conf.conn.suspend_wake_up_event ==
|
|
|
- wl->conf.conn.wake_up_event) &&
|
|
|
- (wl->conf.conn.suspend_listen_interval ==
|
|
|
- wl->conf.conn.listen_interval))
|
|
|
- goto out;
|
|
|
-
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
@@ -1616,6 +1586,12 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl,
|
|
|
if (ret < 0)
|
|
|
goto out_sleep;
|
|
|
|
|
|
+ if ((wl->conf.conn.suspend_wake_up_event ==
|
|
|
+ wl->conf.conn.wake_up_event) &&
|
|
|
+ (wl->conf.conn.suspend_listen_interval ==
|
|
|
+ wl->conf.conn.listen_interval))
|
|
|
+ goto out_sleep;
|
|
|
+
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
wl->conf.conn.suspend_wake_up_event,
|
|
|
wl->conf.conn.suspend_listen_interval);
|
|
@@ -1671,11 +1647,7 @@ static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
if ((!is_ap) && (!is_sta))
|
|
|
return;
|
|
|
|
|
|
- if (is_sta &&
|
|
|
- ((wl->conf.conn.suspend_wake_up_event ==
|
|
|
- wl->conf.conn.wake_up_event) &&
|
|
|
- (wl->conf.conn.suspend_listen_interval ==
|
|
|
- wl->conf.conn.listen_interval)))
|
|
|
+ if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags))
|
|
|
return;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -1685,6 +1657,12 @@ static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
if (is_sta) {
|
|
|
wl1271_configure_wowlan(wl, NULL);
|
|
|
|
|
|
+ if ((wl->conf.conn.suspend_wake_up_event ==
|
|
|
+ wl->conf.conn.wake_up_event) &&
|
|
|
+ (wl->conf.conn.suspend_listen_interval ==
|
|
|
+ wl->conf.conn.listen_interval))
|
|
|
+ goto out_sleep;
|
|
|
+
|
|
|
ret = wl1271_acx_wake_up_conditions(wl, wlvif,
|
|
|
wl->conf.conn.wake_up_event,
|
|
|
wl->conf.conn.listen_interval);
|
|
@@ -1697,6 +1675,7 @@ static void wl1271_configure_resume(struct wl1271 *wl,
|
|
|
ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false);
|
|
|
}
|
|
|
|
|
|
+out_sleep:
|
|
|
wl1271_ps_elp_sleep(wl);
|
|
|
}
|
|
|
|
|
@@ -1833,7 +1812,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF) {
|
|
|
+ if (wl->state == WLCORE_STATE_OFF) {
|
|
|
if (test_and_clear_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
|
|
|
&wl->flags))
|
|
|
wlcore_enable_interrupts(wl);
|
|
@@ -1845,7 +1824,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
|
|
|
* this must be before the cancel_work calls below, so that the work
|
|
|
* functions don't perform further work.
|
|
|
*/
|
|
|
- wl->state = WL1271_STATE_OFF;
|
|
|
+ wl->state = WLCORE_STATE_OFF;
|
|
|
|
|
|
/*
|
|
|
* Use the nosync variant to disable interrupts, so the mutex could be
|
|
@@ -1856,6 +1835,8 @@ static void wlcore_op_stop_locked(struct wl1271 *wl)
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
|
|
|
wlcore_synchronize_interrupts(wl);
|
|
|
+ if (!test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
|
|
|
+ cancel_work_sync(&wl->recovery_work);
|
|
|
wl1271_flush_deferred_work(wl);
|
|
|
cancel_delayed_work_sync(&wl->scan_complete_work);
|
|
|
cancel_work_sync(&wl->netstack_work);
|
|
@@ -1958,6 +1939,27 @@ static void wl12xx_free_rate_policy(struct wl1271 *wl, u8 *idx)
|
|
|
*idx = WL12XX_MAX_RATE_POLICIES;
|
|
|
}
|
|
|
|
|
|
+static int wlcore_allocate_klv_template(struct wl1271 *wl, u8 *idx)
|
|
|
+{
|
|
|
+ u8 policy = find_first_zero_bit(wl->klv_templates_map,
|
|
|
+ WLCORE_MAX_KLV_TEMPLATES);
|
|
|
+ if (policy >= WLCORE_MAX_KLV_TEMPLATES)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ __set_bit(policy, wl->klv_templates_map);
|
|
|
+ *idx = policy;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void wlcore_free_klv_template(struct wl1271 *wl, u8 *idx)
|
|
|
+{
|
|
|
+ if (WARN_ON(*idx >= WLCORE_MAX_KLV_TEMPLATES))
|
|
|
+ return;
|
|
|
+
|
|
|
+ __clear_bit(*idx, wl->klv_templates_map);
|
|
|
+ *idx = WLCORE_MAX_KLV_TEMPLATES;
|
|
|
+}
|
|
|
+
|
|
|
static u8 wl12xx_get_role_type(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
|
{
|
|
|
switch (wlvif->bss_type) {
|
|
@@ -2022,6 +2024,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif)
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.basic_rate_idx);
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.ap_rate_idx);
|
|
|
wl12xx_allocate_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
|
|
|
+ wlcore_allocate_klv_template(wl, &wlvif->sta.klv_template_id);
|
|
|
wlvif->basic_rate_set = CONF_TX_RATE_MASK_BASIC;
|
|
|
wlvif->basic_rate = CONF_TX_RATE_MASK_BASIC;
|
|
|
wlvif->rate_set = CONF_TX_RATE_MASK_BASIC;
|
|
@@ -2098,7 +2101,7 @@ irq_disable:
|
|
|
/* Unlocking the mutex in the middle of handling is
|
|
|
inherently unsafe. In this case we deem it safe to do,
|
|
|
because we need to let any possibly pending IRQ out of
|
|
|
- the system (and while we are WL1271_STATE_OFF the IRQ
|
|
|
+ the system (and while we are WLCORE_STATE_OFF the IRQ
|
|
|
work function will not do anything.) Also, any other
|
|
|
possible concurrent operations will fail due to the
|
|
|
current state, hence the wl1271 struct should be safe. */
|
|
@@ -2133,7 +2136,7 @@ power_off:
|
|
|
wl1271_debug(DEBUG_MAC80211, "11a is %ssupported",
|
|
|
wl->enable_11a ? "" : "not ");
|
|
|
|
|
|
- wl->state = WL1271_STATE_ON;
|
|
|
+ wl->state = WLCORE_STATE_ON;
|
|
|
out:
|
|
|
return booted;
|
|
|
}
|
|
@@ -2167,7 +2170,11 @@ static bool wl12xx_need_fw_change(struct wl1271 *wl,
|
|
|
wl->last_vif_count = vif_count;
|
|
|
|
|
|
/* no need for fw change if the device is OFF */
|
|
|
- if (wl->state == WL1271_STATE_OFF)
|
|
|
+ if (wl->state == WLCORE_STATE_OFF)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /* no need for fw change if a single fw is used */
|
|
|
+ if (!wl->mr_fw_name)
|
|
|
return false;
|
|
|
|
|
|
if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
|
|
@@ -2249,7 +2256,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
|
|
* TODO: after the nvs issue will be solved, move this block
|
|
|
* to start(), and make sure here the driver is ON.
|
|
|
*/
|
|
|
- if (wl->state == WL1271_STATE_OFF) {
|
|
|
+ if (wl->state == WLCORE_STATE_OFF) {
|
|
|
/*
|
|
|
* we still need this in order to configure the fw
|
|
|
* while uploading the nvs
|
|
@@ -2263,21 +2270,6 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (wlvif->bss_type == BSS_TYPE_STA_BSS ||
|
|
|
- wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
- /*
|
|
|
- * The device role is a special role used for
|
|
|
- * rx and tx frames prior to association (as
|
|
|
- * the STA role can get packets only from
|
|
|
- * its associated bssid)
|
|
|
- */
|
|
|
- ret = wl12xx_cmd_role_enable(wl, vif->addr,
|
|
|
- WL1271_ROLE_DEVICE,
|
|
|
- &wlvif->dev_role_id);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
ret = wl12xx_cmd_role_enable(wl, vif->addr,
|
|
|
role_type, &wlvif->role_id);
|
|
|
if (ret < 0)
|
|
@@ -2316,7 +2308,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
|
|
return;
|
|
|
|
|
|
/* because of hardware recovery, we may get here twice */
|
|
|
- if (wl->state != WL1271_STATE_ON)
|
|
|
+ if (wl->state == WLCORE_STATE_OFF)
|
|
|
return;
|
|
|
|
|
|
wl1271_info("down");
|
|
@@ -2346,10 +2338,6 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl,
|
|
|
wlvif->bss_type == BSS_TYPE_IBSS) {
|
|
|
if (wl12xx_dev_role_started(wlvif))
|
|
|
wl12xx_stop_dev(wl, wlvif);
|
|
|
-
|
|
|
- ret = wl12xx_cmd_role_disable(wl, &wlvif->dev_role_id);
|
|
|
- if (ret < 0)
|
|
|
- goto deinit;
|
|
|
}
|
|
|
|
|
|
ret = wl12xx_cmd_role_disable(wl, &wlvif->role_id);
|
|
@@ -2368,6 +2356,7 @@ deinit:
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.basic_rate_idx);
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.ap_rate_idx);
|
|
|
wl12xx_free_rate_policy(wl, &wlvif->sta.p2p_rate_idx);
|
|
|
+ wlcore_free_klv_template(wl, &wlvif->sta.klv_template_id);
|
|
|
} else {
|
|
|
wlvif->ap.bcast_hlid = WL12XX_INVALID_LINK_ID;
|
|
|
wlvif->ap.global_hlid = WL12XX_INVALID_LINK_ID;
|
|
@@ -2432,12 +2421,11 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
|
|
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
|
|
struct wl12xx_vif *iter;
|
|
|
struct vif_counter_data vif_count;
|
|
|
- bool cancel_recovery = true;
|
|
|
|
|
|
wl12xx_get_vif_count(hw, vif, &vif_count);
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF ||
|
|
|
+ if (wl->state == WLCORE_STATE_OFF ||
|
|
|
!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))
|
|
|
goto out;
|
|
|
|
|
@@ -2457,12 +2445,9 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
|
|
wl12xx_force_active_psm(wl);
|
|
|
set_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags);
|
|
|
wl12xx_queue_recovery_work(wl);
|
|
|
- cancel_recovery = false;
|
|
|
}
|
|
|
out:
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
- if (cancel_recovery)
|
|
|
- cancel_work_sync(&wl->recovery_work);
|
|
|
}
|
|
|
|
|
|
static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
|
|
@@ -2536,7 +2521,7 @@ static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_acx_keep_alive_config(wl, wlvif,
|
|
|
- CMD_TEMPL_KLV_IDX_NULL_DATA,
|
|
|
+ wlvif->sta.klv_template_id,
|
|
|
ACX_KEEP_ALIVE_TPL_VALID);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
@@ -2556,6 +2541,11 @@ static int wl1271_unjoin(struct wl1271 *wl, struct wl12xx_vif *wlvif)
|
|
|
ieee80211_chswitch_done(vif, false);
|
|
|
}
|
|
|
|
|
|
+ /* invalidate keep-alive template */
|
|
|
+ wl1271_acx_keep_alive_config(wl, wlvif,
|
|
|
+ wlvif->sta.klv_template_id,
|
|
|
+ ACX_KEEP_ALIVE_TPL_INVALID);
|
|
|
+
|
|
|
/* to stop listening to a channel, we disconnect */
|
|
|
ret = wl12xx_cmd_role_stop_sta(wl, wlvif);
|
|
|
if (ret < 0)
|
|
@@ -2594,11 +2584,6 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
|
|
wlvif->rate_set =
|
|
|
wl1271_tx_min_rate_get(wl, wlvif->basic_rate_set);
|
|
|
ret = wl1271_acx_sta_rate_policies(wl, wlvif);
|
|
|
- if (ret < 0)
|
|
|
- goto out;
|
|
|
- ret = wl1271_acx_keep_alive_config(
|
|
|
- wl, wlvif, CMD_TEMPL_KLV_IDX_NULL_DATA,
|
|
|
- ACX_KEEP_ALIVE_TPL_INVALID);
|
|
|
if (ret < 0)
|
|
|
goto out;
|
|
|
clear_bit(WLVIF_FLAG_IN_USE, &wlvif->flags);
|
|
@@ -2772,7 +2757,7 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
|
|
|
if (changed & IEEE80211_CONF_CHANGE_POWER)
|
|
|
wl->power_level = conf->power_level;
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -2806,10 +2791,6 @@ static u64 wl1271_op_prepare_multicast(struct ieee80211_hw *hw,
|
|
|
{
|
|
|
struct wl1271_filter_params *fp;
|
|
|
struct netdev_hw_addr *ha;
|
|
|
- struct wl1271 *wl = hw->priv;
|
|
|
-
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
- return 0;
|
|
|
|
|
|
fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
|
|
|
if (!fp) {
|
|
@@ -2858,7 +2839,7 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
|
|
|
*total &= WL1271_SUPPORTED_FILTERS;
|
|
|
changed &= WL1271_SUPPORTED_FILTERS;
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -3082,8 +3063,45 @@ static int wlcore_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|
|
struct ieee80211_key_conf *key_conf)
|
|
|
{
|
|
|
struct wl1271 *wl = hw->priv;
|
|
|
+ int ret;
|
|
|
+ bool might_change_spare =
|
|
|
+ key_conf->cipher == WL1271_CIPHER_SUITE_GEM ||
|
|
|
+ key_conf->cipher == WLAN_CIPHER_SUITE_TKIP;
|
|
|
|
|
|
- return wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
|
|
|
+ if (might_change_spare) {
|
|
|
+ /*
|
|
|
+ * stop the queues and flush to ensure the next packets are
|
|
|
+ * in sync with FW spare block accounting
|
|
|
+ */
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+ wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+
|
|
|
+ wl1271_tx_flush(wl);
|
|
|
+ }
|
|
|
+
|
|
|
+ mutex_lock(&wl->mutex);
|
|
|
+
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
+ ret = -EAGAIN;
|
|
|
+ goto out_wake_queues;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wl1271_ps_elp_wakeup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_wake_queues;
|
|
|
+
|
|
|
+ ret = wlcore_hw_set_key(wl, cmd, vif, sta, key_conf);
|
|
|
+
|
|
|
+ wl1271_ps_elp_sleep(wl);
|
|
|
+
|
|
|
+out_wake_queues:
|
|
|
+ if (might_change_spare)
|
|
|
+ wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_SPARE_BLK);
|
|
|
+
|
|
|
+ mutex_unlock(&wl->mutex);
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
@@ -3105,17 +3123,6 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
key_conf->keylen, key_conf->flags);
|
|
|
wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen);
|
|
|
|
|
|
- mutex_lock(&wl->mutex);
|
|
|
-
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
- ret = -EAGAIN;
|
|
|
- goto out_unlock;
|
|
|
- }
|
|
|
-
|
|
|
- ret = wl1271_ps_elp_wakeup(wl);
|
|
|
- if (ret < 0)
|
|
|
- goto out_unlock;
|
|
|
-
|
|
|
switch (key_conf->cipher) {
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
@@ -3145,8 +3152,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
default:
|
|
|
wl1271_error("Unknown key algo 0x%x", key_conf->cipher);
|
|
|
|
|
|
- ret = -EOPNOTSUPP;
|
|
|
- goto out_sleep;
|
|
|
+ return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
switch (cmd) {
|
|
@@ -3157,7 +3163,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
tx_seq_32, tx_seq_16, sta);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("Could not add or replace key");
|
|
|
- goto out_sleep;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -3171,7 +3177,7 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
ret = wl1271_cmd_build_arp_rsp(wl, wlvif);
|
|
|
if (ret < 0) {
|
|
|
wl1271_warning("build arp rsp failed: %d", ret);
|
|
|
- goto out_sleep;
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
@@ -3183,22 +3189,15 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd,
|
|
|
0, 0, sta);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("Could not remove key");
|
|
|
- goto out_sleep;
|
|
|
+ return ret;
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
wl1271_error("Unsupported key cmd 0x%x", cmd);
|
|
|
- ret = -EOPNOTSUPP;
|
|
|
- break;
|
|
|
+ return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
-out_sleep:
|
|
|
- wl1271_ps_elp_sleep(wl);
|
|
|
-
|
|
|
-out_unlock:
|
|
|
- mutex_unlock(&wl->mutex);
|
|
|
-
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(wlcore_set_key);
|
|
@@ -3221,7 +3220,7 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
/*
|
|
|
* We cannot return -EBUSY here because cfg80211 will expect
|
|
|
* a call to ieee80211_scan_completed if we do - in this case
|
|
@@ -3261,7 +3260,7 @@ static void wl1271_op_cancel_hw_scan(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF)
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
if (wl->scan.state == WL1271_SCAN_STATE_IDLE)
|
|
@@ -3310,7 +3309,7 @@ static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
ret = -EAGAIN;
|
|
|
goto out;
|
|
|
}
|
|
@@ -3347,7 +3346,7 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF)
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -3368,7 +3367,7 @@ static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
ret = -EAGAIN;
|
|
|
goto out;
|
|
|
}
|
|
@@ -3397,7 +3396,7 @@ static int wl1271_op_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
ret = -EAGAIN;
|
|
|
goto out;
|
|
|
}
|
|
@@ -4173,7 +4172,7 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags)))
|
|
@@ -4257,7 +4256,7 @@ static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -4456,7 +4455,7 @@ static int wl12xx_op_sta_state(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
ret = -EBUSY;
|
|
|
goto out;
|
|
|
}
|
|
@@ -4495,7 +4494,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
ret = -EAGAIN;
|
|
|
goto out;
|
|
|
}
|
|
@@ -4613,7 +4612,7 @@ static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw,
|
|
|
mask->control[i].legacy,
|
|
|
i);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
if (wlvif->bss_type == BSS_TYPE_STA_BSS &&
|
|
@@ -4649,12 +4648,14 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw,
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF)) {
|
|
|
+ if (unlikely(wl->state == WLCORE_STATE_OFF)) {
|
|
|
wl12xx_for_each_wlvif_sta(wl, wlvif) {
|
|
|
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
|
|
|
ieee80211_chswitch_done(vif, false);
|
|
|
}
|
|
|
goto out;
|
|
|
+ } else if (unlikely(wl->state != WLCORE_STATE_ON)) {
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -4689,7 +4690,7 @@ static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
/* packets are considered pending if in the TX queue or the FW */
|
|
@@ -4938,7 +4939,7 @@ static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
|
|
|
|
|
|
wl->sg_enabled = res;
|
|
|
|
|
|
- if (wl->state == WL1271_STATE_OFF)
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
ret = wl1271_ps_elp_wakeup(wl);
|
|
@@ -5056,7 +5057,7 @@ static void wl1271_connection_loss_work(struct work_struct *work)
|
|
|
|
|
|
mutex_lock(&wl->mutex);
|
|
|
|
|
|
- if (unlikely(wl->state == WL1271_STATE_OFF))
|
|
|
+ if (unlikely(wl->state != WLCORE_STATE_ON))
|
|
|
goto out;
|
|
|
|
|
|
/* Call mac80211 connection loss */
|
|
@@ -5070,18 +5071,17 @@ out:
|
|
|
mutex_unlock(&wl->mutex);
|
|
|
}
|
|
|
|
|
|
-static void wl12xx_derive_mac_addresses(struct wl1271 *wl,
|
|
|
- u32 oui, u32 nic, int n)
|
|
|
+static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x, n %d",
|
|
|
- oui, nic, n);
|
|
|
+ wl1271_debug(DEBUG_PROBE, "base address: oui %06x nic %06x",
|
|
|
+ oui, nic);
|
|
|
|
|
|
- if (nic + n - 1 > 0xffffff)
|
|
|
+ if (nic + WLCORE_NUM_MAC_ADDRESSES - wl->num_mac_addr > 0xffffff)
|
|
|
wl1271_warning("NIC part of the MAC address wraps around!");
|
|
|
|
|
|
- for (i = 0; i < n; i++) {
|
|
|
+ for (i = 0; i < wl->num_mac_addr; i++) {
|
|
|
wl->addresses[i].addr[0] = (u8)(oui >> 16);
|
|
|
wl->addresses[i].addr[1] = (u8)(oui >> 8);
|
|
|
wl->addresses[i].addr[2] = (u8) oui;
|
|
@@ -5091,7 +5091,22 @@ static void wl12xx_derive_mac_addresses(struct wl1271 *wl,
|
|
|
nic++;
|
|
|
}
|
|
|
|
|
|
- wl->hw->wiphy->n_addresses = n;
|
|
|
+ /* we may be one address short at the most */
|
|
|
+ WARN_ON(wl->num_mac_addr + 1 < WLCORE_NUM_MAC_ADDRESSES);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * turn on the LAA bit in the first address and use it as
|
|
|
+ * the last address.
|
|
|
+ */
|
|
|
+ if (wl->num_mac_addr < WLCORE_NUM_MAC_ADDRESSES) {
|
|
|
+ int idx = WLCORE_NUM_MAC_ADDRESSES - 1;
|
|
|
+ memcpy(&wl->addresses[idx], &wl->addresses[0],
|
|
|
+ sizeof(wl->addresses[0]));
|
|
|
+ /* LAA bit */
|
|
|
+ wl->addresses[idx].addr[2] |= BIT(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES;
|
|
|
wl->hw->wiphy->addresses = wl->addresses;
|
|
|
}
|
|
|
|
|
@@ -5130,8 +5145,7 @@ static int wl1271_register_hw(struct wl1271 *wl)
|
|
|
if (wl->mac80211_registered)
|
|
|
return 0;
|
|
|
|
|
|
- wl1271_fetch_nvs(wl);
|
|
|
- if (wl->nvs != NULL) {
|
|
|
+ if (wl->nvs_len >= 12) {
|
|
|
/* NOTE: The wl->nvs->nvs element must be first, in
|
|
|
* order to simplify the casting, we assume it is at
|
|
|
* the beginning of the wl->nvs structure.
|
|
@@ -5151,7 +5165,7 @@ static int wl1271_register_hw(struct wl1271 *wl)
|
|
|
nic_addr = wl->fuse_nic_addr + 1;
|
|
|
}
|
|
|
|
|
|
- wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr, 2);
|
|
|
+ wl12xx_derive_mac_addresses(wl, oui_addr, nic_addr);
|
|
|
|
|
|
ret = ieee80211_register_hw(wl->hw);
|
|
|
if (ret < 0) {
|
|
@@ -5181,7 +5195,7 @@ static void wl1271_unregister_hw(struct wl1271 *wl)
|
|
|
|
|
|
static const struct ieee80211_iface_limit wlcore_iface_limits[] = {
|
|
|
{
|
|
|
- .max = 2,
|
|
|
+ .max = 3,
|
|
|
.types = BIT(NL80211_IFTYPE_STATION),
|
|
|
},
|
|
|
{
|
|
@@ -5196,7 +5210,7 @@ static const struct ieee80211_iface_combination
|
|
|
wlcore_iface_combinations[] = {
|
|
|
{
|
|
|
.num_different_channels = 1,
|
|
|
- .max_interfaces = 2,
|
|
|
+ .max_interfaces = 3,
|
|
|
.limits = wlcore_iface_limits,
|
|
|
.n_limits = ARRAY_SIZE(wlcore_iface_limits),
|
|
|
},
|
|
@@ -5312,7 +5326,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl)
|
|
|
|
|
|
#define WL1271_DEFAULT_CHANNEL 0
|
|
|
|
|
|
-struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
|
|
|
+struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size)
|
|
|
{
|
|
|
struct ieee80211_hw *hw;
|
|
|
struct wl1271 *wl;
|
|
@@ -5392,17 +5406,19 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size)
|
|
|
|
|
|
spin_lock_init(&wl->wl_lock);
|
|
|
|
|
|
- wl->state = WL1271_STATE_OFF;
|
|
|
+ wl->state = WLCORE_STATE_OFF;
|
|
|
wl->fw_type = WL12XX_FW_TYPE_NONE;
|
|
|
mutex_init(&wl->mutex);
|
|
|
mutex_init(&wl->flush_mutex);
|
|
|
+ init_completion(&wl->nvs_loading_complete);
|
|
|
|
|
|
- order = get_order(WL1271_AGGR_BUFFER_SIZE);
|
|
|
+ order = get_order(aggr_buf_size);
|
|
|
wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order);
|
|
|
if (!wl->aggr_buf) {
|
|
|
ret = -ENOMEM;
|
|
|
goto err_wq;
|
|
|
}
|
|
|
+ wl->aggr_buf_size = aggr_buf_size;
|
|
|
|
|
|
wl->dummy_packet = wl12xx_alloc_dummy_packet(wl);
|
|
|
if (!wl->dummy_packet) {
|
|
@@ -5465,8 +5481,7 @@ int wlcore_free_hw(struct wl1271 *wl)
|
|
|
device_remove_file(wl->dev, &dev_attr_bt_coex_state);
|
|
|
free_page((unsigned long)wl->fwlog);
|
|
|
dev_kfree_skb(wl->dummy_packet);
|
|
|
- free_pages((unsigned long)wl->aggr_buf,
|
|
|
- get_order(WL1271_AGGR_BUFFER_SIZE));
|
|
|
+ free_pages((unsigned long)wl->aggr_buf, get_order(wl->aggr_buf_size));
|
|
|
|
|
|
wl1271_debugfs_exit(wl);
|
|
|
|
|
@@ -5516,17 +5531,32 @@ static irqreturn_t wl12xx_hardirq(int irq, void *cookie)
|
|
|
return IRQ_WAKE_THREAD;
|
|
|
}
|
|
|
|
|
|
-int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
+static void wlcore_nvs_cb(const struct firmware *fw, void *context)
|
|
|
{
|
|
|
+ struct wl1271 *wl = context;
|
|
|
+ struct platform_device *pdev = wl->pdev;
|
|
|
struct wl12xx_platform_data *pdata = pdev->dev.platform_data;
|
|
|
unsigned long irqflags;
|
|
|
int ret;
|
|
|
|
|
|
- if (!wl->ops || !wl->ptable) {
|
|
|
- ret = -EINVAL;
|
|
|
- goto out_free_hw;
|
|
|
+ if (fw) {
|
|
|
+ wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL);
|
|
|
+ if (!wl->nvs) {
|
|
|
+ wl1271_error("Could not allocate nvs data");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ wl->nvs_len = fw->size;
|
|
|
+ } else {
|
|
|
+ wl1271_debug(DEBUG_BOOT, "Could not get nvs file %s",
|
|
|
+ WL12XX_NVS_NAME);
|
|
|
+ wl->nvs = NULL;
|
|
|
+ wl->nvs_len = 0;
|
|
|
}
|
|
|
|
|
|
+ ret = wl->ops->setup(wl);
|
|
|
+ if (ret < 0)
|
|
|
+ goto out_free_nvs;
|
|
|
+
|
|
|
BUG_ON(wl->num_tx_desc > WLCORE_MAX_TX_DESCRIPTORS);
|
|
|
|
|
|
/* adjust some runtime configuration parameters */
|
|
@@ -5535,11 +5565,8 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
wl->irq = platform_get_irq(pdev, 0);
|
|
|
wl->platform_quirks = pdata->platform_quirks;
|
|
|
wl->set_power = pdata->set_power;
|
|
|
- wl->dev = &pdev->dev;
|
|
|
wl->if_ops = pdata->ops;
|
|
|
|
|
|
- platform_set_drvdata(pdev, wl);
|
|
|
-
|
|
|
if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ)
|
|
|
irqflags = IRQF_TRIGGER_RISING;
|
|
|
else
|
|
@@ -5550,7 +5577,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
pdev->name, wl);
|
|
|
if (ret < 0) {
|
|
|
wl1271_error("request_irq() failed: %d", ret);
|
|
|
- goto out_free_hw;
|
|
|
+ goto out_free_nvs;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
@@ -5609,6 +5636,7 @@ int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
goto out_hw_pg_ver;
|
|
|
}
|
|
|
|
|
|
+ wl->initialized = true;
|
|
|
goto out;
|
|
|
|
|
|
out_hw_pg_ver:
|
|
@@ -5623,10 +5651,33 @@ out_unreg:
|
|
|
out_irq:
|
|
|
free_irq(wl->irq, wl);
|
|
|
|
|
|
-out_free_hw:
|
|
|
- wlcore_free_hw(wl);
|
|
|
+out_free_nvs:
|
|
|
+ kfree(wl->nvs);
|
|
|
|
|
|
out:
|
|
|
+ release_firmware(fw);
|
|
|
+ complete_all(&wl->nvs_loading_complete);
|
|
|
+}
|
|
|
+
|
|
|
+int __devinit wlcore_probe(struct wl1271 *wl, struct platform_device *pdev)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!wl->ops || !wl->ptable)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ wl->dev = &pdev->dev;
|
|
|
+ wl->pdev = pdev;
|
|
|
+ platform_set_drvdata(pdev, wl);
|
|
|
+
|
|
|
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
|
|
|
+ WL12XX_NVS_NAME, &pdev->dev, GFP_KERNEL,
|
|
|
+ wl, wlcore_nvs_cb);
|
|
|
+ if (ret < 0) {
|
|
|
+ wl1271_error("request_firmware_nowait failed: %d", ret);
|
|
|
+ complete_all(&wl->nvs_loading_complete);
|
|
|
+ }
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(wlcore_probe);
|
|
@@ -5635,6 +5686,10 @@ int __devexit wlcore_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
struct wl1271 *wl = platform_get_drvdata(pdev);
|
|
|
|
|
|
+ wait_for_completion(&wl->nvs_loading_complete);
|
|
|
+ if (!wl->initialized)
|
|
|
+ return 0;
|
|
|
+
|
|
|
if (wl->irq_wake_enabled) {
|
|
|
device_init_wakeup(wl->dev, 0);
|
|
|
disable_irq_wake(wl->irq);
|
|
@@ -5665,3 +5720,4 @@ MODULE_PARM_DESC(no_recovery, "Prevent HW recovery. FW will remain stuck.");
|
|
|
MODULE_LICENSE("GPL");
|
|
|
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
|
|
|
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
|
|
|
+MODULE_FIRMWARE(WL12XX_NVS_NAME);
|