at v3.7 248 lines 5.9 kB view raw
1/* Helpers for managing scan queues 2 * 3 * See copyright notice in main.c 4 */ 5 6#include <linux/gfp.h> 7#include <linux/kernel.h> 8#include <linux/string.h> 9#include <linux/ieee80211.h> 10#include <net/cfg80211.h> 11 12#include "hermes.h" 13#include "orinoco.h" 14#include "main.h" 15 16#include "scan.h" 17 18#define ZERO_DBM_OFFSET 0x95 19#define MAX_SIGNAL_LEVEL 0x8A 20#define MIN_SIGNAL_LEVEL 0x2F 21 22#define SIGNAL_TO_DBM(x) \ 23 (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ 24 - ZERO_DBM_OFFSET) 25#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) 26 27static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) 28{ 29 int i; 30 u8 rate; 31 32 buf[0] = WLAN_EID_SUPP_RATES; 33 for (i = 0; i < 5; i++) { 34 rate = le16_to_cpu(rates[i]); 35 /* NULL terminated */ 36 if (rate == 0x0) 37 break; 38 buf[i + 2] = rate; 39 } 40 buf[1] = i; 41 42 return i + 2; 43} 44 45static int prism_build_supp_rates(u8 *buf, const u8 *rates) 46{ 47 int i; 48 49 buf[0] = WLAN_EID_SUPP_RATES; 50 for (i = 0; i < 8; i++) { 51 /* NULL terminated */ 52 if (rates[i] == 0x0) 53 break; 54 buf[i + 2] = rates[i]; 55 } 56 buf[1] = i; 57 58 /* We might still have another 2 rates, which need to go in 59 * extended supported rates */ 60 if (i == 8 && rates[i] > 0) { 61 buf[10] = WLAN_EID_EXT_SUPP_RATES; 62 for (; i < 10; i++) { 63 /* NULL terminated */ 64 if (rates[i] == 0x0) 65 break; 66 buf[i + 2] = rates[i]; 67 } 68 buf[11] = i - 8; 69 } 70 71 return (i < 8) ? i + 2 : i + 4; 72} 73 74static void orinoco_add_hostscan_result(struct orinoco_private *priv, 75 const union hermes_scan_info *bss) 76{ 77 struct wiphy *wiphy = priv_to_wiphy(priv); 78 struct ieee80211_channel *channel; 79 struct cfg80211_bss *cbss; 80 u8 *ie; 81 u8 ie_buf[46]; 82 u64 timestamp; 83 s32 signal; 84 u16 capability; 85 u16 beacon_interval; 86 int ie_len; 87 int freq; 88 int len; 89 90 len = le16_to_cpu(bss->a.essid_len); 91 92 /* Reconstruct SSID and bitrate IEs to pass up */ 93 ie_buf[0] = WLAN_EID_SSID; 94 ie_buf[1] = len; 95 memcpy(&ie_buf[2], bss->a.essid, len); 96 97 ie = ie_buf + len + 2; 98 ie_len = ie_buf[1] + 2; 99 switch (priv->firmware_type) { 100 case FIRMWARE_TYPE_SYMBOL: 101 ie_len += symbol_build_supp_rates(ie, bss->s.rates); 102 break; 103 104 case FIRMWARE_TYPE_INTERSIL: 105 ie_len += prism_build_supp_rates(ie, bss->p.rates); 106 break; 107 108 case FIRMWARE_TYPE_AGERE: 109 default: 110 break; 111 } 112 113 freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); 114 channel = ieee80211_get_channel(wiphy, freq); 115 if (!channel) { 116 printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", 117 bss->a.channel, freq); 118 return; /* Then ignore it for now */ 119 } 120 timestamp = 0; 121 capability = le16_to_cpu(bss->a.capabilities); 122 beacon_interval = le16_to_cpu(bss->a.beacon_interv); 123 signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); 124 125 cbss = cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, 126 capability, beacon_interval, ie_buf, ie_len, 127 signal, GFP_KERNEL); 128 cfg80211_put_bss(cbss); 129} 130 131void orinoco_add_extscan_result(struct orinoco_private *priv, 132 struct agere_ext_scan_info *bss, 133 size_t len) 134{ 135 struct wiphy *wiphy = priv_to_wiphy(priv); 136 struct ieee80211_channel *channel; 137 struct cfg80211_bss *cbss; 138 const u8 *ie; 139 u64 timestamp; 140 s32 signal; 141 u16 capability; 142 u16 beacon_interval; 143 size_t ie_len; 144 int chan, freq; 145 146 ie_len = len - sizeof(*bss); 147 ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); 148 chan = ie ? ie[2] : 0; 149 freq = ieee80211_dsss_chan_to_freq(chan); 150 channel = ieee80211_get_channel(wiphy, freq); 151 152 timestamp = le64_to_cpu(bss->timestamp); 153 capability = le16_to_cpu(bss->capabilities); 154 beacon_interval = le16_to_cpu(bss->beacon_interval); 155 ie = bss->data; 156 signal = SIGNAL_TO_MBM(bss->level); 157 158 cbss = cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, 159 capability, beacon_interval, ie, ie_len, 160 signal, GFP_KERNEL); 161 cfg80211_put_bss(cbss); 162} 163 164void orinoco_add_hostscan_results(struct orinoco_private *priv, 165 unsigned char *buf, 166 size_t len) 167{ 168 int offset; /* In the scan data */ 169 size_t atom_len; 170 bool abort = false; 171 172 switch (priv->firmware_type) { 173 case FIRMWARE_TYPE_AGERE: 174 atom_len = sizeof(struct agere_scan_apinfo); 175 offset = 0; 176 break; 177 178 case FIRMWARE_TYPE_SYMBOL: 179 /* Lack of documentation necessitates this hack. 180 * Different firmwares have 68 or 76 byte long atoms. 181 * We try modulo first. If the length divides by both, 182 * we check what would be the channel in the second 183 * frame for a 68-byte atom. 76-byte atoms have 0 there. 184 * Valid channel cannot be 0. */ 185 if (len % 76) 186 atom_len = 68; 187 else if (len % 68) 188 atom_len = 76; 189 else if (len >= 1292 && buf[68] == 0) 190 atom_len = 76; 191 else 192 atom_len = 68; 193 offset = 0; 194 break; 195 196 case FIRMWARE_TYPE_INTERSIL: 197 offset = 4; 198 if (priv->has_hostscan) { 199 atom_len = le16_to_cpup((__le16 *)buf); 200 /* Sanity check for atom_len */ 201 if (atom_len < sizeof(struct prism2_scan_apinfo)) { 202 printk(KERN_ERR "%s: Invalid atom_len in scan " 203 "data: %zu\n", priv->ndev->name, 204 atom_len); 205 abort = true; 206 goto scan_abort; 207 } 208 } else 209 atom_len = offsetof(struct prism2_scan_apinfo, atim); 210 break; 211 212 default: 213 abort = true; 214 goto scan_abort; 215 } 216 217 /* Check that we got an whole number of atoms */ 218 if ((len - offset) % atom_len) { 219 printk(KERN_ERR "%s: Unexpected scan data length %zu, " 220 "atom_len %zu, offset %d\n", priv->ndev->name, len, 221 atom_len, offset); 222 abort = true; 223 goto scan_abort; 224 } 225 226 /* Process the entries one by one */ 227 for (; offset + atom_len <= len; offset += atom_len) { 228 union hermes_scan_info *atom; 229 230 atom = (union hermes_scan_info *) (buf + offset); 231 232 orinoco_add_hostscan_result(priv, atom); 233 } 234 235 scan_abort: 236 if (priv->scan_request) { 237 cfg80211_scan_done(priv->scan_request, abort); 238 priv->scan_request = NULL; 239 } 240} 241 242void orinoco_scan_done(struct orinoco_private *priv, bool abort) 243{ 244 if (priv->scan_request) { 245 cfg80211_scan_done(priv->scan_request, abort); 246 priv->scan_request = NULL; 247 } 248}