diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/scan.c')
-rw-r--r-- | drivers/net/wireless/orinoco/scan.c | 291 |
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 | ||
20 | int orinoco_bss_data_allocate(struct orinoco_private *priv) | 26 | static 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 | ||
41 | void 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 | ||
47 | void orinoco_bss_data_init(struct orinoco_private *priv) | 44 | static 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; | |
64 | void 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 | ||
100 | void orinoco_add_ext_scan_result(struct orinoco_private *priv, | 73 | static 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 | ||
136 | int orinoco_process_scan_results(struct orinoco_private *priv, | 123 | void 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 | |||
154 | void 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 | } |