diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-05-13 15:18:35 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-05-13 15:18:35 -0400 |
commit | b376704bf57e1b731fe2dd6e9aa83a1d42a45ef9 (patch) | |
tree | 298b05b5875d63e4228a0416d94844f575028edf | |
parent | 1159024d4c0aafecaa0c6635c55153b4b39cc1c8 (diff) | |
parent | 9439064cd9fce8a4db716a748dbf581eb234f9c7 (diff) |
Merge branch 'for-linville' of git://git.kernel.org/pub/scm/linux/kernel/git/luca/wl12xx
-rw-r--r-- | drivers/net/wireless/wl12xx/boot.c | 4 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/conf.h | 21 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/event.c | 23 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/main.c | 260 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/ps.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.c | 243 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/scan.h | 114 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/sdio.c | 64 | ||||
-rw-r--r-- | drivers/net/wireless/wl12xx/wl12xx.h | 12 |
10 files changed, 728 insertions, 16 deletions
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 2b0cf85788b3..b07f8b7e5f11 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c | |||
@@ -478,7 +478,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) | |||
478 | DISCONNECT_EVENT_COMPLETE_ID | | 478 | DISCONNECT_EVENT_COMPLETE_ID | |
479 | RSSI_SNR_TRIGGER_0_EVENT_ID | | 479 | RSSI_SNR_TRIGGER_0_EVENT_ID | |
480 | PSPOLL_DELIVERY_FAILURE_EVENT_ID | | 480 | PSPOLL_DELIVERY_FAILURE_EVENT_ID | |
481 | SOFT_GEMINI_SENSE_EVENT_ID; | 481 | SOFT_GEMINI_SENSE_EVENT_ID | |
482 | PERIODIC_SCAN_REPORT_EVENT_ID | | ||
483 | PERIODIC_SCAN_COMPLETE_EVENT_ID; | ||
482 | 484 | ||
483 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 485 | if (wl->bss_type == BSS_TYPE_AP_BSS) |
484 | wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; | 486 | wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; |
diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 1f947368f9ee..ba558fcc76d0 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h | |||
@@ -1147,6 +1147,26 @@ struct conf_scan_settings { | |||
1147 | 1147 | ||
1148 | }; | 1148 | }; |
1149 | 1149 | ||
1150 | struct conf_sched_scan_settings { | ||
1151 | /* minimum time to wait on the channel for active scans (in TUs) */ | ||
1152 | u16 min_dwell_time_active; | ||
1153 | |||
1154 | /* maximum time to wait on the channel for active scans (in TUs) */ | ||
1155 | u16 max_dwell_time_active; | ||
1156 | |||
1157 | /* time to wait on the channel for passive scans (in TUs) */ | ||
1158 | u32 dwell_time_passive; | ||
1159 | |||
1160 | /* number of probe requests to send on each channel in active scans */ | ||
1161 | u8 num_probe_reqs; | ||
1162 | |||
1163 | /* RSSI threshold to be used for filtering */ | ||
1164 | s8 rssi_threshold; | ||
1165 | |||
1166 | /* SNR threshold to be used for filtering */ | ||
1167 | s8 snr_threshold; | ||
1168 | }; | ||
1169 | |||
1150 | /* these are number of channels on the band divided by two, rounded up */ | 1170 | /* these are number of channels on the band divided by two, rounded up */ |
1151 | #define CONF_TX_PWR_COMPENSATION_LEN_2 7 | 1171 | #define CONF_TX_PWR_COMPENSATION_LEN_2 7 |
1152 | #define CONF_TX_PWR_COMPENSATION_LEN_5 18 | 1172 | #define CONF_TX_PWR_COMPENSATION_LEN_5 18 |
@@ -1234,6 +1254,7 @@ struct conf_drv_settings { | |||
1234 | struct conf_pm_config_settings pm_config; | 1254 | struct conf_pm_config_settings pm_config; |
1235 | struct conf_roam_trigger_settings roam_trigger; | 1255 | struct conf_roam_trigger_settings roam_trigger; |
1236 | struct conf_scan_settings scan; | 1256 | struct conf_scan_settings scan; |
1257 | struct conf_sched_scan_settings sched_scan; | ||
1237 | struct conf_rf_settings rf; | 1258 | struct conf_rf_settings rf; |
1238 | struct conf_ht_setting ht; | 1259 | struct conf_ht_setting ht; |
1239 | struct conf_memory_settings mem_wl127x; | 1260 | struct conf_memory_settings mem_wl127x; |
diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index b2f692babed7..f1f8df9b6cd7 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c | |||
@@ -377,6 +377,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, | |||
377 | DRIVER_STATE_PRINT_HEX(platform_quirks); | 377 | DRIVER_STATE_PRINT_HEX(platform_quirks); |
378 | DRIVER_STATE_PRINT_HEX(chip.id); | 378 | DRIVER_STATE_PRINT_HEX(chip.id); |
379 | DRIVER_STATE_PRINT_STR(chip.fw_ver_str); | 379 | DRIVER_STATE_PRINT_STR(chip.fw_ver_str); |
380 | DRIVER_STATE_PRINT_INT(sched_scanning); | ||
380 | 381 | ||
381 | #undef DRIVER_STATE_PRINT_INT | 382 | #undef DRIVER_STATE_PRINT_INT |
382 | #undef DRIVER_STATE_PRINT_LONG | 383 | #undef DRIVER_STATE_PRINT_LONG |
diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index ae69330e807c..c3c554cd6580 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c | |||
@@ -135,6 +135,13 @@ static int wl1271_event_ps_report(struct wl1271 *wl, | |||
135 | 135 | ||
136 | /* enable beacon early termination */ | 136 | /* enable beacon early termination */ |
137 | ret = wl1271_acx_bet_enable(wl, true); | 137 | ret = wl1271_acx_bet_enable(wl, true); |
138 | if (ret < 0) | ||
139 | break; | ||
140 | |||
141 | if (wl->ps_compl) { | ||
142 | complete(wl->ps_compl); | ||
143 | wl->ps_compl = NULL; | ||
144 | } | ||
138 | break; | 145 | break; |
139 | default: | 146 | default: |
140 | break; | 147 | break; |
@@ -188,6 +195,22 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) | |||
188 | wl1271_scan_stm(wl); | 195 | wl1271_scan_stm(wl); |
189 | } | 196 | } |
190 | 197 | ||
198 | if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { | ||
199 | wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " | ||
200 | "(status 0x%0x)", mbox->scheduled_scan_status); | ||
201 | |||
202 | wl1271_scan_sched_scan_results(wl); | ||
203 | } | ||
204 | |||
205 | if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { | ||
206 | wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " | ||
207 | "(status 0x%0x)", mbox->scheduled_scan_status); | ||
208 | if (wl->sched_scanning) { | ||
209 | wl1271_scan_sched_scan_stop(wl); | ||
210 | ieee80211_sched_scan_stopped(wl->hw); | ||
211 | } | ||
212 | } | ||
213 | |||
191 | /* disable dynamic PS when requested by the firmware */ | 214 | /* disable dynamic PS when requested by the firmware */ |
192 | if (vector & SOFT_GEMINI_SENSE_EVENT_ID && | 215 | if (vector & SOFT_GEMINI_SENSE_EVENT_ID && |
193 | wl->bss_type == BSS_TYPE_STA_BSS) { | 216 | wl->bss_type == BSS_TYPE_STA_BSS) { |
diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 6dab6f0c91bc..610be03a198b 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c | |||
@@ -257,12 +257,16 @@ static struct conf_drv_settings default_conf = { | |||
257 | .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, | 257 | .wake_up_event = CONF_WAKE_UP_EVENT_DTIM, |
258 | .listen_interval = 1, | 258 | .listen_interval = 1, |
259 | .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, | 259 | .bcn_filt_mode = CONF_BCN_FILT_MODE_ENABLED, |
260 | .bcn_filt_ie_count = 1, | 260 | .bcn_filt_ie_count = 2, |
261 | .bcn_filt_ie = { | 261 | .bcn_filt_ie = { |
262 | [0] = { | 262 | [0] = { |
263 | .ie = WLAN_EID_CHANNEL_SWITCH, | 263 | .ie = WLAN_EID_CHANNEL_SWITCH, |
264 | .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, | 264 | .rule = CONF_BCN_RULE_PASS_ON_APPEARANCE, |
265 | } | 265 | }, |
266 | [1] = { | ||
267 | .ie = WLAN_EID_HT_INFORMATION, | ||
268 | .rule = CONF_BCN_RULE_PASS_ON_CHANGE, | ||
269 | }, | ||
266 | }, | 270 | }, |
267 | .synch_fail_thold = 10, | 271 | .synch_fail_thold = 10, |
268 | .bss_lose_timeout = 100, | 272 | .bss_lose_timeout = 100, |
@@ -302,6 +306,15 @@ static struct conf_drv_settings default_conf = { | |||
302 | .max_dwell_time_passive = 100000, | 306 | .max_dwell_time_passive = 100000, |
303 | .num_probe_reqs = 2, | 307 | .num_probe_reqs = 2, |
304 | }, | 308 | }, |
309 | .sched_scan = { | ||
310 | /* sched_scan requires dwell times in TU instead of TU/1000 */ | ||
311 | .min_dwell_time_active = 8, | ||
312 | .max_dwell_time_active = 30, | ||
313 | .dwell_time_passive = 100, | ||
314 | .num_probe_reqs = 2, | ||
315 | .rssi_threshold = -90, | ||
316 | .snr_threshold = 0, | ||
317 | }, | ||
305 | .rf = { | 318 | .rf = { |
306 | .tx_per_channel_power_compensation_2 = { | 319 | .tx_per_channel_power_compensation_2 = { |
307 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | 320 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
@@ -975,6 +988,11 @@ static void wl1271_recovery_work(struct work_struct *work) | |||
975 | /* Prevent spurious TX during FW restart */ | 988 | /* Prevent spurious TX during FW restart */ |
976 | ieee80211_stop_queues(wl->hw); | 989 | ieee80211_stop_queues(wl->hw); |
977 | 990 | ||
991 | if (wl->sched_scanning) { | ||
992 | ieee80211_sched_scan_stopped(wl->hw); | ||
993 | wl->sched_scanning = false; | ||
994 | } | ||
995 | |||
978 | /* reboot the chipset */ | 996 | /* reboot the chipset */ |
979 | __wl1271_op_remove_interface(wl, false); | 997 | __wl1271_op_remove_interface(wl, false); |
980 | ieee80211_restart_hw(wl->hw); | 998 | ieee80211_restart_hw(wl->hw); |
@@ -1332,6 +1350,150 @@ static struct notifier_block wl1271_dev_notifier = { | |||
1332 | .notifier_call = wl1271_dev_notify, | 1350 | .notifier_call = wl1271_dev_notify, |
1333 | }; | 1351 | }; |
1334 | 1352 | ||
1353 | static int wl1271_configure_suspend(struct wl1271 *wl) | ||
1354 | { | ||
1355 | int ret; | ||
1356 | |||
1357 | if (wl->bss_type != BSS_TYPE_STA_BSS) | ||
1358 | return 0; | ||
1359 | |||
1360 | mutex_lock(&wl->mutex); | ||
1361 | |||
1362 | ret = wl1271_ps_elp_wakeup(wl); | ||
1363 | if (ret < 0) | ||
1364 | goto out_unlock; | ||
1365 | |||
1366 | /* enter psm if needed*/ | ||
1367 | if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { | ||
1368 | DECLARE_COMPLETION_ONSTACK(compl); | ||
1369 | |||
1370 | wl->ps_compl = &compl; | ||
1371 | ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, | ||
1372 | wl->basic_rate, true); | ||
1373 | if (ret < 0) | ||
1374 | goto out_sleep; | ||
1375 | |||
1376 | /* we must unlock here so we will be able to get events */ | ||
1377 | wl1271_ps_elp_sleep(wl); | ||
1378 | mutex_unlock(&wl->mutex); | ||
1379 | |||
1380 | ret = wait_for_completion_timeout( | ||
1381 | &compl, msecs_to_jiffies(WL1271_PS_COMPLETE_TIMEOUT)); | ||
1382 | if (ret <= 0) { | ||
1383 | wl1271_warning("couldn't enter ps mode!"); | ||
1384 | ret = -EBUSY; | ||
1385 | goto out; | ||
1386 | } | ||
1387 | |||
1388 | /* take mutex again, and wakeup */ | ||
1389 | mutex_lock(&wl->mutex); | ||
1390 | |||
1391 | ret = wl1271_ps_elp_wakeup(wl); | ||
1392 | if (ret < 0) | ||
1393 | goto out_unlock; | ||
1394 | } | ||
1395 | out_sleep: | ||
1396 | wl1271_ps_elp_sleep(wl); | ||
1397 | out_unlock: | ||
1398 | mutex_unlock(&wl->mutex); | ||
1399 | out: | ||
1400 | return ret; | ||
1401 | |||
1402 | } | ||
1403 | |||
1404 | static void wl1271_configure_resume(struct wl1271 *wl) | ||
1405 | { | ||
1406 | int ret; | ||
1407 | |||
1408 | if (wl->bss_type != BSS_TYPE_STA_BSS) | ||
1409 | return; | ||
1410 | |||
1411 | mutex_lock(&wl->mutex); | ||
1412 | ret = wl1271_ps_elp_wakeup(wl); | ||
1413 | if (ret < 0) | ||
1414 | goto out; | ||
1415 | |||
1416 | /* exit psm if it wasn't configured */ | ||
1417 | if (!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) | ||
1418 | wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, | ||
1419 | wl->basic_rate, true); | ||
1420 | |||
1421 | wl1271_ps_elp_sleep(wl); | ||
1422 | out: | ||
1423 | mutex_unlock(&wl->mutex); | ||
1424 | } | ||
1425 | |||
1426 | static int wl1271_op_suspend(struct ieee80211_hw *hw, | ||
1427 | struct cfg80211_wowlan *wow) | ||
1428 | { | ||
1429 | struct wl1271 *wl = hw->priv; | ||
1430 | wl1271_debug(DEBUG_MAC80211, "mac80211 suspend wow=%d", !!wow); | ||
1431 | wl->wow_enabled = !!wow; | ||
1432 | if (wl->wow_enabled) { | ||
1433 | int ret; | ||
1434 | ret = wl1271_configure_suspend(wl); | ||
1435 | if (ret < 0) { | ||
1436 | wl1271_warning("couldn't prepare device to suspend"); | ||
1437 | return ret; | ||
1438 | } | ||
1439 | /* flush any remaining work */ | ||
1440 | wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); | ||
1441 | flush_delayed_work(&wl->scan_complete_work); | ||
1442 | |||
1443 | /* | ||
1444 | * disable and re-enable interrupts in order to flush | ||
1445 | * the threaded_irq | ||
1446 | */ | ||
1447 | wl1271_disable_interrupts(wl); | ||
1448 | |||
1449 | /* | ||
1450 | * set suspended flag to avoid triggering a new threaded_irq | ||
1451 | * work. no need for spinlock as interrupts are disabled. | ||
1452 | */ | ||
1453 | set_bit(WL1271_FLAG_SUSPENDED, &wl->flags); | ||
1454 | |||
1455 | wl1271_enable_interrupts(wl); | ||
1456 | flush_work(&wl->tx_work); | ||
1457 | flush_delayed_work(&wl->pspoll_work); | ||
1458 | flush_delayed_work(&wl->elp_work); | ||
1459 | } | ||
1460 | return 0; | ||
1461 | } | ||
1462 | |||
1463 | static int wl1271_op_resume(struct ieee80211_hw *hw) | ||
1464 | { | ||
1465 | struct wl1271 *wl = hw->priv; | ||
1466 | wl1271_debug(DEBUG_MAC80211, "mac80211 resume wow=%d", | ||
1467 | wl->wow_enabled); | ||
1468 | |||
1469 | /* | ||
1470 | * re-enable irq_work enqueuing, and call irq_work directly if | ||
1471 | * there is a pending work. | ||
1472 | */ | ||
1473 | if (wl->wow_enabled) { | ||
1474 | struct wl1271 *wl = hw->priv; | ||
1475 | unsigned long flags; | ||
1476 | bool run_irq_work = false; | ||
1477 | |||
1478 | spin_lock_irqsave(&wl->wl_lock, flags); | ||
1479 | clear_bit(WL1271_FLAG_SUSPENDED, &wl->flags); | ||
1480 | if (test_and_clear_bit(WL1271_FLAG_PENDING_WORK, &wl->flags)) | ||
1481 | run_irq_work = true; | ||
1482 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
1483 | |||
1484 | if (run_irq_work) { | ||
1485 | wl1271_debug(DEBUG_MAC80211, | ||
1486 | "run postponed irq_work directly"); | ||
1487 | wl1271_irq(0, wl); | ||
1488 | wl1271_enable_interrupts(wl); | ||
1489 | } | ||
1490 | |||
1491 | wl1271_configure_resume(wl); | ||
1492 | } | ||
1493 | |||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1335 | static int wl1271_op_start(struct ieee80211_hw *hw) | 1497 | static int wl1271_op_start(struct ieee80211_hw *hw) |
1336 | { | 1498 | { |
1337 | wl1271_debug(DEBUG_MAC80211, "mac80211 start"); | 1499 | wl1271_debug(DEBUG_MAC80211, "mac80211 start"); |
@@ -1563,6 +1725,7 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, | |||
1563 | memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); | 1725 | memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); |
1564 | wl->ap_fw_ps_map = 0; | 1726 | wl->ap_fw_ps_map = 0; |
1565 | wl->ap_ps_map = 0; | 1727 | wl->ap_ps_map = 0; |
1728 | wl->sched_scanning = false; | ||
1566 | 1729 | ||
1567 | /* | 1730 | /* |
1568 | * this is performed after the cancel_work calls and the associated | 1731 | * this is performed after the cancel_work calls and the associated |
@@ -1765,6 +1928,13 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) | |||
1765 | wl->session_counter++; | 1928 | wl->session_counter++; |
1766 | if (wl->session_counter >= SESSION_COUNTER_MAX) | 1929 | if (wl->session_counter >= SESSION_COUNTER_MAX) |
1767 | wl->session_counter = 0; | 1930 | wl->session_counter = 0; |
1931 | |||
1932 | /* The current firmware only supports sched_scan in idle */ | ||
1933 | if (wl->sched_scanning) { | ||
1934 | wl1271_scan_sched_scan_stop(wl); | ||
1935 | ieee80211_sched_scan_stopped(wl->hw); | ||
1936 | } | ||
1937 | |||
1768 | ret = wl1271_dummy_join(wl); | 1938 | ret = wl1271_dummy_join(wl); |
1769 | if (ret < 0) | 1939 | if (ret < 0) |
1770 | goto out; | 1940 | goto out; |
@@ -2317,6 +2487,60 @@ out: | |||
2317 | return ret; | 2487 | return ret; |
2318 | } | 2488 | } |
2319 | 2489 | ||
2490 | static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw, | ||
2491 | struct ieee80211_vif *vif, | ||
2492 | struct cfg80211_sched_scan_request *req, | ||
2493 | struct ieee80211_sched_scan_ies *ies) | ||
2494 | { | ||
2495 | struct wl1271 *wl = hw->priv; | ||
2496 | int ret; | ||
2497 | |||
2498 | wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_start"); | ||
2499 | |||
2500 | mutex_lock(&wl->mutex); | ||
2501 | |||
2502 | ret = wl1271_ps_elp_wakeup(wl); | ||
2503 | if (ret < 0) | ||
2504 | goto out; | ||
2505 | |||
2506 | ret = wl1271_scan_sched_scan_config(wl, req, ies); | ||
2507 | if (ret < 0) | ||
2508 | goto out_sleep; | ||
2509 | |||
2510 | ret = wl1271_scan_sched_scan_start(wl); | ||
2511 | if (ret < 0) | ||
2512 | goto out_sleep; | ||
2513 | |||
2514 | wl->sched_scanning = true; | ||
2515 | |||
2516 | out_sleep: | ||
2517 | wl1271_ps_elp_sleep(wl); | ||
2518 | out: | ||
2519 | mutex_unlock(&wl->mutex); | ||
2520 | return ret; | ||
2521 | } | ||
2522 | |||
2523 | static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, | ||
2524 | struct ieee80211_vif *vif) | ||
2525 | { | ||
2526 | struct wl1271 *wl = hw->priv; | ||
2527 | int ret; | ||
2528 | |||
2529 | wl1271_debug(DEBUG_MAC80211, "wl1271_op_sched_scan_stop"); | ||
2530 | |||
2531 | mutex_lock(&wl->mutex); | ||
2532 | |||
2533 | ret = wl1271_ps_elp_wakeup(wl); | ||
2534 | if (ret < 0) | ||
2535 | goto out; | ||
2536 | |||
2537 | wl1271_scan_sched_scan_stop(wl); | ||
2538 | |||
2539 | wl1271_ps_elp_sleep(wl); | ||
2540 | out: | ||
2541 | mutex_unlock(&wl->mutex); | ||
2542 | } | ||
2543 | |||
2320 | static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) | 2544 | static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) |
2321 | { | 2545 | { |
2322 | struct wl1271 *wl = hw->priv; | 2546 | struct wl1271 *wl = hw->priv; |
@@ -2376,20 +2600,24 @@ out: | |||
2376 | static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, | 2600 | static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, |
2377 | int offset) | 2601 | int offset) |
2378 | { | 2602 | { |
2379 | u8 *ptr = skb->data + offset; | 2603 | u8 ssid_len; |
2604 | const u8 *ptr = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, | ||
2605 | skb->len - offset); | ||
2380 | 2606 | ||
2381 | /* find the location of the ssid in the beacon */ | 2607 | if (!ptr) { |
2382 | while (ptr < skb->data + skb->len) { | 2608 | wl1271_error("No SSID in IEs!"); |
2383 | if (ptr[0] == WLAN_EID_SSID) { | 2609 | return -ENOENT; |
2384 | wl->ssid_len = ptr[1]; | ||
2385 | memcpy(wl->ssid, ptr+2, wl->ssid_len); | ||
2386 | return 0; | ||
2387 | } | ||
2388 | ptr += (ptr[1] + 2); | ||
2389 | } | 2610 | } |
2390 | 2611 | ||
2391 | wl1271_error("No SSID in IEs!\n"); | 2612 | ssid_len = ptr[1]; |
2392 | return -ENOENT; | 2613 | if (ssid_len > IEEE80211_MAX_SSID_LEN) { |
2614 | wl1271_error("SSID is too long!"); | ||
2615 | return -EINVAL; | ||
2616 | } | ||
2617 | |||
2618 | wl->ssid_len = ssid_len; | ||
2619 | memcpy(wl->ssid, ptr+2, ssid_len); | ||
2620 | return 0; | ||
2393 | } | 2621 | } |
2394 | 2622 | ||
2395 | static int wl1271_bss_erp_info_changed(struct wl1271 *wl, | 2623 | static int wl1271_bss_erp_info_changed(struct wl1271 *wl, |
@@ -3422,12 +3650,16 @@ static const struct ieee80211_ops wl1271_ops = { | |||
3422 | .stop = wl1271_op_stop, | 3650 | .stop = wl1271_op_stop, |
3423 | .add_interface = wl1271_op_add_interface, | 3651 | .add_interface = wl1271_op_add_interface, |
3424 | .remove_interface = wl1271_op_remove_interface, | 3652 | .remove_interface = wl1271_op_remove_interface, |
3653 | .suspend = wl1271_op_suspend, | ||
3654 | .resume = wl1271_op_resume, | ||
3425 | .config = wl1271_op_config, | 3655 | .config = wl1271_op_config, |
3426 | .prepare_multicast = wl1271_op_prepare_multicast, | 3656 | .prepare_multicast = wl1271_op_prepare_multicast, |
3427 | .configure_filter = wl1271_op_configure_filter, | 3657 | .configure_filter = wl1271_op_configure_filter, |
3428 | .tx = wl1271_op_tx, | 3658 | .tx = wl1271_op_tx, |
3429 | .set_key = wl1271_op_set_key, | 3659 | .set_key = wl1271_op_set_key, |
3430 | .hw_scan = wl1271_op_hw_scan, | 3660 | .hw_scan = wl1271_op_hw_scan, |
3661 | .sched_scan_start = wl1271_op_sched_scan_start, | ||
3662 | .sched_scan_stop = wl1271_op_sched_scan_stop, | ||
3431 | .bss_info_changed = wl1271_op_bss_info_changed, | 3663 | .bss_info_changed = wl1271_op_bss_info_changed, |
3432 | .set_frag_threshold = wl1271_op_set_frag_threshold, | 3664 | .set_frag_threshold = wl1271_op_set_frag_threshold, |
3433 | .set_rts_threshold = wl1271_op_set_rts_threshold, | 3665 | .set_rts_threshold = wl1271_op_set_rts_threshold, |
@@ -3626,6 +3858,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) | |||
3626 | IEEE80211_HW_CONNECTION_MONITOR | | 3858 | IEEE80211_HW_CONNECTION_MONITOR | |
3627 | IEEE80211_HW_SUPPORTS_CQM_RSSI | | 3859 | IEEE80211_HW_SUPPORTS_CQM_RSSI | |
3628 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | | 3860 | IEEE80211_HW_REPORTS_TX_ACK_STATUS | |
3861 | IEEE80211_HW_SPECTRUM_MGMT | | ||
3629 | IEEE80211_HW_AP_LINK_PS; | 3862 | IEEE80211_HW_AP_LINK_PS; |
3630 | 3863 | ||
3631 | wl->hw->wiphy->cipher_suites = cipher_suites; | 3864 | wl->hw->wiphy->cipher_suites = cipher_suites; |
@@ -3747,6 +3980,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) | |||
3747 | wl->ap_fw_ps_map = 0; | 3980 | wl->ap_fw_ps_map = 0; |
3748 | wl->quirks = 0; | 3981 | wl->quirks = 0; |
3749 | wl->platform_quirks = 0; | 3982 | wl->platform_quirks = 0; |
3983 | wl->sched_scanning = false; | ||
3750 | 3984 | ||
3751 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); | 3985 | memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); |
3752 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) | 3986 | for (i = 0; i < ACX_TX_DESCRIPTORS; i++) |
diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index c41bd0a711bc..25eb9bc9b628 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h | |||
@@ -35,4 +35,6 @@ void wl1271_elp_work(struct work_struct *work); | |||
35 | void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); | 35 | void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); |
36 | void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); | 36 | void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); |
37 | 37 | ||
38 | #define WL1271_PS_COMPLETE_TIMEOUT 500 | ||
39 | |||
38 | #endif /* __WL1271_PS_H__ */ | 40 | #endif /* __WL1271_PS_H__ */ |
diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index 5d0544c8f3f5..f37e5a391976 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c | |||
@@ -320,3 +320,246 @@ int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, | |||
320 | 320 | ||
321 | return 0; | 321 | return 0; |
322 | } | 322 | } |
323 | |||
324 | static int | ||
325 | wl1271_scan_get_sched_scan_channels(struct wl1271 *wl, | ||
326 | struct cfg80211_sched_scan_request *req, | ||
327 | struct conn_scan_ch_params *channels, | ||
328 | u32 band, bool radar, bool passive, | ||
329 | int start) | ||
330 | { | ||
331 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | ||
332 | int i, j; | ||
333 | u32 flags; | ||
334 | |||
335 | for (i = 0, j = start; | ||
336 | i < req->n_channels && j < MAX_CHANNELS_ALL_BANDS; | ||
337 | i++) { | ||
338 | flags = req->channels[i]->flags; | ||
339 | |||
340 | if (!(flags & IEEE80211_CHAN_DISABLED) && | ||
341 | ((flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive) && | ||
342 | ((flags & IEEE80211_CHAN_RADAR) == radar) && | ||
343 | (req->channels[i]->band == band)) { | ||
344 | wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", | ||
345 | req->channels[i]->band, | ||
346 | req->channels[i]->center_freq); | ||
347 | wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", | ||
348 | req->channels[i]->hw_value, | ||
349 | req->channels[i]->flags); | ||
350 | wl1271_debug(DEBUG_SCAN, "max_power %d", | ||
351 | req->channels[i]->max_power); | ||
352 | |||
353 | if (flags & IEEE80211_CHAN_PASSIVE_SCAN) { | ||
354 | channels[j].passive_duration = | ||
355 | cpu_to_le16(c->dwell_time_passive); | ||
356 | } else { | ||
357 | channels[j].min_duration = | ||
358 | cpu_to_le16(c->min_dwell_time_active); | ||
359 | channels[j].max_duration = | ||
360 | cpu_to_le16(c->max_dwell_time_active); | ||
361 | } | ||
362 | channels[j].tx_power_att = req->channels[j]->max_power; | ||
363 | channels[j].channel = req->channels[i]->hw_value; | ||
364 | |||
365 | j++; | ||
366 | } | ||
367 | } | ||
368 | |||
369 | return j - start; | ||
370 | } | ||
371 | |||
372 | static int | ||
373 | wl1271_scan_sched_scan_channels(struct wl1271 *wl, | ||
374 | struct cfg80211_sched_scan_request *req, | ||
375 | struct wl1271_cmd_sched_scan_config *cfg) | ||
376 | { | ||
377 | int idx = 0; | ||
378 | |||
379 | cfg->passive[0] = | ||
380 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
381 | IEEE80211_BAND_2GHZ, | ||
382 | false, true, idx); | ||
383 | idx += cfg->passive[0]; | ||
384 | |||
385 | cfg->active[0] = | ||
386 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
387 | IEEE80211_BAND_2GHZ, | ||
388 | false, false, idx); | ||
389 | idx += cfg->active[0]; | ||
390 | |||
391 | cfg->passive[1] = | ||
392 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
393 | IEEE80211_BAND_5GHZ, | ||
394 | false, true, idx); | ||
395 | idx += cfg->passive[1]; | ||
396 | |||
397 | cfg->active[1] = | ||
398 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
399 | IEEE80211_BAND_5GHZ, | ||
400 | false, false, 14); | ||
401 | idx += cfg->active[1]; | ||
402 | |||
403 | cfg->dfs = | ||
404 | wl1271_scan_get_sched_scan_channels(wl, req, cfg->channels, | ||
405 | IEEE80211_BAND_5GHZ, | ||
406 | true, false, idx); | ||
407 | idx += cfg->dfs; | ||
408 | |||
409 | wl1271_debug(DEBUG_SCAN, " 2.4GHz: active %d passive %d", | ||
410 | cfg->active[0], cfg->passive[0]); | ||
411 | wl1271_debug(DEBUG_SCAN, " 5GHz: active %d passive %d", | ||
412 | cfg->active[1], cfg->passive[1]); | ||
413 | |||
414 | return idx; | ||
415 | } | ||
416 | |||
417 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | ||
418 | struct cfg80211_sched_scan_request *req, | ||
419 | struct ieee80211_sched_scan_ies *ies) | ||
420 | { | ||
421 | struct wl1271_cmd_sched_scan_config *cfg = NULL; | ||
422 | struct conf_sched_scan_settings *c = &wl->conf.sched_scan; | ||
423 | int i, total_channels, ret; | ||
424 | |||
425 | wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); | ||
426 | |||
427 | cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); | ||
428 | if (!cfg) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | cfg->rssi_threshold = c->rssi_threshold; | ||
432 | cfg->snr_threshold = c->snr_threshold; | ||
433 | cfg->n_probe_reqs = c->num_probe_reqs; | ||
434 | /* cycles set to 0 it means infinite (until manually stopped) */ | ||
435 | cfg->cycles = 0; | ||
436 | /* report APs when at least 1 is found */ | ||
437 | cfg->report_after = 1; | ||
438 | /* don't stop scanning automatically when something is found */ | ||
439 | cfg->terminate = 0; | ||
440 | cfg->tag = WL1271_SCAN_DEFAULT_TAG; | ||
441 | /* don't filter on BSS type */ | ||
442 | cfg->bss_type = SCAN_BSS_TYPE_ANY; | ||
443 | /* currently NL80211 supports only a single interval */ | ||
444 | for (i = 0; i < SCAN_MAX_CYCLE_INTERVALS; i++) | ||
445 | cfg->intervals[i] = cpu_to_le32(req->interval); | ||
446 | |||
447 | if (req->ssids[0].ssid_len && req->ssids[0].ssid) { | ||
448 | cfg->filter_type = SCAN_SSID_FILTER_SPECIFIC; | ||
449 | cfg->ssid_len = req->ssids[0].ssid_len; | ||
450 | memcpy(cfg->ssid, req->ssids[0].ssid, | ||
451 | req->ssids[0].ssid_len); | ||
452 | } else { | ||
453 | cfg->filter_type = SCAN_SSID_FILTER_ANY; | ||
454 | cfg->ssid_len = 0; | ||
455 | } | ||
456 | |||
457 | total_channels = wl1271_scan_sched_scan_channels(wl, req, cfg); | ||
458 | if (total_channels == 0) { | ||
459 | wl1271_error("scan channel list is empty"); | ||
460 | ret = -EINVAL; | ||
461 | goto out; | ||
462 | } | ||
463 | |||
464 | if (cfg->active[0]) { | ||
465 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, | ||
466 | req->ssids[0].ssid_len, | ||
467 | ies->ie[IEEE80211_BAND_2GHZ], | ||
468 | ies->len[IEEE80211_BAND_2GHZ], | ||
469 | IEEE80211_BAND_2GHZ); | ||
470 | if (ret < 0) { | ||
471 | wl1271_error("2.4GHz PROBE request template failed"); | ||
472 | goto out; | ||
473 | } | ||
474 | } | ||
475 | |||
476 | if (cfg->active[1]) { | ||
477 | ret = wl1271_cmd_build_probe_req(wl, req->ssids[0].ssid, | ||
478 | req->ssids[0].ssid_len, | ||
479 | ies->ie[IEEE80211_BAND_5GHZ], | ||
480 | ies->len[IEEE80211_BAND_5GHZ], | ||
481 | IEEE80211_BAND_5GHZ); | ||
482 | if (ret < 0) { | ||
483 | wl1271_error("5GHz PROBE request template failed"); | ||
484 | goto out; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | wl1271_dump(DEBUG_SCAN, "SCAN_CFG: ", cfg, sizeof(*cfg)); | ||
489 | |||
490 | ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_CFG, cfg, | ||
491 | sizeof(*cfg), 0); | ||
492 | if (ret < 0) { | ||
493 | wl1271_error("SCAN configuration failed"); | ||
494 | goto out; | ||
495 | } | ||
496 | out: | ||
497 | kfree(cfg); | ||
498 | return ret; | ||
499 | } | ||
500 | |||
501 | int wl1271_scan_sched_scan_start(struct wl1271 *wl) | ||
502 | { | ||
503 | struct wl1271_cmd_sched_scan_start *start; | ||
504 | int ret = 0; | ||
505 | |||
506 | wl1271_debug(DEBUG_CMD, "cmd periodic scan start"); | ||
507 | |||
508 | if (wl->bss_type != BSS_TYPE_STA_BSS) | ||
509 | return -EOPNOTSUPP; | ||
510 | |||
511 | if (!test_bit(WL1271_FLAG_IDLE, &wl->flags)) | ||
512 | return -EBUSY; | ||
513 | |||
514 | start = kzalloc(sizeof(*start), GFP_KERNEL); | ||
515 | if (!start) | ||
516 | return -ENOMEM; | ||
517 | |||
518 | start->tag = WL1271_SCAN_DEFAULT_TAG; | ||
519 | |||
520 | ret = wl1271_cmd_send(wl, CMD_START_PERIODIC_SCAN, start, | ||
521 | sizeof(*start), 0); | ||
522 | if (ret < 0) { | ||
523 | wl1271_error("failed to send scan start command"); | ||
524 | goto out_free; | ||
525 | } | ||
526 | |||
527 | out_free: | ||
528 | kfree(start); | ||
529 | return ret; | ||
530 | } | ||
531 | |||
532 | void wl1271_scan_sched_scan_results(struct wl1271 *wl) | ||
533 | { | ||
534 | wl1271_debug(DEBUG_SCAN, "got periodic scan results"); | ||
535 | |||
536 | ieee80211_sched_scan_results(wl->hw); | ||
537 | } | ||
538 | |||
539 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl) | ||
540 | { | ||
541 | struct wl1271_cmd_sched_scan_stop *stop; | ||
542 | int ret = 0; | ||
543 | |||
544 | wl1271_debug(DEBUG_CMD, "cmd periodic scan stop"); | ||
545 | |||
546 | /* FIXME: what to do if alloc'ing to stop fails? */ | ||
547 | stop = kzalloc(sizeof(*stop), GFP_KERNEL); | ||
548 | if (!stop) { | ||
549 | wl1271_error("failed to alloc memory to send sched scan stop"); | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | stop->tag = WL1271_SCAN_DEFAULT_TAG; | ||
554 | |||
555 | ret = wl1271_cmd_send(wl, CMD_STOP_PERIODIC_SCAN, stop, | ||
556 | sizeof(*stop), 0); | ||
557 | if (ret < 0) { | ||
558 | wl1271_error("failed to send sched scan stop command"); | ||
559 | goto out_free; | ||
560 | } | ||
561 | wl->sched_scanning = false; | ||
562 | |||
563 | out_free: | ||
564 | kfree(stop); | ||
565 | } | ||
diff --git a/drivers/net/wireless/wl12xx/scan.h b/drivers/net/wireless/wl12xx/scan.h index 421a750add5a..c83319579ca3 100644 --- a/drivers/net/wireless/wl12xx/scan.h +++ b/drivers/net/wireless/wl12xx/scan.h | |||
@@ -33,6 +33,12 @@ int wl1271_scan_build_probe_req(struct wl1271 *wl, | |||
33 | const u8 *ie, size_t ie_len, u8 band); | 33 | const u8 *ie, size_t ie_len, u8 band); |
34 | void wl1271_scan_stm(struct wl1271 *wl); | 34 | void wl1271_scan_stm(struct wl1271 *wl); |
35 | void wl1271_scan_complete_work(struct work_struct *work); | 35 | void wl1271_scan_complete_work(struct work_struct *work); |
36 | int wl1271_scan_sched_scan_config(struct wl1271 *wl, | ||
37 | struct cfg80211_sched_scan_request *req, | ||
38 | struct ieee80211_sched_scan_ies *ies); | ||
39 | int wl1271_scan_sched_scan_start(struct wl1271 *wl); | ||
40 | void wl1271_scan_sched_scan_stop(struct wl1271 *wl); | ||
41 | void wl1271_scan_sched_scan_results(struct wl1271 *wl); | ||
36 | 42 | ||
37 | #define WL1271_SCAN_MAX_CHANNELS 24 | 43 | #define WL1271_SCAN_MAX_CHANNELS 24 |
38 | #define WL1271_SCAN_DEFAULT_TAG 1 | 44 | #define WL1271_SCAN_DEFAULT_TAG 1 |
@@ -106,4 +112,112 @@ struct wl1271_cmd_trigger_scan_to { | |||
106 | __le32 timeout; | 112 | __le32 timeout; |
107 | } __packed; | 113 | } __packed; |
108 | 114 | ||
115 | #define MAX_CHANNELS_ALL_BANDS 41 | ||
116 | #define SCAN_MAX_CYCLE_INTERVALS 16 | ||
117 | #define SCAN_MAX_BANDS 3 | ||
118 | |||
119 | enum { | ||
120 | SCAN_CHANNEL_TYPE_2GHZ_PASSIVE, | ||
121 | SCAN_CHANNEL_TYPE_2GHZ_ACTIVE, | ||
122 | SCAN_CHANNEL_TYPE_5GHZ_PASSIVE, | ||
123 | SCAN_CHANNEL_TYPE_5GHZ_ACTIVE, | ||
124 | SCAN_CHANNEL_TYPE_5GHZ_DFS, | ||
125 | }; | ||
126 | |||
127 | enum { | ||
128 | SCAN_SSID_FILTER_ANY = 0, | ||
129 | SCAN_SSID_FILTER_SPECIFIC = 1, | ||
130 | SCAN_SSID_FILTER_LIST = 2, | ||
131 | SCAN_SSID_FILTER_DISABLED = 3 | ||
132 | }; | ||
133 | |||
134 | enum { | ||
135 | SCAN_BSS_TYPE_INDEPENDENT, | ||
136 | SCAN_BSS_TYPE_INFRASTRUCTURE, | ||
137 | SCAN_BSS_TYPE_ANY, | ||
138 | }; | ||
139 | |||
140 | struct conn_scan_ch_params { | ||
141 | __le16 min_duration; | ||
142 | __le16 max_duration; | ||
143 | __le16 passive_duration; | ||
144 | |||
145 | u8 channel; | ||
146 | u8 tx_power_att; | ||
147 | |||
148 | /* bit 0: DFS channel; bit 1: DFS enabled */ | ||
149 | u8 flags; | ||
150 | |||
151 | u8 padding[3]; | ||
152 | } __packed; | ||
153 | |||
154 | struct wl1271_cmd_sched_scan_config { | ||
155 | struct wl1271_cmd_header header; | ||
156 | |||
157 | __le32 intervals[SCAN_MAX_CYCLE_INTERVALS]; | ||
158 | |||
159 | s8 rssi_threshold; /* for filtering (in dBm) */ | ||
160 | s8 snr_threshold; /* for filtering (in dB) */ | ||
161 | |||
162 | u8 cycles; /* maximum number of scan cycles */ | ||
163 | u8 report_after; /* report when this number of results are received */ | ||
164 | u8 terminate; /* stop scanning after reporting */ | ||
165 | |||
166 | u8 tag; | ||
167 | u8 bss_type; /* for filtering */ | ||
168 | u8 filter_type; | ||
169 | |||
170 | u8 ssid_len; /* For SCAN_SSID_FILTER_SPECIFIC */ | ||
171 | u8 ssid[IW_ESSID_MAX_SIZE]; | ||
172 | |||
173 | u8 n_probe_reqs; /* Number of probes requests per channel */ | ||
174 | |||
175 | u8 passive[SCAN_MAX_BANDS]; | ||
176 | u8 active[SCAN_MAX_BANDS]; | ||
177 | |||
178 | u8 dfs; | ||
179 | |||
180 | u8 padding[3]; | ||
181 | |||
182 | struct conn_scan_ch_params channels[MAX_CHANNELS_ALL_BANDS]; | ||
183 | } __packed; | ||
184 | |||
185 | |||
186 | #define SCHED_SCAN_MAX_SSIDS 8 | ||
187 | |||
188 | enum { | ||
189 | SCAN_SSID_TYPE_PUBLIC = 0, | ||
190 | SCAN_SSID_TYPE_HIDDEN = 1, | ||
191 | }; | ||
192 | |||
193 | struct wl1271_ssid { | ||
194 | u8 type; | ||
195 | u8 len; | ||
196 | u8 ssid[IW_ESSID_MAX_SIZE]; | ||
197 | /* u8 padding[2]; */ | ||
198 | } __packed; | ||
199 | |||
200 | struct wl1271_cmd_sched_scan_ssid_list { | ||
201 | struct wl1271_cmd_header header; | ||
202 | |||
203 | u8 n_ssids; | ||
204 | struct wl1271_ssid ssids[SCHED_SCAN_MAX_SSIDS]; | ||
205 | u8 padding[3]; | ||
206 | } __packed; | ||
207 | |||
208 | struct wl1271_cmd_sched_scan_start { | ||
209 | struct wl1271_cmd_header header; | ||
210 | |||
211 | u8 tag; | ||
212 | u8 padding[3]; | ||
213 | } __packed; | ||
214 | |||
215 | struct wl1271_cmd_sched_scan_stop { | ||
216 | struct wl1271_cmd_header header; | ||
217 | |||
218 | u8 tag; | ||
219 | u8 padding[3]; | ||
220 | } __packed; | ||
221 | |||
222 | |||
109 | #endif /* __WL1271_SCAN_H__ */ | 223 | #endif /* __WL1271_SCAN_H__ */ |
diff --git a/drivers/net/wireless/wl12xx/sdio.c b/drivers/net/wireless/wl12xx/sdio.c index bcd4ad7ba90d..92d29a860fc0 100644 --- a/drivers/net/wireless/wl12xx/sdio.c +++ b/drivers/net/wireless/wl12xx/sdio.c | |||
@@ -82,6 +82,16 @@ static irqreturn_t wl1271_hardirq(int irq, void *cookie) | |||
82 | complete(wl->elp_compl); | 82 | complete(wl->elp_compl); |
83 | wl->elp_compl = NULL; | 83 | wl->elp_compl = NULL; |
84 | } | 84 | } |
85 | |||
86 | if (test_bit(WL1271_FLAG_SUSPENDED, &wl->flags)) { | ||
87 | /* don't enqueue a work right now. mark it as pending */ | ||
88 | set_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); | ||
89 | wl1271_debug(DEBUG_IRQ, "should not enqueue work"); | ||
90 | disable_irq_nosync(wl->irq); | ||
91 | pm_wakeup_event(wl1271_sdio_wl_to_dev(wl), 0); | ||
92 | spin_unlock_irqrestore(&wl->wl_lock, flags); | ||
93 | return IRQ_HANDLED; | ||
94 | } | ||
85 | spin_unlock_irqrestore(&wl->wl_lock, flags); | 95 | spin_unlock_irqrestore(&wl->wl_lock, flags); |
86 | 96 | ||
87 | return IRQ_WAKE_THREAD; | 97 | return IRQ_WAKE_THREAD; |
@@ -221,6 +231,7 @@ static int __devinit wl1271_probe(struct sdio_func *func, | |||
221 | const struct wl12xx_platform_data *wlan_data; | 231 | const struct wl12xx_platform_data *wlan_data; |
222 | struct wl1271 *wl; | 232 | struct wl1271 *wl; |
223 | unsigned long irqflags; | 233 | unsigned long irqflags; |
234 | mmc_pm_flag_t mmcflags; | ||
224 | int ret; | 235 | int ret; |
225 | 236 | ||
226 | /* We are only able to handle the wlan function */ | 237 | /* We are only able to handle the wlan function */ |
@@ -267,8 +278,18 @@ static int __devinit wl1271_probe(struct sdio_func *func, | |||
267 | goto out_free; | 278 | goto out_free; |
268 | } | 279 | } |
269 | 280 | ||
281 | enable_irq_wake(wl->irq); | ||
282 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 1); | ||
283 | |||
270 | disable_irq(wl->irq); | 284 | disable_irq(wl->irq); |
271 | 285 | ||
286 | /* if sdio can keep power while host is suspended, enable wow */ | ||
287 | mmcflags = sdio_get_host_pm_caps(func); | ||
288 | wl1271_debug(DEBUG_SDIO, "sdio PM caps = 0x%x", mmcflags); | ||
289 | |||
290 | if (mmcflags & MMC_PM_KEEP_POWER) | ||
291 | hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY; | ||
292 | |||
272 | ret = wl1271_init_ieee80211(wl); | 293 | ret = wl1271_init_ieee80211(wl); |
273 | if (ret) | 294 | if (ret) |
274 | goto out_irq; | 295 | goto out_irq; |
@@ -303,6 +324,8 @@ static void __devexit wl1271_remove(struct sdio_func *func) | |||
303 | pm_runtime_get_noresume(&func->dev); | 324 | pm_runtime_get_noresume(&func->dev); |
304 | 325 | ||
305 | wl1271_unregister_hw(wl); | 326 | wl1271_unregister_hw(wl); |
327 | device_init_wakeup(wl1271_sdio_wl_to_dev(wl), 0); | ||
328 | disable_irq_wake(wl->irq); | ||
306 | free_irq(wl->irq, wl); | 329 | free_irq(wl->irq, wl); |
307 | wl1271_free_hw(wl); | 330 | wl1271_free_hw(wl); |
308 | } | 331 | } |
@@ -311,11 +334,50 @@ static int wl1271_suspend(struct device *dev) | |||
311 | { | 334 | { |
312 | /* Tell MMC/SDIO core it's OK to power down the card | 335 | /* Tell MMC/SDIO core it's OK to power down the card |
313 | * (if it isn't already), but not to remove it completely */ | 336 | * (if it isn't already), but not to remove it completely */ |
314 | return 0; | 337 | struct sdio_func *func = dev_to_sdio_func(dev); |
338 | struct wl1271 *wl = sdio_get_drvdata(func); | ||
339 | mmc_pm_flag_t sdio_flags; | ||
340 | int ret = 0; | ||
341 | |||
342 | wl1271_debug(DEBUG_MAC80211, "wl1271 suspend. wow_enabled: %d", | ||
343 | wl->wow_enabled); | ||
344 | |||
345 | /* check whether sdio should keep power */ | ||
346 | if (wl->wow_enabled) { | ||
347 | sdio_flags = sdio_get_host_pm_caps(func); | ||
348 | |||
349 | if (!(sdio_flags & MMC_PM_KEEP_POWER)) { | ||
350 | wl1271_error("can't keep power while host " | ||
351 | "is suspended"); | ||
352 | ret = -EINVAL; | ||
353 | goto out; | ||
354 | } | ||
355 | |||
356 | /* keep power while host suspended */ | ||
357 | ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); | ||
358 | if (ret) { | ||
359 | wl1271_error("error while trying to keep power"); | ||
360 | goto out; | ||
361 | } | ||
362 | |||
363 | /* release host */ | ||
364 | sdio_release_host(func); | ||
365 | } | ||
366 | out: | ||
367 | return ret; | ||
315 | } | 368 | } |
316 | 369 | ||
317 | static int wl1271_resume(struct device *dev) | 370 | static int wl1271_resume(struct device *dev) |
318 | { | 371 | { |
372 | struct sdio_func *func = dev_to_sdio_func(dev); | ||
373 | struct wl1271 *wl = sdio_get_drvdata(func); | ||
374 | |||
375 | wl1271_debug(DEBUG_MAC80211, "wl1271 resume"); | ||
376 | if (wl->wow_enabled) { | ||
377 | /* claim back host */ | ||
378 | sdio_claim_host(func); | ||
379 | } | ||
380 | |||
319 | return 0; | 381 | return 0; |
320 | } | 382 | } |
321 | 383 | ||
diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index b7601438ecac..fbe8f46d1232 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h | |||
@@ -351,13 +351,14 @@ enum wl12xx_flags { | |||
351 | WL1271_FLAG_PSM_REQUESTED, | 351 | WL1271_FLAG_PSM_REQUESTED, |
352 | WL1271_FLAG_IRQ_RUNNING, | 352 | WL1271_FLAG_IRQ_RUNNING, |
353 | WL1271_FLAG_IDLE, | 353 | WL1271_FLAG_IDLE, |
354 | WL1271_FLAG_IDLE_REQUESTED, | ||
355 | WL1271_FLAG_PSPOLL_FAILURE, | 354 | WL1271_FLAG_PSPOLL_FAILURE, |
356 | WL1271_FLAG_STA_STATE_SENT, | 355 | WL1271_FLAG_STA_STATE_SENT, |
357 | WL1271_FLAG_FW_TX_BUSY, | 356 | WL1271_FLAG_FW_TX_BUSY, |
358 | WL1271_FLAG_AP_STARTED, | 357 | WL1271_FLAG_AP_STARTED, |
359 | WL1271_FLAG_IF_INITIALIZED, | 358 | WL1271_FLAG_IF_INITIALIZED, |
360 | WL1271_FLAG_DUMMY_PACKET_PENDING, | 359 | WL1271_FLAG_DUMMY_PACKET_PENDING, |
360 | WL1271_FLAG_SUSPENDED, | ||
361 | WL1271_FLAG_PENDING_WORK, | ||
361 | }; | 362 | }; |
362 | 363 | ||
363 | struct wl1271_link { | 364 | struct wl1271_link { |
@@ -480,6 +481,8 @@ struct wl1271 { | |||
480 | struct wl1271_scan scan; | 481 | struct wl1271_scan scan; |
481 | struct delayed_work scan_complete_work; | 482 | struct delayed_work scan_complete_work; |
482 | 483 | ||
484 | bool sched_scanning; | ||
485 | |||
483 | /* probe-req template for the current AP */ | 486 | /* probe-req template for the current AP */ |
484 | struct sk_buff *probereq; | 487 | struct sk_buff *probereq; |
485 | 488 | ||
@@ -510,6 +513,7 @@ struct wl1271 { | |||
510 | unsigned int rx_filter; | 513 | unsigned int rx_filter; |
511 | 514 | ||
512 | struct completion *elp_compl; | 515 | struct completion *elp_compl; |
516 | struct completion *ps_compl; | ||
513 | struct delayed_work elp_work; | 517 | struct delayed_work elp_work; |
514 | struct delayed_work pspoll_work; | 518 | struct delayed_work pspoll_work; |
515 | 519 | ||
@@ -564,6 +568,12 @@ struct wl1271 { | |||
564 | int tcxo_clock; | 568 | int tcxo_clock; |
565 | 569 | ||
566 | /* | 570 | /* |
571 | * wowlan trigger was configured during suspend. | ||
572 | * (currently, only "ANY" trigger is supported) | ||
573 | */ | ||
574 | bool wow_enabled; | ||
575 | |||
576 | /* | ||
567 | * AP-mode - links indexed by HLID. The global and broadcast links | 577 | * AP-mode - links indexed by HLID. The global and broadcast links |
568 | * are always active. | 578 | * are always active. |
569 | */ | 579 | */ |