]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/net/wireless/orinoco/scan.c
orinoco: Move scan helpers to a separate file
[mirror_ubuntu-artful-kernel.git] / drivers / net / wireless / orinoco / scan.c
CommitLineData
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
20int 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
41void orinoco_bss_data_free(struct orinoco_private *priv)
42{
43 kfree(priv->bss_xbss_data);
44 priv->bss_xbss_data = NULL;
45}
46
47void 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
64void 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
100void 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
136int 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}