forked from Minki/linux
wl12xx: dynamically change fw according to number of active roles
wl12xx uses different fw for single-role and multi-role scenarios (due to lack of space, some of the fw advanced features are disabled in the multi-role fw). Add checks on add_interfae and remove_interface in order to determine whether a fw switch is needed (and initiate recovery in this case). Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Luciano Coelho <coelho@ti.com>
This commit is contained in:
parent
3fcdab7066
commit
4549d09c57
@ -993,6 +993,35 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
struct vif_counter_data {
|
||||
u8 counter;
|
||||
|
||||
struct ieee80211_vif *cur_vif;
|
||||
bool cur_vif_running;
|
||||
};
|
||||
|
||||
static void wl12xx_vif_count_iter(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct vif_counter_data *counter = data;
|
||||
|
||||
counter->counter++;
|
||||
if (counter->cur_vif == vif)
|
||||
counter->cur_vif_running = true;
|
||||
}
|
||||
|
||||
/* caller must not hold wl->mutex, as it might deadlock */
|
||||
static void wl12xx_get_vif_count(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *cur_vif,
|
||||
struct vif_counter_data *data)
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
data->cur_vif = cur_vif;
|
||||
|
||||
ieee80211_iterate_active_interfaces(hw,
|
||||
wl12xx_vif_count_iter, data);
|
||||
}
|
||||
|
||||
static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
@ -1007,11 +1036,23 @@ static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
|
||||
else
|
||||
fw_name = WL127X_PLT_FW_NAME;
|
||||
} else {
|
||||
fw_type = WL12XX_FW_TYPE_NORMAL;
|
||||
if (wl->chip.id == CHIP_ID_1283_PG20)
|
||||
fw_name = WL128X_FW_NAME;
|
||||
else
|
||||
fw_name = WL127X_FW_NAME;
|
||||
/*
|
||||
* 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) {
|
||||
fw_type = WL12XX_FW_TYPE_MULTI;
|
||||
if (wl->chip.id == CHIP_ID_1283_PG20)
|
||||
fw_name = WL128X_FW_NAME_MULTI;
|
||||
else
|
||||
fw_name = WL127X_FW_NAME_MULTI;
|
||||
} else {
|
||||
fw_type = WL12XX_FW_TYPE_NORMAL;
|
||||
if (wl->chip.id == CHIP_ID_1283_PG20)
|
||||
fw_name = WL128X_FW_NAME_SINGLE;
|
||||
else
|
||||
fw_name = WL127X_FW_NAME_SINGLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (wl->fw_type == fw_type)
|
||||
@ -2074,11 +2115,47 @@ static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif)
|
||||
return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a fw switch (i.e. moving from one loaded
|
||||
* fw to another) is needed. This function is also responsible
|
||||
* for updating wl->last_vif_count, so it must be called before
|
||||
* loading a non-plt fw (so the correct fw (single-role/multi-role)
|
||||
* will be used).
|
||||
*/
|
||||
static bool wl12xx_need_fw_change(struct wl1271 *wl,
|
||||
struct vif_counter_data vif_counter_data,
|
||||
bool add)
|
||||
{
|
||||
enum wl12xx_fw_type current_fw = wl->fw_type;
|
||||
u8 vif_count = vif_counter_data.counter;
|
||||
|
||||
if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags))
|
||||
return false;
|
||||
|
||||
/* increase the vif count if this is a new vif */
|
||||
if (add && !vif_counter_data.cur_vif_running)
|
||||
vif_count++;
|
||||
|
||||
wl->last_vif_count = vif_count;
|
||||
|
||||
/* no need for fw change if the device is OFF */
|
||||
if (wl->state == WL1271_STATE_OFF)
|
||||
return false;
|
||||
|
||||
if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL)
|
||||
return true;
|
||||
if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
|
||||
struct vif_counter_data vif_count;
|
||||
int ret = 0;
|
||||
u8 role_type;
|
||||
bool booted = false;
|
||||
@ -2089,6 +2166,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
|
||||
ieee80211_vif_type_p2p(vif), vif->addr);
|
||||
|
||||
wl12xx_get_vif_count(hw, vif, &vif_count);
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
ret = wl1271_ps_elp_wakeup(wl);
|
||||
if (ret < 0)
|
||||
@ -2124,6 +2203,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wl12xx_need_fw_change(wl, vif_count, true)) {
|
||||
mutex_unlock(&wl->mutex);
|
||||
wl1271_recovery_work(&wl->recovery_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: after the nvs issue will be solved, move this block
|
||||
* to start(), and make sure here the driver is ON.
|
||||
@ -2287,7 +2372,10 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
||||
struct wl1271 *wl = hw->priv;
|
||||
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 ||
|
||||
@ -2306,20 +2394,32 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
||||
break;
|
||||
}
|
||||
WARN_ON(iter != wlvif);
|
||||
if (wl12xx_need_fw_change(wl, vif_count, false)) {
|
||||
wl12xx_queue_recovery_work(wl);
|
||||
cancel_recovery = false;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&wl->mutex);
|
||||
cancel_work_sync(&wl->recovery_work);
|
||||
if (cancel_recovery)
|
||||
cancel_work_sync(&wl->recovery_work);
|
||||
}
|
||||
|
||||
static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
enum nl80211_iftype new_type, bool p2p)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int ret;
|
||||
|
||||
set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
|
||||
wl1271_op_remove_interface(hw, vif);
|
||||
|
||||
vif->type = ieee80211_iftype_p2p(new_type, p2p);
|
||||
vif->p2p = p2p;
|
||||
return wl1271_op_add_interface(hw, vif);
|
||||
ret = wl1271_op_add_interface(hw, vif);
|
||||
|
||||
clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,
|
||||
|
@ -370,7 +370,9 @@ module_exit(wl1271_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
|
||||
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME);
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
|
||||
MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
|
||||
MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
|
||||
|
@ -433,8 +433,10 @@ module_exit(wl1271_exit);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
|
||||
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME);
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
|
||||
MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
|
||||
MODULE_FIRMWARE(WL127X_PLT_FW_NAME);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE);
|
||||
MODULE_FIRMWARE(WL128X_FW_NAME_MULTI);
|
||||
MODULE_FIRMWARE(WL128X_PLT_FW_NAME);
|
||||
MODULE_ALIAS("spi:wl1271");
|
||||
|
@ -35,8 +35,12 @@
|
||||
#include "conf.h"
|
||||
#include "ini.h"
|
||||
|
||||
#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin"
|
||||
#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.bin"
|
||||
#define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin"
|
||||
#define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin"
|
||||
|
||||
#define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin"
|
||||
#define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin"
|
||||
|
||||
#define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin"
|
||||
#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin"
|
||||
|
||||
@ -97,6 +101,7 @@ enum wl1271_state {
|
||||
enum wl12xx_fw_type {
|
||||
WL12XX_FW_TYPE_NONE,
|
||||
WL12XX_FW_TYPE_NORMAL,
|
||||
WL12XX_FW_TYPE_MULTI,
|
||||
WL12XX_FW_TYPE_PLT,
|
||||
};
|
||||
|
||||
@ -254,6 +259,7 @@ enum wl12xx_flags {
|
||||
WL1271_FLAG_PENDING_WORK,
|
||||
WL1271_FLAG_SOFT_GEMINI,
|
||||
WL1271_FLAG_RECOVERY_IN_PROGRESS,
|
||||
WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
|
||||
};
|
||||
|
||||
enum wl12xx_vif_flags {
|
||||
@ -303,6 +309,7 @@ struct wl1271 {
|
||||
enum wl1271_state state;
|
||||
enum wl12xx_fw_type fw_type;
|
||||
bool plt;
|
||||
u8 last_vif_count;
|
||||
struct mutex mutex;
|
||||
|
||||
unsigned long flags;
|
||||
|
Loading…
Reference in New Issue
Block a user