Back to home page

OSCL-LXR

 
 

    


0001 /* Helpers for managing scan queues
0002  *
0003  * See copyright notice in main.c
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         /* NULL terminated */
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         /* NULL terminated */
0052         if (rates[i] == 0x0)
0053             break;
0054         buf[i + 2] = rates[i];
0055     }
0056     buf[1] = i;
0057 
0058     /* We might still have another 2 rates, which need to go in
0059      * extended supported rates */
0060     if (i == 8 && rates[i] > 0) {
0061         buf[10] = WLAN_EID_EXT_SUPP_RATES;
0062         for (; i < 10; i++) {
0063             /* NULL terminated */
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     /* Reconstruct SSID and bitrate IEs to pass up */
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; /* Then ignore it for now */
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;     /* In the scan data */
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         /* Lack of documentation necessitates this hack.
0183          * Different firmwares have 68 or 76 byte long atoms.
0184          * We try modulo first.  If the length divides by both,
0185          * we check what would be the channel in the second
0186          * frame for a 68-byte atom.  76-byte atoms have 0 there.
0187          * Valid channel cannot be 0.  */
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             /* Sanity check for atom_len */
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     /* Check that we got an whole number of atoms */
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     /* Process the entries one by one */
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 }