]>
Commit | Line | Data |
---|---|---|
fb791b1c DK |
1 | /* Helpers for managing scan queues |
2 | * | |
3 | * See copyright notice in main.c | |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
7 | #include <linux/string.h> | |
8 | #include <linux/etherdevice.h> | |
9 | ||
10 | #include "hermes.h" | |
11 | #include "orinoco.h" | |
12 | ||
13 | #include "scan.h" | |
14 | ||
15 | #define ORINOCO_MAX_BSS_COUNT 64 | |
16 | ||
17 | #define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data) | |
18 | #define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data) | |
19 | ||
20 | int orinoco_bss_data_allocate(struct orinoco_private *priv) | |
21 | { | |
22 | if (priv->bss_xbss_data) | |
23 | return 0; | |
24 | ||
25 | if (priv->has_ext_scan) | |
26 | priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * | |
27 | sizeof(struct xbss_element), | |
28 | GFP_KERNEL); | |
29 | else | |
30 | priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT * | |
31 | sizeof(struct bss_element), | |
32 | GFP_KERNEL); | |
33 | ||
34 | if (!priv->bss_xbss_data) { | |
35 | printk(KERN_WARNING "Out of memory allocating beacons"); | |
36 | return -ENOMEM; | |
37 | } | |
38 | return 0; | |
39 | } | |
40 | ||
41 | void orinoco_bss_data_free(struct orinoco_private *priv) | |
42 | { | |
43 | kfree(priv->bss_xbss_data); | |
44 | priv->bss_xbss_data = NULL; | |
45 | } | |
46 | ||
47 | void orinoco_bss_data_init(struct orinoco_private *priv) | |
48 | { | |
49 | int i; | |
50 | ||
51 | INIT_LIST_HEAD(&priv->bss_free_list); | |
52 | INIT_LIST_HEAD(&priv->bss_list); | |
53 | if (priv->has_ext_scan) | |
54 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) | |
55 | list_add_tail(&(PRIV_XBSS[i].list), | |
56 | &priv->bss_free_list); | |
57 | else | |
58 | for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++) | |
59 | list_add_tail(&(PRIV_BSS[i].list), | |
60 | &priv->bss_free_list); | |
61 | ||
62 | } | |
63 | ||
64 | void orinoco_clear_scan_results(struct orinoco_private *priv, | |
65 | unsigned long scan_age) | |
66 | { | |
67 | if (priv->has_ext_scan) { | |
68 | struct xbss_element *bss; | |
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 | } | |
97 | } | |
98 | } | |
99 | ||
100 | void orinoco_add_ext_scan_result(struct orinoco_private *priv, | |
101 | struct agere_ext_scan_info *atom) | |
102 | { | |
103 | struct xbss_element *bss = NULL; | |
104 | int found = 0; | |
105 | ||
106 | /* Try to update an existing bss first */ | |
107 | list_for_each_entry(bss, &priv->bss_list, list) { | |
108 | if (compare_ether_addr(bss->bss.bssid, atom->bssid)) | |
109 | continue; | |
110 | /* ESSID lengths */ | |
111 | if (bss->bss.data[1] != atom->data[1]) | |
112 | continue; | |
113 | if (memcmp(&bss->bss.data[2], &atom->data[2], | |
114 | atom->data[1])) | |
115 | continue; | |
116 | found = 1; | |
117 | break; | |
118 | } | |
119 | ||
120 | /* Grab a bss off the free list */ | |
121 | if (!found && !list_empty(&priv->bss_free_list)) { | |
122 | bss = list_entry(priv->bss_free_list.next, | |
123 | struct xbss_element, list); | |
124 | list_del(priv->bss_free_list.next); | |
125 | ||
126 | list_add_tail(&bss->list, &priv->bss_list); | |
127 | } | |
128 | ||
129 | if (bss) { | |
130 | /* Always update the BSS to get latest beacon info */ | |
131 | memcpy(&bss->bss, atom, sizeof(bss->bss)); | |
132 | bss->last_scanned = jiffies; | |
133 | } | |
134 | } | |
135 | ||
136 | int orinoco_process_scan_results(struct orinoco_private *priv, | |
137 | unsigned char *buf, | |
138 | int len) | |
139 | { | |
140 | int offset; /* In the scan data */ | |
141 | union hermes_scan_info *atom; | |
142 | int atom_len; | |
143 | ||
144 | switch (priv->firmware_type) { | |
145 | case FIRMWARE_TYPE_AGERE: | |
146 | atom_len = sizeof(struct agere_scan_apinfo); | |
147 | offset = 0; | |
148 | break; | |
149 | case FIRMWARE_TYPE_SYMBOL: | |
150 | /* Lack of documentation necessitates this hack. | |
151 | * Different firmwares have 68 or 76 byte long atoms. | |
152 | * We try modulo first. If the length divides by both, | |
153 | * we check what would be the channel in the second | |
154 | * frame for a 68-byte atom. 76-byte atoms have 0 there. | |
155 | * Valid channel cannot be 0. */ | |
156 | if (len % 76) | |
157 | atom_len = 68; | |
158 | else if (len % 68) | |
159 | atom_len = 76; | |
160 | else if (len >= 1292 && buf[68] == 0) | |
161 | atom_len = 76; | |
162 | else | |
163 | atom_len = 68; | |
164 | offset = 0; | |
165 | break; | |
166 | case FIRMWARE_TYPE_INTERSIL: | |
167 | offset = 4; | |
168 | if (priv->has_hostscan) { | |
169 | atom_len = le16_to_cpup((__le16 *)buf); | |
170 | /* Sanity check for atom_len */ | |
171 | if (atom_len < sizeof(struct prism2_scan_apinfo)) { | |
172 | printk(KERN_ERR "%s: Invalid atom_len in scan " | |
173 | "data: %d\n", priv->ndev->name, | |
174 | atom_len); | |
175 | return -EIO; | |
176 | } | |
177 | } else | |
178 | atom_len = offsetof(struct prism2_scan_apinfo, atim); | |
179 | break; | |
180 | default: | |
181 | return -EOPNOTSUPP; | |
182 | } | |
183 | ||
184 | /* Check that we got an whole number of atoms */ | |
185 | if ((len - offset) % atom_len) { | |
186 | printk(KERN_ERR "%s: Unexpected scan data length %d, " | |
187 | "atom_len %d, offset %d\n", priv->ndev->name, len, | |
188 | atom_len, offset); | |
189 | return -EIO; | |
190 | } | |
191 | ||
192 | orinoco_clear_scan_results(priv, msecs_to_jiffies(15000)); | |
193 | ||
194 | /* Read the entries one by one */ | |
195 | for (; offset + atom_len <= len; offset += atom_len) { | |
196 | int found = 0; | |
197 | struct bss_element *bss = NULL; | |
198 | ||
199 | /* Get next atom */ | |
200 | atom = (union hermes_scan_info *) (buf + offset); | |
201 | ||
202 | /* Try to update an existing bss first */ | |
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 | } | |
231 | ||
232 | return 0; | |
233 | } |