From 4dbd5b751767bf88ffdda5d55646738871c751ad Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 23 Sep 2009 14:27:26 +0200 Subject: Add ar6000 wireless driver. --- drivers/ar6000/wlan/wlan_node.c | 371 +++++++++++++++++++++++++++++++++ drivers/ar6000/wlan/wlan_recv_beacon.c | 192 +++++++++++++++++ drivers/ar6000/wlan/wlan_utils.c | 59 ++++++ 3 files changed, 622 insertions(+) create mode 100644 drivers/ar6000/wlan/wlan_node.c create mode 100644 drivers/ar6000/wlan/wlan_recv_beacon.c create mode 100644 drivers/ar6000/wlan/wlan_utils.c (limited to 'drivers/ar6000/wlan') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include + +#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 +#include +#include +#include + +/* + * 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; +} -- cgit v1.2.3