10a51b27eSJohannes Berg /* 25484e237SJohannes Berg * Scanning implementation 35484e237SJohannes Berg * 40a51b27eSJohannes Berg * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> 50a51b27eSJohannes Berg * Copyright 2004, Instant802 Networks, Inc. 60a51b27eSJohannes Berg * Copyright 2005, Devicescape Software, Inc. 70a51b27eSJohannes Berg * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 80a51b27eSJohannes Berg * Copyright 2007, Michael Wu <flamingice@sourmilk.net> 90a51b27eSJohannes Berg * 100a51b27eSJohannes Berg * This program is free software; you can redistribute it and/or modify 110a51b27eSJohannes Berg * it under the terms of the GNU General Public License version 2 as 120a51b27eSJohannes Berg * published by the Free Software Foundation. 130a51b27eSJohannes Berg */ 140a51b27eSJohannes Berg 155484e237SJohannes Berg /* TODO: 165484e237SJohannes Berg * order BSS list by RSSI(?) ("quality of AP") 175484e237SJohannes Berg * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, 185484e237SJohannes Berg * SSID) 195484e237SJohannes Berg */ 205484e237SJohannes Berg 210a51b27eSJohannes Berg #include <linux/wireless.h> 220a51b27eSJohannes Berg #include <linux/if_arp.h> 230a51b27eSJohannes Berg #include <net/mac80211.h> 240a51b27eSJohannes Berg #include <net/iw_handler.h> 250a51b27eSJohannes Berg 260a51b27eSJohannes Berg #include "ieee80211_i.h" 275484e237SJohannes Berg #include "mesh.h" 280a51b27eSJohannes Berg 290a51b27eSJohannes Berg #define IEEE80211_PROBE_DELAY (HZ / 33) 300a51b27eSJohannes Berg #define IEEE80211_CHANNEL_TIME (HZ / 33) 310a51b27eSJohannes Berg #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5) 320a51b27eSJohannes Berg 335484e237SJohannes Berg void ieee80211_rx_bss_list_init(struct ieee80211_local *local) 345484e237SJohannes Berg { 355484e237SJohannes Berg spin_lock_init(&local->sta_bss_lock); 365484e237SJohannes Berg INIT_LIST_HEAD(&local->sta_bss_list); 375484e237SJohannes Berg } 385484e237SJohannes Berg 395484e237SJohannes Berg void ieee80211_rx_bss_list_deinit(struct ieee80211_local *local) 405484e237SJohannes Berg { 415484e237SJohannes Berg struct ieee80211_sta_bss *bss, *tmp; 425484e237SJohannes Berg 435484e237SJohannes Berg list_for_each_entry_safe(bss, tmp, &local->sta_bss_list, list) 445484e237SJohannes Berg ieee80211_rx_bss_put(local, bss); 455484e237SJohannes Berg } 465484e237SJohannes Berg 475484e237SJohannes Berg struct ieee80211_sta_bss * 485484e237SJohannes Berg ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, 495484e237SJohannes Berg u8 *ssid, u8 ssid_len) 505484e237SJohannes Berg { 515484e237SJohannes Berg struct ieee80211_sta_bss *bss; 525484e237SJohannes Berg 535484e237SJohannes Berg spin_lock_bh(&local->sta_bss_lock); 545484e237SJohannes Berg bss = local->sta_bss_hash[STA_HASH(bssid)]; 555484e237SJohannes Berg while (bss) { 565484e237SJohannes Berg if (!bss_mesh_cfg(bss) && 575484e237SJohannes Berg !memcmp(bss->bssid, bssid, ETH_ALEN) && 585484e237SJohannes Berg bss->freq == freq && 595484e237SJohannes Berg bss->ssid_len == ssid_len && 605484e237SJohannes Berg (ssid_len == 0 || !memcmp(bss->ssid, ssid, ssid_len))) { 615484e237SJohannes Berg atomic_inc(&bss->users); 625484e237SJohannes Berg break; 635484e237SJohannes Berg } 645484e237SJohannes Berg bss = bss->hnext; 655484e237SJohannes Berg } 665484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 675484e237SJohannes Berg return bss; 685484e237SJohannes Berg } 695484e237SJohannes Berg 705484e237SJohannes Berg /* Caller must hold local->sta_bss_lock */ 715484e237SJohannes Berg static void __ieee80211_rx_bss_hash_add(struct ieee80211_local *local, 725484e237SJohannes Berg struct ieee80211_sta_bss *bss) 735484e237SJohannes Berg { 745484e237SJohannes Berg u8 hash_idx; 755484e237SJohannes Berg 765484e237SJohannes Berg if (bss_mesh_cfg(bss)) 775484e237SJohannes Berg hash_idx = mesh_id_hash(bss_mesh_id(bss), 785484e237SJohannes Berg bss_mesh_id_len(bss)); 795484e237SJohannes Berg else 805484e237SJohannes Berg hash_idx = STA_HASH(bss->bssid); 815484e237SJohannes Berg 825484e237SJohannes Berg bss->hnext = local->sta_bss_hash[hash_idx]; 835484e237SJohannes Berg local->sta_bss_hash[hash_idx] = bss; 845484e237SJohannes Berg } 855484e237SJohannes Berg 865484e237SJohannes Berg /* Caller must hold local->sta_bss_lock */ 875484e237SJohannes Berg static void __ieee80211_rx_bss_hash_del(struct ieee80211_local *local, 885484e237SJohannes Berg struct ieee80211_sta_bss *bss) 895484e237SJohannes Berg { 905484e237SJohannes Berg struct ieee80211_sta_bss *b, *prev = NULL; 915484e237SJohannes Berg b = local->sta_bss_hash[STA_HASH(bss->bssid)]; 925484e237SJohannes Berg while (b) { 935484e237SJohannes Berg if (b == bss) { 945484e237SJohannes Berg if (!prev) 955484e237SJohannes Berg local->sta_bss_hash[STA_HASH(bss->bssid)] = 965484e237SJohannes Berg bss->hnext; 975484e237SJohannes Berg else 985484e237SJohannes Berg prev->hnext = bss->hnext; 995484e237SJohannes Berg break; 1005484e237SJohannes Berg } 1015484e237SJohannes Berg prev = b; 1025484e237SJohannes Berg b = b->hnext; 1035484e237SJohannes Berg } 1045484e237SJohannes Berg } 1055484e237SJohannes Berg 1065484e237SJohannes Berg struct ieee80211_sta_bss * 1075484e237SJohannes Berg ieee80211_rx_bss_add(struct ieee80211_local *local, u8 *bssid, int freq, 1085484e237SJohannes Berg u8 *ssid, u8 ssid_len) 1095484e237SJohannes Berg { 1105484e237SJohannes Berg struct ieee80211_sta_bss *bss; 1115484e237SJohannes Berg 1125484e237SJohannes Berg bss = kzalloc(sizeof(*bss), GFP_ATOMIC); 1135484e237SJohannes Berg if (!bss) 1145484e237SJohannes Berg return NULL; 1155484e237SJohannes Berg atomic_set(&bss->users, 2); 1165484e237SJohannes Berg memcpy(bss->bssid, bssid, ETH_ALEN); 1175484e237SJohannes Berg bss->freq = freq; 1185484e237SJohannes Berg if (ssid && ssid_len <= IEEE80211_MAX_SSID_LEN) { 1195484e237SJohannes Berg memcpy(bss->ssid, ssid, ssid_len); 1205484e237SJohannes Berg bss->ssid_len = ssid_len; 1215484e237SJohannes Berg } 1225484e237SJohannes Berg 1235484e237SJohannes Berg spin_lock_bh(&local->sta_bss_lock); 1245484e237SJohannes Berg /* TODO: order by RSSI? */ 1255484e237SJohannes Berg list_add_tail(&bss->list, &local->sta_bss_list); 1265484e237SJohannes Berg __ieee80211_rx_bss_hash_add(local, bss); 1275484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 1285484e237SJohannes Berg return bss; 1295484e237SJohannes Berg } 1305484e237SJohannes Berg 1315484e237SJohannes Berg #ifdef CONFIG_MAC80211_MESH 1325484e237SJohannes Berg static struct ieee80211_sta_bss * 1335484e237SJohannes Berg ieee80211_rx_mesh_bss_get(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len, 1345484e237SJohannes Berg u8 *mesh_cfg, int freq) 1355484e237SJohannes Berg { 1365484e237SJohannes Berg struct ieee80211_sta_bss *bss; 1375484e237SJohannes Berg 1385484e237SJohannes Berg spin_lock_bh(&local->sta_bss_lock); 1395484e237SJohannes Berg bss = local->sta_bss_hash[mesh_id_hash(mesh_id, mesh_id_len)]; 1405484e237SJohannes Berg while (bss) { 1415484e237SJohannes Berg if (bss_mesh_cfg(bss) && 1425484e237SJohannes Berg !memcmp(bss_mesh_cfg(bss), mesh_cfg, MESH_CFG_CMP_LEN) && 1435484e237SJohannes Berg bss->freq == freq && 1445484e237SJohannes Berg mesh_id_len == bss->mesh_id_len && 1455484e237SJohannes Berg (mesh_id_len == 0 || !memcmp(bss->mesh_id, mesh_id, 1465484e237SJohannes Berg mesh_id_len))) { 1475484e237SJohannes Berg atomic_inc(&bss->users); 1485484e237SJohannes Berg break; 1495484e237SJohannes Berg } 1505484e237SJohannes Berg bss = bss->hnext; 1515484e237SJohannes Berg } 1525484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 1535484e237SJohannes Berg return bss; 1545484e237SJohannes Berg } 1555484e237SJohannes Berg 1565484e237SJohannes Berg static struct ieee80211_sta_bss * 1575484e237SJohannes Berg ieee80211_rx_mesh_bss_add(struct ieee80211_local *local, u8 *mesh_id, int mesh_id_len, 1585484e237SJohannes Berg u8 *mesh_cfg, int mesh_config_len, int freq) 1595484e237SJohannes Berg { 1605484e237SJohannes Berg struct ieee80211_sta_bss *bss; 1615484e237SJohannes Berg 1625484e237SJohannes Berg if (mesh_config_len != MESH_CFG_LEN) 1635484e237SJohannes Berg return NULL; 1645484e237SJohannes Berg 1655484e237SJohannes Berg bss = kzalloc(sizeof(*bss), GFP_ATOMIC); 1665484e237SJohannes Berg if (!bss) 1675484e237SJohannes Berg return NULL; 1685484e237SJohannes Berg 1695484e237SJohannes Berg bss->mesh_cfg = kmalloc(MESH_CFG_CMP_LEN, GFP_ATOMIC); 1705484e237SJohannes Berg if (!bss->mesh_cfg) { 1715484e237SJohannes Berg kfree(bss); 1725484e237SJohannes Berg return NULL; 1735484e237SJohannes Berg } 1745484e237SJohannes Berg 1755484e237SJohannes Berg if (mesh_id_len && mesh_id_len <= IEEE80211_MAX_MESH_ID_LEN) { 1765484e237SJohannes Berg bss->mesh_id = kmalloc(mesh_id_len, GFP_ATOMIC); 1775484e237SJohannes Berg if (!bss->mesh_id) { 1785484e237SJohannes Berg kfree(bss->mesh_cfg); 1795484e237SJohannes Berg kfree(bss); 1805484e237SJohannes Berg return NULL; 1815484e237SJohannes Berg } 1825484e237SJohannes Berg memcpy(bss->mesh_id, mesh_id, mesh_id_len); 1835484e237SJohannes Berg } 1845484e237SJohannes Berg 1855484e237SJohannes Berg atomic_set(&bss->users, 2); 1865484e237SJohannes Berg memcpy(bss->mesh_cfg, mesh_cfg, MESH_CFG_CMP_LEN); 1875484e237SJohannes Berg bss->mesh_id_len = mesh_id_len; 1885484e237SJohannes Berg bss->freq = freq; 1895484e237SJohannes Berg spin_lock_bh(&local->sta_bss_lock); 1905484e237SJohannes Berg /* TODO: order by RSSI? */ 1915484e237SJohannes Berg list_add_tail(&bss->list, &local->sta_bss_list); 1925484e237SJohannes Berg __ieee80211_rx_bss_hash_add(local, bss); 1935484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 1945484e237SJohannes Berg return bss; 1955484e237SJohannes Berg } 1965484e237SJohannes Berg #endif 1975484e237SJohannes Berg 1985484e237SJohannes Berg static void ieee80211_rx_bss_free(struct ieee80211_sta_bss *bss) 1995484e237SJohannes Berg { 2005484e237SJohannes Berg kfree(bss->ies); 2015484e237SJohannes Berg kfree(bss_mesh_id(bss)); 2025484e237SJohannes Berg kfree(bss_mesh_cfg(bss)); 2035484e237SJohannes Berg kfree(bss); 2045484e237SJohannes Berg } 2055484e237SJohannes Berg 2065484e237SJohannes Berg void ieee80211_rx_bss_put(struct ieee80211_local *local, 2075484e237SJohannes Berg struct ieee80211_sta_bss *bss) 2085484e237SJohannes Berg { 2095484e237SJohannes Berg local_bh_disable(); 2105484e237SJohannes Berg if (!atomic_dec_and_lock(&bss->users, &local->sta_bss_lock)) { 2115484e237SJohannes Berg local_bh_enable(); 2125484e237SJohannes Berg return; 2135484e237SJohannes Berg } 2145484e237SJohannes Berg 2155484e237SJohannes Berg __ieee80211_rx_bss_hash_del(local, bss); 2165484e237SJohannes Berg list_del(&bss->list); 2175484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 2185484e237SJohannes Berg ieee80211_rx_bss_free(bss); 2195484e237SJohannes Berg } 2205484e237SJohannes Berg 2215484e237SJohannes Berg struct ieee80211_sta_bss * 2225484e237SJohannes Berg ieee80211_bss_info_update(struct ieee80211_local *local, 2235484e237SJohannes Berg struct ieee80211_rx_status *rx_status, 2245484e237SJohannes Berg struct ieee80211_mgmt *mgmt, 2255484e237SJohannes Berg size_t len, 2265484e237SJohannes Berg struct ieee802_11_elems *elems, 2275484e237SJohannes Berg int freq, bool beacon) 2285484e237SJohannes Berg { 2295484e237SJohannes Berg struct ieee80211_sta_bss *bss; 2305484e237SJohannes Berg int clen; 2315484e237SJohannes Berg 2325484e237SJohannes Berg #ifdef CONFIG_MAC80211_MESH 2335484e237SJohannes Berg if (elems->mesh_config) 2345484e237SJohannes Berg bss = ieee80211_rx_mesh_bss_get(local, elems->mesh_id, 2355484e237SJohannes Berg elems->mesh_id_len, elems->mesh_config, freq); 2365484e237SJohannes Berg else 2375484e237SJohannes Berg #endif 2385484e237SJohannes Berg bss = ieee80211_rx_bss_get(local, mgmt->bssid, freq, 2395484e237SJohannes Berg elems->ssid, elems->ssid_len); 2405484e237SJohannes Berg if (!bss) { 2415484e237SJohannes Berg #ifdef CONFIG_MAC80211_MESH 2425484e237SJohannes Berg if (elems->mesh_config) 2435484e237SJohannes Berg bss = ieee80211_rx_mesh_bss_add(local, elems->mesh_id, 2445484e237SJohannes Berg elems->mesh_id_len, elems->mesh_config, 2455484e237SJohannes Berg elems->mesh_config_len, freq); 2465484e237SJohannes Berg else 2475484e237SJohannes Berg #endif 2485484e237SJohannes Berg bss = ieee80211_rx_bss_add(local, mgmt->bssid, freq, 2495484e237SJohannes Berg elems->ssid, elems->ssid_len); 2505484e237SJohannes Berg if (!bss) 2515484e237SJohannes Berg return NULL; 2525484e237SJohannes Berg } else { 2535484e237SJohannes Berg #if 0 2545484e237SJohannes Berg /* TODO: order by RSSI? */ 2555484e237SJohannes Berg spin_lock_bh(&local->sta_bss_lock); 2565484e237SJohannes Berg list_move_tail(&bss->list, &local->sta_bss_list); 2575484e237SJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 2585484e237SJohannes Berg #endif 2595484e237SJohannes Berg } 2605484e237SJohannes Berg 2615484e237SJohannes Berg /* save the ERP value so that it is available at association time */ 2625484e237SJohannes Berg if (elems->erp_info && elems->erp_info_len >= 1) { 2635484e237SJohannes Berg bss->erp_value = elems->erp_info[0]; 2645484e237SJohannes Berg bss->has_erp_value = 1; 2655484e237SJohannes Berg } 2665484e237SJohannes Berg 2675484e237SJohannes Berg bss->beacon_int = le16_to_cpu(mgmt->u.beacon.beacon_int); 2685484e237SJohannes Berg bss->capability = le16_to_cpu(mgmt->u.beacon.capab_info); 2695484e237SJohannes Berg 2705484e237SJohannes Berg if (elems->tim) { 2715484e237SJohannes Berg struct ieee80211_tim_ie *tim_ie = 2725484e237SJohannes Berg (struct ieee80211_tim_ie *)elems->tim; 2735484e237SJohannes Berg bss->dtim_period = tim_ie->dtim_period; 2745484e237SJohannes Berg } 2755484e237SJohannes Berg 2765484e237SJohannes Berg /* set default value for buggy APs */ 2775484e237SJohannes Berg if (!elems->tim || bss->dtim_period == 0) 2785484e237SJohannes Berg bss->dtim_period = 1; 2795484e237SJohannes Berg 2805484e237SJohannes Berg bss->supp_rates_len = 0; 2815484e237SJohannes Berg if (elems->supp_rates) { 2825484e237SJohannes Berg clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; 2835484e237SJohannes Berg if (clen > elems->supp_rates_len) 2845484e237SJohannes Berg clen = elems->supp_rates_len; 2855484e237SJohannes Berg memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates, 2865484e237SJohannes Berg clen); 2875484e237SJohannes Berg bss->supp_rates_len += clen; 2885484e237SJohannes Berg } 2895484e237SJohannes Berg if (elems->ext_supp_rates) { 2905484e237SJohannes Berg clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len; 2915484e237SJohannes Berg if (clen > elems->ext_supp_rates_len) 2925484e237SJohannes Berg clen = elems->ext_supp_rates_len; 2935484e237SJohannes Berg memcpy(&bss->supp_rates[bss->supp_rates_len], 2945484e237SJohannes Berg elems->ext_supp_rates, clen); 2955484e237SJohannes Berg bss->supp_rates_len += clen; 2965484e237SJohannes Berg } 2975484e237SJohannes Berg 2985484e237SJohannes Berg bss->band = rx_status->band; 2995484e237SJohannes Berg 3005484e237SJohannes Berg bss->timestamp = le64_to_cpu(mgmt->u.beacon.timestamp); 3015484e237SJohannes Berg bss->last_update = jiffies; 3025484e237SJohannes Berg bss->signal = rx_status->signal; 3035484e237SJohannes Berg bss->noise = rx_status->noise; 3045484e237SJohannes Berg bss->qual = rx_status->qual; 3055484e237SJohannes Berg bss->wmm_used = elems->wmm_param || elems->wmm_info; 3065484e237SJohannes Berg 3075484e237SJohannes Berg if (!beacon) 3085484e237SJohannes Berg bss->last_probe_resp = jiffies; 3095484e237SJohannes Berg 3105484e237SJohannes Berg /* 3115484e237SJohannes Berg * For probe responses, or if we don't have any information yet, 3125484e237SJohannes Berg * use the IEs from the beacon. 3135484e237SJohannes Berg */ 3145484e237SJohannes Berg if (!bss->ies || !beacon) { 3155484e237SJohannes Berg if (bss->ies == NULL || bss->ies_len < elems->total_len) { 3165484e237SJohannes Berg kfree(bss->ies); 3175484e237SJohannes Berg bss->ies = kmalloc(elems->total_len, GFP_ATOMIC); 3185484e237SJohannes Berg } 3195484e237SJohannes Berg if (bss->ies) { 3205484e237SJohannes Berg memcpy(bss->ies, elems->ie_start, elems->total_len); 3215484e237SJohannes Berg bss->ies_len = elems->total_len; 3225484e237SJohannes Berg } else 3235484e237SJohannes Berg bss->ies_len = 0; 3245484e237SJohannes Berg } 3255484e237SJohannes Berg 3265484e237SJohannes Berg return bss; 3275484e237SJohannes Berg } 3280a51b27eSJohannes Berg 32998c8fccfSJohannes Berg ieee80211_rx_result 33098c8fccfSJohannes Berg ieee80211_sta_rx_scan(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, 33198c8fccfSJohannes Berg struct ieee80211_rx_status *rx_status) 33298c8fccfSJohannes Berg { 33398c8fccfSJohannes Berg struct ieee80211_mgmt *mgmt; 33498c8fccfSJohannes Berg struct ieee80211_sta_bss *bss; 33598c8fccfSJohannes Berg u8 *elements; 33698c8fccfSJohannes Berg struct ieee80211_channel *channel; 33798c8fccfSJohannes Berg size_t baselen; 33898c8fccfSJohannes Berg int freq; 33998c8fccfSJohannes Berg __le16 fc; 34098c8fccfSJohannes Berg bool presp, beacon = false; 34198c8fccfSJohannes Berg struct ieee802_11_elems elems; 34298c8fccfSJohannes Berg 34398c8fccfSJohannes Berg if (skb->len < 2) 34498c8fccfSJohannes Berg return RX_DROP_UNUSABLE; 34598c8fccfSJohannes Berg 34698c8fccfSJohannes Berg mgmt = (struct ieee80211_mgmt *) skb->data; 34798c8fccfSJohannes Berg fc = mgmt->frame_control; 34898c8fccfSJohannes Berg 34998c8fccfSJohannes Berg if (ieee80211_is_ctl(fc)) 35098c8fccfSJohannes Berg return RX_CONTINUE; 35198c8fccfSJohannes Berg 35298c8fccfSJohannes Berg if (skb->len < 24) 35398c8fccfSJohannes Berg return RX_DROP_MONITOR; 35498c8fccfSJohannes Berg 35598c8fccfSJohannes Berg presp = ieee80211_is_probe_resp(fc); 35698c8fccfSJohannes Berg if (presp) { 35798c8fccfSJohannes Berg /* ignore ProbeResp to foreign address */ 35898c8fccfSJohannes Berg if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN)) 35998c8fccfSJohannes Berg return RX_DROP_MONITOR; 36098c8fccfSJohannes Berg 36198c8fccfSJohannes Berg presp = true; 36298c8fccfSJohannes Berg elements = mgmt->u.probe_resp.variable; 36398c8fccfSJohannes Berg baselen = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); 36498c8fccfSJohannes Berg } else { 36598c8fccfSJohannes Berg beacon = ieee80211_is_beacon(fc); 36698c8fccfSJohannes Berg baselen = offsetof(struct ieee80211_mgmt, u.beacon.variable); 36798c8fccfSJohannes Berg elements = mgmt->u.beacon.variable; 36898c8fccfSJohannes Berg } 36998c8fccfSJohannes Berg 37098c8fccfSJohannes Berg if (!presp && !beacon) 37198c8fccfSJohannes Berg return RX_CONTINUE; 37298c8fccfSJohannes Berg 37398c8fccfSJohannes Berg if (baselen > skb->len) 37498c8fccfSJohannes Berg return RX_DROP_MONITOR; 37598c8fccfSJohannes Berg 37698c8fccfSJohannes Berg ieee802_11_parse_elems(elements, skb->len - baselen, &elems); 37798c8fccfSJohannes Berg 37898c8fccfSJohannes Berg if (elems.ds_params && elems.ds_params_len == 1) 37998c8fccfSJohannes Berg freq = ieee80211_channel_to_frequency(elems.ds_params[0]); 38098c8fccfSJohannes Berg else 38198c8fccfSJohannes Berg freq = rx_status->freq; 38298c8fccfSJohannes Berg 38398c8fccfSJohannes Berg channel = ieee80211_get_channel(sdata->local->hw.wiphy, freq); 38498c8fccfSJohannes Berg 38598c8fccfSJohannes Berg if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) 38698c8fccfSJohannes Berg return RX_DROP_MONITOR; 38798c8fccfSJohannes Berg 38898c8fccfSJohannes Berg bss = ieee80211_bss_info_update(sdata->local, rx_status, 38998c8fccfSJohannes Berg mgmt, skb->len, &elems, 39098c8fccfSJohannes Berg freq, beacon); 39198c8fccfSJohannes Berg ieee80211_rx_bss_put(sdata->local, bss); 39298c8fccfSJohannes Berg 39398c8fccfSJohannes Berg dev_kfree_skb(skb); 39498c8fccfSJohannes Berg return RX_QUEUED; 39598c8fccfSJohannes Berg } 39698c8fccfSJohannes Berg 3970a51b27eSJohannes Berg static void ieee80211_send_nullfunc(struct ieee80211_local *local, 3980a51b27eSJohannes Berg struct ieee80211_sub_if_data *sdata, 3990a51b27eSJohannes Berg int powersave) 4000a51b27eSJohannes Berg { 4010a51b27eSJohannes Berg struct sk_buff *skb; 4020a51b27eSJohannes Berg struct ieee80211_hdr *nullfunc; 4030a51b27eSJohannes Berg __le16 fc; 4040a51b27eSJohannes Berg 4050a51b27eSJohannes Berg skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24); 4060a51b27eSJohannes Berg if (!skb) { 4070a51b27eSJohannes Berg printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " 4080a51b27eSJohannes Berg "frame\n", sdata->dev->name); 4090a51b27eSJohannes Berg return; 4100a51b27eSJohannes Berg } 4110a51b27eSJohannes Berg skb_reserve(skb, local->hw.extra_tx_headroom); 4120a51b27eSJohannes Berg 4130a51b27eSJohannes Berg nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24); 4140a51b27eSJohannes Berg memset(nullfunc, 0, 24); 4150a51b27eSJohannes Berg fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | 4160a51b27eSJohannes Berg IEEE80211_FCTL_TODS); 4170a51b27eSJohannes Berg if (powersave) 4180a51b27eSJohannes Berg fc |= cpu_to_le16(IEEE80211_FCTL_PM); 4190a51b27eSJohannes Berg nullfunc->frame_control = fc; 4200a51b27eSJohannes Berg memcpy(nullfunc->addr1, sdata->u.sta.bssid, ETH_ALEN); 4210a51b27eSJohannes Berg memcpy(nullfunc->addr2, sdata->dev->dev_addr, ETH_ALEN); 4220a51b27eSJohannes Berg memcpy(nullfunc->addr3, sdata->u.sta.bssid, ETH_ALEN); 4230a51b27eSJohannes Berg 424*e50db65cSJohannes Berg ieee80211_tx_skb(sdata, skb, 0); 4250a51b27eSJohannes Berg } 4260a51b27eSJohannes Berg 4270a51b27eSJohannes Berg static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata) 4280a51b27eSJohannes Berg { 4290a51b27eSJohannes Berg if (sdata->vif.type == IEEE80211_IF_TYPE_STA || 4300a51b27eSJohannes Berg ieee80211_vif_is_mesh(&sdata->vif)) 4310a51b27eSJohannes Berg ieee80211_sta_timer((unsigned long)sdata); 4320a51b27eSJohannes Berg } 4330a51b27eSJohannes Berg 4340a51b27eSJohannes Berg void ieee80211_scan_completed(struct ieee80211_hw *hw) 4350a51b27eSJohannes Berg { 4360a51b27eSJohannes Berg struct ieee80211_local *local = hw_to_local(hw); 4370a51b27eSJohannes Berg struct ieee80211_sub_if_data *sdata; 4380a51b27eSJohannes Berg union iwreq_data wrqu; 4390a51b27eSJohannes Berg 4400a51b27eSJohannes Berg local->last_scan_completed = jiffies; 4410a51b27eSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 4420a51b27eSJohannes Berg wireless_send_event(local->scan_sdata->dev, SIOCGIWSCAN, &wrqu, NULL); 4430a51b27eSJohannes Berg 4440a51b27eSJohannes Berg if (local->sta_hw_scanning) { 4450a51b27eSJohannes Berg local->sta_hw_scanning = 0; 4460a51b27eSJohannes Berg if (ieee80211_hw_config(local)) 4470a51b27eSJohannes Berg printk(KERN_DEBUG "%s: failed to restore operational " 4480a51b27eSJohannes Berg "channel after scan\n", wiphy_name(local->hw.wiphy)); 4490a51b27eSJohannes Berg /* Restart STA timer for HW scan case */ 4500a51b27eSJohannes Berg rcu_read_lock(); 4510a51b27eSJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) 4520a51b27eSJohannes Berg ieee80211_restart_sta_timer(sdata); 4530a51b27eSJohannes Berg rcu_read_unlock(); 4540a51b27eSJohannes Berg 4550a51b27eSJohannes Berg goto done; 4560a51b27eSJohannes Berg } 4570a51b27eSJohannes Berg 4580a51b27eSJohannes Berg local->sta_sw_scanning = 0; 4590a51b27eSJohannes Berg if (ieee80211_hw_config(local)) 4600a51b27eSJohannes Berg printk(KERN_DEBUG "%s: failed to restore operational " 4610a51b27eSJohannes Berg "channel after scan\n", wiphy_name(local->hw.wiphy)); 4620a51b27eSJohannes Berg 4630a51b27eSJohannes Berg 4640a51b27eSJohannes Berg netif_tx_lock_bh(local->mdev); 4650a51b27eSJohannes Berg netif_addr_lock(local->mdev); 4660a51b27eSJohannes Berg local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC; 4670a51b27eSJohannes Berg local->ops->configure_filter(local_to_hw(local), 4680a51b27eSJohannes Berg FIF_BCN_PRBRESP_PROMISC, 4690a51b27eSJohannes Berg &local->filter_flags, 4700a51b27eSJohannes Berg local->mdev->mc_count, 4710a51b27eSJohannes Berg local->mdev->mc_list); 4720a51b27eSJohannes Berg 4730a51b27eSJohannes Berg netif_addr_unlock(local->mdev); 4740a51b27eSJohannes Berg netif_tx_unlock_bh(local->mdev); 4750a51b27eSJohannes Berg 4760a51b27eSJohannes Berg rcu_read_lock(); 4770a51b27eSJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 4780a51b27eSJohannes Berg /* Tell AP we're back */ 4790a51b27eSJohannes Berg if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { 4800a51b27eSJohannes Berg if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { 4810a51b27eSJohannes Berg ieee80211_send_nullfunc(local, sdata, 0); 4820a51b27eSJohannes Berg netif_tx_wake_all_queues(sdata->dev); 4830a51b27eSJohannes Berg } 4840a51b27eSJohannes Berg } else 4850a51b27eSJohannes Berg netif_tx_wake_all_queues(sdata->dev); 4860a51b27eSJohannes Berg 4870a51b27eSJohannes Berg ieee80211_restart_sta_timer(sdata); 4880a51b27eSJohannes Berg } 4890a51b27eSJohannes Berg rcu_read_unlock(); 4900a51b27eSJohannes Berg 4910a51b27eSJohannes Berg done: 4920a51b27eSJohannes Berg ieee80211_mlme_notify_scan_completed(local); 4930a51b27eSJohannes Berg } 4940a51b27eSJohannes Berg EXPORT_SYMBOL(ieee80211_scan_completed); 4950a51b27eSJohannes Berg 4960a51b27eSJohannes Berg 4970a51b27eSJohannes Berg void ieee80211_sta_scan_work(struct work_struct *work) 4980a51b27eSJohannes Berg { 4990a51b27eSJohannes Berg struct ieee80211_local *local = 5000a51b27eSJohannes Berg container_of(work, struct ieee80211_local, scan_work.work); 5010a51b27eSJohannes Berg struct ieee80211_sub_if_data *sdata = local->scan_sdata; 5020a51b27eSJohannes Berg struct ieee80211_supported_band *sband; 5030a51b27eSJohannes Berg struct ieee80211_channel *chan; 5040a51b27eSJohannes Berg int skip; 5050a51b27eSJohannes Berg unsigned long next_delay = 0; 5060a51b27eSJohannes Berg 5070a51b27eSJohannes Berg if (!local->sta_sw_scanning) 5080a51b27eSJohannes Berg return; 5090a51b27eSJohannes Berg 5100a51b27eSJohannes Berg switch (local->scan_state) { 5110a51b27eSJohannes Berg case SCAN_SET_CHANNEL: 5120a51b27eSJohannes Berg /* 5130a51b27eSJohannes Berg * Get current scan band. scan_band may be IEEE80211_NUM_BANDS 5140a51b27eSJohannes Berg * after we successfully scanned the last channel of the last 5150a51b27eSJohannes Berg * band (and the last band is supported by the hw) 5160a51b27eSJohannes Berg */ 5170a51b27eSJohannes Berg if (local->scan_band < IEEE80211_NUM_BANDS) 5180a51b27eSJohannes Berg sband = local->hw.wiphy->bands[local->scan_band]; 5190a51b27eSJohannes Berg else 5200a51b27eSJohannes Berg sband = NULL; 5210a51b27eSJohannes Berg 5220a51b27eSJohannes Berg /* 5230a51b27eSJohannes Berg * If we are at an unsupported band and have more bands 5240a51b27eSJohannes Berg * left to scan, advance to the next supported one. 5250a51b27eSJohannes Berg */ 5260a51b27eSJohannes Berg while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) { 5270a51b27eSJohannes Berg local->scan_band++; 5280a51b27eSJohannes Berg sband = local->hw.wiphy->bands[local->scan_band]; 5290a51b27eSJohannes Berg local->scan_channel_idx = 0; 5300a51b27eSJohannes Berg } 5310a51b27eSJohannes Berg 5320a51b27eSJohannes Berg /* if no more bands/channels left, complete scan */ 5330a51b27eSJohannes Berg if (!sband || local->scan_channel_idx >= sband->n_channels) { 5340a51b27eSJohannes Berg ieee80211_scan_completed(local_to_hw(local)); 5350a51b27eSJohannes Berg return; 5360a51b27eSJohannes Berg } 5370a51b27eSJohannes Berg skip = 0; 5380a51b27eSJohannes Berg chan = &sband->channels[local->scan_channel_idx]; 5390a51b27eSJohannes Berg 5400a51b27eSJohannes Berg if (chan->flags & IEEE80211_CHAN_DISABLED || 5410a51b27eSJohannes Berg (sdata->vif.type == IEEE80211_IF_TYPE_IBSS && 5420a51b27eSJohannes Berg chan->flags & IEEE80211_CHAN_NO_IBSS)) 5430a51b27eSJohannes Berg skip = 1; 5440a51b27eSJohannes Berg 5450a51b27eSJohannes Berg if (!skip) { 5460a51b27eSJohannes Berg local->scan_channel = chan; 5470a51b27eSJohannes Berg if (ieee80211_hw_config(local)) { 5480a51b27eSJohannes Berg printk(KERN_DEBUG "%s: failed to set freq to " 5490a51b27eSJohannes Berg "%d MHz for scan\n", wiphy_name(local->hw.wiphy), 5500a51b27eSJohannes Berg chan->center_freq); 5510a51b27eSJohannes Berg skip = 1; 5520a51b27eSJohannes Berg } 5530a51b27eSJohannes Berg } 5540a51b27eSJohannes Berg 5550a51b27eSJohannes Berg /* advance state machine to next channel/band */ 5560a51b27eSJohannes Berg local->scan_channel_idx++; 5570a51b27eSJohannes Berg if (local->scan_channel_idx >= sband->n_channels) { 5580a51b27eSJohannes Berg /* 5590a51b27eSJohannes Berg * scan_band may end up == IEEE80211_NUM_BANDS, but 5600a51b27eSJohannes Berg * we'll catch that case above and complete the scan 5610a51b27eSJohannes Berg * if that is the case. 5620a51b27eSJohannes Berg */ 5630a51b27eSJohannes Berg local->scan_band++; 5640a51b27eSJohannes Berg local->scan_channel_idx = 0; 5650a51b27eSJohannes Berg } 5660a51b27eSJohannes Berg 5670a51b27eSJohannes Berg if (skip) 5680a51b27eSJohannes Berg break; 5690a51b27eSJohannes Berg 5700a51b27eSJohannes Berg next_delay = IEEE80211_PROBE_DELAY + 5710a51b27eSJohannes Berg usecs_to_jiffies(local->hw.channel_change_time); 5720a51b27eSJohannes Berg local->scan_state = SCAN_SEND_PROBE; 5730a51b27eSJohannes Berg break; 5740a51b27eSJohannes Berg case SCAN_SEND_PROBE: 5750a51b27eSJohannes Berg next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; 5760a51b27eSJohannes Berg local->scan_state = SCAN_SET_CHANNEL; 5770a51b27eSJohannes Berg 5780a51b27eSJohannes Berg if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) 5790a51b27eSJohannes Berg break; 5800a51b27eSJohannes Berg ieee80211_send_probe_req(sdata, NULL, local->scan_ssid, 5810a51b27eSJohannes Berg local->scan_ssid_len); 5820a51b27eSJohannes Berg next_delay = IEEE80211_CHANNEL_TIME; 5830a51b27eSJohannes Berg break; 5840a51b27eSJohannes Berg } 5850a51b27eSJohannes Berg 5860a51b27eSJohannes Berg if (local->sta_sw_scanning) 5870a51b27eSJohannes Berg queue_delayed_work(local->hw.workqueue, &local->scan_work, 5880a51b27eSJohannes Berg next_delay); 5890a51b27eSJohannes Berg } 5900a51b27eSJohannes Berg 5910a51b27eSJohannes Berg 5920a51b27eSJohannes Berg int ieee80211_sta_start_scan(struct ieee80211_sub_if_data *scan_sdata, 5930a51b27eSJohannes Berg u8 *ssid, size_t ssid_len) 5940a51b27eSJohannes Berg { 5950a51b27eSJohannes Berg struct ieee80211_local *local = scan_sdata->local; 5960a51b27eSJohannes Berg struct ieee80211_sub_if_data *sdata; 5970a51b27eSJohannes Berg 5980a51b27eSJohannes Berg if (ssid_len > IEEE80211_MAX_SSID_LEN) 5990a51b27eSJohannes Berg return -EINVAL; 6000a51b27eSJohannes Berg 6010a51b27eSJohannes Berg /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) 6020a51b27eSJohannes Berg * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS 6030a51b27eSJohannes Berg * BSSID: MACAddress 6040a51b27eSJohannes Berg * SSID 6050a51b27eSJohannes Berg * ScanType: ACTIVE, PASSIVE 6060a51b27eSJohannes Berg * ProbeDelay: delay (in microseconds) to be used prior to transmitting 6070a51b27eSJohannes Berg * a Probe frame during active scanning 6080a51b27eSJohannes Berg * ChannelList 6090a51b27eSJohannes Berg * MinChannelTime (>= ProbeDelay), in TU 6100a51b27eSJohannes Berg * MaxChannelTime: (>= MinChannelTime), in TU 6110a51b27eSJohannes Berg */ 6120a51b27eSJohannes Berg 6130a51b27eSJohannes Berg /* MLME-SCAN.confirm 6140a51b27eSJohannes Berg * BSSDescriptionSet 6150a51b27eSJohannes Berg * ResultCode: SUCCESS, INVALID_PARAMETERS 6160a51b27eSJohannes Berg */ 6170a51b27eSJohannes Berg 6180a51b27eSJohannes Berg if (local->sta_sw_scanning || local->sta_hw_scanning) { 6190a51b27eSJohannes Berg if (local->scan_sdata == scan_sdata) 6200a51b27eSJohannes Berg return 0; 6210a51b27eSJohannes Berg return -EBUSY; 6220a51b27eSJohannes Berg } 6230a51b27eSJohannes Berg 6240a51b27eSJohannes Berg if (local->ops->hw_scan) { 6250a51b27eSJohannes Berg int rc = local->ops->hw_scan(local_to_hw(local), 6260a51b27eSJohannes Berg ssid, ssid_len); 6270a51b27eSJohannes Berg if (!rc) { 6280a51b27eSJohannes Berg local->sta_hw_scanning = 1; 6290a51b27eSJohannes Berg local->scan_sdata = scan_sdata; 6300a51b27eSJohannes Berg } 6310a51b27eSJohannes Berg return rc; 6320a51b27eSJohannes Berg } 6330a51b27eSJohannes Berg 6340a51b27eSJohannes Berg local->sta_sw_scanning = 1; 6350a51b27eSJohannes Berg 6360a51b27eSJohannes Berg rcu_read_lock(); 6370a51b27eSJohannes Berg list_for_each_entry_rcu(sdata, &local->interfaces, list) { 6380a51b27eSJohannes Berg if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { 6390a51b27eSJohannes Berg if (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED) { 6400a51b27eSJohannes Berg netif_tx_stop_all_queues(sdata->dev); 6410a51b27eSJohannes Berg ieee80211_send_nullfunc(local, sdata, 1); 6420a51b27eSJohannes Berg } 6430a51b27eSJohannes Berg } else 6440a51b27eSJohannes Berg netif_tx_stop_all_queues(sdata->dev); 6450a51b27eSJohannes Berg } 6460a51b27eSJohannes Berg rcu_read_unlock(); 6470a51b27eSJohannes Berg 6480a51b27eSJohannes Berg if (ssid) { 6490a51b27eSJohannes Berg local->scan_ssid_len = ssid_len; 6500a51b27eSJohannes Berg memcpy(local->scan_ssid, ssid, ssid_len); 6510a51b27eSJohannes Berg } else 6520a51b27eSJohannes Berg local->scan_ssid_len = 0; 6530a51b27eSJohannes Berg local->scan_state = SCAN_SET_CHANNEL; 6540a51b27eSJohannes Berg local->scan_channel_idx = 0; 6550a51b27eSJohannes Berg local->scan_band = IEEE80211_BAND_2GHZ; 6560a51b27eSJohannes Berg local->scan_sdata = scan_sdata; 6570a51b27eSJohannes Berg 6580a51b27eSJohannes Berg netif_addr_lock_bh(local->mdev); 6590a51b27eSJohannes Berg local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; 6600a51b27eSJohannes Berg local->ops->configure_filter(local_to_hw(local), 6610a51b27eSJohannes Berg FIF_BCN_PRBRESP_PROMISC, 6620a51b27eSJohannes Berg &local->filter_flags, 6630a51b27eSJohannes Berg local->mdev->mc_count, 6640a51b27eSJohannes Berg local->mdev->mc_list); 6650a51b27eSJohannes Berg netif_addr_unlock_bh(local->mdev); 6660a51b27eSJohannes Berg 6670a51b27eSJohannes Berg /* TODO: start scan as soon as all nullfunc frames are ACKed */ 6680a51b27eSJohannes Berg queue_delayed_work(local->hw.workqueue, &local->scan_work, 6690a51b27eSJohannes Berg IEEE80211_CHANNEL_TIME); 6700a51b27eSJohannes Berg 6710a51b27eSJohannes Berg return 0; 6720a51b27eSJohannes Berg } 6730a51b27eSJohannes Berg 6740a51b27eSJohannes Berg 6750a51b27eSJohannes Berg int ieee80211_sta_req_scan(struct ieee80211_sub_if_data *sdata, u8 *ssid, size_t ssid_len) 6760a51b27eSJohannes Berg { 6770a51b27eSJohannes Berg struct ieee80211_local *local = sdata->local; 6789116dd01SJohannes Berg struct ieee80211_if_sta *ifsta; 6790a51b27eSJohannes Berg 6800a51b27eSJohannes Berg if (sdata->vif.type != IEEE80211_IF_TYPE_STA) 6810a51b27eSJohannes Berg return ieee80211_sta_start_scan(sdata, ssid, ssid_len); 6820a51b27eSJohannes Berg 6839116dd01SJohannes Berg /* 6849116dd01SJohannes Berg * STA has a state machine that might need to defer scanning 6859116dd01SJohannes Berg * while it's trying to associate/authenticate, therefore we 6869116dd01SJohannes Berg * queue it up to the state machine in that case. 6879116dd01SJohannes Berg */ 6889116dd01SJohannes Berg 6890a51b27eSJohannes Berg if (local->sta_sw_scanning || local->sta_hw_scanning) { 6900a51b27eSJohannes Berg if (local->scan_sdata == sdata) 6910a51b27eSJohannes Berg return 0; 6920a51b27eSJohannes Berg return -EBUSY; 6930a51b27eSJohannes Berg } 6940a51b27eSJohannes Berg 6959116dd01SJohannes Berg ifsta = &sdata->u.sta; 6969116dd01SJohannes Berg 6970a51b27eSJohannes Berg ifsta->scan_ssid_len = ssid_len; 6980a51b27eSJohannes Berg if (ssid_len) 6990a51b27eSJohannes Berg memcpy(ifsta->scan_ssid, ssid, ssid_len); 7000a51b27eSJohannes Berg set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); 7010a51b27eSJohannes Berg queue_work(local->hw.workqueue, &ifsta->work); 7029116dd01SJohannes Berg 7030a51b27eSJohannes Berg return 0; 7040a51b27eSJohannes Berg } 7050a51b27eSJohannes Berg 7060a51b27eSJohannes Berg 7070a51b27eSJohannes Berg static void ieee80211_sta_add_scan_ies(struct iw_request_info *info, 7080a51b27eSJohannes Berg struct ieee80211_sta_bss *bss, 7090a51b27eSJohannes Berg char **current_ev, char *end_buf) 7100a51b27eSJohannes Berg { 7110a51b27eSJohannes Berg u8 *pos, *end, *next; 7120a51b27eSJohannes Berg struct iw_event iwe; 7130a51b27eSJohannes Berg 7140a51b27eSJohannes Berg if (bss == NULL || bss->ies == NULL) 7150a51b27eSJohannes Berg return; 7160a51b27eSJohannes Berg 7170a51b27eSJohannes Berg /* 7180a51b27eSJohannes Berg * If needed, fragment the IEs buffer (at IE boundaries) into short 7190a51b27eSJohannes Berg * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. 7200a51b27eSJohannes Berg */ 7210a51b27eSJohannes Berg pos = bss->ies; 7220a51b27eSJohannes Berg end = pos + bss->ies_len; 7230a51b27eSJohannes Berg 7240a51b27eSJohannes Berg while (end - pos > IW_GENERIC_IE_MAX) { 7250a51b27eSJohannes Berg next = pos + 2 + pos[1]; 7260a51b27eSJohannes Berg while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) 7270a51b27eSJohannes Berg next = next + 2 + next[1]; 7280a51b27eSJohannes Berg 7290a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7300a51b27eSJohannes Berg iwe.cmd = IWEVGENIE; 7310a51b27eSJohannes Berg iwe.u.data.length = next - pos; 7320a51b27eSJohannes Berg *current_ev = iwe_stream_add_point(info, *current_ev, 7330a51b27eSJohannes Berg end_buf, &iwe, pos); 7340a51b27eSJohannes Berg 7350a51b27eSJohannes Berg pos = next; 7360a51b27eSJohannes Berg } 7370a51b27eSJohannes Berg 7380a51b27eSJohannes Berg if (end > pos) { 7390a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7400a51b27eSJohannes Berg iwe.cmd = IWEVGENIE; 7410a51b27eSJohannes Berg iwe.u.data.length = end - pos; 7420a51b27eSJohannes Berg *current_ev = iwe_stream_add_point(info, *current_ev, 7430a51b27eSJohannes Berg end_buf, &iwe, pos); 7440a51b27eSJohannes Berg } 7450a51b27eSJohannes Berg } 7460a51b27eSJohannes Berg 7470a51b27eSJohannes Berg 7480a51b27eSJohannes Berg static char * 7490a51b27eSJohannes Berg ieee80211_sta_scan_result(struct ieee80211_local *local, 7500a51b27eSJohannes Berg struct iw_request_info *info, 7510a51b27eSJohannes Berg struct ieee80211_sta_bss *bss, 7520a51b27eSJohannes Berg char *current_ev, char *end_buf) 7530a51b27eSJohannes Berg { 7540a51b27eSJohannes Berg struct iw_event iwe; 7550a51b27eSJohannes Berg char *buf; 7560a51b27eSJohannes Berg 7570a51b27eSJohannes Berg if (time_after(jiffies, 7580a51b27eSJohannes Berg bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) 7590a51b27eSJohannes Berg return current_ev; 7600a51b27eSJohannes Berg 7610a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7620a51b27eSJohannes Berg iwe.cmd = SIOCGIWAP; 7630a51b27eSJohannes Berg iwe.u.ap_addr.sa_family = ARPHRD_ETHER; 7640a51b27eSJohannes Berg memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); 7650a51b27eSJohannes Berg current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, 7660a51b27eSJohannes Berg IW_EV_ADDR_LEN); 7670a51b27eSJohannes Berg 7680a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7690a51b27eSJohannes Berg iwe.cmd = SIOCGIWESSID; 7700a51b27eSJohannes Berg if (bss_mesh_cfg(bss)) { 7710a51b27eSJohannes Berg iwe.u.data.length = bss_mesh_id_len(bss); 7720a51b27eSJohannes Berg iwe.u.data.flags = 1; 7730a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, end_buf, 7740a51b27eSJohannes Berg &iwe, bss_mesh_id(bss)); 7750a51b27eSJohannes Berg } else { 7760a51b27eSJohannes Berg iwe.u.data.length = bss->ssid_len; 7770a51b27eSJohannes Berg iwe.u.data.flags = 1; 7780a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, end_buf, 7790a51b27eSJohannes Berg &iwe, bss->ssid); 7800a51b27eSJohannes Berg } 7810a51b27eSJohannes Berg 7820a51b27eSJohannes Berg if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) 7830a51b27eSJohannes Berg || bss_mesh_cfg(bss)) { 7840a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7850a51b27eSJohannes Berg iwe.cmd = SIOCGIWMODE; 7860a51b27eSJohannes Berg if (bss_mesh_cfg(bss)) 7870a51b27eSJohannes Berg iwe.u.mode = IW_MODE_MESH; 7880a51b27eSJohannes Berg else if (bss->capability & WLAN_CAPABILITY_ESS) 7890a51b27eSJohannes Berg iwe.u.mode = IW_MODE_MASTER; 7900a51b27eSJohannes Berg else 7910a51b27eSJohannes Berg iwe.u.mode = IW_MODE_ADHOC; 7920a51b27eSJohannes Berg current_ev = iwe_stream_add_event(info, current_ev, end_buf, 7930a51b27eSJohannes Berg &iwe, IW_EV_UINT_LEN); 7940a51b27eSJohannes Berg } 7950a51b27eSJohannes Berg 7960a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 7970a51b27eSJohannes Berg iwe.cmd = SIOCGIWFREQ; 7980a51b27eSJohannes Berg iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); 7990a51b27eSJohannes Berg iwe.u.freq.e = 0; 8000a51b27eSJohannes Berg current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, 8010a51b27eSJohannes Berg IW_EV_FREQ_LEN); 8020a51b27eSJohannes Berg 8030a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8040a51b27eSJohannes Berg iwe.cmd = SIOCGIWFREQ; 8050a51b27eSJohannes Berg iwe.u.freq.m = bss->freq; 8060a51b27eSJohannes Berg iwe.u.freq.e = 6; 8070a51b27eSJohannes Berg current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, 8080a51b27eSJohannes Berg IW_EV_FREQ_LEN); 8090a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8100a51b27eSJohannes Berg iwe.cmd = IWEVQUAL; 8110a51b27eSJohannes Berg iwe.u.qual.qual = bss->qual; 8120a51b27eSJohannes Berg iwe.u.qual.level = bss->signal; 8130a51b27eSJohannes Berg iwe.u.qual.noise = bss->noise; 8140a51b27eSJohannes Berg iwe.u.qual.updated = local->wstats_flags; 8150a51b27eSJohannes Berg current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, 8160a51b27eSJohannes Berg IW_EV_QUAL_LEN); 8170a51b27eSJohannes Berg 8180a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8190a51b27eSJohannes Berg iwe.cmd = SIOCGIWENCODE; 8200a51b27eSJohannes Berg if (bss->capability & WLAN_CAPABILITY_PRIVACY) 8210a51b27eSJohannes Berg iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; 8220a51b27eSJohannes Berg else 8230a51b27eSJohannes Berg iwe.u.data.flags = IW_ENCODE_DISABLED; 8240a51b27eSJohannes Berg iwe.u.data.length = 0; 8250a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, end_buf, 8260a51b27eSJohannes Berg &iwe, ""); 8270a51b27eSJohannes Berg 8280a51b27eSJohannes Berg ieee80211_sta_add_scan_ies(info, bss, ¤t_ev, end_buf); 8290a51b27eSJohannes Berg 8300a51b27eSJohannes Berg if (bss->supp_rates_len > 0) { 8310a51b27eSJohannes Berg /* display all supported rates in readable format */ 8320a51b27eSJohannes Berg char *p = current_ev + iwe_stream_lcp_len(info); 8330a51b27eSJohannes Berg int i; 8340a51b27eSJohannes Berg 8350a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8360a51b27eSJohannes Berg iwe.cmd = SIOCGIWRATE; 8370a51b27eSJohannes Berg /* Those two flags are ignored... */ 8380a51b27eSJohannes Berg iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; 8390a51b27eSJohannes Berg 8400a51b27eSJohannes Berg for (i = 0; i < bss->supp_rates_len; i++) { 8410a51b27eSJohannes Berg iwe.u.bitrate.value = ((bss->supp_rates[i] & 8420a51b27eSJohannes Berg 0x7f) * 500000); 8430a51b27eSJohannes Berg p = iwe_stream_add_value(info, current_ev, p, 8440a51b27eSJohannes Berg end_buf, &iwe, IW_EV_PARAM_LEN); 8450a51b27eSJohannes Berg } 8460a51b27eSJohannes Berg current_ev = p; 8470a51b27eSJohannes Berg } 8480a51b27eSJohannes Berg 8490a51b27eSJohannes Berg buf = kmalloc(30, GFP_ATOMIC); 8500a51b27eSJohannes Berg if (buf) { 8510a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8520a51b27eSJohannes Berg iwe.cmd = IWEVCUSTOM; 8530a51b27eSJohannes Berg sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); 8540a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8550a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, end_buf, 8560a51b27eSJohannes Berg &iwe, buf); 8570a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8580a51b27eSJohannes Berg iwe.cmd = IWEVCUSTOM; 8590a51b27eSJohannes Berg sprintf(buf, " Last beacon: %dms ago", 8600a51b27eSJohannes Berg jiffies_to_msecs(jiffies - bss->last_update)); 8610a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8620a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 8630a51b27eSJohannes Berg end_buf, &iwe, buf); 8640a51b27eSJohannes Berg kfree(buf); 8650a51b27eSJohannes Berg } 8660a51b27eSJohannes Berg 8670a51b27eSJohannes Berg if (bss_mesh_cfg(bss)) { 8680a51b27eSJohannes Berg u8 *cfg = bss_mesh_cfg(bss); 8690a51b27eSJohannes Berg buf = kmalloc(50, GFP_ATOMIC); 8700a51b27eSJohannes Berg if (buf) { 8710a51b27eSJohannes Berg memset(&iwe, 0, sizeof(iwe)); 8720a51b27eSJohannes Berg iwe.cmd = IWEVCUSTOM; 8730a51b27eSJohannes Berg sprintf(buf, "Mesh network (version %d)", cfg[0]); 8740a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8750a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 8760a51b27eSJohannes Berg end_buf, 8770a51b27eSJohannes Berg &iwe, buf); 8780a51b27eSJohannes Berg sprintf(buf, "Path Selection Protocol ID: " 8790a51b27eSJohannes Berg "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], 8800a51b27eSJohannes Berg cfg[4]); 8810a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8820a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 8830a51b27eSJohannes Berg end_buf, 8840a51b27eSJohannes Berg &iwe, buf); 8850a51b27eSJohannes Berg sprintf(buf, "Path Selection Metric ID: " 8860a51b27eSJohannes Berg "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], 8870a51b27eSJohannes Berg cfg[8]); 8880a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8890a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 8900a51b27eSJohannes Berg end_buf, 8910a51b27eSJohannes Berg &iwe, buf); 8920a51b27eSJohannes Berg sprintf(buf, "Congestion Control Mode ID: " 8930a51b27eSJohannes Berg "0x%02X%02X%02X%02X", cfg[9], cfg[10], 8940a51b27eSJohannes Berg cfg[11], cfg[12]); 8950a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 8960a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 8970a51b27eSJohannes Berg end_buf, 8980a51b27eSJohannes Berg &iwe, buf); 8990a51b27eSJohannes Berg sprintf(buf, "Channel Precedence: " 9000a51b27eSJohannes Berg "0x%02X%02X%02X%02X", cfg[13], cfg[14], 9010a51b27eSJohannes Berg cfg[15], cfg[16]); 9020a51b27eSJohannes Berg iwe.u.data.length = strlen(buf); 9030a51b27eSJohannes Berg current_ev = iwe_stream_add_point(info, current_ev, 9040a51b27eSJohannes Berg end_buf, 9050a51b27eSJohannes Berg &iwe, buf); 9060a51b27eSJohannes Berg kfree(buf); 9070a51b27eSJohannes Berg } 9080a51b27eSJohannes Berg } 9090a51b27eSJohannes Berg 9100a51b27eSJohannes Berg return current_ev; 9110a51b27eSJohannes Berg } 9120a51b27eSJohannes Berg 9130a51b27eSJohannes Berg 9140a51b27eSJohannes Berg int ieee80211_sta_scan_results(struct ieee80211_local *local, 9150a51b27eSJohannes Berg struct iw_request_info *info, 9160a51b27eSJohannes Berg char *buf, size_t len) 9170a51b27eSJohannes Berg { 9180a51b27eSJohannes Berg char *current_ev = buf; 9190a51b27eSJohannes Berg char *end_buf = buf + len; 9200a51b27eSJohannes Berg struct ieee80211_sta_bss *bss; 9210a51b27eSJohannes Berg 9220a51b27eSJohannes Berg spin_lock_bh(&local->sta_bss_lock); 9230a51b27eSJohannes Berg list_for_each_entry(bss, &local->sta_bss_list, list) { 9240a51b27eSJohannes Berg if (buf + len - current_ev <= IW_EV_ADDR_LEN) { 9250a51b27eSJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 9260a51b27eSJohannes Berg return -E2BIG; 9270a51b27eSJohannes Berg } 9280a51b27eSJohannes Berg current_ev = ieee80211_sta_scan_result(local, info, bss, 9290a51b27eSJohannes Berg current_ev, end_buf); 9300a51b27eSJohannes Berg } 9310a51b27eSJohannes Berg spin_unlock_bh(&local->sta_bss_lock); 9320a51b27eSJohannes Berg return current_ev - buf; 9330a51b27eSJohannes Berg } 934