aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/orinoco/scan.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/orinoco/scan.c')
-rw-r--r--drivers/net/wireless/orinoco/scan.c291
1 files changed, 144 insertions, 147 deletions
diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c
index 89d699d4dfe6..d2f10e9c2162 100644
--- a/drivers/net/wireless/orinoco/scan.c
+++ b/drivers/net/wireless/orinoco/scan.c
@@ -5,147 +5,166 @@
5 5
6#include <linux/kernel.h> 6#include <linux/kernel.h>
7#include <linux/string.h> 7#include <linux/string.h>
8#include <linux/etherdevice.h> 8#include <linux/ieee80211.h>
9#include <net/cfg80211.h>
9 10
10#include "hermes.h" 11#include "hermes.h"
11#include "orinoco.h" 12#include "orinoco.h"
13#include "main.h"
12 14
13#include "scan.h" 15#include "scan.h"
14 16
15#define ORINOCO_MAX_BSS_COUNT 64 17#define ZERO_DBM_OFFSET 0x95
18#define MAX_SIGNAL_LEVEL 0x8A
19#define MIN_SIGNAL_LEVEL 0x2F
16 20
17#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data) 21#define SIGNAL_TO_DBM(x) \
18#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data) 22 (clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \
23 - ZERO_DBM_OFFSET)
24#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100)
19 25
20int orinoco_bss_data_allocate(struct orinoco_private *priv) 26static int symbol_build_supp_rates(u8 *buf, const __le16 *rates)
21{ 27{
22 if (priv->bss_xbss_data) 28 int i;
23 return 0; 29 u8 rate;
24 30
25 if (priv->has_ext_scan) 31 buf[0] = WLAN_EID_SUPP_RATES;
26 priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * 32 for (i = 0; i < 5; i++) {
27 sizeof(struct xbss_element), 33 rate = le16_to_cpu(rates[i]);
28 GFP_KERNEL); 34 /* NULL terminated */
29 else 35 if (rate == 0x0)
30 priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * 36 break;
31 sizeof(struct bss_element), 37 buf[i + 2] = rate;
32 GFP_KERNEL);
33
34 if (!priv->bss_xbss_data) {
35 printk(KERN_WARNING "Out of memory allocating beacons");
36 return -ENOMEM;
37 } 38 }
38 return 0; 39 buf[1] = i;
39}
40 40
41void orinoco_bss_data_free(struct orinoco_private *priv) 41 return i + 2;
42{
43 kfree(priv->bss_xbss_data);
44 priv->bss_xbss_data = NULL;
45} 42}
46 43
47void orinoco_bss_data_init(struct orinoco_private *priv) 44static int prism_build_supp_rates(u8 *buf, const u8 *rates)
48{ 45{
49 int i; 46 int i;
50 47
51 INIT_LIST_HEAD(&priv->bss_free_list); 48 buf[0] = WLAN_EID_SUPP_RATES;
52 INIT_LIST_HEAD(&priv->bss_list); 49 for (i = 0; i < 8; i++) {
53 if (priv->has_ext_scan) 50 /* NULL terminated */
54 for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) 51 if (rates[i] == 0x0)
55 list_add_tail(&(PRIV_XBSS[i].list), 52 break;
56 &priv->bss_free_list); 53 buf[i + 2] = rates[i];
57 else 54 }
58 for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) 55 buf[1] = i;
59 list_add_tail(&(PRIV_BSS[i].list), 56
60 &priv->bss_free_list); 57 /* We might still have another 2 rates, which need to go in
61 58 * extended supported rates */
62} 59 if (i == 8 && rates[i] > 0) {
63 60 buf[10] = WLAN_EID_EXT_SUPP_RATES;
64void orinoco_clear_scan_results(struct orinoco_private *priv, 61 for (; i < 10; i++) {
65 unsigned long scan_age) 62 /* NULL terminated */
66{ 63 if (rates[i] == 0x0)
67 if (priv->has_ext_scan) { 64 break;
68 struct xbss_element *bss; 65 buf[i + 2] = rates[i];
69 struct xbss_element *tmp_bss;
70
71 /* Blow away current list of scan results */
72 list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
73 if (!scan_age ||
74 time_after(jiffies, bss->last_scanned + scan_age)) {
75 list_move_tail(&bss->list,
76 &priv->bss_free_list);
77 /* Don't blow away ->list, just BSS data */
78 memset(&bss->bss, 0, sizeof(bss->bss));
79 bss->last_scanned = 0;
80 }
81 }
82 } else {
83 struct bss_element *bss;
84 struct bss_element *tmp_bss;
85
86 /* Blow away current list of scan results */
87 list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
88 if (!scan_age ||
89 time_after(jiffies, bss->last_scanned + scan_age)) {
90 list_move_tail(&bss->list,
91 &priv->bss_free_list);
92 /* Don't blow away ->list, just BSS data */
93 memset(&bss->bss, 0, sizeof(bss->bss));
94 bss->last_scanned = 0;
95 }
96 } 66 }
67 buf[11] = i - 8;
97 } 68 }
69
70 return (i < 8) ? i + 2 : i + 4;
98} 71}
99 72
100void orinoco_add_ext_scan_result(struct orinoco_private *priv, 73static void orinoco_add_hostscan_result(struct orinoco_private *priv,
101 struct agere_ext_scan_info *atom) 74 const union hermes_scan_info *bss)
102{ 75{
103 struct xbss_element *bss = NULL; 76 struct wiphy *wiphy = priv_to_wiphy(priv);
104 int found = 0; 77 struct ieee80211_channel *channel;
105 78 u8 *ie;
106 /* Try to update an existing bss first */ 79 u8 ie_buf[46];
107 list_for_each_entry(bss, &priv->bss_list, list) { 80 u64 timestamp;
108 if (compare_ether_addr(bss->bss.bssid, atom->bssid)) 81 s32 signal;
109 continue; 82 u16 capability;
110 /* ESSID lengths */ 83 u16 beacon_interval;
111 if (bss->bss.data[1] != atom->data[1]) 84 int ie_len;
112 continue; 85 int freq;
113 if (memcmp(&bss->bss.data[2], &atom->data[2], 86 int len;
114 atom->data[1])) 87
115 continue; 88 len = le16_to_cpu(bss->a.essid_len);
116 found = 1; 89
90 /* Reconstruct SSID and bitrate IEs to pass up */
91 ie_buf[0] = WLAN_EID_SSID;
92 ie_buf[1] = len;
93 memcpy(&ie_buf[2], bss->a.essid, len);
94
95 ie = ie_buf + len + 2;
96 ie_len = ie_buf[1] + 2;
97 switch (priv->firmware_type) {
98 case FIRMWARE_TYPE_SYMBOL:
99 ie_len += symbol_build_supp_rates(ie, bss->s.rates);
117 break; 100 break;
118 }
119 101
120 /* Grab a bss off the free list */ 102 case FIRMWARE_TYPE_INTERSIL:
121 if (!found && !list_empty(&priv->bss_free_list)) { 103 ie_len += prism_build_supp_rates(ie, bss->p.rates);
122 bss = list_entry(priv->bss_free_list.next, 104 break;
123 struct xbss_element, list);
124 list_del(priv->bss_free_list.next);
125 105
126 list_add_tail(&bss->list, &priv->bss_list); 106 case FIRMWARE_TYPE_AGERE:
107 default:
108 break;
127 } 109 }
128 110
129 if (bss) { 111 freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel));
130 /* Always update the BSS to get latest beacon info */ 112 channel = ieee80211_get_channel(wiphy, freq);
131 memcpy(&bss->bss, atom, sizeof(bss->bss)); 113 timestamp = 0;
132 bss->last_scanned = jiffies; 114 capability = le16_to_cpu(bss->a.capabilities);
133 } 115 beacon_interval = le16_to_cpu(bss->a.beacon_interv);
116 signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
117
118 cfg80211_inform_bss(wiphy, channel, bss->a.bssid, timestamp,
119 capability, beacon_interval, ie_buf, ie_len,
120 signal, GFP_KERNEL);
134} 121}
135 122
136int orinoco_process_scan_results(struct orinoco_private *priv, 123void orinoco_add_extscan_result(struct orinoco_private *priv,
137 unsigned char *buf, 124 struct agere_ext_scan_info *bss,
138 int len) 125 size_t len)
139{ 126{
140 int offset; /* In the scan data */ 127 struct wiphy *wiphy = priv_to_wiphy(priv);
141 union hermes_scan_info *atom; 128 struct ieee80211_channel *channel;
142 int atom_len; 129 u8 *ie;
130 u64 timestamp;
131 s32 signal;
132 u16 capability;
133 u16 beacon_interval;
134 size_t ie_len;
135 int chan, freq;
136
137 ie_len = len - sizeof(*bss);
138 ie = orinoco_get_ie(bss->data, ie_len, WLAN_EID_DS_PARAMS);
139 chan = ie ? ie[2] : 0;
140 freq = ieee80211_dsss_chan_to_freq(chan);
141 channel = ieee80211_get_channel(wiphy, freq);
142
143 timestamp = le64_to_cpu(bss->timestamp);
144 capability = le16_to_cpu(bss->capabilities);
145 beacon_interval = le16_to_cpu(bss->beacon_interval);
146 ie = bss->data;
147 signal = SIGNAL_TO_MBM(bss->level);
148
149 cfg80211_inform_bss(wiphy, channel, bss->bssid, timestamp,
150 capability, beacon_interval, ie, ie_len,
151 signal, GFP_KERNEL);
152}
153
154void orinoco_add_hostscan_results(struct orinoco_private *priv,
155 unsigned char *buf,
156 size_t len)
157{
158 int offset; /* In the scan data */
159 size_t atom_len;
160 bool abort = false;
143 161
144 switch (priv->firmware_type) { 162 switch (priv->firmware_type) {
145 case FIRMWARE_TYPE_AGERE: 163 case FIRMWARE_TYPE_AGERE:
146 atom_len = sizeof(struct agere_scan_apinfo); 164 atom_len = sizeof(struct agere_scan_apinfo);
147 offset = 0; 165 offset = 0;
148 break; 166 break;
167
149 case FIRMWARE_TYPE_SYMBOL: 168 case FIRMWARE_TYPE_SYMBOL:
150 /* Lack of documentation necessitates this hack. 169 /* Lack of documentation necessitates this hack.
151 * Different firmwares have 68 or 76 byte long atoms. 170 * Different firmwares have 68 or 76 byte long atoms.
@@ -163,6 +182,7 @@ int orinoco_process_scan_results(struct orinoco_private *priv,
163 atom_len = 68; 182 atom_len = 68;
164 offset = 0; 183 offset = 0;
165 break; 184 break;
185
166 case FIRMWARE_TYPE_INTERSIL: 186 case FIRMWARE_TYPE_INTERSIL:
167 offset = 4; 187 offset = 4;
168 if (priv->has_hostscan) { 188 if (priv->has_hostscan) {
@@ -170,64 +190,41 @@ int orinoco_process_scan_results(struct orinoco_private *priv,
170 /* Sanity check for atom_len */ 190 /* Sanity check for atom_len */
171 if (atom_len < sizeof(struct prism2_scan_apinfo)) { 191 if (atom_len < sizeof(struct prism2_scan_apinfo)) {
172 printk(KERN_ERR "%s: Invalid atom_len in scan " 192 printk(KERN_ERR "%s: Invalid atom_len in scan "
173 "data: %d\n", priv->ndev->name, 193 "data: %zu\n", priv->ndev->name,
174 atom_len); 194 atom_len);
175 return -EIO; 195 abort = true;
196 goto scan_abort;
176 } 197 }
177 } else 198 } else
178 atom_len = offsetof(struct prism2_scan_apinfo, atim); 199 atom_len = offsetof(struct prism2_scan_apinfo, atim);
179 break; 200 break;
201
180 default: 202 default:
181 return -EOPNOTSUPP; 203 abort = true;
204 goto scan_abort;
182 } 205 }
183 206
184 /* Check that we got an whole number of atoms */ 207 /* Check that we got an whole number of atoms */
185 if ((len - offset) % atom_len) { 208 if ((len - offset) % atom_len) {
186 printk(KERN_ERR "%s: Unexpected scan data length %d, " 209 printk(KERN_ERR "%s: Unexpected scan data length %zu, "
187 "atom_len %d, offset %d\n", priv->ndev->name, len, 210 "atom_len %zu, offset %d\n", priv->ndev->name, len,
188 atom_len, offset); 211 atom_len, offset);
189 return -EIO; 212 abort = true;
213 goto scan_abort;
190 } 214 }
191 215
192 orinoco_clear_scan_results(priv, msecs_to_jiffies(15000)); 216 /* Process the entries one by one */
193
194 /* Read the entries one by one */
195 for (; offset + atom_len <= len; offset += atom_len) { 217 for (; offset + atom_len <= len; offset += atom_len) {
196 int found = 0; 218 union hermes_scan_info *atom;
197 struct bss_element *bss = NULL;
198 219
199 /* Get next atom */
200 atom = (union hermes_scan_info *) (buf + offset); 220 atom = (union hermes_scan_info *) (buf + offset);
201 221
202 /* Try to update an existing bss first */ 222 orinoco_add_hostscan_result(priv, atom);
203 list_for_each_entry(bss, &priv->bss_list, list) {
204 if (compare_ether_addr(bss->bss.a.bssid, atom->a.bssid))
205 continue;
206 if (le16_to_cpu(bss->bss.a.essid_len) !=
207 le16_to_cpu(atom->a.essid_len))
208 continue;
209 if (memcmp(bss->bss.a.essid, atom->a.essid,
210 le16_to_cpu(atom->a.essid_len)))
211 continue;
212 found = 1;
213 break;
214 }
215
216 /* Grab a bss off the free list */
217 if (!found && !list_empty(&priv->bss_free_list)) {
218 bss = list_entry(priv->bss_free_list.next,
219 struct bss_element, list);
220 list_del(priv->bss_free_list.next);
221
222 list_add_tail(&bss->list, &priv->bss_list);
223 }
224
225 if (bss) {
226 /* Always update the BSS to get latest beacon info */
227 memcpy(&bss->bss, atom, sizeof(bss->bss));
228 bss->last_scanned = jiffies;
229 }
230 } 223 }
231 224
232 return 0; 225 scan_abort:
226 if (priv->scan_request) {
227 cfg80211_scan_done(priv->scan_request, abort);
228 priv->scan_request = NULL;
229 }
233} 230}