mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 17:59:32 +00:00
![Larry Finger](/assets/img/avatar_default.png)
When this driver was upgraded to the vendor 20100831 version in
commit 93c55dda09
et al,, one listhead initialization was missed.
This broke complete operation of the driver whenever AP mode was
enabled. This fixes https://bugs.archlinux.org/task/27996.
The configuration parameter R8712_AP is misleading as the driver cannot
function as an AP without a heavily hacked version of hostapd. Thus, it
makes sense to remove the parameter; however the code and data configured
for the option is left in.
Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
296 lines
9.3 KiB
C
296 lines
9.3 KiB
C
/******************************************************************************
|
|
* rtl871x_sta_mgt.c
|
|
*
|
|
* Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
|
|
* Linux device driver for RTL8192SU
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of version 2 of the GNU General Public License as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
|
|
*
|
|
* Modifications for inclusion into the Linux staging tree are
|
|
* Copyright(c) 2010 Larry Finger. All rights reserved.
|
|
*
|
|
* Contact information:
|
|
* WLAN FAE <wlanfae@realtek.com>
|
|
* Larry Finger <Larry.Finger@lwfinger.net>
|
|
*
|
|
******************************************************************************/
|
|
|
|
#define _RTL871X_STA_MGT_C_
|
|
|
|
#include "osdep_service.h"
|
|
#include "drv_types.h"
|
|
#include "recv_osdep.h"
|
|
#include "xmit_osdep.h"
|
|
#include "sta_info.h"
|
|
|
|
static void _init_stainfo(struct sta_info *psta)
|
|
{
|
|
memset((u8 *)psta, 0, sizeof(struct sta_info));
|
|
spin_lock_init(&psta->lock);
|
|
_init_listhead(&psta->list);
|
|
_init_listhead(&psta->hash_list);
|
|
_r8712_init_sta_xmit_priv(&psta->sta_xmitpriv);
|
|
_r8712_init_sta_recv_priv(&psta->sta_recvpriv);
|
|
_init_listhead(&psta->asoc_list);
|
|
_init_listhead(&psta->auth_list);
|
|
}
|
|
|
|
u32 _r8712_init_sta_priv(struct sta_priv *pstapriv)
|
|
{
|
|
struct sta_info *psta;
|
|
s32 i;
|
|
|
|
pstapriv->pallocated_stainfo_buf = _malloc(sizeof(struct sta_info) *
|
|
NUM_STA + 4);
|
|
if (pstapriv->pallocated_stainfo_buf == NULL)
|
|
return _FAIL;
|
|
pstapriv->pstainfo_buf = pstapriv->pallocated_stainfo_buf + 4 -
|
|
((addr_t)(pstapriv->pallocated_stainfo_buf) & 3);
|
|
_init_queue(&pstapriv->free_sta_queue);
|
|
spin_lock_init(&pstapriv->sta_hash_lock);
|
|
pstapriv->asoc_sta_count = 0;
|
|
_init_queue(&pstapriv->sleep_q);
|
|
_init_queue(&pstapriv->wakeup_q);
|
|
psta = (struct sta_info *)(pstapriv->pstainfo_buf);
|
|
for (i = 0; i < NUM_STA; i++) {
|
|
_init_stainfo(psta);
|
|
_init_listhead(&(pstapriv->sta_hash[i]));
|
|
list_insert_tail(&psta->list,
|
|
get_list_head(&pstapriv->free_sta_queue));
|
|
psta++;
|
|
}
|
|
_init_listhead(&pstapriv->asoc_list);
|
|
_init_listhead(&pstapriv->auth_list);
|
|
return _SUCCESS;
|
|
}
|
|
|
|
/* this function is used to free the memory of lock || sema for all stainfos */
|
|
static void mfree_all_stainfo(struct sta_priv *pstapriv)
|
|
{
|
|
unsigned long irqL;
|
|
struct list_head *plist, *phead;
|
|
struct sta_info *psta = NULL;
|
|
|
|
spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
|
|
phead = get_list_head(&pstapriv->free_sta_queue);
|
|
plist = get_next(phead);
|
|
while ((end_of_queue_search(phead, plist)) == false) {
|
|
psta = LIST_CONTAINOR(plist, struct sta_info, list);
|
|
plist = get_next(plist);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
|
|
}
|
|
|
|
|
|
static void mfree_sta_priv_lock(struct sta_priv *pstapriv)
|
|
{
|
|
mfree_all_stainfo(pstapriv); /* be done before free sta_hash_lock */
|
|
}
|
|
|
|
u32 _r8712_free_sta_priv(struct sta_priv *pstapriv)
|
|
{
|
|
if (pstapriv) {
|
|
mfree_sta_priv_lock(pstapriv);
|
|
kfree(pstapriv->pallocated_stainfo_buf);
|
|
}
|
|
return _SUCCESS;
|
|
}
|
|
|
|
struct sta_info *r8712_alloc_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
|
|
{
|
|
uint tmp_aid;
|
|
s32 index;
|
|
struct list_head *phash_list;
|
|
struct sta_info *psta;
|
|
struct __queue *pfree_sta_queue;
|
|
struct recv_reorder_ctrl *preorder_ctrl;
|
|
int i = 0;
|
|
u16 wRxSeqInitialValue = 0xffff;
|
|
unsigned long flags;
|
|
|
|
pfree_sta_queue = &pstapriv->free_sta_queue;
|
|
spin_lock_irqsave(&(pfree_sta_queue->lock), flags);
|
|
if (_queue_empty(pfree_sta_queue) == true)
|
|
psta = NULL;
|
|
else {
|
|
psta = LIST_CONTAINOR(get_next(&pfree_sta_queue->queue),
|
|
struct sta_info, list);
|
|
list_delete(&(psta->list));
|
|
tmp_aid = psta->aid;
|
|
_init_stainfo(psta);
|
|
memcpy(psta->hwaddr, hwaddr, ETH_ALEN);
|
|
index = wifi_mac_hash(hwaddr);
|
|
if (index >= NUM_STA) {
|
|
psta = NULL;
|
|
goto exit;
|
|
}
|
|
phash_list = &(pstapriv->sta_hash[index]);
|
|
list_insert_tail(&psta->hash_list, phash_list);
|
|
pstapriv->asoc_sta_count++ ;
|
|
|
|
/* For the SMC router, the sequence number of first packet of WPS handshake
|
|
* will be 0. In this case, this packet will be dropped by recv_decache function
|
|
* if we use the 0x00 as the default value for tid_rxseq variable. So, we
|
|
* initialize the tid_rxseq variable as the 0xffff.
|
|
*/
|
|
for (i = 0; i < 16; i++)
|
|
memcpy(&psta->sta_recvpriv.rxcache.tid_rxseq[i],
|
|
&wRxSeqInitialValue, 2);
|
|
/* for A-MPDU Rx reordering buffer control */
|
|
for (i = 0; i < 16 ; i++) {
|
|
preorder_ctrl = &psta->recvreorder_ctrl[i];
|
|
preorder_ctrl->padapter = pstapriv->padapter;
|
|
preorder_ctrl->indicate_seq = 0xffff;
|
|
preorder_ctrl->wend_b = 0xffff;
|
|
preorder_ctrl->wsize_b = 64;
|
|
_init_queue(&preorder_ctrl->pending_recvframe_queue);
|
|
r8712_init_recv_timer(preorder_ctrl);
|
|
}
|
|
}
|
|
exit:
|
|
spin_unlock_irqrestore(&(pfree_sta_queue->lock), flags);
|
|
return psta;
|
|
}
|
|
|
|
/* using pstapriv->sta_hash_lock to protect */
|
|
void r8712_free_stainfo(struct _adapter *padapter, struct sta_info *psta)
|
|
{
|
|
int i;
|
|
unsigned long irqL0;
|
|
struct __queue *pfree_sta_queue;
|
|
struct recv_reorder_ctrl *preorder_ctrl;
|
|
struct sta_xmit_priv *pstaxmitpriv;
|
|
struct xmit_priv *pxmitpriv = &padapter->xmitpriv;
|
|
struct sta_priv *pstapriv = &padapter->stapriv;
|
|
|
|
if (psta == NULL)
|
|
return;
|
|
pfree_sta_queue = &pstapriv->free_sta_queue;
|
|
pstaxmitpriv = &psta->sta_xmitpriv;
|
|
spin_lock_irqsave(&(pxmitpriv->vo_pending.lock), irqL0);
|
|
r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vo_q.sta_pending);
|
|
list_delete(&(pstaxmitpriv->vo_q.tx_pending));
|
|
spin_unlock_irqrestore(&(pxmitpriv->vo_pending.lock), irqL0);
|
|
spin_lock_irqsave(&(pxmitpriv->vi_pending.lock), irqL0);
|
|
r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->vi_q.sta_pending);
|
|
list_delete(&(pstaxmitpriv->vi_q.tx_pending));
|
|
spin_unlock_irqrestore(&(pxmitpriv->vi_pending.lock), irqL0);
|
|
spin_lock_irqsave(&(pxmitpriv->bk_pending.lock), irqL0);
|
|
r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->bk_q.sta_pending);
|
|
list_delete(&(pstaxmitpriv->bk_q.tx_pending));
|
|
spin_unlock_irqrestore(&(pxmitpriv->bk_pending.lock), irqL0);
|
|
spin_lock_irqsave(&(pxmitpriv->be_pending.lock), irqL0);
|
|
r8712_free_xmitframe_queue(pxmitpriv, &pstaxmitpriv->be_q.sta_pending);
|
|
list_delete(&(pstaxmitpriv->be_q.tx_pending));
|
|
spin_unlock_irqrestore(&(pxmitpriv->be_pending.lock), irqL0);
|
|
list_delete(&psta->hash_list);
|
|
pstapriv->asoc_sta_count--;
|
|
/* re-init sta_info; 20061114 */
|
|
_r8712_init_sta_xmit_priv(&psta->sta_xmitpriv);
|
|
_r8712_init_sta_recv_priv(&psta->sta_recvpriv);
|
|
/* for A-MPDU Rx reordering buffer control,
|
|
* cancel reordering_ctrl_timer */
|
|
for (i = 0; i < 16; i++) {
|
|
preorder_ctrl = &psta->recvreorder_ctrl[i];
|
|
_cancel_timer_ex(&preorder_ctrl->reordering_ctrl_timer);
|
|
}
|
|
spin_lock(&(pfree_sta_queue->lock));
|
|
/* insert into free_sta_queue; 20061114 */
|
|
list_insert_tail(&psta->list, get_list_head(pfree_sta_queue));
|
|
spin_unlock(&(pfree_sta_queue->lock));
|
|
}
|
|
|
|
/* free all stainfo which in sta_hash[all] */
|
|
void r8712_free_all_stainfo(struct _adapter *padapter)
|
|
{
|
|
unsigned long irqL;
|
|
struct list_head *plist, *phead;
|
|
s32 index;
|
|
struct sta_info *psta = NULL;
|
|
struct sta_priv *pstapriv = &padapter->stapriv;
|
|
struct sta_info *pbcmc_stainfo = r8712_get_bcmc_stainfo(padapter);
|
|
|
|
if (pstapriv->asoc_sta_count == 1)
|
|
return;
|
|
spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
|
|
for (index = 0; index < NUM_STA; index++) {
|
|
phead = &(pstapriv->sta_hash[index]);
|
|
plist = get_next(phead);
|
|
while ((end_of_queue_search(phead, plist)) == false) {
|
|
psta = LIST_CONTAINOR(plist,
|
|
struct sta_info, hash_list);
|
|
plist = get_next(plist);
|
|
if (pbcmc_stainfo != psta)
|
|
r8712_free_stainfo(padapter , psta);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
|
|
}
|
|
|
|
/* any station allocated can be searched by hash list */
|
|
struct sta_info *r8712_get_stainfo(struct sta_priv *pstapriv, u8 *hwaddr)
|
|
{
|
|
unsigned long irqL;
|
|
struct list_head *plist, *phead;
|
|
struct sta_info *psta = NULL;
|
|
u32 index;
|
|
|
|
if (hwaddr == NULL)
|
|
return NULL;
|
|
index = wifi_mac_hash(hwaddr);
|
|
spin_lock_irqsave(&pstapriv->sta_hash_lock, irqL);
|
|
phead = &(pstapriv->sta_hash[index]);
|
|
plist = get_next(phead);
|
|
while ((end_of_queue_search(phead, plist)) == false) {
|
|
psta = LIST_CONTAINOR(plist, struct sta_info, hash_list);
|
|
if ((!memcmp(psta->hwaddr, hwaddr, ETH_ALEN))) {
|
|
/* if found the matched address */
|
|
break;
|
|
}
|
|
psta = NULL;
|
|
plist = get_next(plist);
|
|
}
|
|
spin_unlock_irqrestore(&pstapriv->sta_hash_lock, irqL);
|
|
return psta;
|
|
}
|
|
|
|
void r8712_init_bcmc_stainfo(struct _adapter *padapter)
|
|
{
|
|
struct sta_info *psta;
|
|
struct tx_servq *ptxservq;
|
|
unsigned char bcast_addr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
struct sta_priv *pstapriv = &padapter->stapriv;
|
|
|
|
psta = r8712_alloc_stainfo(pstapriv, bcast_addr);
|
|
if (psta == NULL)
|
|
return;
|
|
ptxservq = &(psta->sta_xmitpriv.be_q);
|
|
}
|
|
|
|
struct sta_info *r8712_get_bcmc_stainfo(struct _adapter *padapter)
|
|
{
|
|
struct sta_info *psta;
|
|
struct sta_priv *pstapriv = &padapter->stapriv;
|
|
u8 bc_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
|
|
|
psta = r8712_get_stainfo(pstapriv, bc_addr);
|
|
return psta;
|
|
}
|
|
|
|
|
|
u8 r8712_access_ctrl(struct wlan_acl_pool *pacl_list, u8 *mac_addr)
|
|
{
|
|
return true;
|
|
}
|