aboutsummaryrefslogtreecommitdiff
path: root/drivers/ar6000/wlan
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ar6000/wlan')
-rw-r--r--drivers/ar6000/wlan/wlan_node.c371
-rw-r--r--drivers/ar6000/wlan/wlan_recv_beacon.c192
-rw-r--r--drivers/ar6000/wlan/wlan_utils.c59
3 files changed, 622 insertions, 0 deletions
diff --git a/drivers/ar6000/wlan/wlan_node.c b/drivers/ar6000/wlan/wlan_node.c
new file mode 100644
index 00000000000..b12484597f8
--- /dev/null
+++ b/drivers/ar6000/wlan/wlan_node.c
@@ -0,0 +1,371 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * Copyright (c) 2004-2005 Atheros Communications
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: //depot/sw/releases/olca2.0-GPL/host/wlan/src/wlan_node.c#1 $
+ */
+/*
+ * IEEE 802.11 node handling support.
+ */
+#include <a_config.h>
+#include <athdefs.h>
+#include <a_types.h>
+#include <a_osapi.h>
+#include <a_debug.h>
+#include <ieee80211.h>
+#include <wlan_api.h>
+#include <ieee80211_node.h>
+#include <htc_api.h>
+#include <wmi.h>
+#include <wmi_api.h>
+
+static void wlan_node_timeout(A_ATH_TIMER arg);
+static bss_t * _ieee80211_find_node(struct ieee80211_node_table *nt,
+ const A_UINT8 *macaddr);
+
+bss_t *
+wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size)
+{
+ bss_t *ni;
+
+ ni = A_MALLOC_NOWAIT(sizeof(bss_t));
+
+ if (ni != NULL) {
+ ni->ni_buf = A_MALLOC_NOWAIT(wh_size);
+ if (ni->ni_buf == NULL) {
+ A_FREE(ni);
+ ni = NULL;
+ return ni;
+ }
+ } else {
+ return ni;
+ }
+
+ /* Make sure our lists are clean */
+ ni->ni_list_next = NULL;
+ ni->ni_list_prev = NULL;
+ ni->ni_hash_next = NULL;
+ ni->ni_hash_prev = NULL;
+
+ //
+ // ni_scangen never initialized before and during suspend/resume of winmobile, customer (LG/SEMCO) identified
+ // that some junk has been stored in this, due to this scan list didn't properly updated
+ //
+ ni->ni_scangen = 0;
+
+ return ni;
+}
+
+void
+wlan_node_free(bss_t *ni)
+{
+ if (ni->ni_buf != NULL) {
+ A_FREE(ni->ni_buf);
+ }
+ A_FREE(ni);
+}
+
+void
+wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
+ const A_UINT8 *macaddr)
+{
+ int hash;
+
+ A_MEMCPY(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN);
+ hash = IEEE80211_NODE_HASH(macaddr);
+ ieee80211_node_initref(ni); /* mark referenced */
+
+ ni->ni_tstamp = A_GET_MS(WLAN_NODE_INACT_TIMEOUT_MSEC);
+ IEEE80211_NODE_LOCK_BH(nt);
+
+ /* Insert at the end of the node list */
+ ni->ni_list_next = NULL;
+ ni->ni_list_prev = nt->nt_node_last;
+ if(nt->nt_node_last != NULL)
+ {
+ nt->nt_node_last->ni_list_next = ni;
+ }
+ nt->nt_node_last = ni;
+ if(nt->nt_node_first == NULL)
+ {
+ nt->nt_node_first = ni;
+ }
+
+ /* Insert into the hash list i.e. the bucket */
+ if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL)
+ {
+ nt->nt_hash[hash]->ni_hash_prev = ni;
+ }
+ ni->ni_hash_prev = NULL;
+ nt->nt_hash[hash] = ni;
+
+ if (!nt->isTimerArmed) {
+ A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0);
+ nt->isTimerArmed = TRUE;
+ }
+
+ IEEE80211_NODE_UNLOCK_BH(nt);
+}
+
+static bss_t *
+_ieee80211_find_node(struct ieee80211_node_table *nt,
+ const A_UINT8 *macaddr)
+{
+ bss_t *ni;
+ int hash;
+
+ IEEE80211_NODE_LOCK_ASSERT(nt);
+
+ hash = IEEE80211_NODE_HASH(macaddr);
+ for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
+ if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
+ ieee80211_node_incref(ni); /* mark referenced */
+ return ni;
+ }
+ }
+ return NULL;
+}
+
+bss_t *
+wlan_find_node(struct ieee80211_node_table *nt, const A_UINT8 *macaddr)
+{
+ bss_t *ni;
+
+ IEEE80211_NODE_LOCK(nt);
+ ni = _ieee80211_find_node(nt, macaddr);
+ IEEE80211_NODE_UNLOCK(nt);
+ return ni;
+}
+
+/*
+ * Reclaim a node. If this is the last reference count then
+ * do the normal free work. Otherwise remove it from the node
+ * table and mark it gone by clearing the back-reference.
+ */
+void
+wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni)
+{
+ IEEE80211_NODE_LOCK(nt);
+
+ if(ni->ni_list_prev == NULL)
+ {
+ /* First in list so fix the list head */
+ nt->nt_node_first = ni->ni_list_next;
+ }
+ else
+ {
+ ni->ni_list_prev->ni_list_next = ni->ni_list_next;
+ }
+
+ if(ni->ni_list_next == NULL)
+ {
+ /* Last in list so fix list tail */
+ nt->nt_node_last = ni->ni_list_prev;
+ }
+ else
+ {
+ ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
+ }
+
+ if(ni->ni_hash_prev == NULL)
+ {
+ /* First in list so fix the list head */
+ int hash;
+ hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
+ nt->nt_hash[hash] = ni->ni_hash_next;
+ }
+ else
+ {
+ ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
+ }
+
+ if(ni->ni_hash_next != NULL)
+ {
+ ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
+ }
+ wlan_node_free(ni);
+
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+static void
+wlan_node_dec_free(bss_t *ni)
+{
+ if (ieee80211_node_dectestref(ni)) {
+ wlan_node_free(ni);
+ }
+}
+
+void
+wlan_free_allnodes(struct ieee80211_node_table *nt)
+{
+ bss_t *ni;
+
+ while ((ni = nt->nt_node_first) != NULL) {
+ wlan_node_reclaim(nt, ni);
+ }
+}
+
+void
+wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
+ void *arg)
+{
+ bss_t *ni;
+ A_UINT32 gen;
+
+ gen = ++nt->nt_scangen;
+
+ IEEE80211_NODE_LOCK(nt);
+ for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
+ if (ni->ni_scangen != gen) {
+ ni->ni_scangen = gen;
+ (void) ieee80211_node_incref(ni);
+ (*f)(arg, ni);
+ wlan_node_dec_free(ni);
+ }
+ }
+ IEEE80211_NODE_UNLOCK(nt);
+}
+
+/*
+ * Node table support.
+ */
+void
+wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt)
+{
+ int i;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%x\n", (A_UINT32)nt));
+ IEEE80211_NODE_LOCK_INIT(nt);
+
+ nt->nt_node_first = nt->nt_node_last = NULL;
+ for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
+ {
+ nt->nt_hash[i] = NULL;
+ }
+ A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt);
+ nt->isTimerArmed = FALSE;
+ nt->nt_wmip = wmip;
+}
+
+static void
+wlan_node_timeout(A_ATH_TIMER arg)
+{
+ struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg;
+ bss_t *bss, *nextBss;
+ A_UINT8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = FALSE;
+
+ wmi_get_current_bssid(nt->nt_wmip, myBssid);
+
+ bss = nt->nt_node_first;
+ while (bss != NULL)
+ {
+ nextBss = bss->ni_list_next;
+ if (A_MEMCMP(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
+ {
+
+ if (bss->ni_tstamp <= A_GET_MS(0))
+ {
+ /*
+ * free up all but the current bss - if set
+ */
+ wlan_node_reclaim(nt, bss);
+ }
+ else
+ {
+ /*
+ * Re-arm timer, only when we have a bss other than
+ * current bss AND it is not aged-out.
+ */
+ reArmTimer = TRUE;
+ }
+ }
+ bss = nextBss;
+ }
+
+ if(reArmTimer)
+ A_TIMEOUT_MS(&nt->nt_inact_timer, WLAN_NODE_INACT_TIMEOUT_MSEC, 0);
+
+ nt->isTimerArmed = reArmTimer;
+}
+
+void
+wlan_node_table_cleanup(struct ieee80211_node_table *nt)
+{
+ A_UNTIMEOUT(&nt->nt_inact_timer);
+ A_DELETE_TIMER(&nt->nt_inact_timer);
+ wlan_free_allnodes(nt);
+ IEEE80211_NODE_LOCK_DESTROY(nt);
+}
+
+bss_t *
+wlan_find_Ssidnode (struct ieee80211_node_table *nt, A_UCHAR *pSsid,
+ A_UINT32 ssidLength, A_BOOL bIsWPA2)
+{
+ bss_t *ni = NULL;
+ A_UCHAR *pIESsid = NULL;
+
+ IEEE80211_NODE_LOCK (nt);
+
+ for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
+ pIESsid = ni->ni_cie.ie_ssid;
+ if (pIESsid[1] <= 32) {
+
+ // Step 1 : Check SSID
+ if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) {
+
+ // Step 2 : if SSID matches, check WPA or WPA2
+ if (TRUE == bIsWPA2 && NULL != ni->ni_cie.ie_rsn) {
+ ieee80211_node_incref (ni); /* mark referenced */
+ IEEE80211_NODE_UNLOCK (nt);
+ return ni;
+ }
+ if (FALSE == bIsWPA2 && NULL != ni->ni_cie.ie_wpa) {
+ ieee80211_node_incref(ni); /* mark referenced */
+ IEEE80211_NODE_UNLOCK (nt);
+ return ni;
+ }
+ }
+ }
+ }
+
+ IEEE80211_NODE_UNLOCK (nt);
+
+ return NULL;
+}
+
+void
+wlan_node_return (struct ieee80211_node_table *nt, bss_t *ni)
+{
+ IEEE80211_NODE_LOCK (nt);
+ wlan_node_dec_free (ni);
+ IEEE80211_NODE_UNLOCK (nt);
+}
diff --git a/drivers/ar6000/wlan/wlan_recv_beacon.c b/drivers/ar6000/wlan/wlan_recv_beacon.c
new file mode 100644
index 00000000000..15beabb6549
--- /dev/null
+++ b/drivers/ar6000/wlan/wlan_recv_beacon.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2001 Atsushi Onoe
+ * Copyright (c) 2002-2004 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * IEEE 802.11 input handling.
+ */
+
+#include "a_config.h"
+#include "athdefs.h"
+#include "a_types.h"
+#include "a_osapi.h"
+#include <wmi.h>
+#include <ieee80211.h>
+#include <wlan_api.h>
+
+#define IEEE80211_VERIFY_LENGTH(_len, _minlen) do { \
+ if ((_len) < (_minlen)) { \
+ return A_EINVAL; \
+ } \
+} while (0)
+
+#define IEEE80211_VERIFY_ELEMENT(__elem, __maxlen) do { \
+ if ((__elem) == NULL) { \
+ return A_EINVAL; \
+ } \
+ if ((__elem)[1] > (__maxlen)) { \
+ return A_EINVAL; \
+ } \
+} while (0)
+
+
+/* unaligned little endian access */
+#define LE_READ_2(p) \
+ ((A_UINT16) \
+ ((((A_UINT8 *)(p))[0] ) | (((A_UINT8 *)(p))[1] << 8)))
+
+#define LE_READ_4(p) \
+ ((A_UINT32) \
+ ((((A_UINT8 *)(p))[0] ) | (((A_UINT8 *)(p))[1] << 8) | \
+ (((A_UINT8 *)(p))[2] << 16) | (((A_UINT8 *)(p))[3] << 24)))
+
+
+static int __inline
+iswpaoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI);
+}
+
+static int __inline
+iswmmoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((WMM_OUI_TYPE<<24)|WMM_OUI);
+}
+
+static int __inline
+iswmmparam(const A_UINT8 *frm)
+{
+ return frm[1] > 5 && frm[6] == WMM_PARAM_OUI_SUBTYPE;
+}
+
+static int __inline
+iswmminfo(const A_UINT8 *frm)
+{
+ return frm[1] > 5 && frm[6] == WMM_INFO_OUI_SUBTYPE;
+}
+
+static int __inline
+isatherosoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((ATH_OUI_TYPE<<24)|ATH_OUI);
+}
+
+static int __inline
+iswscoui(const A_UINT8 *frm)
+{
+ return frm[1] > 3 && LE_READ_4(frm+2) == ((0x04<<24)|WPA_OUI);
+}
+
+A_STATUS
+wlan_parse_beacon(A_UINT8 *buf, int framelen, struct ieee80211_common_ie *cie)
+{
+ A_UINT8 *frm, *efrm;
+
+ frm = buf;
+ efrm = (A_UINT8 *) (frm + framelen);
+
+ /*
+ * beacon/probe response frame format
+ * [8] time stamp
+ * [2] beacon interval
+ * [2] capability information
+ * [tlv] ssid
+ * [tlv] supported rates
+ * [tlv] country information
+ * [tlv] parameter set (FH/DS)
+ * [tlv] erp information
+ * [tlv] extended supported rates
+ * [tlv] WMM
+ * [tlv] WPA or RSN
+ * [tlv] Atheros Advanced Capabilities
+ */
+ IEEE80211_VERIFY_LENGTH(efrm - frm, 12);
+ A_MEMZERO(cie, sizeof(*cie));
+
+ cie->ie_tstamp = frm; frm += 8;
+ cie->ie_beaconInt = A_LE2CPU16(*(A_UINT16 *)frm); frm += 2;
+ cie->ie_capInfo = A_LE2CPU16(*(A_UINT16 *)frm); frm += 2;
+ cie->ie_chan = 0;
+
+ while (frm < efrm) {
+ switch (*frm) {
+ case IEEE80211_ELEMID_SSID:
+ cie->ie_ssid = frm;
+ break;
+ case IEEE80211_ELEMID_RATES:
+ cie->ie_rates = frm;
+ break;
+ case IEEE80211_ELEMID_COUNTRY:
+ cie->ie_country = frm;
+ break;
+ case IEEE80211_ELEMID_FHPARMS:
+ break;
+ case IEEE80211_ELEMID_DSPARMS:
+ cie->ie_chan = frm[2];
+ break;
+ case IEEE80211_ELEMID_TIM:
+ cie->ie_tim = frm;
+ break;
+ case IEEE80211_ELEMID_IBSSPARMS:
+ break;
+ case IEEE80211_ELEMID_XRATES:
+ cie->ie_xrates = frm;
+ break;
+ case IEEE80211_ELEMID_ERP:
+ if (frm[1] != 1) {
+ //A_PRINTF("Discarding ERP Element - Bad Len\n");
+ return A_EINVAL;
+ }
+ cie->ie_erp = frm[2];
+ break;
+ case IEEE80211_ELEMID_RSN:
+ cie->ie_rsn = frm;
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (iswpaoui(frm)) {
+ cie->ie_wpa = frm;
+ } else if (iswmmoui(frm)) {
+ cie->ie_wmm = frm;
+ } else if (isatherosoui(frm)) {
+ cie->ie_ath = frm;
+ } else if(iswscoui(frm)) {
+ cie->ie_wsc = frm;
+ }
+ break;
+ default:
+ break;
+ }
+ frm += frm[1] + 2;
+ }
+ IEEE80211_VERIFY_ELEMENT(cie->ie_rates, IEEE80211_RATE_MAXSIZE);
+ IEEE80211_VERIFY_ELEMENT(cie->ie_ssid, IEEE80211_NWID_LEN);
+
+ return A_OK;
+}
diff --git a/drivers/ar6000/wlan/wlan_utils.c b/drivers/ar6000/wlan/wlan_utils.c
new file mode 100644
index 00000000000..fd5aac90e61
--- /dev/null
+++ b/drivers/ar6000/wlan/wlan_utils.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2004-2005 Atheros Communications Inc.
+ * All rights reserved.
+ *
+ * This module implements frequently used wlan utilies
+ *
+ * $Id: //depot/sw/releases/olca2.0-GPL/host/wlan/src/wlan_utils.c#1 $
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ *
+ *
+ */
+
+#include <a_config.h>
+#include <athdefs.h>
+#include <a_types.h>
+#include <a_osapi.h>
+
+/*
+ * converts ieee channel number to frequency
+ */
+A_UINT16
+wlan_ieee2freq(int chan)
+{
+ if (chan == 14) {
+ return 2484;
+ }
+ if (chan < 14) { /* 0-13 */
+ return (2407 + (chan*5));
+ }
+ if (chan < 27) { /* 15-26 */
+ return (2512 + ((chan-15)*20));
+ }
+ return (5000 + (chan*5));
+}
+
+/*
+ * Converts MHz frequency to IEEE channel number.
+ */
+A_UINT32
+wlan_freq2ieee(A_UINT16 freq)
+{
+ if (freq == 2484)
+ return 14;
+ if (freq < 2484)
+ return (freq - 2407) / 5;
+ if (freq < 5000)
+ return 15 + ((freq - 2512) / 20);
+ return (freq - 5000) / 5;
+}