0001
0002
0003
0004
0005
0006 #include <linux/gfp.h>
0007 #include <linux/kernel.h>
0008 #include <linux/string.h>
0009 #include <linux/ieee80211.h>
0010 #include <net/cfg80211.h>
0011
0012 #include "hermes.h"
0013 #include "orinoco.h"
0014 #include "main.h"
0015
0016 #include "scan.h"
0017
0018 #define ZERO_DBM_OFFSET 0x95
0019 #define MAX_SIGNAL_LEVEL 0x8A
0020 #define MIN_SIGNAL_LEVEL 0x2F
0021
0022 #define SIGNAL_TO_DBM(x) \
0023 (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \
0024 - ZERO_DBM_OFFSET)
0025 #define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100)
0026
0027 static int symbol_build_supp_rates(u8 *buf, const __le16 *rates)
0028 {
0029 int i;
0030 u8 rate;
0031
0032 buf[0] = WLAN_EID_SUPP_RATES;
0033 for (i = 0; i < 5; i++) {
0034 rate = le16_to_cpu(rates[i]);
0035
0036 if (rate == 0x0)
0037 break;
0038 buf[i + 2] = rate;
0039 }
0040 buf[1] = i;
0041
0042 return i + 2;
0043 }
0044
0045 static int prism_build_supp_rates(u8 *buf, const u8 *rates)
0046 {
0047 int i;
0048
0049 buf[0] = WLAN_EID_SUPP_RATES;
0050 for (i = 0; i < 8; i++) {
0051
0052 if (rates[i] == 0x0)
0053 break;
0054 buf[i + 2] = rates[i];
0055 }
0056 buf[1] = i;
0057
0058
0059
0060 if (i == 8 && rates[i] > 0) {
0061 buf[10] = WLAN_EID_EXT_SUPP_RATES;
0062 for (; i < 10; i++) {
0063
0064 if (rates[i] == 0x0)
0065 break;
0066 buf[i + 2] = rates[i];
0067 }
0068 buf[11] = i - 8;
0069 }
0070
0071 return (i < 8) ? i + 2 : i + 4;
0072 }
0073
0074 static void orinoco_add_hostscan_result(struct orinoco_private *priv,
0075 const union hermes_scan_info *bss)
0076 {
0077 struct wiphy *wiphy = priv_to_wiphy(priv);
0078 struct ieee80211_channel *channel;
0079 struct cfg80211_bss *cbss;
0080 u8 *ie;
0081 u8 ie_buf[46];
0082 u64 timestamp;
0083 s32 signal;
0084 u16 capability;
0085 u16 beacon_interval;
0086 int ie_len;
0087 int freq;
0088 int len;
0089
0090 len = le16_to_cpu(bss->a.essid_len);
0091
0092
0093 ie_buf[0] = WLAN_EID_SSID;
0094 ie_buf[1] = len;
0095 memcpy(&ie_buf[2], bss->a.essid, len);
0096
0097 ie = ie_buf + len + 2;
0098 ie_len = ie_buf[1] + 2;
0099 switch (priv->firmware_type) {
0100 case FIRMWARE_TYPE_SYMBOL:
0101 ie_len += symbol_build_supp_rates(ie, bss->s.rates);
0102 break;
0103
0104 case FIRMWARE_TYPE_INTERSIL:
0105 ie_len += prism_build_supp_rates(ie, bss->p.rates);
0106 break;
0107
0108 case FIRMWARE_TYPE_AGERE:
0109 default:
0110 break;
0111 }
0112
0113 freq = ieee80211_channel_to_frequency(
0114 le16_to_cpu(bss->a.channel), NL80211_BAND_2GHZ);
0115 channel = ieee80211_get_channel(wiphy, freq);
0116 if (!channel) {
0117 printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
0118 bss->a.channel, freq);
0119 return;
0120 }
0121 timestamp = 0;
0122 capability = le16_to_cpu(bss->a.capabilities);
0123 beacon_interval = le16_to_cpu(bss->a.beacon_interv);
0124 signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
0125
0126 cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
0127 bss->a.bssid, timestamp, capability,
0128 beacon_interval, ie_buf, ie_len, signal,
0129 GFP_KERNEL);
0130 cfg80211_put_bss(wiphy, cbss);
0131 }
0132
0133 void orinoco_add_extscan_result(struct orinoco_private *priv,
0134 struct agere_ext_scan_info *bss,
0135 size_t len)
0136 {
0137 struct wiphy *wiphy = priv_to_wiphy(priv);
0138 struct ieee80211_channel *channel;
0139 struct cfg80211_bss *cbss;
0140 const u8 *ie;
0141 u64 timestamp;
0142 s32 signal;
0143 u16 capability;
0144 u16 beacon_interval;
0145 size_t ie_len;
0146 int chan, freq;
0147
0148 ie_len = len - sizeof(*bss);
0149 ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
0150 chan = ie ? ie[2] : 0;
0151 freq = ieee80211_channel_to_frequency(chan, NL80211_BAND_2GHZ);
0152 channel = ieee80211_get_channel(wiphy, freq);
0153
0154 timestamp = le64_to_cpu(bss->timestamp);
0155 capability = le16_to_cpu(bss->capabilities);
0156 beacon_interval = le16_to_cpu(bss->beacon_interval);
0157 ie = bss->data;
0158 signal = SIGNAL_TO_MBM(bss->level);
0159
0160 cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
0161 bss->bssid, timestamp, capability,
0162 beacon_interval, ie, ie_len, signal,
0163 GFP_KERNEL);
0164 cfg80211_put_bss(wiphy, cbss);
0165 }
0166
0167 void orinoco_add_hostscan_results(struct orinoco_private *priv,
0168 unsigned char *buf,
0169 size_t len)
0170 {
0171 int offset;
0172 size_t atom_len;
0173 bool abort = false;
0174
0175 switch (priv->firmware_type) {
0176 case FIRMWARE_TYPE_AGERE:
0177 atom_len = sizeof(struct agere_scan_apinfo);
0178 offset = 0;
0179 break;
0180
0181 case FIRMWARE_TYPE_SYMBOL:
0182
0183
0184
0185
0186
0187
0188 if (len % 76)
0189 atom_len = 68;
0190 else if (len % 68)
0191 atom_len = 76;
0192 else if (len >= 1292 && buf[68] == 0)
0193 atom_len = 76;
0194 else
0195 atom_len = 68;
0196 offset = 0;
0197 break;
0198
0199 case FIRMWARE_TYPE_INTERSIL:
0200 offset = 4;
0201 if (priv->has_hostscan) {
0202 atom_len = le16_to_cpup((__le16 *)buf);
0203
0204 if (atom_len < sizeof(struct prism2_scan_apinfo)) {
0205 printk(KERN_ERR "%s: Invalid atom_len in scan "
0206 "data: %zu\n", priv->ndev->name,
0207 atom_len);
0208 abort = true;
0209 goto scan_abort;
0210 }
0211 } else
0212 atom_len = offsetof(struct prism2_scan_apinfo, atim);
0213 break;
0214
0215 default:
0216 abort = true;
0217 goto scan_abort;
0218 }
0219
0220
0221 if ((len - offset) % atom_len) {
0222 printk(KERN_ERR "%s: Unexpected scan data length %zu, "
0223 "atom_len %zu, offset %d\n", priv->ndev->name, len,
0224 atom_len, offset);
0225 abort = true;
0226 goto scan_abort;
0227 }
0228
0229
0230 for (; offset + atom_len <= len; offset += atom_len) {
0231 union hermes_scan_info *atom;
0232
0233 atom = (union hermes_scan_info *) (buf + offset);
0234
0235 orinoco_add_hostscan_result(priv, atom);
0236 }
0237
0238 scan_abort:
0239 if (priv->scan_request) {
0240 struct cfg80211_scan_info info = {
0241 .aborted = abort,
0242 };
0243
0244 cfg80211_scan_done(priv->scan_request, &info);
0245 priv->scan_request = NULL;
0246 }
0247 }
0248
0249 void orinoco_scan_done(struct orinoco_private *priv, bool abort)
0250 {
0251 if (priv->scan_request) {
0252 struct cfg80211_scan_info info = {
0253 .aborted = abort,
0254 };
0255
0256 cfg80211_scan_done(priv->scan_request, &info);
0257 priv->scan_request = NULL;
0258 }
0259 }