linux/drivers/net/wireless/intersil/orinoco/scan.c
<<
>>
Prefs
   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_channel_to_frequency(
 114                le16_to_cpu(bss->a.channel), NL80211_BAND_2GHZ);
 115        channel = ieee80211_get_channel(wiphy, freq);
 116        if (!channel) {
 117                printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
 118                        bss->a.channel, freq);
 119                return; /* Then ignore it for now */
 120        }
 121        timestamp = 0;
 122        capability = le16_to_cpu(bss->a.capabilities);
 123        beacon_interval = le16_to_cpu(bss->a.beacon_interv);
 124        signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
 125
 126        cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
 127                                   bss->a.bssid, timestamp, capability,
 128                                   beacon_interval, ie_buf, ie_len, signal,
 129                                   GFP_KERNEL);
 130        cfg80211_put_bss(wiphy, cbss);
 131}
 132
 133void orinoco_add_extscan_result(struct orinoco_private *priv,
 134                                struct agere_ext_scan_info *bss,
 135                                size_t len)
 136{
 137        struct wiphy *wiphy = priv_to_wiphy(priv);
 138        struct ieee80211_channel *channel;
 139        struct cfg80211_bss *cbss;
 140        const u8 *ie;
 141        u64 timestamp;
 142        s32 signal;
 143        u16 capability;
 144        u16 beacon_interval;
 145        size_t ie_len;
 146        int chan, freq;
 147
 148        ie_len = len - sizeof(*bss);
 149        ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
 150        chan = ie ? ie[2] : 0;
 151        freq = ieee80211_channel_to_frequency(chan, NL80211_BAND_2GHZ);
 152        channel = ieee80211_get_channel(wiphy, freq);
 153
 154        timestamp = le64_to_cpu(bss->timestamp);
 155        capability = le16_to_cpu(bss->capabilities);
 156        beacon_interval = le16_to_cpu(bss->beacon_interval);
 157        ie = bss->data;
 158        signal = SIGNAL_TO_MBM(bss->level);
 159
 160        cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
 161                                   bss->bssid, timestamp, capability,
 162                                   beacon_interval, ie, ie_len, signal,
 163                                   GFP_KERNEL);
 164        cfg80211_put_bss(wiphy, cbss);
 165}
 166
 167void orinoco_add_hostscan_results(struct orinoco_private *priv,
 168                                  unsigned char *buf,
 169                                  size_t len)
 170{
 171        int offset;             /* In the scan data */
 172        size_t atom_len;
 173        bool abort = false;
 174
 175        switch (priv->firmware_type) {
 176        case FIRMWARE_TYPE_AGERE:
 177                atom_len = sizeof(struct agere_scan_apinfo);
 178                offset = 0;
 179                break;
 180
 181        case FIRMWARE_TYPE_SYMBOL:
 182                /* Lack of documentation necessitates this hack.
 183                 * Different firmwares have 68 or 76 byte long atoms.
 184                 * We try modulo first.  If the length divides by both,
 185                 * we check what would be the channel in the second
 186                 * frame for a 68-byte atom.  76-byte atoms have 0 there.
 187                 * Valid channel cannot be 0.  */
 188                if (len % 76)
 189                        atom_len = 68;
 190                else if (len % 68)
 191                        atom_len = 76;
 192                else if (len >= 1292 && buf[68] == 0)
 193                        atom_len = 76;
 194                else
 195                        atom_len = 68;
 196                offset = 0;
 197                break;
 198
 199        case FIRMWARE_TYPE_INTERSIL:
 200                offset = 4;
 201                if (priv->has_hostscan) {
 202                        atom_len = le16_to_cpup((__le16 *)buf);
 203                        /* Sanity check for atom_len */
 204                        if (atom_len < sizeof(struct prism2_scan_apinfo)) {
 205                                printk(KERN_ERR "%s: Invalid atom_len in scan "
 206                                       "data: %zu\n", priv->ndev->name,
 207                                       atom_len);
 208                                abort = true;
 209                                goto scan_abort;
 210                        }
 211                } else
 212                        atom_len = offsetof(struct prism2_scan_apinfo, atim);
 213                break;
 214
 215        default:
 216                abort = true;
 217                goto scan_abort;
 218        }
 219
 220        /* Check that we got an whole number of atoms */
 221        if ((len - offset) % atom_len) {
 222                printk(KERN_ERR "%s: Unexpected scan data length %zu, "
 223                       "atom_len %zu, offset %d\n", priv->ndev->name, len,
 224                       atom_len, offset);
 225                abort = true;
 226                goto scan_abort;
 227        }
 228
 229        /* Process the entries one by one */
 230        for (; offset + atom_len <= len; offset += atom_len) {
 231                union hermes_scan_info *atom;
 232
 233                atom = (union hermes_scan_info *) (buf + offset);
 234
 235                orinoco_add_hostscan_result(priv, atom);
 236        }
 237
 238 scan_abort:
 239        if (priv->scan_request) {
 240                struct cfg80211_scan_info info = {
 241                        .aborted = abort,
 242                };
 243
 244                cfg80211_scan_done(priv->scan_request, &info);
 245                priv->scan_request = NULL;
 246        }
 247}
 248
 249void orinoco_scan_done(struct orinoco_private *priv, bool abort)
 250{
 251        if (priv->scan_request) {
 252                struct cfg80211_scan_info info = {
 253                        .aborted = abort,
 254                };
 255
 256                cfg80211_scan_done(priv->scan_request, &info);
 257                priv->scan_request = NULL;
 258        }
 259}
 260