linux/drivers/net/wireless/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        u8 *ie;
  80        u8 ie_buf[46];
  81        u64 timestamp;
  82        s32 signal;
  83        u16 capability;
  84        u16 beacon_interval;
  85        int ie_len;
  86        int freq;
  87        int len;
  88
  89        len = le16_to_cpu(bss->a.essid_len);
  90
  91        /* Reconstruct SSID and bitrate IEs to pass up */
  92        ie_buf[0] = WLAN_EID_SSID;
  93        ie_buf[1] = len;
  94        memcpy(&ie_buf[2], bss->a.essid, len);
  95
  96        ie = ie_buf + len + 2;
  97        ie_len = ie_buf[1] + 2;
  98        switch (priv->firmware_type) {
  99        case FIRMWARE_TYPE_SYMBOL:
 100                ie_len += symbol_build_supp_rates(ie, bss->s.rates);
 101                break;
 102
 103        case FIRMWARE_TYPE_INTERSIL:
 104                ie_len += prism_build_supp_rates(ie, bss->p.rates);
 105                break;
 106
 107        case FIRMWARE_TYPE_AGERE:
 108        default:
 109                break;
 110        }
 111
 112        freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel));
 113        channel = ieee80211_get_channel(wiphy, freq);
 114        if (!channel) {
 115                printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
 116                        bss->a.channel, freq);
 117                return; /* Then ignore it for now */
 118        }
 119        timestamp = 0;
 120        capability = le16_to_cpu(bss->a.capabilities);
 121        beacon_interval = le16_to_cpu(bss->a.beacon_interv);
 122        signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
 123
 124        cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp,
 125                            capability, beacon_interval, ie_buf, ie_len,
 126                            signal, GFP_KERNEL);
 127}
 128
 129void orinoco_add_extscan_result(struct orinoco_private *priv,
 130                                struct agere_ext_scan_info *bss,
 131                                size_t len)
 132{
 133        struct wiphy *wiphy = priv_to_wiphy(priv);
 134        struct ieee80211_channel *channel;
 135        const u8 *ie;
 136        u64 timestamp;
 137        s32 signal;
 138        u16 capability;
 139        u16 beacon_interval;
 140        size_t ie_len;
 141        int chan, freq;
 142
 143        ie_len = len - sizeof(*bss);
 144        ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
 145        chan = ie ? ie[2] : 0;
 146        freq = ieee80211_dsss_chan_to_freq(chan);
 147        channel = ieee80211_get_channel(wiphy, freq);
 148
 149        timestamp = le64_to_cpu(bss->timestamp);
 150        capability = le16_to_cpu(bss->capabilities);
 151        beacon_interval = le16_to_cpu(bss->beacon_interval);
 152        ie = bss->data;
 153        signal = SIGNAL_TO_MBM(bss->level);
 154
 155        cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp,
 156                            capability, beacon_interval, ie, ie_len,
 157                            signal, GFP_KERNEL);
 158}
 159
 160void orinoco_add_hostscan_results(struct orinoco_private *priv,
 161                                  unsigned char *buf,
 162                                  size_t len)
 163{
 164        int offset;             /* In the scan data */
 165        size_t atom_len;
 166        bool abort = false;
 167
 168        switch (priv->firmware_type) {
 169        case FIRMWARE_TYPE_AGERE:
 170                atom_len = sizeof(struct agere_scan_apinfo);
 171                offset = 0;
 172                break;
 173
 174        case FIRMWARE_TYPE_SYMBOL:
 175                /* Lack of documentation necessitates this hack.
 176                 * Different firmwares have 68 or 76 byte long atoms.
 177                 * We try modulo first.  If the length divides by both,
 178                 * we check what would be the channel in the second
 179                 * frame for a 68-byte atom.  76-byte atoms have 0 there.
 180                 * Valid channel cannot be 0.  */
 181                if (len % 76)
 182                        atom_len = 68;
 183                else if (len % 68)
 184                        atom_len = 76;
 185                else if (len >= 1292 && buf[68] == 0)
 186                        atom_len = 76;
 187                else
 188                        atom_len = 68;
 189                offset = 0;
 190                break;
 191
 192        case FIRMWARE_TYPE_INTERSIL:
 193                offset = 4;
 194                if (priv->has_hostscan) {
 195                        atom_len = le16_to_cpup((__le16 *)buf);
 196                        /* Sanity check for atom_len */
 197                        if (atom_len < sizeof(struct prism2_scan_apinfo)) {
 198                                printk(KERN_ERR "%s: Invalid atom_len in scan "
 199                                       "data: %zu\n", priv->ndev->name,
 200                                       atom_len);
 201                                abort = true;
 202                                goto scan_abort;
 203                        }
 204                } else
 205                        atom_len = offsetof(struct prism2_scan_apinfo, atim);
 206                break;
 207
 208        default:
 209                abort = true;
 210                goto scan_abort;
 211        }
 212
 213        /* Check that we got an whole number of atoms */
 214        if ((len - offset) % atom_len) {
 215                printk(KERN_ERR "%s: Unexpected scan data length %zu, "
 216                       "atom_len %zu, offset %d\n", priv->ndev->name, len,
 217                       atom_len, offset);
 218                abort = true;
 219                goto scan_abort;
 220        }
 221
 222        /* Process the entries one by one */
 223        for (; offset + atom_len <= len; offset += atom_len) {
 224                union hermes_scan_info *atom;
 225
 226                atom = (union hermes_scan_info *) (buf + offset);
 227
 228                orinoco_add_hostscan_result(priv, atom);
 229        }
 230
 231 scan_abort:
 232        if (priv->scan_request) {
 233                cfg80211_scan_done(priv->scan_request, abort);
 234                priv->scan_request = NULL;
 235        }
 236}
 237
 238void orinoco_scan_done(struct orinoco_private *priv, bool abort)
 239{
 240        if (priv->scan_request) {
 241                cfg80211_scan_done(priv->scan_request, abort);
 242                priv->scan_request = NULL;
 243        }
 244}
 245