diff options
author | Eliad Peller <eliad@wizery.com> | 2012-02-06 06:07:52 -0500 |
---|---|---|
committer | Luciano Coelho <coelho@ti.com> | 2012-02-15 01:38:36 -0500 |
commit | 4549d09c57cf44ae9ab6095c375bad5c100658c7 (patch) | |
tree | f6093845de683aa220183a85aab6e1bcd082b9bf /drivers/net/wireless/wl12xx | |
parent | 3fcdab7066a31ae90ac2beba7d38e8e606374998 (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/wl12xx')
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 114 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/sdio.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/spi.c | 6 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 11 |
4 files changed, 124 insertions, 13 deletions
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index c10940703e8c..b3b4c4019787 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c | |||
@@ -993,6 +993,35 @@ out: | |||
993 | return IRQ_HANDLED; | 993 | return IRQ_HANDLED; |
994 | } | 994 | } |
995 | 995 | ||
996 | struct vif_counter_data { | ||
997 | u8 counter; | ||
998 | |||
999 | struct ieee80211_vif *cur_vif; | ||
1000 | bool cur_vif_running; | ||
1001 | }; | ||
1002 | |||
1003 | static void wl12xx_vif_count_iter(void *data, u8 *mac, | ||
1004 | struct ieee80211_vif *vif) | ||
1005 | { | ||
1006 | struct vif_counter_data *counter = data; | ||
1007 | |||
1008 | counter->counter++; | ||
1009 | if (counter->cur_vif == vif) | ||
1010 | counter->cur_vif_running = true; | ||
1011 | } | ||
1012 | |||
1013 | /* caller must not hold wl->mutex, as it might deadlock */ | ||
1014 | static void wl12xx_get_vif_count(struct ieee80211_hw *hw, | ||
1015 | struct ieee80211_vif *cur_vif, | ||
1016 | struct vif_counter_data *data) | ||
1017 | { | ||
1018 | memset(data, 0, sizeof(*data)); | ||
1019 | data->cur_vif = cur_vif; | ||
1020 | |||
1021 | ieee80211_iterate_active_interfaces(hw, | ||
1022 | wl12xx_vif_count_iter, data); | ||
1023 | } | ||
1024 | |||
996 | static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) | 1025 | static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) |
997 | { | 1026 | { |
998 | const struct firmware *fw; | 1027 | const struct firmware *fw; |
@@ -1007,11 +1036,23 @@ static int wl12xx_fetch_firmware(struct wl1271 *wl, bool plt) | |||
1007 | else | 1036 | else |
1008 | fw_name = WL127X_PLT_FW_NAME; | 1037 | fw_name = WL127X_PLT_FW_NAME; |
1009 | } else { | 1038 | } else { |
1010 | fw_type = WL12XX_FW_TYPE_NORMAL; | 1039 | /* |
1011 | if (wl->chip.id == CHIP_ID_1283_PG20) | 1040 | * we can't call wl12xx_get_vif_count() here because |
1012 | fw_name = WL128X_FW_NAME; | 1041 | * wl->mutex is taken, so use the cached last_vif_count value |
1013 | else | 1042 | */ |
1014 | fw_name = WL127X_FW_NAME; | 1043 | if (wl->last_vif_count > 1) { |
1044 | fw_type = WL12XX_FW_TYPE_MULTI; | ||
1045 | if (wl->chip.id == CHIP_ID_1283_PG20) | ||
1046 | fw_name = WL128X_FW_NAME_MULTI; | ||
1047 | else | ||
1048 | fw_name = WL127X_FW_NAME_MULTI; | ||
1049 | } else { | ||
1050 | fw_type = WL12XX_FW_TYPE_NORMAL; | ||
1051 | if (wl->chip.id == CHIP_ID_1283_PG20) | ||
1052 | fw_name = WL128X_FW_NAME_SINGLE; | ||
1053 | else | ||
1054 | fw_name = WL127X_FW_NAME_SINGLE; | ||
1055 | } | ||
1015 | } | 1056 | } |
1016 | 1057 | ||
1017 | if (wl->fw_type == fw_type) | 1058 | if (wl->fw_type == fw_type) |
@@ -2074,11 +2115,47 @@ static bool wl12xx_dev_role_started(struct wl12xx_vif *wlvif) | |||
2074 | return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID; | 2115 | return wlvif->dev_hlid != WL12XX_INVALID_LINK_ID; |
2075 | } | 2116 | } |
2076 | 2117 | ||
2118 | /* | ||
2119 | * Check whether a fw switch (i.e. moving from one loaded | ||
2120 | * fw to another) is needed. This function is also responsible | ||
2121 | * for updating wl->last_vif_count, so it must be called before | ||
2122 | * loading a non-plt fw (so the correct fw (single-role/multi-role) | ||
2123 | * will be used). | ||
2124 | */ | ||
2125 | static bool wl12xx_need_fw_change(struct wl1271 *wl, | ||
2126 | struct vif_counter_data vif_counter_data, | ||
2127 | bool add) | ||
2128 | { | ||
2129 | enum wl12xx_fw_type current_fw = wl->fw_type; | ||
2130 | u8 vif_count = vif_counter_data.counter; | ||
2131 | |||
2132 | if (test_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags)) | ||
2133 | return false; | ||
2134 | |||
2135 | /* increase the vif count if this is a new vif */ | ||
2136 | if (add && !vif_counter_data.cur_vif_running) | ||
2137 | vif_count++; | ||
2138 | |||
2139 | wl->last_vif_count = vif_count; | ||
2140 | |||
2141 | /* no need for fw change if the device is OFF */ | ||
2142 | if (wl->state == WL1271_STATE_OFF) | ||
2143 | return false; | ||
2144 | |||
2145 | if (vif_count > 1 && current_fw == WL12XX_FW_TYPE_NORMAL) | ||
2146 | return true; | ||
2147 | if (vif_count <= 1 && current_fw == WL12XX_FW_TYPE_MULTI) | ||
2148 | return true; | ||
2149 | |||
2150 | return false; | ||
2151 | } | ||
2152 | |||
2077 | static int wl1271_op_add_interface(struct ieee80211_hw *hw, | 2153 | static int wl1271_op_add_interface(struct ieee80211_hw *hw, |
2078 | struct ieee80211_vif *vif) | 2154 | struct ieee80211_vif *vif) |
2079 | { | 2155 | { |
2080 | struct wl1271 *wl = hw->priv; | 2156 | struct wl1271 *wl = hw->priv; |
2081 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); | 2157 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
2158 | struct vif_counter_data vif_count; | ||
2082 | int ret = 0; | 2159 | int ret = 0; |
2083 | u8 role_type; | 2160 | u8 role_type; |
2084 | bool booted = false; | 2161 | bool booted = false; |
@@ -2089,6 +2166,8 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, | |||
2089 | wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", | 2166 | wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", |
2090 | ieee80211_vif_type_p2p(vif), vif->addr); | 2167 | ieee80211_vif_type_p2p(vif), vif->addr); |
2091 | 2168 | ||
2169 | wl12xx_get_vif_count(hw, vif, &vif_count); | ||
2170 | |||
2092 | mutex_lock(&wl->mutex); | 2171 | mutex_lock(&wl->mutex); |
2093 | ret = wl1271_ps_elp_wakeup(wl); | 2172 | ret = wl1271_ps_elp_wakeup(wl); |
2094 | if (ret < 0) | 2173 | if (ret < 0) |
@@ -2124,6 +2203,12 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, | |||
2124 | goto out; | 2203 | goto out; |
2125 | } | 2204 | } |
2126 | 2205 | ||
2206 | if (wl12xx_need_fw_change(wl, vif_count, true)) { | ||
2207 | mutex_unlock(&wl->mutex); | ||
2208 | wl1271_recovery_work(&wl->recovery_work); | ||
2209 | return 0; | ||
2210 | } | ||
2211 | |||
2127 | /* | 2212 | /* |
2128 | * TODO: after the nvs issue will be solved, move this block | 2213 | * TODO: after the nvs issue will be solved, move this block |
2129 | * to start(), and make sure here the driver is ON. | 2214 | * to start(), and make sure here the driver is ON. |
@@ -2287,7 +2372,10 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, | |||
2287 | struct wl1271 *wl = hw->priv; | 2372 | struct wl1271 *wl = hw->priv; |
2288 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); | 2373 | struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); |
2289 | struct wl12xx_vif *iter; | 2374 | struct wl12xx_vif *iter; |
2375 | struct vif_counter_data vif_count; | ||
2376 | bool cancel_recovery = true; | ||
2290 | 2377 | ||
2378 | wl12xx_get_vif_count(hw, vif, &vif_count); | ||
2291 | mutex_lock(&wl->mutex); | 2379 | mutex_lock(&wl->mutex); |
2292 | 2380 | ||
2293 | if (wl->state == WL1271_STATE_OFF || | 2381 | if (wl->state == WL1271_STATE_OFF || |
@@ -2306,20 +2394,32 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw, | |||
2306 | break; | 2394 | break; |
2307 | } | 2395 | } |
2308 | WARN_ON(iter != wlvif); | 2396 | WARN_ON(iter != wlvif); |
2397 | if (wl12xx_need_fw_change(wl, vif_count, false)) { | ||
2398 | wl12xx_queue_recovery_work(wl); | ||
2399 | cancel_recovery = false; | ||
2400 | } | ||
2309 | out: | 2401 | out: |
2310 | mutex_unlock(&wl->mutex); | 2402 | mutex_unlock(&wl->mutex); |
2311 | cancel_work_sync(&wl->recovery_work); | 2403 | if (cancel_recovery) |
2404 | cancel_work_sync(&wl->recovery_work); | ||
2312 | } | 2405 | } |
2313 | 2406 | ||
2314 | static int wl12xx_op_change_interface(struct ieee80211_hw *hw, | 2407 | static int wl12xx_op_change_interface(struct ieee80211_hw *hw, |
2315 | struct ieee80211_vif *vif, | 2408 | struct ieee80211_vif *vif, |
2316 | enum nl80211_iftype new_type, bool p2p) | 2409 | enum nl80211_iftype new_type, bool p2p) |
2317 | { | 2410 | { |
2411 | struct wl1271 *wl = hw->priv; | ||
2412 | int ret; | ||
2413 | |||
2414 | set_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); | ||
2318 | wl1271_op_remove_interface(hw, vif); | 2415 | wl1271_op_remove_interface(hw, vif); |
2319 | 2416 | ||
2320 | vif->type = ieee80211_iftype_p2p(new_type, p2p); | 2417 | vif->type = ieee80211_iftype_p2p(new_type, p2p); |
2321 | vif->p2p = p2p; | 2418 | vif->p2p = p2p; |
2322 | return wl1271_op_add_interface(hw, vif); | 2419 | ret = wl1271_op_add_interface(hw, vif); |
2420 | |||
2421 | clear_bit(WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, &wl->flags); | ||
2422 | return ret; | ||
2323 | } | 2423 | } |
2324 | 2424 | ||
2325 | static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, | 2425 | static int wl1271_join(struct wl1271 *wl, struct wl12xx_vif *wlvif, |
diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index 1c0264ca021f..4b3c32774bae 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c | |||
@@ -370,7 +370,9 @@ module_exit(wl1271_exit); | |||
370 | MODULE_LICENSE("GPL"); | 370 | MODULE_LICENSE("GPL"); |
371 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); | 371 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); |
372 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); | 372 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); |
373 | MODULE_FIRMWARE(WL127X_FW_NAME); | 373 | MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); |
374 | MODULE_FIRMWARE(WL128X_FW_NAME); | 374 | MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); |
375 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); | 375 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); |
376 | MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); | ||
377 | MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); | ||
376 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); | 378 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); |
diff --git a/drivers/net/wireless/wl12xx/spi.c b/drivers/net/wireless/wl12xx/spi.c index 5c2d4a0ef000..2fc18a8dcce8 100644 --- a/drivers/net/wireless/wl12xx/spi.c +++ b/drivers/net/wireless/wl12xx/spi.c | |||
@@ -433,8 +433,10 @@ module_exit(wl1271_exit); | |||
433 | MODULE_LICENSE("GPL"); | 433 | MODULE_LICENSE("GPL"); |
434 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); | 434 | MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); |
435 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); | 435 | MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); |
436 | MODULE_FIRMWARE(WL127X_FW_NAME); | 436 | MODULE_FIRMWARE(WL127X_FW_NAME_SINGLE); |
437 | MODULE_FIRMWARE(WL128X_FW_NAME); | 437 | MODULE_FIRMWARE(WL127X_FW_NAME_MULTI); |
438 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); | 438 | MODULE_FIRMWARE(WL127X_PLT_FW_NAME); |
439 | MODULE_FIRMWARE(WL128X_FW_NAME_SINGLE); | ||
440 | MODULE_FIRMWARE(WL128X_FW_NAME_MULTI); | ||
439 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); | 441 | MODULE_FIRMWARE(WL128X_PLT_FW_NAME); |
440 | MODULE_ALIAS("spi:wl1271"); | 442 | MODULE_ALIAS("spi:wl1271"); |
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 9b48aa48cdca..04cddaaf9498 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h | |||
@@ -35,8 +35,12 @@ | |||
35 | #include "conf.h" | 35 | #include "conf.h" |
36 | #include "ini.h" | 36 | #include "ini.h" |
37 | 37 | ||
38 | #define WL127X_FW_NAME "ti-connectivity/wl127x-fw-4-sr.bin" | 38 | #define WL127X_FW_NAME_MULTI "ti-connectivity/wl127x-fw-4-mr.bin" |
39 | #define WL128X_FW_NAME "ti-connectivity/wl128x-fw-4-sr.bin" | 39 | #define WL127X_FW_NAME_SINGLE "ti-connectivity/wl127x-fw-4-sr.bin" |
40 | |||
41 | #define WL128X_FW_NAME_MULTI "ti-connectivity/wl128x-fw-4-mr.bin" | ||
42 | #define WL128X_FW_NAME_SINGLE "ti-connectivity/wl128x-fw-4-sr.bin" | ||
43 | |||
40 | #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" | 44 | #define WL127X_PLT_FW_NAME "ti-connectivity/wl127x-fw-4-plt.bin" |
41 | #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" | 45 | #define WL128X_PLT_FW_NAME "ti-connectivity/wl128x-fw-4-plt.bin" |
42 | 46 | ||
@@ -97,6 +101,7 @@ enum wl1271_state { | |||
97 | enum wl12xx_fw_type { | 101 | enum wl12xx_fw_type { |
98 | WL12XX_FW_TYPE_NONE, | 102 | WL12XX_FW_TYPE_NONE, |
99 | WL12XX_FW_TYPE_NORMAL, | 103 | WL12XX_FW_TYPE_NORMAL, |
104 | WL12XX_FW_TYPE_MULTI, | ||
100 | WL12XX_FW_TYPE_PLT, | 105 | WL12XX_FW_TYPE_PLT, |
101 | }; | 106 | }; |
102 | 107 | ||
@@ -254,6 +259,7 @@ enum wl12xx_flags { | |||
254 | WL1271_FLAG_PENDING_WORK, | 259 | WL1271_FLAG_PENDING_WORK, |
255 | WL1271_FLAG_SOFT_GEMINI, | 260 | WL1271_FLAG_SOFT_GEMINI, |
256 | WL1271_FLAG_RECOVERY_IN_PROGRESS, | 261 | WL1271_FLAG_RECOVERY_IN_PROGRESS, |
262 | WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, | ||
257 | }; | 263 | }; |
258 | 264 | ||
259 | enum wl12xx_vif_flags { | 265 | enum wl12xx_vif_flags { |
@@ -303,6 +309,7 @@ struct wl1271 { | |||
303 | enum wl1271_state state; | 309 | enum wl1271_state state; |
304 | enum wl12xx_fw_type fw_type; | 310 | enum wl12xx_fw_type fw_type; |
305 | bool plt; | 311 | bool plt; |
312 | u8 last_vif_count; | ||
306 | struct mutex mutex; | 313 | struct mutex mutex; |
307 | 314 | ||
308 | unsigned long flags; | 315 | unsigned long flags; |