Merge branch 'fix/asoc' into for-linus
[pandora-kernel.git] / drivers / net / wireless / orinoco / scan.c
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 }