Index: chromeos/compat-wireless/drivers/net/wireless/orinoco/scan.c |
diff --git a/chromeos/compat-wireless/drivers/net/wireless/orinoco/scan.c b/chromeos/compat-wireless/drivers/net/wireless/orinoco/scan.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4300d9db7d8caab2d70442b7cbd37a9da69adef9 |
--- /dev/null |
+++ b/chromeos/compat-wireless/drivers/net/wireless/orinoco/scan.c |
@@ -0,0 +1,231 @@ |
+/* Helpers for managing scan queues |
+ * |
+ * See copyright notice in main.c |
+ */ |
+ |
+#include <linux/gfp.h> |
+#include <linux/kernel.h> |
+#include <linux/string.h> |
+#include <linux/ieee80211.h> |
+#include <net/cfg80211.h> |
+ |
+#include "hermes.h" |
+#include "orinoco.h" |
+#include "main.h" |
+ |
+#include "scan.h" |
+ |
+#define ZERO_DBM_OFFSET 0x95 |
+#define MAX_SIGNAL_LEVEL 0x8A |
+#define MIN_SIGNAL_LEVEL 0x2F |
+ |
+#define SIGNAL_TO_DBM(x) \ |
+ (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \ |
+ - ZERO_DBM_OFFSET) |
+#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100) |
+ |
+static int symbol_build_supp_rates(u8 *buf, const __le16 *rates) |
+{ |
+ int i; |
+ u8 rate; |
+ |
+ buf[0] = WLAN_EID_SUPP_RATES; |
+ for (i = 0; i < 5; i++) { |
+ rate = le16_to_cpu(rates[i]); |
+ /* NULL terminated */ |
+ if (rate == 0x0) |
+ break; |
+ buf[i + 2] = rate; |
+ } |
+ buf[1] = i; |
+ |
+ return i + 2; |
+} |
+ |
+static int prism_build_supp_rates(u8 *buf, const u8 *rates) |
+{ |
+ int i; |
+ |
+ buf[0] = WLAN_EID_SUPP_RATES; |
+ for (i = 0; i < 8; i++) { |
+ /* NULL terminated */ |
+ if (rates[i] == 0x0) |
+ break; |
+ buf[i + 2] = rates[i]; |
+ } |
+ buf[1] = i; |
+ |
+ /* We might still have another 2 rates, which need to go in |
+ * extended supported rates */ |
+ if (i == 8 && rates[i] > 0) { |
+ buf[10] = WLAN_EID_EXT_SUPP_RATES; |
+ for (; i < 10; i++) { |
+ /* NULL terminated */ |
+ if (rates[i] == 0x0) |
+ break; |
+ buf[i + 2] = rates[i]; |
+ } |
+ buf[11] = i - 8; |
+ } |
+ |
+ return (i < 8) ? i + 2 : i + 4; |
+} |
+ |
+static void orinoco_add_hostscan_result(struct orinoco_private *priv, |
+ const union hermes_scan_info *bss) |
+{ |
+ struct wiphy *wiphy = priv_to_wiphy(priv); |
+ struct ieee80211_channel *channel; |
+ u8 *ie; |
+ u8 ie_buf[46]; |
+ u64 timestamp; |
+ s32 signal; |
+ u16 capability; |
+ u16 beacon_interval; |
+ int ie_len; |
+ int freq; |
+ int len; |
+ |
+ len = le16_to_cpu(bss->a.essid_len); |
+ |
+ /* Reconstruct SSID and bitrate IEs to pass up */ |
+ ie_buf[0] = WLAN_EID_SSID; |
+ ie_buf[1] = len; |
+ memcpy(&ie_buf[2], bss->a.essid, len); |
+ |
+ ie = ie_buf + len + 2; |
+ ie_len = ie_buf[1] + 2; |
+ switch (priv->firmware_type) { |
+ case FIRMWARE_TYPE_SYMBOL: |
+ ie_len += symbol_build_supp_rates(ie, bss->s.rates); |
+ break; |
+ |
+ case FIRMWARE_TYPE_INTERSIL: |
+ ie_len += prism_build_supp_rates(ie, bss->p.rates); |
+ break; |
+ |
+ case FIRMWARE_TYPE_AGERE: |
+ default: |
+ break; |
+ } |
+ |
+ freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); |
+ channel = ieee80211_get_channel(wiphy, freq); |
+ timestamp = 0; |
+ capability = le16_to_cpu(bss->a.capabilities); |
+ beacon_interval = le16_to_cpu(bss->a.beacon_interv); |
+ signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level)); |
+ |
+ cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp, |
+ capability, beacon_interval, ie_buf, ie_len, |
+ signal, GFP_KERNEL); |
+} |
+ |
+void orinoco_add_extscan_result(struct orinoco_private *priv, |
+ struct agere_ext_scan_info *bss, |
+ size_t len) |
+{ |
+ struct wiphy *wiphy = priv_to_wiphy(priv); |
+ struct ieee80211_channel *channel; |
+ const u8 *ie; |
+ u64 timestamp; |
+ s32 signal; |
+ u16 capability; |
+ u16 beacon_interval; |
+ size_t ie_len; |
+ int chan, freq; |
+ |
+ ie_len = len - sizeof(*bss); |
+ ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); |
+ chan = ie ? ie[2] : 0; |
+ freq = ieee80211_dsss_chan_to_freq(chan); |
+ channel = ieee80211_get_channel(wiphy, freq); |
+ |
+ timestamp = le64_to_cpu(bss->timestamp); |
+ capability = le16_to_cpu(bss->capabilities); |
+ beacon_interval = le16_to_cpu(bss->beacon_interval); |
+ ie = bss->data; |
+ signal = SIGNAL_TO_MBM(bss->level); |
+ |
+ cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp, |
+ capability, beacon_interval, ie, ie_len, |
+ signal, GFP_KERNEL); |
+} |
+ |
+void orinoco_add_hostscan_results(struct orinoco_private *priv, |
+ unsigned char *buf, |
+ size_t len) |
+{ |
+ int offset; /* In the scan data */ |
+ size_t atom_len; |
+ bool abort = false; |
+ |
+ switch (priv->firmware_type) { |
+ case FIRMWARE_TYPE_AGERE: |
+ atom_len = sizeof(struct agere_scan_apinfo); |
+ offset = 0; |
+ break; |
+ |
+ case FIRMWARE_TYPE_SYMBOL: |
+ /* Lack of documentation necessitates this hack. |
+ * Different firmwares have 68 or 76 byte long atoms. |
+ * We try modulo first. If the length divides by both, |
+ * we check what would be the channel in the second |
+ * frame for a 68-byte atom. 76-byte atoms have 0 there. |
+ * Valid channel cannot be 0. */ |
+ if (len % 76) |
+ atom_len = 68; |
+ else if (len % 68) |
+ atom_len = 76; |
+ else if (len >= 1292 && buf[68] == 0) |
+ atom_len = 76; |
+ else |
+ atom_len = 68; |
+ offset = 0; |
+ break; |
+ |
+ case FIRMWARE_TYPE_INTERSIL: |
+ offset = 4; |
+ if (priv->has_hostscan) { |
+ atom_len = le16_to_cpup((__le16 *)buf); |
+ /* Sanity check for atom_len */ |
+ if (atom_len < sizeof(struct prism2_scan_apinfo)) { |
+ printk(KERN_ERR "%s: Invalid atom_len in scan " |
+ "data: %zu\n", priv->ndev->name, |
+ atom_len); |
+ abort = true; |
+ goto scan_abort; |
+ } |
+ } else |
+ atom_len = offsetof(struct prism2_scan_apinfo, atim); |
+ break; |
+ |
+ default: |
+ abort = true; |
+ goto scan_abort; |
+ } |
+ |
+ /* Check that we got an whole number of atoms */ |
+ if ((len - offset) % atom_len) { |
+ printk(KERN_ERR "%s: Unexpected scan data length %zu, " |
+ "atom_len %zu, offset %d\n", priv->ndev->name, len, |
+ atom_len, offset); |
+ abort = true; |
+ goto scan_abort; |
+ } |
+ |
+ /* Process the entries one by one */ |
+ for (; offset + atom_len <= len; offset += atom_len) { |
+ union hermes_scan_info *atom; |
+ |
+ atom = (union hermes_scan_info *) (buf + offset); |
+ |
+ orinoco_add_hostscan_result(priv, atom); |
+ } |
+ |
+ scan_abort: |
+ if (priv->scan_request) { |
+ cfg80211_scan_done(priv->scan_request, abort); |
+ priv->scan_request = NULL; |
+ } |
+} |