Merge branch 'sh-latest' of git://git.kernel.org/pub/scm/linux/kernel/git/lethal...
[pandora-kernel.git] / drivers / staging / ath6kl / wlan / src / wlan_recv_beacon.c
1 //------------------------------------------------------------------------------
2 // <copyright file="wlan_recv_beacon.c" company="Atheros">
3 //    Copyright (c) 2004-2010 Atheros Corporation.  All rights reserved.
4 // 
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // IEEE 802.11 input handling.
22 //
23 // Author(s): ="Atheros"
24 //==============================================================================
25
26 #include "a_config.h"
27 #include "athdefs.h"
28 #include "a_osapi.h"
29 #include <wmi.h>
30 #include <ieee80211.h>
31 #include <wlan_api.h>
32
33 #define IEEE80211_VERIFY_LENGTH(_len, _minlen) do {         \
34     if ((_len) < (_minlen)) {                   \
35         return A_EINVAL;                         \
36     }                               \
37 } while (0)
38
39 #define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do {         \
40     if ((__elem) == NULL) {                     \
41         return A_EINVAL;                         \
42     }                               \
43     if ((__elem)[1] > (__maxlen)) {                 \
44         return A_EINVAL;                         \
45     }                               \
46 } while (0)
47
48
49 /* unaligned little endian access */
50 #define LE_READ_2(p)                            \
51     ((u16)                            \
52      ((((u8 *)(p))[0]      ) | (((u8 *)(p))[1] <<  8)))
53
54 #define LE_READ_4(p)                            \
55     ((u32)                            \
56      ((((u8 *)(p))[0]      ) | (((u8 *)(p))[1] <<  8) | \
57       (((u8 *)(p))[2] << 16) | (((u8 *)(p))[3] << 24)))
58
59
60 static int __inline
61 iswpaoui(const u8 *frm)
62 {
63     return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
64 }
65
66 static int __inline
67 iswmmoui(const u8 *frm)
68 {
69     return frm[1] > 3 && LE_READ_4(frm+2) == ((WMM_OUI_TYPE<<24)|WMM_OUI);
70 }
71
72 /* unused functions for now */
73 #if 0
74 static int __inline
75 iswmmparam(const u8 *frm)
76 {
77     return frm[1] > 5 && frm[6] == WMM_PARAM_OUI_SUBTYPE;
78 }
79
80 static int __inline
81 iswmminfo(const u8 *frm)
82 {
83     return frm[1] > 5 && frm[6] == WMM_INFO_OUI_SUBTYPE;
84 }
85 #endif
86
87 static int __inline
88 isatherosoui(const u8 *frm)
89 {
90     return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
91 }
92
93 static int __inline
94 iswscoui(const u8 *frm)
95 {
96     return frm[1] > 3 && LE_READ_4(frm+2) == ((0x04<<24)|WPA_OUI);
97 }
98
99 int
100 wlan_parse_beacon(u8 *buf, int framelen, struct ieee80211_common_ie *cie)
101 {
102     u8 *frm, *efrm;
103     u8 elemid_ssid = false;
104
105     frm = buf;
106     efrm = (u8 *) (frm + framelen);
107
108     /*
109      * beacon/probe response frame format
110      *  [8] time stamp
111      *  [2] beacon interval
112      *  [2] capability information
113      *  [tlv] ssid
114      *  [tlv] supported rates
115      *  [tlv] country information
116      *  [tlv] parameter set (FH/DS)
117      *  [tlv] erp information
118      *  [tlv] extended supported rates
119      *  [tlv] WMM
120      *  [tlv] WPA or RSN
121      *  [tlv] Atheros Advanced Capabilities
122      */
123     IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
124     A_MEMZERO(cie, sizeof(*cie));
125
126     cie->ie_tstamp = frm; frm += 8;
127     cie->ie_beaconInt = A_LE2CPU16(*(u16 *)frm);  frm += 2;
128     cie->ie_capInfo = A_LE2CPU16(*(u16 *)frm);  frm += 2;
129     cie->ie_chan = 0;
130
131     while (frm < efrm) {
132         switch (*frm) {
133         case IEEE80211_ELEMID_SSID:
134             if (!elemid_ssid) {
135                 cie->ie_ssid = frm;
136                 elemid_ssid = true;
137             }
138             break;
139         case IEEE80211_ELEMID_RATES:
140             cie->ie_rates = frm;
141             break;
142         case IEEE80211_ELEMID_COUNTRY:
143             cie->ie_country = frm;
144             break;
145         case IEEE80211_ELEMID_FHPARMS:
146             break;
147         case IEEE80211_ELEMID_DSPARMS:
148             cie->ie_chan = frm[2];
149             break;
150         case IEEE80211_ELEMID_TIM:
151             cie->ie_tim = frm;
152             break;
153         case IEEE80211_ELEMID_IBSSPARMS:
154             break;
155         case IEEE80211_ELEMID_XRATES:
156             cie->ie_xrates = frm;
157             break;
158         case IEEE80211_ELEMID_ERP:
159             if (frm[1] != 1) {
160                 //A_PRINTF("Discarding ERP Element - Bad Len\n");
161                 return A_EINVAL;
162             }
163             cie->ie_erp = frm[2];
164             break;
165         case IEEE80211_ELEMID_RSN:
166             cie->ie_rsn = frm;
167             break;
168         case IEEE80211_ELEMID_HTCAP_ANA:
169             cie->ie_htcap = frm;
170             break;
171         case IEEE80211_ELEMID_HTINFO_ANA:
172             cie->ie_htop = frm;
173             break;
174 #ifdef WAPI_ENABLE
175                 case IEEE80211_ELEMID_WAPI:
176             cie->ie_wapi = frm;
177             break;
178 #endif
179         case IEEE80211_ELEMID_VENDOR:
180             if (iswpaoui(frm)) {
181                 cie->ie_wpa = frm;
182             } else if (iswmmoui(frm)) {
183                 cie->ie_wmm = frm;
184             } else if (isatherosoui(frm)) {
185                 cie->ie_ath = frm;
186             } else if(iswscoui(frm)) {
187                 cie->ie_wsc = frm;
188             }
189             break;
190         default:
191             break;
192         }
193         frm += frm[1] + 2;
194     }
195     IEEE80211_VERIFY_ELEMENT(cie->ie_rates, IEEE80211_RATE_MAXSIZE);
196     IEEE80211_VERIFY_ELEMENT(cie->ie_ssid, IEEE80211_NWID_LEN);
197
198     return 0;
199 }