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:
Eliad Peller 2012-02-06 13:07:52 +02:00 committed by Luciano Coelho
parent 3fcdab7066
commit 4549d09c57
4 changed files with 124 additions and 13 deletions

View File

@ -993,6 +993,35 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
return IRQ_HANDLED; 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) static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
{ {
const struct firmware *fw; const struct firmware *fw;
@ -1006,12 +1035,24 @@ static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt)
fw_name = WL128X_PLT_FW_NAME; fw_name = WL128X_PLT_FW_NAME;
else else
fw_name = WL127X_PLT_FW_NAME; fw_name = WL127X_PLT_FW_NAME;
} else {
/*
* 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 { } else {
fw_type = WL12XX_FW_TYPE_NORMAL; fw_type = WL12XX_FW_TYPE_NORMAL;
if (wl->chip.id == CHIP_ID_1283_PG20) if (wl->chip.id == CHIP_ID_1283_PG20)
fw_name = WL128X_FW_NAME; fw_name = WL128X_FW_NAME_SINGLE;
else else
fw_name = WL127X_FW_NAME; fw_name = WL127X_FW_NAME_SINGLE;
}
} }
if (wl->fw_type == fw_type) 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; 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, static int wl1271_op_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct vif_counter_data vif_count;
int ret = 0; int ret = 0;
u8 role_type; u8 role_type;
bool booted = false; 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", wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
ieee80211_vif_type_p2p(vif), vif->addr); ieee80211_vif_type_p2p(vif), vif->addr);
wl12xx_get_vif_count(hw, vif, &vif_count);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl); ret = wl1271_ps_elp_wakeup(wl);
if (ret < 0) if (ret < 0)
@ -2124,6 +2203,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw,
goto out; 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 * TODO: after the nvs issue will be solved, move this block
* to start(), and make sure here the driver is ON. * 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 wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
struct wl12xx_vif *iter; 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); mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF || if (wl->state == WL1271_STATE_OFF ||
@ -2306,8 +2394,13 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
break; break;
} }
WARN_ON(iter != wlvif); WARN_ON(iter != wlvif);
if (wl12xx_need_fw_change(wl, vif_count, false)) {
wl12xx_queue_recovery_work(wl);
cancel_recovery = false;
}
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
if (cancel_recovery)
cancel_work_sync(&wl->recovery_work); cancel_work_sync(&wl->recovery_work);
} }
@ -2315,11 +2408,18 @@ static int wl12xx_op_change_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
enum nl80211_iftype new_type, bool p2p) 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); wl1271_op_remove_interface(hw, vif);
vif->type = ieee80211_iftype_p2p(new_type, p2p); vif->type = ieee80211_iftype_p2p(new_type, p2p);
vif->p2p = 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, static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif,

View File

@ -370,7 +370,9 @@ module_exit(wl1271_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL127X_FW_NAME); MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL128X_FW_NAME); MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL127X_PLT_FW_NAME); 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_FIRMWARE(WL128X_PLT_FW_NAME);

View File

@ -433,8 +433,10 @@ module_exit(wl1271_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL127X_FW_NAME); MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE);
MODULE_FIRMWARE(WL128X_FW_NAME); MODULE_FIRMWARE(WL127X_FW_NAME_MULTI);
MODULE_FIRMWARE(WL127X_PLT_FW_NAME); 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_FIRMWARE(WL128X_PLT_FW_NAME);
MODULE_ALIAS("spi:wl1271"); MODULE_ALIAS("spi:wl1271");

View File

@ -35,8 +35,12 @@
#include "conf.h" #include "conf.h"
#include "ini.h" #include "ini.h"
#define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin" #define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin"
#define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.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 WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin"
#define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-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 { enum wl12xx_fw_type {
WL12XX_FW_TYPE_NONE, WL12XX_FW_TYPE_NONE,
WL12XX_FW_TYPE_NORMAL, WL12XX_FW_TYPE_NORMAL,
WL12XX_FW_TYPE_MULTI,
WL12XX_FW_TYPE_PLT, WL12XX_FW_TYPE_PLT,
}; };
@ -254,6 +259,7 @@ enum wl12xx_flags {
WL1271_FLAG_PENDING_WORK, WL1271_FLAG_PENDING_WORK,
WL1271_FLAG_SOFT_GEMINI, WL1271_FLAG_SOFT_GEMINI,
WL1271_FLAG_RECOVERY_IN_PROGRESS, WL1271_FLAG_RECOVERY_IN_PROGRESS,
WL1271_FLAG_VIF_CHANGE_IN_PROGRESS,
}; };
enum wl12xx_vif_flags { enum wl12xx_vif_flags {
@ -303,6 +309,7 @@ struct wl1271 {
enum wl1271_state state; enum wl1271_state state;
enum wl12xx_fw_type fw_type; enum wl12xx_fw_type fw_type;
bool plt; bool plt;
u8 last_vif_count;
struct mutex mutex; struct mutex mutex;
unsigned long flags; unsigned long flags;