blob: 5eb45f5ab8465e11ae8641b1cf86b5da004e1243 [file] [log] [blame]
/******************************************************************************
*
* This file is provided under a dual license. When you use or
* distribute this software, you may choose to be licensed under
* version 2 of the GNU General Public License ("GPLv2 License")
* or BSD License.
*
* GPLv2 License
*
* Copyright(C) 2016 MediaTek Inc.
*
* 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 http://www.gnu.org/licenses/gpl-2.0.html for more details.
*
* BSD LICENSE
*
* Copyright(C) 2016 MediaTek Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* HOLDER OR CONTRIBUTORS 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: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/os/linux/gl_kal.c#10
*/
/*! \file gl_kal.c
* \brief GLUE Layer will export the required procedures here for internal driver stack.
*
* This file contains all routines which are exported from GLUE Layer to internal
* driver stack.
*/
/*******************************************************************************
* C O M P I L E R F L A G S
********************************************************************************
*/
/*******************************************************************************
* E X T E R N A L R E F E R E N C E S
********************************************************************************
*/
#include "gl_os.h"
#include "gl_kal.h"
#include "gl_wext.h"
#include "precomp.h"
#if CFG_SUPPORT_AGPS_ASSIST
#include <net/netlink.h>
#endif
#include <linux/ctype.h>
#ifdef CFG_USE_LINUX_GPIO_GLUE
#include <linux/interrupt.h>
#endif
/*******************************************************************************
* C O N S T A N T S
********************************************************************************
*/
#define FILE_NAME_MAX CFG_FW_NAME_MAX_LEN /* the maximum length of a file name */
#define FILE_NAME_TOTAL 8 /* the maximum number of all possible file name */
/*******************************************************************************
* D A T A T Y P E S
********************************************************************************
*/
/*******************************************************************************
* P U B L I C D A T A
********************************************************************************
*/
#if DBG
int allocatedMemSize;
#endif
/*******************************************************************************
* P R I V A T E D A T A
********************************************************************************
*/
static PVOID pvIoBuffer;
static UINT_32 pvIoBufferSize;
static UINT_32 pvIoBufferUsage;
/*******************************************************************************
* M A C R O S
********************************************************************************
*/
/*******************************************************************************
* F U N C T I O N D E C L A R A T I O N S
********************************************************************************
*/
/*******************************************************************************
* F U N C T I O N S
********************************************************************************
*/
#if CFG_ENABLE_FW_DOWNLOAD
#if (defined(CONFIG_UIDGID_STRICT_TYPE_CHECKS) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)))
#define KUIDT_VALUE(v) (v.val)
#define KGIDT_VALUE(v) (v.val)
#else
#define KUIDT_VALUE(v) v
#define KGIDT_VALUE(v) v
#endif
const struct firmware *fw_entry;
/* Default */
static PUINT_8 apucFwName[] = {
(PUINT_8) CFG_FW_FILENAME "_MT",
NULL
};
static PUINT_8 apucCr4FwName[] = {
(PUINT_8) CFG_CR4_FW_FILENAME "_" HIF_NAME "_MT",
(PUINT_8) CFG_CR4_FW_FILENAME "_MT",
NULL
};
static PUINT_8 apucPatchName[] = {
(PUINT_8) "mt6632_patch_e1_hdr.bin",
(PUINT_8) "mt7666_patch_e1_hdr.bin",
NULL
};
static PPUINT_8 appucFwNameTable[] = {
apucFwName
};
#if CFG_ASSERT_DUMP
/* Core dump debug usage */
#if MTK_WCN_HIF_SDIO
PUINT_8 apucCorDumpN9FileName = "/data/misc/wifi/FW_DUMP_N9";
PUINT_8 apucCorDumpCr4FileName = "/data/misc/wifi/FW_DUMP_Cr4";
#else
PUINT_8 apucCorDumpN9FileName = "/tmp/FW_DUMP_N9";
PUINT_8 apucCorDumpCr4FileName = "/tmp/FW_DUMP_Cr4";
#endif
#endif
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to
* open firmware image in kernel space
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
*
* \retval WLAN_STATUS_SUCCESS.
* \retval WLAN_STATUS_FAILURE.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalFirmwareOpen(IN P_GLUE_INFO_T prGlueInfo, IN PPUINT_8 apucNameTable)
{
UINT_8 ucNameIdx;
/* PPUINT_8 apucNameTable; */
UINT_8 ucMaxEcoVer = (sizeof(appucFwNameTable) / sizeof(PPUINT_8));
UINT_8 ucCurEcoVer = wlanGetEcoVersion(prGlueInfo->prAdapter);
BOOLEAN fgResult = FALSE;
int ret;
/* Try to open FW binary */
for (ucNameIdx = 0; apucNameTable[ucNameIdx]; ucNameIdx++) {
/*
* Driver support request_firmware() to get files
* Android path: "/etc/firmware", "/vendor/firmware", "/firmware/image"
* Linux path: "/lib/firmware", "/lib/firmware/update"
*/
ret = REQUEST_FIRMWARE(&fw_entry, apucNameTable[ucNameIdx], prGlueInfo->prDev);
if (ret) {
DBGLOG(INIT, TRACE, "Request FW image: %s failed, errno[%d]\n",
apucNameTable[ucNameIdx], fgResult);
RELEASE_FIRMWARE(fw_entry);
continue;
} else {
DBGLOG(INIT, TRACE, "Request FW image: %s done\n", apucNameTable[ucNameIdx]);
fgResult = TRUE;
break;
}
}
/* Check result */
if (!fgResult)
goto error_open;
return WLAN_STATUS_SUCCESS;
error_open:
DBGLOG(INIT, ERROR, "Request FW image failed! Cur/Max ECO Ver[E%u/E%u]\n", ucCurEcoVer, ucMaxEcoVer);
return WLAN_STATUS_FAILURE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to
* release firmware image in kernel space
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
*
* \retval WLAN_STATUS_SUCCESS.
* \retval WLAN_STATUS_FAILURE.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalFirmwareClose(IN P_GLUE_INFO_T prGlueInfo)
{
RELEASE_FIRMWARE(fw_entry);
return WLAN_STATUS_SUCCESS;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to
* load firmware image in kernel space
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
*
* \retval WLAN_STATUS_SUCCESS.
* \retval WLAN_STATUS_FAILURE.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalFirmwareLoad(IN P_GLUE_INFO_T prGlueInfo, OUT PVOID prBuf, IN UINT_32 u4Offset, OUT PUINT_32 pu4Size)
{
ASSERT(prGlueInfo);
ASSERT(pu4Size);
ASSERT(prBuf);
if ((fw_entry == NULL) || (fw_entry->size == 0) || (fw_entry->data == NULL)) {
goto error_read;
} else {
memcpy(prBuf, fw_entry->data, fw_entry->size);
*pu4Size = fw_entry->size;
}
return WLAN_STATUS_SUCCESS;
error_read:
return WLAN_STATUS_FAILURE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to
* query firmware image size in kernel space
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
*
* \retval WLAN_STATUS_SUCCESS.
* \retval WLAN_STATUS_FAILURE.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalFirmwareSize(IN P_GLUE_INFO_T prGlueInfo, OUT PUINT_32 pu4Size)
{
ASSERT(prGlueInfo);
ASSERT(pu4Size);
*pu4Size = fw_entry->size;
return WLAN_STATUS_SUCCESS;
}
VOID
kalConstructDefaultFirmwarePrio(P_GLUE_INFO_T prGlueInfo, PPUINT_8 apucNameTable,
PPUINT_8 apucName, PUINT_8 pucNameIdx, UINT_8 ucMaxNameIdx)
{
struct mt66xx_chip_info *prChipInfo = prGlueInfo->prAdapter->chip_info;
UINT_32 chip_id = prChipInfo->chip_id;
UINT_8 sub_idx = 0;
for (sub_idx = 0; apucNameTable[sub_idx]; sub_idx++) {
if ((*pucNameIdx + 3) < ucMaxNameIdx) {
/* Type 1. WIFI_RAM_CODE_MTxxxx_Ex */
snprintf(*(apucName + (*pucNameIdx)), FILE_NAME_MAX, "%s%x_E%u",
apucNameTable[sub_idx], chip_id,
wlanGetEcoVersion(prGlueInfo->prAdapter));
(*pucNameIdx) += 1;
/* Type 2. WIFI_RAM_CODE_MTxxxx_Ex.bin */
snprintf(*(apucName + (*pucNameIdx)), FILE_NAME_MAX, "%s%x_E%u.bin",
apucNameTable[sub_idx], chip_id,
wlanGetEcoVersion(prGlueInfo->prAdapter));
(*pucNameIdx) += 1;
/* Type 3. WIFI_RAM_CODE_MTxxxx */
snprintf(*(apucName + (*pucNameIdx)), FILE_NAME_MAX, "%s%x",
apucNameTable[sub_idx], chip_id);
(*pucNameIdx) += 1;
/* Type 4. WIFI_RAM_CODE_MTxxxx.bin */
snprintf(*(apucName + (*pucNameIdx)), FILE_NAME_MAX, "%s%x.bin",
apucNameTable[sub_idx], chip_id);
(*pucNameIdx) += 1;
} else {
/* the table is not large enough */
DBGLOG(INIT, ERROR, "kalFirmwareImageMapping >> file name array is not enough.\n");
ASSERT(0);
}
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to load firmware image
*
* \param pvGlueInfo Pointer of GLUE Data Structure
* \param ppvMapFileBuf Pointer of pointer to memory-mapped firmware image
* \param pu4FileLength File length and memory mapped length as well
* \retval Map File Handle, used for unammping
*/
/*----------------------------------------------------------------------------*/
PVOID
kalFirmwareImageMapping(IN P_GLUE_INFO_T prGlueInfo,
OUT PPVOID ppvMapFileBuf, OUT PUINT_32 pu4FileLength, IN ENUM_IMG_DL_IDX_T eDlIdx)
{
PPUINT_8 apucNameTable = NULL;
PUINT_8 apucName[FILE_NAME_TOTAL + 1]; /* extra +1, for the purpose of detecting the end of the array */
UINT_8 idx = 0, max_idx, aucNameBody[FILE_NAME_TOTAL][FILE_NAME_MAX], sub_idx = 0;
struct mt66xx_chip_info *prChipInfo = prGlueInfo->prAdapter->chip_info;
UINT_32 chip_id = prChipInfo->chip_id;
DEBUGFUNC("kalFirmwareImageMapping");
ASSERT(prGlueInfo);
ASSERT(ppvMapFileBuf);
ASSERT(pu4FileLength);
*ppvMapFileBuf = NULL;
*pu4FileLength = 0;
do {
/* <0.0> Get FW name prefix table */
switch (eDlIdx) {
case IMG_DL_IDX_N9_FW:
apucNameTable = apucFwName;
break;
case IMG_DL_IDX_CR4_FW:
apucNameTable = apucCr4FwName;
break;
case IMG_DL_IDX_PATCH:
apucNameTable = apucPatchName;
break;
default:
ASSERT(0);
break;
}
/* <0.2> Construct FW name */
memset(apucName, 0, sizeof(apucName));
/* magic number 1: reservation for detection
* of the end of the array
*/
max_idx = (sizeof(apucName) / sizeof(PUINT_8)) - 1;
idx = 0;
apucName[idx] = (PUINT_8)(aucNameBody + idx);
if (eDlIdx == IMG_DL_IDX_PATCH) {
/* construct the file name for patch */
/* mtxxxx_patch_ex_hdr.bin*/
snprintf(apucName[idx], FILE_NAME_MAX, "mt%x_patch_e%x_hdr.bin",
chip_id, wlanGetEcoVersion(prGlueInfo->prAdapter));
idx += 1;
} else {
for (sub_idx = 0; sub_idx < max_idx; sub_idx++)
apucName[sub_idx] = (PUINT_8)(aucNameBody + sub_idx);
if (prChipInfo->constructFirmwarePrio)
prChipInfo->constructFirmwarePrio(prGlueInfo, apucNameTable, apucName, &idx, max_idx);
else
kalConstructDefaultFirmwarePrio(prGlueInfo, apucNameTable, apucName, &idx, max_idx);
}
/* let the last pointer point to NULL
* so that we can detect the end of the array in kalFirmwareOpen().
*/
apucName[idx] = NULL;
apucNameTable = apucName;
/* <1> Open firmware */
if (kalFirmwareOpen(prGlueInfo, apucNameTable) != WLAN_STATUS_SUCCESS)
break;
{
UINT_32 u4FwSize = 0;
PVOID prFwBuffer = NULL;
/* <2> Query firmare size */
kalFirmwareSize(prGlueInfo, &u4FwSize);
/* <3> Use vmalloc for allocating large memory trunk */
prFwBuffer = vmalloc(ALIGN_4(u4FwSize));
/* <4> Load image binary into buffer */
if (kalFirmwareLoad(prGlueInfo, prFwBuffer, 0, &u4FwSize) != WLAN_STATUS_SUCCESS) {
vfree(prFwBuffer);
kalFirmwareClose(prGlueInfo);
break;
}
/* <5> write back info */
*pu4FileLength = u4FwSize;
*ppvMapFileBuf = prFwBuffer;
return prFwBuffer;
}
} while (FALSE);
return NULL;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to unload firmware image mapped memory
*
* \param pvGlueInfo Pointer of GLUE Data Structure
* \param pvFwHandle Pointer to mapping handle
* \param pvMapFileBuf Pointer to memory-mapped firmware image
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalFirmwareImageUnmapping(IN P_GLUE_INFO_T prGlueInfo, IN PVOID prFwHandle, IN PVOID pvMapFileBuf)
{
DEBUGFUNC("kalFirmwareImageUnmapping");
ASSERT(prGlueInfo);
/* pvMapFileBuf might be NULL when file doesn't exist */
if (pvMapFileBuf)
vfree(pvMapFileBuf);
kalFirmwareClose(prGlueInfo);
}
#endif
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to acquire
* OS SPIN_LOCK.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] rLockCategory Specify which SPIN_LOCK
* \param[out] pu4Flags Pointer of a variable for saving IRQ flags
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalAcquireSpinLock(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_SPIN_LOCK_CATEGORY_E rLockCategory, OUT PULONG plFlags)
{
ULONG ulFlags = 0;
ASSERT(prGlueInfo);
ASSERT(plFlags);
if (rLockCategory < SPIN_LOCK_NUM) {
DBGLOG(INIT, LOUD, "SPIN_LOCK[%u] Try to acquire\n", rLockCategory);
#if CFG_USE_SPIN_LOCK_BOTTOM_HALF
spin_lock_bh(&prGlueInfo->rSpinLock[rLockCategory]);
#else /* !CFG_USE_SPIN_LOCK_BOTTOM_HALF */
spin_lock_irqsave(&prGlueInfo->rSpinLock[rLockCategory], ulFlags);
#endif /* !CFG_USE_SPIN_LOCK_BOTTOM_HALF */
*plFlags = ulFlags;
DBGLOG(INIT, LOUD, "SPIN_LOCK[%u] Acquired\n", rLockCategory);
}
} /* end of kalAcquireSpinLock() */
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to release
* OS SPIN_LOCK.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] rLockCategory Specify which SPIN_LOCK
* \param[in] u4Flags Saved IRQ flags
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalReleaseSpinLock(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_SPIN_LOCK_CATEGORY_E rLockCategory, IN ULONG ulFlags)
{
ASSERT(prGlueInfo);
if (rLockCategory < SPIN_LOCK_NUM) {
#if CFG_USE_SPIN_LOCK_BOTTOM_HALF
spin_unlock_bh(&prGlueInfo->rSpinLock[rLockCategory]);
#else /* !CFG_USE_SPIN_LOCK_BOTTOM_HALF */
spin_unlock_irqrestore(&prGlueInfo->rSpinLock[rLockCategory], ulFlags);
#endif /* !CFG_USE_SPIN_LOCK_BOTTOM_HALF */
DBGLOG(INIT, LOUD, "SPIN_UNLOCK[%u]\n", rLockCategory);
}
} /* end of kalReleaseSpinLock() */
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to acquire
* OS MUTEX.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] rMutexCategory Specify which MUTEX
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalAcquireMutex(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_MUTEX_CATEGORY_E rMutexCategory)
{
ASSERT(prGlueInfo);
if (rMutexCategory < MUTEX_NUM) {
DBGLOG(INIT, TRACE, "MUTEX_LOCK[%u] Try to acquire\n", rMutexCategory);
mutex_lock(&prGlueInfo->arMutex[rMutexCategory]);
DBGLOG(INIT, TRACE, "MUTEX_LOCK[%u] Acquired\n", rMutexCategory);
}
} /* end of kalAcquireMutex() */
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to release
* OS MUTEX.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] rMutexCategory Specify which MUTEX
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalReleaseMutex(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_MUTEX_CATEGORY_E rMutexCategory)
{
ASSERT(prGlueInfo);
if (rMutexCategory < MUTEX_NUM) {
mutex_unlock(&prGlueInfo->arMutex[rMutexCategory]);
DBGLOG(INIT, TRACE, "MUTEX_UNLOCK[%u]\n", rMutexCategory);
}
} /* end of kalReleaseMutex() */
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is provided by GLUE Layer for internal driver stack to update
* current MAC address.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] pucMacAddr Pointer of current MAC address
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalUpdateMACAddress(IN P_GLUE_INFO_T prGlueInfo, IN PUINT_8 pucMacAddr)
{
ASSERT(prGlueInfo);
ASSERT(pucMacAddr);
if (UNEQUAL_MAC_ADDR(prGlueInfo->prDevHandler->dev_addr, pucMacAddr))
memcpy(prGlueInfo->prDevHandler->dev_addr, pucMacAddr, PARAM_MAC_ADDR_LEN);
}
#if CFG_TCP_IP_CHKSUM_OFFLOAD
/*----------------------------------------------------------------------------*/
/*!
* \brief To query the packet information for offload related parameters.
*
* \param[in] pvPacket Pointer to the packet descriptor.
* \param[in] pucFlag Points to the offload related parameter.
*
* \return (none)
*
*/
/*----------------------------------------------------------------------------*/
VOID kalQueryTxChksumOffloadParam(IN PVOID pvPacket, OUT PUINT_8 pucFlag)
{
struct sk_buff *skb = (struct sk_buff *)pvPacket;
UINT_8 ucFlag = 0;
ASSERT(pvPacket);
ASSERT(pucFlag);
if (skb->ip_summed == CHECKSUM_PARTIAL) {
#if DBG
/* Kevin: do double check, we can remove this part in Normal Driver.
* Because we register NIC feature with NETIF_F_IP_CSUM for MT5912B MAC, so
* we'll process IP packet only.
*/
if (skb->protocol != htons(ETH_P_IP)) {
/* printk("Wrong skb->protocol( = %08x) for TX Checksum Offload.\n", skb->protocol); */
} else
#endif
ucFlag |= (TX_CS_IP_GEN | TX_CS_TCP_UDP_GEN);
}
*pucFlag = ucFlag;
} /* kalQueryChksumOffloadParam */
/* 4 2007/10/8, mikewu, this is rewritten by Mike */
/*----------------------------------------------------------------------------*/
/*!
* \brief To update the checksum offload status to the packet to be indicated to OS.
*
* \param[in] pvPacket Pointer to the packet descriptor.
* \param[in] pucFlag Points to the offload related parameter.
*
* \return (none)
*
*/
/*----------------------------------------------------------------------------*/
VOID kalUpdateRxCSUMOffloadParam(IN PVOID pvPacket, IN ENUM_CSUM_RESULT_T aeCSUM[])
{
struct sk_buff *skb = (struct sk_buff *)pvPacket;
ASSERT(pvPacket);
if ((aeCSUM[CSUM_TYPE_IPV4] == CSUM_RES_SUCCESS || aeCSUM[CSUM_TYPE_IPV6] == CSUM_RES_SUCCESS)
&& ((aeCSUM[CSUM_TYPE_TCP] == CSUM_RES_SUCCESS)
|| (aeCSUM[CSUM_TYPE_UDP] == CSUM_RES_SUCCESS))) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else {
skb->ip_summed = CHECKSUM_NONE;
#if DBG
if (aeCSUM[CSUM_TYPE_IPV4] == CSUM_RES_NONE && aeCSUM[CSUM_TYPE_IPV6] == CSUM_RES_NONE)
DBGLOG(RX, TRACE, "RX: \"non-IPv4/IPv6\" Packet\n");
else if (aeCSUM[CSUM_TYPE_IPV4] == CSUM_RES_FAILED)
DBGLOG(RX, TRACE, "RX: \"bad IP Checksum\" Packet\n");
else if (aeCSUM[CSUM_TYPE_TCP] == CSUM_RES_FAILED)
DBGLOG(RX, TRACE, "RX: \"bad TCP Checksum\" Packet\n");
else if (aeCSUM[CSUM_TYPE_UDP] == CSUM_RES_FAILED)
DBGLOG(RX, TRACE, "RX: \"bad UDP Checksum\" Packet\n");
#endif
}
} /* kalUpdateRxCSUMOffloadParam */
#endif /* CFG_TCP_IP_CHKSUM_OFFLOAD */
/*----------------------------------------------------------------------------*/
/*!
* \brief This function is called to free packet allocated from kalPacketAlloc.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] pvPacket Pointer of the packet descriptor
*
* \return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalPacketFree(IN P_GLUE_INFO_T prGlueInfo, IN PVOID pvPacket)
{
dev_kfree_skb((struct sk_buff *)pvPacket);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Only handles driver own creating packet (coalescing buffer).
*
* \param prGlueInfo Pointer of GLUE Data Structure
* \param u4Size Pointer of Packet Handle
* \param ppucData Status Code for OS upper layer
*
* \return NULL: Failed to allocate skb, Not NULL get skb
*/
/*----------------------------------------------------------------------------*/
PVOID kalPacketAlloc(IN P_GLUE_INFO_T prGlueInfo, IN UINT_32 u4Size, OUT PUINT_8 *ppucData)
{
struct sk_buff *prSkb;
if (in_interrupt())
prSkb = __dev_alloc_skb(u4Size + NIC_TX_HEAD_ROOM, GFP_ATOMIC);
else
prSkb = __dev_alloc_skb(u4Size + NIC_TX_HEAD_ROOM, GFP_KERNEL);
if (prSkb) {
skb_reserve(prSkb, NIC_TX_HEAD_ROOM);
*ppucData = (PUINT_8) (prSkb->data);
/* DBGLOG(TDLS, INFO, "kalPacketAlloc, skb head[0x%x] data[0x%x] tail[0x%x] end[0x%x]\n",
* prSkb->head, prSkb->data, prSkb->tail, prSkb->end);
*/
kalResetPacket(prGlueInfo, (P_NATIVE_PACKET) prSkb);
}
#if DBG
{
PUINT_32 pu4Head = (PUINT_32) &prSkb->cb[0];
*pu4Head = (UINT_32) prSkb->head;
DBGLOG(RX, TRACE, "prSkb->head = %#lx, prSkb->cb = %#lx\n", (UINT_32) prSkb->head, *pu4Head);
}
#endif
return (PVOID) prSkb;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Process the received packet for indicating to OS.
*
* \param[in] prGlueInfo Pointer to the Adapter structure.
* \param[in] pvPacket Pointer of the packet descriptor
* \param[in] pucPacketStart The starting address of the buffer of Rx packet.
* \param[in] u4PacketLen The packet length.
* \param[in] pfgIsRetain Is the packet to be retained.
* \param[in] aerCSUM The result of TCP/ IP checksum offload.
*
* \retval WLAN_STATUS_SUCCESS.
* \retval WLAN_STATUS_FAILURE.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS
kalProcessRxPacket(IN P_GLUE_INFO_T prGlueInfo, IN PVOID pvPacket, IN PUINT_8 pucPacketStart, IN UINT_32 u4PacketLen,
/* IN PBOOLEAN pfgIsRetain, */
IN BOOLEAN fgIsRetain, IN ENUM_CSUM_RESULT_T aerCSUM[])
{
WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
struct sk_buff *skb = (struct sk_buff *)pvPacket;
skb->data = (unsigned char *)pucPacketStart;
/* Reset skb */
skb_reset_tail_pointer(skb);
skb_trim(skb, 0);
/* Put data */
skb_put(skb, u4PacketLen);
#if CFG_TCP_IP_CHKSUM_OFFLOAD
if (prGlueInfo->prAdapter->fgIsSupportCsumOffload)
kalUpdateRxCSUMOffloadParam(skb, aerCSUM);
#endif
return rStatus;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate an array of received packets is available for higher
* level protocol uses.
*
* \param[in] prGlueInfo Pointer to the Adapter structure.
* \param[in] apvPkts The packet array to be indicated
* \param[in] ucPktNum The number of packets to be indicated
*
* \retval TRUE Success.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalRxIndicatePkts(IN P_GLUE_INFO_T prGlueInfo, IN PVOID apvPkts[], IN UINT_8 ucPktNum)
{
UINT_8 ucIdx = 0;
ASSERT(prGlueInfo);
ASSERT(apvPkts);
for (ucIdx = 0; ucIdx < ucPktNum; ucIdx++)
kalRxIndicateOnePkt(prGlueInfo, apvPkts[ucIdx]);
KAL_WAKE_LOCK_TIMEOUT(prGlueInfo->prAdapter, &prGlueInfo->rTimeoutWakeLock,
MSEC_TO_JIFFIES(prGlueInfo->prAdapter->rWifiVar.u4WakeLockRxTimeout));
return WLAN_STATUS_SUCCESS;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate one received packets is available for higher
* level protocol uses.
*
* \param[in] prGlueInfo Pointer to the Adapter structure.
* \param[in] pvPkt The packet to be indicated
*
* \retval TRUE Success.
*
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS kalRxIndicateOnePkt(IN P_GLUE_INFO_T prGlueInfo, IN PVOID pvPkt)
{
struct net_device *prNetDev = prGlueInfo->prDevHandler;
struct sk_buff *prSkb = NULL;
struct mt66xx_chip_info *prChipInfo;
ASSERT(prGlueInfo);
ASSERT(pvPkt);
prSkb = pvPkt;
prChipInfo = prGlueInfo->prAdapter->chip_info;
#if DBG && 0
do {
PUINT_8 pu4Head = (PUINT_8) &prSkb->cb[0];
UINT_32 u4HeadValue = 0;
kalMemCopy(&u4HeadValue, pu4Head, sizeof(u4HeadValue));
DBGLOG(RX, TRACE, "prSkb->head = 0x%p, prSkb->cb = 0x%lx\n", pu4Head, u4HeadValue);
} while (0);
#endif
#if 1
prNetDev = (struct net_device *)wlanGetNetInterfaceByBssIdx(prGlueInfo, GLUE_GET_PKT_BSS_IDX(prSkb));
if (!prNetDev)
prNetDev = prGlueInfo->prDevHandler;
#if CFG_SUPPORT_SNIFFER
if (prGlueInfo->fgIsEnableMon)
prNetDev = prGlueInfo->prMonDevHandler;
#endif
prNetDev->stats.rx_bytes += prSkb->len;
prNetDev->stats.rx_packets++;
#else
if (GLUE_GET_PKT_IS_P2P(prSkb)) {
/* P2P */
#if CFG_ENABLE_WIFI_DIRECT
if (prGlueInfo->prAdapter->fgIsP2PRegistered)
prNetDev = kalP2PGetDevHdlr(prGlueInfo);
/* prNetDev->stats.rx_bytes += prSkb->len; */
/* prNetDev->stats.rx_packets++; */
prGlueInfo->prP2PInfo[0]->rNetDevStats.rx_bytes += prSkb->len;
prGlueInfo->prP2PInfo[0]->rNetDevStats.rx_packets++;
#else
prNetDev = prGlueInfo->prDevHandler;
#endif
} else if (GLUE_GET_PKT_IS_PAL(prSkb)) {
/* BOW */
#if CFG_ENABLE_BT_OVER_WIFI && CFG_BOW_SEPARATE_DATA_PATH
if (prGlueInfo->rBowInfo.fgIsNetRegistered)
prNetDev = prGlueInfo->rBowInfo.prDevHandler;
#else
prNetDev = prGlueInfo->prDevHandler;
#endif
} else {
/* AIS */
prNetDev = prGlueInfo->prDevHandler;
prGlueInfo->rNetDevStats.rx_bytes += prSkb->len;
prGlueInfo->rNetDevStats.rx_packets++;
}
#endif
prNetDev->last_rx = jiffies;
#if CFG_SUPPORT_SNIFFER
if (prGlueInfo->fgIsEnableMon) {
skb_reset_mac_header(prSkb);
prSkb->ip_summed = CHECKSUM_UNNECESSARY;
prSkb->pkt_type = PACKET_OTHERHOST;
prSkb->protocol = htons(ETH_P_802_2);
} else {
prSkb->protocol = eth_type_trans(prSkb, prNetDev);
}
#else
prSkb->protocol = eth_type_trans(prSkb, prNetDev);
#endif
prSkb->dev = prNetDev;
/* DBGLOG_MEM32(RX, TRACE, (PUINT_32)prSkb->data, prSkb->len); */
/* DBGLOG(RX, EVENT, ("kalRxIndicatePkts len = %d\n", prSkb->len)); */
if (prSkb->tail > prSkb->end) {
DBGLOG(RX, ERROR,
"kalRxIndicateOnePkt [prSkb = 0x%p][prSkb->len = %d][prSkb->protocol = 0x%02X] %p,%p\n",
(PUINT_8) prSkb, prSkb->len, prSkb->protocol, prSkb->tail, prSkb->end);
DBGLOG_MEM32(RX, ERROR, (PUINT_32) prSkb->data, prSkb->len);
}
if (prSkb->protocol == NTOHS(ETH_P_8021Q)
&& !FEAT_SUP_LLC_VLAN_RX(prChipInfo)) {
/*
* DA-MAC + SA-MAC + 0x8100 was removed in eth_type_trans()
* pkt format here is TCI(2-bytes) + Len(2-btyes) + payload-type(2-bytes) + payload
* Remove "Len" field inserted by RX VLAN header translation
* Note: TCI+payload-type is a standard 8021Q header
*
* This format update is based on RX VLAN HW header translation.
* If the setting was changed, you may need to change rules here as well.
*/
const UINT_8 vlan_skb_mem_move = 2;
/* Remove "Len" and shift data pointer 2 bytes */
kalMemCopy(prSkb->data+vlan_skb_mem_move, prSkb->data, vlan_skb_mem_move);
skb_pull_rcsum(prSkb, vlan_skb_mem_move);
/* Have to update MAC header properly. Otherwise, wrong MACs woud be passed up */
kalMemMove(prSkb->data - ETH_HLEN, prSkb->data - ETH_HLEN - vlan_skb_mem_move, ETH_HLEN);
prSkb->mac_header += vlan_skb_mem_move;
skb_reset_network_header(prSkb);
skb_reset_transport_header(prSkb);
kal_skb_reset_mac_len(prSkb);
}
if (!in_interrupt())
netif_rx_ni(prSkb); /* only in non-interrupt context */
else
netif_rx(prSkb);
return WLAN_STATUS_SUCCESS;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Called by driver to indicate event to upper layer, for example, the wpa
* supplicant or wireless tools.
*
* \param[in] pvAdapter Pointer to the adapter descriptor.
* \param[in] eStatus Indicated status.
* \param[in] pvBuf Indicated message buffer.
* \param[in] u4BufLen Indicated message buffer size.
*
* \return (none)
*
*/
/*----------------------------------------------------------------------------*/
VOID
kalIndicateStatusAndComplete(IN P_GLUE_INFO_T prGlueInfo, IN WLAN_STATUS eStatus, IN PVOID pvBuf, IN UINT_32 u4BufLen)
{
UINT_32 bufLen;
P_PARAM_STATUS_INDICATION_T pStatus = (P_PARAM_STATUS_INDICATION_T) pvBuf;
P_PARAM_AUTH_EVENT_T pAuth = (P_PARAM_AUTH_EVENT_T) pStatus;
P_PARAM_PMKID_CANDIDATE_LIST_T pPmkid = (P_PARAM_PMKID_CANDIDATE_LIST_T) (pStatus + 1);
PARAM_MAC_ADDRESS arBssid;
PARAM_SSID_T ssid;
struct ieee80211_channel *prChannel = NULL;
struct cfg80211_bss *bss;
UINT_8 ucChannelNum;
P_BSS_DESC_T prBssDesc = NULL;
GLUE_SPIN_LOCK_DECLARATION();
kalMemZero(arBssid, MAC_ADDR_LEN);
ASSERT(prGlueInfo);
switch (eStatus) {
case WLAN_STATUS_ROAM_OUT_FIND_BEST:
case WLAN_STATUS_MEDIA_CONNECT:
prGlueInfo->eParamMediaStateIndicated = PARAM_MEDIA_STATE_CONNECTED;
/* indicate assoc event */
wlanQueryInformation(prGlueInfo->prAdapter, wlanoidQueryBssid, &arBssid[0], sizeof(arBssid), &bufLen);
wext_indicate_wext_event(prGlueInfo, SIOCGIWAP, arBssid, bufLen);
/* switch netif on */
netif_carrier_on(prGlueInfo->prDevHandler);
do {
/* print message on console */
wlanQueryInformation(prGlueInfo->prAdapter, wlanoidQuerySsid, &ssid, sizeof(ssid), &bufLen);
ssid.aucSsid[(ssid.u4SsidLen >= PARAM_MAX_LEN_SSID) ?
(PARAM_MAX_LEN_SSID - 1) : ssid.u4SsidLen] = '\0';
DBGLOG(INIT, INFO, "[wifi] %s netif_carrier_on [ssid:%s " MACSTR "]\n",
prGlueInfo->prDevHandler->name, ssid.aucSsid, MAC2STR(arBssid));
} while (0);
if (prGlueInfo->fgIsRegistered == TRUE) {
/* retrieve channel */
ucChannelNum =
wlanGetChannelNumberByNetwork(prGlueInfo->prAdapter,
prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex);
if (ucChannelNum <= 14) {
prChannel =
ieee80211_get_channel(priv_to_wiphy(prGlueInfo),
ieee80211_channel_to_frequency
(ucChannelNum, KAL_BAND_2GHZ));
} else {
prChannel =
ieee80211_get_channel(priv_to_wiphy(prGlueInfo),
ieee80211_channel_to_frequency
(ucChannelNum, KAL_BAND_5GHZ));
}
/* ensure BSS exists */
bss = cfg80211_get_bss(priv_to_wiphy(prGlueInfo), prChannel, arBssid,
ssid.aucSsid, ssid.u4SsidLen, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
if (bss == NULL) {
/* create BSS on-the-fly */
prBssDesc = ((P_AIS_FSM_INFO_T)
(&(prGlueInfo->prAdapter->rWifiVar.rAisFsmInfo)))->prTargetBssDesc;
if (prChannel && prBssDesc != NULL) {
#if KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE
bss = cfg80211_inform_bss(priv_to_wiphy(prGlueInfo),
prChannel,
CFG80211_BSS_FTYPE_PRESP,
arBssid,
0, /* TSF */
WLAN_CAPABILITY_ESS,
prBssDesc->u2BeaconInterval, /* beacon interval */
prBssDesc->aucIEBuf, /* IE */
prBssDesc->u2IELength, /* IE Length */
RCPI_TO_dBm(prBssDesc->ucRCPI) * 100, /* MBM */
GFP_KERNEL);
#else
bss = cfg80211_inform_bss(priv_to_wiphy(prGlueInfo), prChannel,
arBssid, 0, /* TSF */
WLAN_CAPABILITY_ESS,
prBssDesc->u2BeaconInterval, /* beacon interval */
prBssDesc->aucIEBuf, /* IE */
prBssDesc->u2IELength, /* IE Length */
RCPI_TO_dBm(prBssDesc->ucRCPI) * 100, /* MBM */
GFP_KERNEL);
#endif
}
}
/* CFG80211 Indication */
if (eStatus == WLAN_STATUS_ROAM_OUT_FIND_BEST) {
struct ieee80211_channel *prChannel = NULL;
UINT_8 ucChannelNum = wlanGetChannelNumberByNetwork(prGlueInfo->prAdapter,
prGlueInfo->prAdapter->
prAisBssInfo->ucBssIndex);
if (ucChannelNum <= 14) {
prChannel = ieee80211_get_channel(priv_to_wiphy(prGlueInfo),
ieee80211_channel_to_frequency
(ucChannelNum, KAL_BAND_2GHZ));
} else {
prChannel = ieee80211_get_channel(priv_to_wiphy(prGlueInfo),
ieee80211_channel_to_frequency
(ucChannelNum, KAL_BAND_5GHZ));
}
cfg80211_roamed(prGlueInfo->prDevHandler,
prChannel,
arBssid,
prGlueInfo->aucReqIe,
prGlueInfo->u4ReqIeLength,
prGlueInfo->aucRspIe, prGlueInfo->u4RspIeLength, GFP_KERNEL);
} else {
cfg80211_connect_result(prGlueInfo->prDevHandler, arBssid,
prGlueInfo->aucReqIe,
prGlueInfo->u4ReqIeLength,
prGlueInfo->aucRspIe,
prGlueInfo->u4RspIeLength, WLAN_STATUS_SUCCESS, GFP_KERNEL);
}
}
break;
case WLAN_STATUS_MEDIA_DISCONNECT:
case WLAN_STATUS_MEDIA_DISCONNECT_LOCALLY:
/* indicate disassoc event */
wext_indicate_wext_event(prGlueInfo, SIOCGIWAP, NULL, 0);
/* For CR 90 and CR99, While supplicant do reassociate, driver will do netif_carrier_off first,
* after associated success, at joinComplete(), do netif_carier_on,
* but for unknown reason, the supplicant 1x pkt will not called the driver
* hardStartXmit, for template workaround these bugs, add this compiling flag
*/
/* switch netif off */
#if 1 /* CONSOLE_MESSAGE */
DBGLOG(INIT, INFO, "[wifi] %s netif_carrier_off\n", prGlueInfo->prDevHandler->name);
#endif
netif_carrier_off(prGlueInfo->prDevHandler);
if (prGlueInfo->fgIsRegistered == TRUE) {
P_BSS_INFO_T prBssInfo = prGlueInfo->prAdapter->prAisBssInfo;
UINT_16 u2DeauthReason = 0;
#if CFG_WPS_DISCONNECT || (KERNEL_VERSION(4, 4, 0) <= CFG80211_VERSION_CODE)
if (prBssInfo)
u2DeauthReason = prBssInfo->u2DeauthReason;
/* CFG80211 Indication */
DBGLOG(INIT, INFO, "[wifi]Indicate disconnection: Locally[%d]\n",
(eStatus == WLAN_STATUS_MEDIA_DISCONNECT_LOCALLY));
cfg80211_disconnected(prGlueInfo->prDevHandler, u2DeauthReason, NULL, 0,
eStatus == WLAN_STATUS_MEDIA_DISCONNECT_LOCALLY,
GFP_KERNEL);
#else
#ifdef CONFIG_ANDROID
#if LINUX_VERSION_CODE == KERNEL_VERSION(3, 10, 0)
/* Don't indicate disconnection to upper layer for ANDROID kernel 3.10 */
/* since cfg80211 will indicate disconnection to wpa_supplicant for this kernel */
if (eStatus == WLAN_STATUS_MEDIA_DISCONNECT)
#endif
#endif
{
if (prBssInfo)
u2DeauthReason = prBssInfo->u2DeauthReason;
/* CFG80211 Indication */
cfg80211_disconnected(prGlueInfo->prDevHandler, u2DeauthReason, NULL, 0,
GFP_KERNEL);
}
#endif
}
prGlueInfo->eParamMediaStateIndicated = PARAM_MEDIA_STATE_DISCONNECTED;
break;
case WLAN_STATUS_SCAN_COMPLETE:
/* indicate scan complete event */
wext_indicate_wext_event(prGlueInfo, SIOCGIWSCAN, NULL, 0);
/* 1. reset first for newly incoming request */
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
if (prGlueInfo->prScanRequest != NULL) {
/* 2. then CFG80211 Indication */
kalCfg80211ScanDone(prGlueInfo->prScanRequest, FALSE);
prGlueInfo->prScanRequest = NULL;
}
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
break;
#if 0
case WLAN_STATUS_MSDU_OK:
if (netif_running(prGlueInfo->prDevHandler))
netif_wake_queue(prGlueInfo->prDevHandler);
break;
#endif
case WLAN_STATUS_MEDIA_SPECIFIC_INDICATION:
if (pStatus) {
switch (pStatus->eStatusType) {
case ENUM_STATUS_TYPE_AUTHENTICATION:
/*
* printk(KERN_NOTICE "ENUM_STATUS_TYPE_AUTHENTICATION: L(%ld) [" MACSTR "] F:%lx\n",
* pAuth->Request[0].Length,
* MAC2STR(pAuth->Request[0].Bssid),
* pAuth->Request[0].Flags);
*/
/* indicate (UC/GC) MIC ERROR event only */
if ((pAuth->arRequest[0].u4Flags ==
PARAM_AUTH_REQUEST_PAIRWISE_ERROR) ||
(pAuth->arRequest[0].u4Flags == PARAM_AUTH_REQUEST_GROUP_ERROR)) {
cfg80211_michael_mic_failure(prGlueInfo->prDevHandler, NULL,
(pAuth->arRequest[0].u4Flags ==
PARAM_AUTH_REQUEST_PAIRWISE_ERROR)
? NL80211_KEYTYPE_PAIRWISE :
NL80211_KEYTYPE_GROUP, 0, NULL, GFP_KERNEL);
wext_indicate_wext_event(prGlueInfo, IWEVMICHAELMICFAILURE,
(unsigned char *)&pAuth->arRequest[0],
pAuth->arRequest[0].u4Length);
}
break;
case ENUM_STATUS_TYPE_CANDIDATE_LIST:
/*
* printk(KERN_NOTICE "Param_StatusType_PMKID_CandidateList: Ver(%ld) Num(%ld)\n",
* pPmkid->u2Version,
* pPmkid->u4NumCandidates);
* if (pPmkid->u4NumCandidates > 0) {
* printk(KERN_NOTICE "candidate[" MACSTR "] preAuth Flag:%lx\n",
* MAC2STR(pPmkid->arCandidateList[0].rBSSID),
* pPmkid->arCandidateList[0].fgFlags);
* }
*/
{
UINT_32 i;
for (i = 0; i < pPmkid->u4NumCandidates; i++) {
wext_indicate_wext_event(prGlueInfo,
IWEVPMKIDCAND,
(unsigned char *)&pPmkid->arCandidateList[i],
pPmkid->u4NumCandidates);
}
}
break;
default:
/* case ENUM_STATUS_TYPE_MEDIA_STREAM_MODE */
/*
* printk(KERN_NOTICE "unknown media specific indication type:%x\n",
* pStatus->StatusType);
*/
break;
}
} else {
/*
* printk(KERN_WARNING "media specific indication buffer NULL\n");
*/
}
break;
#if CFG_SUPPORT_BCM && CFG_SUPPORT_BCM_BWCS
case WLAN_STATUS_BWCS_UPDATE:
{
wext_indicate_wext_event(prGlueInfo, IWEVCUSTOM, pvBuf, sizeof(PTA_IPC_T));
}
break;
#endif
case WLAN_STATUS_JOIN_TIMEOUT:
{
P_BSS_DESC_T prBssDesc = prGlueInfo->prAdapter->rWifiVar.rAisFsmInfo.prTargetBssDesc;
if (prBssDesc)
COPY_MAC_ADDR(arBssid, prBssDesc->aucBSSID);
cfg80211_connect_result(prGlueInfo->prDevHandler,
arBssid,
prGlueInfo->aucReqIe,
prGlueInfo->u4ReqIeLength,
prGlueInfo->aucRspIe,
prGlueInfo->u4RspIeLength, WLAN_STATUS_AUTH_TIMEOUT, GFP_KERNEL);
break;
}
default:
/*
* printk(KERN_WARNING "unknown indication:%lx\n", eStatus);
*/
break;
}
} /* kalIndicateStatusAndComplete */
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is called to update the (re)association request
* information to the structure used to query and set
* OID_802_11_ASSOCIATION_INFORMATION.
*
* \param[in] prGlueInfo Pointer to the Glue structure.
* \param[in] pucFrameBody Pointer to the frame body of the last (Re)Association
* Request frame from the AP.
* \param[in] u4FrameBodyLen The length of the frame body of the last
* (Re)Association Request frame.
* \param[in] fgReassocRequest TRUE, if it is a Reassociation Request frame.
*
* \return (none)
*
*/
/*----------------------------------------------------------------------------*/
VOID
kalUpdateReAssocReqInfo(IN P_GLUE_INFO_T prGlueInfo,
IN PUINT_8 pucFrameBody, IN UINT_32 u4FrameBodyLen, IN BOOLEAN fgReassocRequest)
{
PUINT_8 cp;
ASSERT(prGlueInfo);
/* reset */
prGlueInfo->u4ReqIeLength = 0;
if (fgReassocRequest) {
if (u4FrameBodyLen < 15) {
/*
* printk(KERN_WARNING "frameBodyLen too short:%ld\n", frameBodyLen);
*/
return;
}
} else {
if (u4FrameBodyLen < 9) {
/*
* printk(KERN_WARNING "frameBodyLen too short:%ld\n", frameBodyLen);
*/
return;
}
}
cp = pucFrameBody;
if (fgReassocRequest) {
/* Capability information field 2 */
/* Listen interval field 2 */
/* Current AP address 6 */
cp += 10;
u4FrameBodyLen -= 10;
} else {
/* Capability information field 2 */
/* Listen interval field 2 */
cp += 4;
u4FrameBodyLen -= 4;
}
wext_indicate_wext_event(prGlueInfo, IWEVASSOCREQIE, cp, u4FrameBodyLen);
if (u4FrameBodyLen <= CFG_CFG80211_IE_BUF_LEN) {
prGlueInfo->u4ReqIeLength = u4FrameBodyLen;
kalMemCopy(prGlueInfo->aucReqIe, cp, u4FrameBodyLen);
}
}
/*----------------------------------------------------------------------------*/
/*!
* @brief This routine is called to update the (re)association
* response information to the structure used to reply with
* cfg80211_connect_result
*
* @param prGlueInfo Pointer to adapter descriptor
* @param pucFrameBody Pointer to the frame body of the last (Re)Association
* Response frame from the AP
* @param u4FrameBodyLen The length of the frame body of the last
* (Re)Association Response frame
*
* @return (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalUpdateReAssocRspInfo(IN P_GLUE_INFO_T prGlueInfo, IN PUINT_8 pucFrameBody, IN UINT_32 u4FrameBodyLen)
{
UINT_32 u4IEOffset = 6; /* cap_info, status_code & assoc_id */
UINT_32 u4IELength = u4FrameBodyLen - u4IEOffset;
ASSERT(prGlueInfo);
/* reset */
prGlueInfo->u4RspIeLength = 0;
if (u4IELength <= CFG_CFG80211_IE_BUF_LEN) {
prGlueInfo->u4RspIeLength = u4IELength;
kalMemCopy(prGlueInfo->aucRspIe, pucFrameBody + u4IEOffset, u4IELength);
}
} /* kalUpdateReAssocRspInfo */
VOID kalResetPacket(IN P_GLUE_INFO_T prGlueInfo, IN P_NATIVE_PACKET prPacket)
{
struct sk_buff *prSkb = (struct sk_buff *)prPacket;
/* Reset cb */
kalMemZero(prSkb->cb, sizeof(prSkb->cb));
}
/*----------------------------------------------------------------------------*/
/*
* \brief This function is to check the pairwise eapol and wapi 1x.
*
* \param[in] prPacket Pointer to struct net_device
*
* \retval WLAN_STATUS
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalIsPairwiseEapolPacket(IN P_NATIVE_PACKET prPacket)
{
struct sk_buff *prSkb = (struct sk_buff *)prPacket;
PUINT_8 pucPacket = (PUINT_8)prSkb->data;
UINT_16 u2EthType = 0;
UINT_16 u2KeyInfo = 0;
WLAN_GET_FIELD_BE16(&pucPacket[ETHER_HEADER_LEN - ETHER_TYPE_LEN], &u2EthType);
#if CFG_SUPPORT_WAPI
/* prBssInfo && prBssInfo->eNetworkType == NETWORK_TYPE_AIS && wlanQueryWapiMode(prAdapter) */
if (u2EthType == ETH_WPI_1X)
return TRUE;
#endif
if (u2EthType != ETH_P_1X)
return FALSE;
u2KeyInfo = pucPacket[5+ETHER_HEADER_LEN]<<8 | pucPacket[6+ETHER_HEADER_LEN];
#if 1
/* BIT3 is pairwise key bit, and check SM is 0. it means this is 4-way handshake frame */
DBGLOG(RSN, INFO, "u2KeyInfo=%d\n", u2KeyInfo);
if ((u2KeyInfo & BIT(3)) && !(u2KeyInfo & BIT(13)))
return TRUE;
#else
/* BIT3 is pairwise key bit, bit 8 is key mic bit.
* only the two bits are set, it means this is 4-way handshake 4/4 or 2/4 frame
*/
DBGLOG(RSN, INFO, "u2KeyInfo=%d\n", u2KeyInfo);
if ((u2KeyInfo & (BIT(3) | BIT(8))) == (BIT(3) | BIT(8)))
return TRUE;
#endif
return FALSE;
}
/*----------------------------------------------------------------------------*/
/*
* \brief This function is TX entry point of NET DEVICE.
*
* \param[in] prSkb Pointer of the sk_buff to be sent
* \param[in] prDev Pointer to struct net_device
* \param[in] prGlueInfo Pointer of prGlueInfo
* \param[in] ucBssIndex BSS index of this net device
*
* \retval WLAN_STATUS
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS
kalHardStartXmit(struct sk_buff *prOrgSkb, IN struct net_device *prDev, P_GLUE_INFO_T prGlueInfo, UINT_8 ucBssIndex)
{
P_QUE_ENTRY_T prQueueEntry = NULL;
P_QUE_T prTxQueue = NULL;
UINT_16 u2QueueIdx = 0;
UINT_32 u4MaxTxPendingNum = prGlueInfo->prAdapter->rWifiVar.u4NetifStopTh;
struct sk_buff *prSkbNew = NULL;
struct sk_buff *prSkb = NULL;
ASSERT(prOrgSkb);
ASSERT(prGlueInfo);
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(INIT, INFO, "GLUE_FLAG_HALT skip tx\n");
dev_kfree_skb(prOrgSkb);
return WLAN_STATUS_ADAPTER_NOT_READY;
}
#if defined(_HIF_USB)
if (prGlueInfo->rHifInfo.state != USB_STATE_LINK_UP) {
DBGLOG(INIT, WARN, "USB in suspend skip tx\n");
dev_kfree_skb(prOrgSkb);
return WLAN_STATUS_ADAPTER_NOT_READY;
}
#endif
if (prGlueInfo->prAdapter->fgIsEnableLpdvt) {
DBGLOG(INIT, INFO, "LPDVT enable, skip this frame\n");
dev_kfree_skb(prOrgSkb);
return WLAN_STATUS_NOT_ACCEPTED;
}
if (skb_headroom(prOrgSkb) < NIC_TX_HEAD_ROOM) {
prSkbNew = skb_realloc_headroom(prOrgSkb, NIC_TX_HEAD_ROOM);
ASSERT(prSkbNew);
prSkb = prSkbNew;
dev_kfree_skb(prOrgSkb);
} else
prSkb = prOrgSkb;
prQueueEntry = (P_QUE_ENTRY_T) GLUE_GET_PKT_QUEUE_ENTRY(prSkb);
prTxQueue = &prGlueInfo->rTxQueue;
GLUE_SET_PKT_BSS_IDX(prSkb, ucBssIndex);
/* Parsing frame info */
if (!wlanProcessTxFrame(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb)) {
/* Cannot extract packet */
DBGLOG(INIT, INFO, "Cannot extract content, skip this frame\n");
dev_kfree_skb(prSkb);
return WLAN_STATUS_INVALID_PACKET;
}
/* Tx profiling */
wlanTxProfilingTagPacket(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb, TX_PROF_TAG_OS_TO_DRV);
/* Handle normal data frame */
u2QueueIdx = skb_get_queue_mapping(prSkb);
if (u2QueueIdx >= CFG_MAX_TXQ_NUM) {
DBGLOG(INIT, INFO, "Incorrect queue index, skip this frame\n");
dev_kfree_skb(prSkb);
return WLAN_STATUS_INVALID_PACKET;
}
if (!HAL_IS_TX_DIRECT(prGlueInfo->prAdapter)) {
GLUE_SPIN_LOCK_DECLARATION();
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
QUEUE_INSERT_TAIL(prTxQueue, prQueueEntry);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
}
GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingFrameNum);
GLUE_INC_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx]);
/*
* WMM flow control
* 1. To enlarge threshold for WMM certification, WMM phase two may hit netif_stop_subquene
* Which may cause test case fail due to high priority packets are not enough.
* 2. Dynamic control threshold for AC queue.
* If there is high priority traffic, decrease low priority threshold.
* If these is low priority traffic, increase high priority threshold.
* Else, remians the original threshold.
*/
if (prGlueInfo->prAdapter->rWifiVar.ucTpTestMode == ENUM_TP_TEST_MODE_SIGMA_AC_N_PMF) {
P_BSS_INFO_T prWmmBssInfo = prGlueInfo->prAdapter->aprBssInfo[ucBssIndex];
if ((u2QueueIdx < 3) &&
(GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx+1])
> CFG_CERT_WMM_MAX_TX_PENDING)) {
/*
* Use au8Statistics[RX_SIZE_ERR_DROP_COUNT] to track RX traffic in certification.
*/
if ((prWmmBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) &&
((prDev->stats.rx_packets -
(prGlueInfo->prAdapter->rRxCtrl.au8Statistics[RX_SIZE_ERR_DROP_COUNT]))
> CFG_CERT_WMM_MAX_RX_NUM))
u4MaxTxPendingNum = CFG_CERT_WMM_LOW_STOP_TX_WITH_RX;
else
u4MaxTxPendingNum = CFG_CERT_WMM_LOW_STOP_TX_WO_RX;
}
else if ((u2QueueIdx > 0) &&
(GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx-1])
> CFG_CERT_WMM_MAX_TX_PENDING)) {
/*
* Use au8Statistics[RX_SIZE_ERR_DROP_COUNT] to track RX traffic in certification.
*/
if ((prWmmBssInfo->eCurrentOPMode == OP_MODE_ACCESS_POINT) &&
((prDev->stats.rx_packets -
(prGlueInfo->prAdapter->rRxCtrl.au8Statistics[RX_SIZE_ERR_DROP_COUNT]))
> CFG_CERT_WMM_MAX_RX_NUM))
u4MaxTxPendingNum = CFG_CERT_WMM_HIGH_STOP_TX_WITH_RX;
else
u4MaxTxPendingNum = CFG_CERT_WMM_HIGH_STOP_TX_WO_RX;
}
else
u4MaxTxPendingNum = prGlueInfo->prAdapter->rWifiVar.u4NetifStopTh;
}
if (GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx])
>= u4MaxTxPendingNum) {
netif_stop_subqueue(prDev, u2QueueIdx);
DBGLOG(TX, TRACE,
"Stop subqueue for BSS[%u] QIDX[%u] PKT_LEN[%u] TOT_CNT[%ld] PER-Q_CNT[%ld]\n",
ucBssIndex, u2QueueIdx, prSkb->len,
GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum),
GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex]
[u2QueueIdx]));
/* Re-use au8Statistics[RX_SIZE_ERR_DROP_COUNT] buffer to track RX traffic in certification */
if (prGlueInfo->prAdapter->rWifiVar.ucTpTestMode == ENUM_TP_TEST_MODE_SIGMA_AC_N_PMF)
prGlueInfo->prAdapter->rRxCtrl.au8Statistics[RX_SIZE_ERR_DROP_COUNT] = prDev->stats.rx_packets;
}
/* Update NetDev statisitcs */
prDev->stats.tx_bytes += prSkb->len;
prDev->stats.tx_packets++;
DBGLOG(TX, LOUD,
"Enqueue frame for BSS[%u] QIDX[%u] PKT_LEN[%u] TOT_CNT[%ld] PER-Q_CNT[%ld]\n",
ucBssIndex, u2QueueIdx, prSkb->len,
GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum),
GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx]));
if (HAL_IS_TX_DIRECT(prGlueInfo->prAdapter))
return nicTxDirectStartXmit(prSkb, prGlueInfo);
kalSetEvent(prGlueInfo);
return WLAN_STATUS_SUCCESS;
} /* end of kalHardStartXmit() */
WLAN_STATUS kalResetStats(IN struct net_device *prDev)
{
DBGLOG(QM, INFO, "Reset NetDev[0x%p] statistics\n", prDev);
kalMemZero(kalGetStats(prDev), sizeof(struct net_device_stats));
return WLAN_STATUS_SUCCESS;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief A method of struct net_device, to get the network interface statistical
* information.
*
* Whenever an application needs to get statistics for the interface, this method
* is called. This happens, for example, when ifconfig or netstat -i is run.
*
* \param[in] prDev Pointer to struct net_device.
*
* \return net_device_stats buffer pointer.
*/
/*----------------------------------------------------------------------------*/
PVOID kalGetStats(IN struct net_device *prDev)
{
return (PVOID) &prDev->stats;
} /* end of wlanGetStats() */
/*----------------------------------------------------------------------------*/
/*!
* \brief Notify OS with SendComplete event of the specific packet. Linux should
* free packets here.
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
* \param[in] pvPacket Pointer of Packet Handle
* \param[in] status Status Code for OS upper layer
*
* \return -
*/
/*----------------------------------------------------------------------------*/
VOID kalSendCompleteAndAwakeQueue(IN P_GLUE_INFO_T prGlueInfo, IN PVOID pvPacket)
{
struct net_device *prDev = NULL;
struct sk_buff *prSkb = NULL;
UINT_16 u2QueueIdx = 0;
UINT_8 ucBssIndex = 0;
BOOLEAN fgIsValidDevice = TRUE;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(pvPacket);
/* ASSERT(prGlueInfo->i4TxPendingFrameNum); */
prSkb = (struct sk_buff *)pvPacket;
u2QueueIdx = skb_get_queue_mapping(prSkb);
ASSERT(u2QueueIdx < CFG_MAX_TXQ_NUM);
ucBssIndex = GLUE_GET_PKT_BSS_IDX(pvPacket);
#if 0
if ((GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum) <= 0)) {
UINT_8 ucBssIdx;
UINT_16 u2QIdx;
DBGLOG(INIT, INFO, "TxPendingFrameNum[%u] CurFrameId[%u]\n", prGlueInfo->i4TxPendingFrameNum,
GLUE_GET_PKT_ARRIVAL_TIME(pvPacket));
for (ucBssIdx = 0; ucBssIdx < HW_BSSID_NUM; ucBssIdx++) {
for (u2QIdx = 0; u2QIdx < CFG_MAX_TXQ_NUM; u2QIdx++) {
DBGLOG(INIT, INFO, "BSS[%u] Q[%u] TxPendingFrameNum[%u]\n",
ucBssIdx, u2QIdx, prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIdx][u2QIdx]);
}
}
}
ASSERT((GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum) > 0));
#endif
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingFrameNum);
GLUE_DEC_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx]);
DBGLOG(TX, LOUD,
"Release frame for BSS[%u] QIDX[%u] PKT_LEN[%u] TOT_CNT[%ld] PER-Q_CNT[%ld]\n",
ucBssIndex, u2QueueIdx, prSkb->len,
GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum),
GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx]));
prDev = prSkb->dev;
ASSERT(prDev);
#if CFG_ENABLE_WIFI_DIRECT
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
{
P_BSS_INFO_T prBssInfo = GET_BSS_INFO_BY_INDEX(prGlueInfo->prAdapter, ucBssIndex);
P_GL_P2P_INFO_T prGlueP2pInfo = (P_GL_P2P_INFO_T) NULL;
struct net_device *prNetdevice = NULL;
/* in case packet was sent after P2P device is unregistered or the net_device was be free */
if (prBssInfo->eNetworkType == NETWORK_TYPE_P2P) {
if (prGlueInfo->prAdapter->fgIsP2PRegistered == FALSE)
fgIsValidDevice = FALSE;
else {
ASSERT(prBssInfo->u4PrivateData < KAL_P2P_NUM);
prGlueP2pInfo = prGlueInfo->prP2PInfo[prBssInfo->u4PrivateData];
if (prGlueP2pInfo) {
prNetdevice = prGlueP2pInfo->aprRoleHandler;
/* The net_device may be free */
if ((prDev != prNetdevice) && (prDev != prGlueP2pInfo->prDevHandler)) {
fgIsValidDevice = FALSE;
DBGLOG(TX, LOUD,
"kalSendCompleteAndAwakeQueue net device deleted! ucBssIndex = %u\n",
ucBssIndex);
}
}
}
}
}
#endif
if (fgIsValidDevice == TRUE) {
UINT_32 u4StartTh = prGlueInfo->prAdapter->rWifiVar.u4NetifStartTh;
if (netif_subqueue_stopped(prDev, prSkb) &&
prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx] <= u4StartTh) {
netif_wake_subqueue(prDev, u2QueueIdx);
DBGLOG(TX, TRACE,
"WakeUp Queue BSS[%u] QIDX[%u] PKT_LEN[%u] TOT_CNT[%ld] PER-Q_CNT[%ld]\n",
ucBssIndex, u2QueueIdx, prSkb->len,
GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum),
GLUE_GET_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex]
[u2QueueIdx]));
}
}
#if CFG_ENABLE_WIFI_DIRECT
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
#endif
dev_kfree_skb_any((struct sk_buff *)pvPacket);
DBGLOG(TX, LOUD, "----- pending frame %d -----\n", prGlueInfo->i4TxPendingFrameNum);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Copy Mac Address setting from registry. It's All Zeros in Linux.
*
* \param[in] prAdapter Pointer to the Adapter structure
*
* \param[out] paucMacAddr Pointer to the Mac Address buffer
*
* \retval WLAN_STATUS_SUCCESS
*
* \note
*/
/*----------------------------------------------------------------------------*/
VOID kalQueryRegistryMacAddr(IN P_GLUE_INFO_T prGlueInfo, OUT PUINT_8 paucMacAddr)
{
UINT_8 aucZeroMac[MAC_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }
DEBUGFUNC("kalQueryRegistryMacAddr");
ASSERT(prGlueInfo);
ASSERT(paucMacAddr);
kalMemCopy((PVOID) paucMacAddr, (PVOID) aucZeroMac, MAC_ADDR_LEN);
} /* end of kalQueryRegistryMacAddr() */
#if CFG_SUPPORT_EXT_CONFIG
/*----------------------------------------------------------------------------*/
/*!
* \brief Read external configuration, ex. NVRAM or file
*
* \param[in] prGlueInfo Pointer of GLUE Data Structure
*
* \return none
*/
/*----------------------------------------------------------------------------*/
UINT_32 kalReadExtCfg(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
/* External data is given from user space by ioctl or /proc, not read by
* driver.
*/
if (prGlueInfo->u4ExtCfgLength != 0)
DBGLOG(INIT, TRACE, "Read external configuration data -- OK\n");
else
DBGLOG(INIT, TRACE, "Read external configuration data -- fail\n");
return prGlueInfo->u4ExtCfgLength;
}
#endif
BOOLEAN
kalIPv4FrameClassifier(IN P_GLUE_INFO_T prGlueInfo,
IN P_NATIVE_PACKET prPacket, IN PUINT_8 pucIpHdr, OUT P_TX_PACKET_INFO prTxPktInfo)
{
UINT_8 ucIpVersion;
/* UINT_16 u2IpId; */
/* IPv4 version check */
ucIpVersion = (pucIpHdr[0] & IP_VERSION_MASK) >> IP_VERSION_OFFSET;
if (ucIpVersion != IP_VERSION_4) {
DBGLOG(INIT, WARN, "Invalid IPv4 packet version: %u\n", ucIpVersion);
return FALSE;
}
/* WLAN_GET_FIELD_16(&pucIpHdr[IPV4_HDR_IP_IDENTIFICATION_OFFSET], &u2IpId); */
if (pucIpHdr[IPV4_HDR_IP_PROTOCOL_OFFSET] == IP_PROTOCOL_UDP) {
PUINT_8 pucUdpHdr = &pucIpHdr[IPV4_HDR_LEN];
UINT_16 u2DstPort;
/* UINT_16 u2SrcPort; */
/* DBGLOG_MEM8(INIT, INFO, pucUdpHdr, 256); */
/* Get UDP DST port */
WLAN_GET_FIELD_BE16(&pucUdpHdr[UDP_HDR_DST_PORT_OFFSET], &u2DstPort);
/* DBGLOG(INIT, INFO, ("UDP DST[%u]\n", u2DstPort)); */
/* Get UDP SRC port */
/* WLAN_GET_FIELD_BE16(&pucUdpHdr[UDP_HDR_SRC_PORT_OFFSET], &u2SrcPort); */
/* BOOTP/DHCP protocol */
if ((u2DstPort == IP_PORT_BOOTP_SERVER) || (u2DstPort == IP_PORT_BOOTP_CLIENT)) {
P_BOOTP_PROTOCOL_T prBootp = (P_BOOTP_PROTOCOL_T) &pucUdpHdr[UDP_HDR_LEN];
UINT_32 u4DhcpMagicCode;
WLAN_GET_FIELD_BE32(&prBootp->aucOptions[0], &u4DhcpMagicCode);
#if 0
DBGLOG(INIT, INFO, "DHCP MGC[0x%08x] XID[%u] OPT[%u] TYPE[%u]\n",
u4DhcpMagicCode, prBootp->u4TransId, prBootp->aucOptions[4], prBootp->aucOptions[6]);
#endif
if (u4DhcpMagicCode == DHCP_MAGIC_NUMBER) {
UINT_32 u4Xid;
WLAN_GET_FIELD_BE32(&prBootp->u4TransId, &u4Xid);
DBGLOG(SW4, INFO, "DHCP PKT[0x%p] XID[0x%08x] OPT[%u] TYPE[%u]\n",
prPacket, u4Xid, prBootp->aucOptions[4], prBootp->aucOptions[6]);
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_DHCP);
}
}
}
return TRUE;
}
/*----------------------------------------------------------------------------*/
/*!
* @brief This inline function is to extract some packet information, including
* user priority, packet length, destination address, 802.1x and BT over Wi-Fi
* or not.
*
* @param prGlueInfo Pointer to the glue structure
* @param prPacket Packet descriptor
* @param prTxPktInfo Extracted packet info
*
* @retval TRUE Success to extract information
* @retval FALSE Fail to extract correct information
*/
/*----------------------------------------------------------------------------*/
BOOLEAN
kalQoSFrameClassifierAndPacketInfo(IN P_GLUE_INFO_T prGlueInfo,
IN P_NATIVE_PACKET prPacket, OUT P_TX_PACKET_INFO prTxPktInfo)
{
UINT_32 u4PacketLen;
UINT_16 u2EtherTypeLen;
struct sk_buff *prSkb = (struct sk_buff *)prPacket;
PUINT_8 aucLookAheadBuf = NULL;
UINT_8 ucEthTypeLenOffset = ETHER_HEADER_LEN - ETHER_TYPE_LEN;
PUINT_8 pucNextProtocol = NULL;
UINT_16 u2KeyInfo = 0;
UINT_8 ucEAPoLKey = 0;
UINT_8 ucEapOffset = ETHER_HEADER_LEN;
u4PacketLen = prSkb->len;
if (u4PacketLen < ETHER_HEADER_LEN) {
DBGLOG(INIT, WARN, "Invalid Ether packet length: %lu\n", u4PacketLen);
return FALSE;
}
aucLookAheadBuf = prSkb->data;
/* Reset Packet Info */
kalMemZero(prTxPktInfo, sizeof(TX_PACKET_INFO));
/* 4 <0> Obtain Ether Type/Len */
WLAN_GET_FIELD_BE16(&aucLookAheadBuf[ucEthTypeLenOffset], &u2EtherTypeLen);
/* 4 <1> Skip 802.1Q header (VLAN Tagging) */
if (u2EtherTypeLen == ETH_P_VLAN) {
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_VLAN_EXIST);
ucEthTypeLenOffset += ETH_802_1Q_HEADER_LEN;
WLAN_GET_FIELD_BE16(&aucLookAheadBuf[ucEthTypeLenOffset], &u2EtherTypeLen);
}
/* 4 <2> Obtain next protocol pointer */
pucNextProtocol = &aucLookAheadBuf[ucEthTypeLenOffset + ETHER_TYPE_LEN];
/* 4 <3> Handle ethernet format */
switch (u2EtherTypeLen) {
/* IPv4 */
case ETH_P_IPV4:
/* IPv4 header length check */
if (u4PacketLen < (ucEthTypeLenOffset + ETHER_TYPE_LEN + IPV4_HDR_LEN)) {
DBGLOG(INIT, WARN, "Invalid IPv4 packet length: %lu\n", u4PacketLen);
break;
}
kalIPv4FrameClassifier(prGlueInfo, prPacket, pucNextProtocol, prTxPktInfo);
break;
#if 0
/* IPv6 */
case ETH_P_IPV6:
{
PUINT_8 pucIpHdr = pucNextProtocol;
UINT_8 ucIpVersion;
/* IPv6 header length check */
if (u4PacketLen < (ucEthTypeLenOffset + ETHER_TYPE_LEN + IPV6_HDR_LEN)) {
DBGLOG(INIT, WARN, "Invalid IPv6 packet length: %lu\n", u4PacketLen);
return FALSE;
}
/* IPv6 version check */
ucIpVersion = (pucIpHdr[0] & IP_VERSION_MASK) >> IP_VERSION_OFFSET;
if (ucIpVersion != IP_VERSION_6) {
DBGLOG(INIT, WARN, "Invalid IPv6 packet version: %u\n", ucIpVersion);
return FALSE;
}
/* Get the DSCP value from the header of IP packet. */
ucUserPriority = ((pucIpHdr[0] & IPV6_HDR_TC_PREC_MASK) >> IPV6_HDR_TC_PREC_OFFSET);
}
break;
#endif
case ETH_P_ARP:
{
UINT_16 u2ArpOp;
WLAN_GET_FIELD_BE16(&pucNextProtocol[ARP_OPERATION_OFFSET], &u2ArpOp);
DBGLOG(SW4, INFO, "ARP %s PKT[0x%p] TAR MAC/IP[" MACSTR "]/[" IPV4STR "]\n",
u2ArpOp == ARP_OPERATION_REQUEST ? "REQ" : "RSP",
prPacket, MAC2STR(&pucNextProtocol[ARP_TARGET_MAC_OFFSET]),
IPV4TOSTR(&pucNextProtocol[ARP_TARGET_IP_OFFSET]));
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_ARP);
}
break;
case ETH_P_1X:
case ETH_P_PRE_1X:
#if CFG_SUPPORT_WAPI
case ETH_WPI_1X:
#endif
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_1X);
DBGLOG(RSN, INFO, "T1x like normal data, PKT[0x%p]\n", prPacket);
if (u2EtherTypeLen == ETH_P_1X) {
/* Leave EAP to check */
ucEAPoLKey = aucLookAheadBuf[1 + ucEapOffset];
if (ucEAPoLKey != ETH_EAPOL_KEY)
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_NON_PROTECTED_1X);
else {
WLAN_GET_FIELD_BE16(&aucLookAheadBuf[5 + ucEapOffset], &u2KeyInfo);
/* BIT3 is pairwise key bit */
DBGLOG(RSN, INFO, "u2KeyInfo=%d\n", u2KeyInfo);
if (u2KeyInfo & BIT(3))
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_NON_PROTECTED_1X);
}
}
#if CFG_SUPPORT_WAPI
else if (u2EtherTypeLen == ETH_WPI_1X)
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_NON_PROTECTED_1X);
#endif
break;
default:
/* 4 <4> Handle 802.3 format if LEN <= 1500 */
if (u2EtherTypeLen <= ETH_802_3_MAX_LEN)
prTxPktInfo->u2Flag |= BIT(ENUM_PKT_802_3);
break;
}
/* 4 <4.1> Check for PAL (BT over Wi-Fi) */
/* Move to kalBowFrameClassifier */
/* 4 <5> Return the value of Priority Parameter. */
/* prSkb->priority is assigned by Linux wireless utility function(cfg80211_classify8021d) */
/* at net_dev selection callback (ndo_select_queue) */
prTxPktInfo->ucPriorityParam = prSkb->priority;
/* 4 <6> Retrieve Packet Information - DA */
/* Packet Length/ Destination Address */
prTxPktInfo->u4PacketLen = u4PacketLen;
kalMemCopy(prTxPktInfo->aucEthDestAddr, aucLookAheadBuf, PARAM_MAC_ADDR_LEN);
return TRUE;
} /* end of kalQoSFrameClassifier() */
BOOLEAN kalGetEthDestAddr(IN P_GLUE_INFO_T prGlueInfo, IN P_NATIVE_PACKET prPacket, OUT PUINT_8 pucEthDestAddr)
{
struct sk_buff *prSkb = (struct sk_buff *)prPacket;
PUINT_8 aucLookAheadBuf = NULL;
/* Sanity Check */
if (!prPacket || !prGlueInfo)
return FALSE;
aucLookAheadBuf = prSkb->data;
kalMemCopy(pucEthDestAddr, aucLookAheadBuf, PARAM_MAC_ADDR_LEN);
return TRUE;
}
VOID
kalOidComplete(IN P_GLUE_INFO_T prGlueInfo,
IN BOOLEAN fgSetQuery, IN UINT_32 u4SetQueryInfoLen, IN WLAN_STATUS rOidStatus)
{
ASSERT(prGlueInfo);
/* remove timeout check timer */
wlanoidClearTimeoutCheck(prGlueInfo->prAdapter);
prGlueInfo->rPendStatus = rOidStatus;
prGlueInfo->u4OidCompleteFlag = 1;
/* complete ONLY if there are waiters */
if (!completion_done(&prGlueInfo->rPendComp)) {
complete(&prGlueInfo->rPendComp);
} else {
DBGLOG(INIT, WARN, "SKIP multiple OID complete!\n");
/* WARN_ON(TRUE); */
}
if (rOidStatus == WLAN_STATUS_SUCCESS)
DBGLOG(INIT, TRACE, "Complete OID, status:success\n");
else
DBGLOG(INIT, WARN, "Complete OID, status:0x%08x\n", rOidStatus);
/* else let it timeout on kalIoctl entry */
}
VOID kalOidClearance(IN P_GLUE_INFO_T prGlueInfo)
{
}
/*----------------------------------------------------------------------------*/
/*!
* @brief This function is used to transfer linux ioctl to OID, and we
* need to specify the behavior of the OID by ourself
*
* @param prGlueInfo Pointer to the glue structure
* @param pvInfoBuf Data buffer
* @param u4InfoBufLen Data buffer length
* @param fgRead Is this a read OID
* @param fgWaitResp does this OID need to wait for values
* @param fgCmd does this OID compose command packet
* @param pu4QryInfoLen The data length of the return values
*
* @retval TRUE Success to extract information
* @retval FALSE Fail to extract correct information
*/
/*----------------------------------------------------------------------------*/
/* todo: enqueue the i/o requests for multiple processes access */
/* */
/* currently, return -1 */
/* */
/* static GL_IO_REQ_T OidEntry; */
WLAN_STATUS
kalIoctl(IN P_GLUE_INFO_T prGlueInfo,
IN PFN_OID_HANDLER_FUNC pfnOidHandler,
IN PVOID pvInfoBuf,
IN UINT_32 u4InfoBufLen, IN BOOL fgRead, IN BOOL fgWaitResp, IN BOOL fgCmd, OUT PUINT_32 pu4QryInfoLen)
{
return kalIoctlTimeout(prGlueInfo,
pfnOidHandler,
pvInfoBuf,
u4InfoBufLen, fgRead, fgWaitResp, fgCmd, -1,
pu4QryInfoLen);
}
/*----------------------------------------------------------------------------*/
/*!
* @brief This function is used to transfer linux ioctl to OID, and we
* need to specify the behavior of the OID by ourself
*
* @param prGlueInfo Pointer to the glue structure
* @param pvInfoBuf Data buffer
* @param u4InfoBufLen Data buffer length
* @param fgRead Is this a read OID
* @param fgWaitResp does this OID need to wait for values
* @param fgCmd does this OID compose command packet
* @param i4OidTimeout timeout for this OID
* @param pu4QryInfoLen The data length of the return values
*
* @retval TRUE Success to extract information
* @retval FALSE Fail to extract correct information
*/
/*----------------------------------------------------------------------------*/
WLAN_STATUS
kalIoctlTimeout(IN P_GLUE_INFO_T prGlueInfo,
IN PFN_OID_HANDLER_FUNC pfnOidHandler,
IN PVOID pvInfoBuf,
IN UINT_32 u4InfoBufLen, IN BOOL fgRead, IN BOOL fgWaitResp, IN BOOL fgCmd, IN INT_32 i4OidTimeout,
OUT PUINT_32 pu4QryInfoLen)
{
P_GL_IO_REQ_T prIoReq = NULL;
WLAN_STATUS ret = WLAN_STATUS_SUCCESS;
#if CFG_CHIP_RESET_SUPPORT
if (kalIsResetting())
return WLAN_STATUS_SUCCESS;
#endif
if (wlanIsChipAssert(prGlueInfo->prAdapter))
return WLAN_STATUS_SUCCESS;
/* GLUE_SPIN_LOCK_DECLARATION(); */
ASSERT(prGlueInfo);
/* <1> Check if driver is halt */
/* if (prGlueInfo->u4Flag & GLUE_FLAG_HALT) { */
/* return WLAN_STATUS_ADAPTER_NOT_READY; */
/* } */
if (down_interruptible(&g_halt_sem))
return WLAN_STATUS_FAILURE;
if (g_u4HaltFlag) {
up(&g_halt_sem);
return WLAN_STATUS_ADAPTER_NOT_READY;
}
if (down_interruptible(&prGlueInfo->ioctl_sem)) {
up(&g_halt_sem);
return WLAN_STATUS_FAILURE;
}
/* <2> TODO: thread-safe */
/* <3> point to the OidEntry of Glue layer */
prIoReq = &(prGlueInfo->OidEntry);
ASSERT(prIoReq);
/* <4> Compose the I/O request */
prIoReq->prAdapter = prGlueInfo->prAdapter;
prIoReq->pfnOidHandler = pfnOidHandler;
prIoReq->pvInfoBuf = pvInfoBuf;
prIoReq->u4InfoBufLen = u4InfoBufLen;
prIoReq->pu4QryInfoLen = pu4QryInfoLen;
prIoReq->fgRead = fgRead;
prIoReq->fgWaitResp = fgWaitResp;
prIoReq->rStatus = WLAN_STATUS_FAILURE;
if (i4OidTimeout >= 0 && i4OidTimeout <= WLAN_OID_TIMEOUT_THRESHOLD_MAX)
prIoReq->u4Timeout = (UINT_32)i4OidTimeout;
else
prIoReq->u4Timeout = WLAN_OID_TIMEOUT_THRESHOLD;
/* <5> Reset the status of pending OID */
prGlueInfo->rPendStatus = WLAN_STATUS_FAILURE;
/* prGlueInfo->u4TimeoutFlag = 0; */
prGlueInfo->u4OidCompleteFlag = 0;
/* <6> Check if we use the command queue */
prIoReq->u4Flag = fgCmd;
/* <7> schedule the OID bit */
set_bit(GLUE_FLAG_OID_BIT, &prGlueInfo->ulFlag);
/* <7.1> Hold wakelock to ensure OS won't be suspended */
KAL_WAKE_LOCK_TIMEOUT(prGlueInfo->prAdapter, &prGlueInfo->rTimeoutWakeLock,
MSEC_TO_JIFFIES(prGlueInfo->prAdapter->rWifiVar.u4WakeLockThreadWakeup));
/* <8> Wake up tx thread to handle kick start the I/O request */
wake_up_interruptible(&prGlueInfo->waitq);
/* <9> Block and wait for event or timeout, current the timeout is 2 secs */
wait_for_completion(&prGlueInfo->rPendComp);
{
/* Case 1: No timeout. */
/* if return WLAN_STATUS_PENDING, the status of cmd is stored in prGlueInfo */
if (prIoReq->rStatus == WLAN_STATUS_PENDING)
ret = prGlueInfo->rPendStatus;
else
ret = prIoReq->rStatus;
}
#if 0
{
/* Case 2: timeout */
/* clear pending OID's cmd in CMD queue */
if (fgCmd) {
prGlueInfo->u4TimeoutFlag = 1;
wlanReleasePendingOid(prGlueInfo->prAdapter, 0);
}
ret = WLAN_STATUS_FAILURE;
}
#endif
/* <10> Clear bit for error handling */
clear_bit(GLUE_FLAG_OID_BIT, &prGlueInfo->ulFlag);
up(&prGlueInfo->ioctl_sem);
up(&g_halt_sem);
return ret;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear all pending security frames
*
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalClearSecurityFrames(IN P_GLUE_INFO_T prGlueInfo)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
/* Clear pending security frames in prGlueInfo->rCmdQueue */
prCmdQue = &prGlueInfo->rCmdQueue;
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_MOVE_ALL(prTempCmdQue, prCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
while (prQueueEntry) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
if (prCmdInfo->eCmdType == COMMAND_TYPE_SECURITY_FRAME) {
if (prCmdInfo->pfCmdTimeoutHandler)
prCmdInfo->pfCmdTimeoutHandler(prGlueInfo->prAdapter, prCmdInfo);
else
wlanReleaseCommand(prGlueInfo->prAdapter, prCmdInfo, TX_RESULT_QUEUE_CLEARANCE);
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
} else {
QUEUE_INSERT_TAIL(prReturnCmdQue, prQueueEntry);
}
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
}
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_CONCATENATE_QUEUES_HEAD(prCmdQue, prReturnCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear pending security frames
* belongs to dedicated network type
*
* \param prGlueInfo Pointer of GLUE Data Structure
* \param eNetworkTypeIdx Network Type Index
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalClearSecurityFramesByBssIdx(IN P_GLUE_INFO_T prGlueInfo, IN UINT_8 ucBssIndex)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
P_MSDU_INFO_T prMsduInfo;
BOOLEAN fgFree;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
/* Clear pending security frames in prGlueInfo->rCmdQueue */
prCmdQue = &prGlueInfo->rCmdQueue;
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_MOVE_ALL(prTempCmdQue, prCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
while (prQueueEntry) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
prMsduInfo = prCmdInfo->prMsduInfo;
fgFree = FALSE;
if (prCmdInfo->eCmdType == COMMAND_TYPE_SECURITY_FRAME && prMsduInfo) {
if (prMsduInfo->ucBssIndex == ucBssIndex)
fgFree = TRUE;
}
if (fgFree) {
if (prCmdInfo->pfCmdTimeoutHandler)
prCmdInfo->pfCmdTimeoutHandler(prGlueInfo->prAdapter, prCmdInfo);
else
wlanReleaseCommand(prGlueInfo->prAdapter, prCmdInfo, TX_RESULT_QUEUE_CLEARANCE);
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
} else
QUEUE_INSERT_TAIL(prReturnCmdQue, prQueueEntry);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
}
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_CONCATENATE_QUEUES_HEAD(prCmdQue, prReturnCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear all pending management frames
*
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalClearMgmtFrames(IN P_GLUE_INFO_T prGlueInfo)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
/* Clear pending management frames in prGlueInfo->rCmdQueue */
prCmdQue = &prGlueInfo->rCmdQueue;
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_MOVE_ALL(prTempCmdQue, prCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
while (prQueueEntry) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
if (prCmdInfo->eCmdType == COMMAND_TYPE_MANAGEMENT_FRAME) {
wlanReleaseCommand(prGlueInfo->prAdapter, prCmdInfo, TX_RESULT_QUEUE_CLEARANCE);
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
} else {
QUEUE_INSERT_TAIL(prReturnCmdQue, prQueueEntry);
}
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
}
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_CONCATENATE_QUEUES_HEAD(prCmdQue, prReturnCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear all pending management frames
* belongs to dedicated network type
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalClearMgmtFramesByBssIdx(IN P_GLUE_INFO_T prGlueInfo, IN UINT_8 ucBssIndex)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
P_MSDU_INFO_T prMsduInfo;
BOOLEAN fgFree;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
/* Clear pending management frames in prGlueInfo->rCmdQueue */
prCmdQue = &prGlueInfo->rCmdQueue;
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_MOVE_ALL(prTempCmdQue, prCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
while (prQueueEntry) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
prMsduInfo = prCmdInfo->prMsduInfo;
fgFree = FALSE;
if (prCmdInfo->eCmdType == COMMAND_TYPE_MANAGEMENT_FRAME && prMsduInfo) {
if (prMsduInfo->ucBssIndex == ucBssIndex)
fgFree = TRUE;
}
if (fgFree) {
wlanReleaseCommand(prGlueInfo->prAdapter, prCmdInfo, TX_RESULT_QUEUE_CLEARANCE);
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
} else {
QUEUE_INSERT_TAIL(prReturnCmdQue, prQueueEntry);
}
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
}
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_CONCATENATE_QUEUES_HEAD(prCmdQue, prReturnCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
} /* kalClearMgmtFramesByBssIdx */
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear all commands in command queue
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalClearCommandQueue(IN P_GLUE_INFO_T prGlueInfo)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
/* Clear ALL in prGlueInfo->rCmdQueue */
prCmdQue = &prGlueInfo->rCmdQueue;
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_MOVE_ALL(prTempCmdQue, prCmdQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
while (prQueueEntry) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
if (prCmdInfo->pfCmdTimeoutHandler)
prCmdInfo->pfCmdTimeoutHandler(prGlueInfo->prAdapter, prCmdInfo);
else
wlanReleaseCommand(prGlueInfo->prAdapter, prCmdInfo, TX_RESULT_QUEUE_CLEARANCE);
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
QUEUE_REMOVE_HEAD(prTempCmdQue, prQueueEntry, P_QUE_ENTRY_T);
}
}
UINT_32 kalProcessTxPacket(P_GLUE_INFO_T prGlueInfo, struct sk_buff *prSkb)
{
UINT_32 u4Status = WLAN_STATUS_SUCCESS;
if (prSkb == NULL) {
DBGLOG(INIT, WARN, "prSkb == NULL in tx\n");
return u4Status;
}
/* Handle security frame */
if (0 /* GLUE_TEST_PKT_FLAG(prSkb, ENUM_PKT_1X) *//* No more sending via cmd */) {
if (wlanProcessSecurityFrame(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb)) {
u4Status = WLAN_STATUS_SUCCESS;
GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum);
} else {
u4Status = WLAN_STATUS_RESOURCES;
}
}
/* Handle normal frame */
else
u4Status = wlanEnqueueTxPacket(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb);
return u4Status;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to process Tx request to main_thread
*
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalProcessTxReq(P_GLUE_INFO_T prGlueInfo, PBOOLEAN pfgNeedHwAccess)
{
P_QUE_T prCmdQue = NULL;
P_QUE_T prTxQueue = NULL;
QUE_T rTempQue;
P_QUE_T prTempQue = &rTempQue;
QUE_T rTempReturnQue;
P_QUE_T prTempReturnQue = &rTempReturnQue;
P_QUE_ENTRY_T prQueueEntry = NULL;
/* struct sk_buff *prSkb = NULL; */
UINT_32 u4Status;
#if CFG_SUPPORT_MULTITHREAD
UINT_32 u4CmdCount = 0;
#endif
UINT_32 u4TxLoopCount;
/* for spin lock acquire and release */
GLUE_SPIN_LOCK_DECLARATION();
prTxQueue = &prGlueInfo->rTxQueue;
prCmdQue = &prGlueInfo->rCmdQueue;
QUEUE_INITIALIZE(prTempQue);
QUEUE_INITIALIZE(prTempReturnQue);
u4TxLoopCount = prGlueInfo->prAdapter->rWifiVar.u4TxFromOsLoopCount;
/* Process Mailbox Messages */
wlanProcessMboxMessage(prGlueInfo->prAdapter);
/* Process CMD request */
#if CFG_SUPPORT_MULTITHREAD
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
u4CmdCount = prCmdQue->u4NumElem;
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
if (u4CmdCount > 0)
wlanProcessCommandQueue(prGlueInfo->prAdapter, prCmdQue);
#else
if (prCmdQue->u4NumElem > 0) {
if (*pfgNeedHwAccess == FALSE) {
*pfgNeedHwAccess = TRUE;
wlanAcquirePowerControl(prGlueInfo->prAdapter);
}
wlanProcessCommandQueue(prGlueInfo->prAdapter, prCmdQue);
}
#endif
while (u4TxLoopCount--) {
while (QUEUE_IS_NOT_EMPTY(prTxQueue)) {
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
QUEUE_MOVE_ALL(prTempQue, prTxQueue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
/* Handle Packet Tx */
while (QUEUE_IS_NOT_EMPTY(prTempQue)) {
QUEUE_REMOVE_HEAD(prTempQue, prQueueEntry, P_QUE_ENTRY_T);
if (prQueueEntry == NULL)
break;
u4Status = kalProcessTxPacket(prGlueInfo,
(struct sk_buff *)GLUE_GET_PKT_DESCRIPTOR(prQueueEntry));
#if 0
prSkb = (struct sk_buff *)GLUE_GET_PKT_DESCRIPTOR(prQueueEntry);
ASSERT(prSkb);
if (prSkb == NULL) {
DBGLOG(INIT, WARN, "prSkb == NULL in tx\n");
continue;
}
/* Handle security frame */
if (GLUE_GET_PKT_IS_1X(prSkb)) {
if (wlanProcessSecurityFrame(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb)) {
u4Status = WLAN_STATUS_SUCCESS;
GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum);
} else {
u4Status = WLAN_STATUS_RESOURCES;
}
}
/* Handle normal frame */
else
u4Status = wlanEnqueueTxPacket(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb);
#endif
/* Enqueue packet back into TxQueue if resource is not enough */
if (u4Status == WLAN_STATUS_RESOURCES) {
QUEUE_INSERT_TAIL(prTempReturnQue, prQueueEntry);
break;
}
}
if (wlanGetTxPendingFrameCount(prGlueInfo->prAdapter) > 0)
wlanTxPendingPackets(prGlueInfo->prAdapter, pfgNeedHwAccess);
/* Enqueue packet back into TxQueue if resource is not enough */
if (QUEUE_IS_NOT_EMPTY(prTempReturnQue)) {
QUEUE_CONCATENATE_QUEUES(prTempReturnQue, prTempQue);
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
QUEUE_CONCATENATE_QUEUES_HEAD(prTxQueue, prTempReturnQue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
break;
}
}
if (wlanGetTxPendingFrameCount(prGlueInfo->prAdapter) > 0)
wlanTxPendingPackets(prGlueInfo->prAdapter, pfgNeedHwAccess);
}
}
#if CFG_SUPPORT_MULTITHREAD
/*----------------------------------------------------------------------------*/
/*!
* @brief
*
* @param data data pointer to private data of hif_thread
*
* @retval If the function succeeds, the return value is 0.
* Otherwise, an error code is returned.
*
*/
/*----------------------------------------------------------------------------*/
int hif_thread(void *data)
{
struct net_device *dev = data;
P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(dev));
int ret = 0;
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
KAL_WAKE_LOCK_T rHifThreadWakeLock;
#endif
KAL_WAKE_LOCK_INIT(prGlueInfo->prAdapter, &rHifThreadWakeLock, "WLAN hif_thread");
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rHifThreadWakeLock);
DBGLOG(INIT, INFO, "%s:%u starts running...\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
prGlueInfo->u4HifThreadPid = KAL_GET_CURRENT_THREAD_ID();
set_user_nice(current, prGlueInfo->prAdapter->rWifiVar.cThreadNice);
while (TRUE) {
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(INIT, INFO, "hif_thread should stop now...\n");
break;
}
/* Unlock wakelock if hif_thread going to idle */
if (!(prGlueInfo->ulFlag & GLUE_FLAG_HIF_PROCESS))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rHifThreadWakeLock);
/*
* sleep on waitqueue if no events occurred. Event contain (1) GLUE_FLAG_INT
* (2) GLUE_FLAG_OID (3) GLUE_FLAG_TXREQ (4) GLUE_FLAG_HALT
*
*/
do {
ret = wait_event_interruptible(prGlueInfo->waitq_hif,
((prGlueInfo->ulFlag & GLUE_FLAG_HIF_PROCESS) != 0));
} while (ret != 0);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (!KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rHifThreadWakeLock))
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rHifThreadWakeLock);
#endif
wlanAcquirePowerControl(prGlueInfo->prAdapter);
/* Handle Interrupt */
if (test_and_clear_bit(GLUE_FLAG_INT_BIT, &prGlueInfo->ulFlag)) {
/* the Wi-Fi interrupt is already disabled in mmc thread,
* so we set the flag only to enable the interrupt later
*/
prGlueInfo->prAdapter->fgIsIntEnable = FALSE;
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
/* Should stop now... skip pending interrupt */
DBGLOG(INIT, INFO, "ignore pending interrupt\n");
} else {
/* DBGLOG(INIT, INFO, ("HIF Interrupt!\n")); */
wlanIST(prGlueInfo->prAdapter);
}
}
/* Skip Tx request if SER is operating */
if (!nicSerIsTxStop(prGlueInfo->prAdapter)) {
/* TX Commands */
if (test_and_clear_bit(GLUE_FLAG_HIF_TX_CMD_BIT, &prGlueInfo->ulFlag))
wlanTxCmdMthread(prGlueInfo->prAdapter);
/* Process TX data packet to HIF */
if (test_and_clear_bit(GLUE_FLAG_HIF_TX_BIT, &prGlueInfo->ulFlag))
nicTxMsduQueueMthread(prGlueInfo->prAdapter);
}
/* Read chip status when chip no response */
if (test_and_clear_bit(GLUE_FLAG_HIF_PRT_HIF_DBG_INFO_BIT, &prGlueInfo->ulFlag))
halPrintHifDbgInfo(prGlueInfo->prAdapter);
/* Set FW own */
if (test_and_clear_bit(GLUE_FLAG_HIF_FW_OWN_BIT, &prGlueInfo->ulFlag))
prGlueInfo->prAdapter->fgWiFiInSleepyState = TRUE;
/* Release to FW own */
wlanReleasePowerControl(prGlueInfo->prAdapter);
}
complete(&prGlueInfo->rHifHaltComp);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rHifThreadWakeLock))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rHifThreadWakeLock);
KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &rHifThreadWakeLock);
#endif
DBGLOG(INIT, INFO, "%s:%u stopped!\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
return 0;
}
int rx_thread(void *data)
{
struct net_device *dev = data;
P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(dev));
QUE_T rTempRxQue;
P_QUE_T prTempRxQue = NULL;
P_QUE_ENTRY_T prQueueEntry = NULL;
int ret = 0;
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
KAL_WAKE_LOCK_T rRxThreadWakeLock;
#endif
UINT_32 u4LoopCount;
/* for spin lock acquire and release */
KAL_SPIN_LOCK_DECLARATION();
KAL_WAKE_LOCK_INIT(prGlueInfo->prAdapter, &rRxThreadWakeLock, "WLAN rx_thread");
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rRxThreadWakeLock);
DBGLOG(INIT, INFO, "%s:%u starts running...\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
prGlueInfo->u4RxThreadPid = KAL_GET_CURRENT_THREAD_ID();
set_user_nice(current, prGlueInfo->prAdapter->rWifiVar.cThreadNice);
prTempRxQue = &rTempRxQue;
while (TRUE) {
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(INIT, INFO, "rx_thread should stop now...\n");
break;
}
/* Unlock wakelock if rx_thread going to idle */
if (!(prGlueInfo->ulFlag & GLUE_FLAG_RX_PROCESS))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rRxThreadWakeLock);
/*
* sleep on waitqueue if no events occurred.
*/
do {
ret = wait_event_interruptible(prGlueInfo->waitq_rx,
((prGlueInfo->ulFlag & GLUE_FLAG_RX_PROCESS) != 0));
} while (ret != 0);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (!KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rRxThreadWakeLock))
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rRxThreadWakeLock);
#endif
if (test_and_clear_bit(GLUE_FLAG_RX_TO_OS_BIT, &prGlueInfo->ulFlag)) {
u4LoopCount = prGlueInfo->prAdapter->rWifiVar.u4Rx2OsLoopCount;
while (u4LoopCount--) {
while (QUEUE_IS_NOT_EMPTY(&prGlueInfo->prAdapter->rRxQueue)) {
QUEUE_INITIALIZE(prTempRxQue);
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_RX_TO_OS_QUE);
QUEUE_MOVE_ALL(prTempRxQue, &prGlueInfo->prAdapter->rRxQueue);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_RX_TO_OS_QUE);
while (QUEUE_IS_NOT_EMPTY(prTempRxQue)) {
QUEUE_REMOVE_HEAD(prTempRxQue, prQueueEntry, P_QUE_ENTRY_T);
kalRxIndicateOnePkt(prGlueInfo,
(PVOID) GLUE_GET_PKT_DESCRIPTOR(prQueueEntry));
}
KAL_WAKE_LOCK_TIMEOUT(prGlueInfo->prAdapter, &prGlueInfo->rTimeoutWakeLock,
MSEC_TO_JIFFIES(prGlueInfo->prAdapter->rWifiVar.u4WakeLockRxTimeout));
}
}
}
}
complete(&prGlueInfo->rRxHaltComp);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rRxThreadWakeLock))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rRxThreadWakeLock);
KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &rRxThreadWakeLock);
#endif
DBGLOG(INIT, INFO, "%s:%u stopped!\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
return 0;
}
#endif
/*----------------------------------------------------------------------------*/
/*!
* @brief This function is a kernel thread function for handling command packets
* Tx requests and interrupt events
*
* @param data data pointer to private data of main_thread
*
* @retval If the function succeeds, the return value is 0.
* Otherwise, an error code is returned.
*
*/
/*----------------------------------------------------------------------------*/
int main_thread(void *data)
{
struct net_device *dev = data;
P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(dev));
P_GL_IO_REQ_T prIoReq = NULL;
int ret = 0;
BOOLEAN fgNeedHwAccess = FALSE;
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
KAL_WAKE_LOCK_T rTxThreadWakeLock;
#endif
#if CFG_SUPPORT_MULTITHREAD
prGlueInfo->u4TxThreadPid = KAL_GET_CURRENT_THREAD_ID();
#endif
current->flags |= PF_NOFREEZE;
ASSERT(prGlueInfo);
ASSERT(prGlueInfo->prAdapter);
set_user_nice(current, prGlueInfo->prAdapter->rWifiVar.cThreadNice);
KAL_WAKE_LOCK_INIT(prGlueInfo->prAdapter, &rTxThreadWakeLock, "WLAN main_thread");
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rTxThreadWakeLock);
DBGLOG(INIT, INFO, "%s:%u starts running...\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
while (TRUE) {
#if CFG_ENABLE_WIFI_DIRECT
/*run p2p multicast list work. */
if (test_and_clear_bit(GLUE_FLAG_SUB_MOD_MULTICAST_BIT, &prGlueInfo->ulFlag))
p2pSetMulticastListWorkQueueWrapper(prGlueInfo);
#endif
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(INIT, INFO, "%s should stop now...\n", KAL_GET_CURRENT_THREAD_NAME());
break;
}
/* Unlock wakelock if main_thread going to idle */
if (!(prGlueInfo->ulFlag & GLUE_FLAG_MAIN_PROCESS))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rTxThreadWakeLock);
/*
* sleep on waitqueue if no events occurred. Event contain (1) GLUE_FLAG_INT
* (2) GLUE_FLAG_OID (3) GLUE_FLAG_TXREQ (4) GLUE_FLAG_HALT
*
*/
do {
ret = wait_event_interruptible(prGlueInfo->waitq,
((prGlueInfo->ulFlag & GLUE_FLAG_MAIN_PROCESS) != 0));
} while (ret != 0);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (!KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rTxThreadWakeLock))
KAL_WAKE_LOCK(prGlueInfo->prAdapter, &rTxThreadWakeLock);
#endif
#if CFG_ENABLE_WIFI_DIRECT
/*run p2p multicast list work. */
if (test_and_clear_bit(GLUE_FLAG_SUB_MOD_MULTICAST_BIT, &prGlueInfo->ulFlag))
p2pSetMulticastListWorkQueueWrapper(prGlueInfo);
if (test_and_clear_bit(GLUE_FLAG_FRAME_FILTER_BIT, &prGlueInfo->ulFlag)) {
p2pFuncUpdateMgmtFrameRegister(prGlueInfo->prAdapter,
prGlueInfo->prP2PDevInfo->u4OsMgmtFrameFilter);
}
#endif
if (test_and_clear_bit(GLUE_FLAG_FRAME_FILTER_AIS_BIT, &prGlueInfo->ulFlag)) {
P_AIS_FSM_INFO_T prAisFsmInfo = (P_AIS_FSM_INFO_T) NULL;
/* printk("prGlueInfo->u4OsMgmtFrameFilter = %x", prGlueInfo->u4OsMgmtFrameFilter); */
prAisFsmInfo = &(prGlueInfo->prAdapter->rWifiVar.rAisFsmInfo);
prAisFsmInfo->u4AisPacketFilter = prGlueInfo->u4OsMgmtFrameFilter;
}
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(INIT, INFO, "%s should stop now...\n", KAL_GET_CURRENT_THREAD_NAME());
break;
}
fgNeedHwAccess = FALSE;
#if CFG_SUPPORT_SDIO_READ_WRITE_PATTERN
if (prGlueInfo->fgEnSdioTestPattern == TRUE) {
if (fgNeedHwAccess == FALSE) {
fgNeedHwAccess = TRUE;
wlanAcquirePowerControl(prGlueInfo->prAdapter);
}
if (prGlueInfo->fgIsSdioTestInitialized == FALSE) {
/* enable PRBS mode */
kalDevRegWrite(prGlueInfo, MCR_WTMCR, 0x00080002);
prGlueInfo->fgIsSdioTestInitialized = TRUE;
}
if (prGlueInfo->fgSdioReadWriteMode == TRUE) {
/* read test */
kalDevPortRead(prGlueInfo,
MCR_WTMDR,
256,
prGlueInfo->aucSdioTestBuffer, sizeof(prGlueInfo->aucSdioTestBuffer));
} else {
/* write test */
kalDevPortWrite(prGlueInfo,
MCR_WTMDR,
172,
prGlueInfo->aucSdioTestBuffer, sizeof(prGlueInfo->aucSdioTestBuffer));
}
}
#endif
#if CFG_SUPPORT_MULTITHREAD
#else
/* Handle Interrupt */
if (test_and_clear_bit(GLUE_FLAG_INT_BIT, &prGlueInfo->ulFlag)) {
if (fgNeedHwAccess == FALSE) {
fgNeedHwAccess = TRUE;
wlanAcquirePowerControl(prGlueInfo->prAdapter);
}
/* the Wi-Fi interrupt is already disabled in mmc thread,
* so we set the flag only to enable the interrupt later
*/
prGlueInfo->prAdapter->fgIsIntEnable = FALSE;
/* wlanISR(prGlueInfo->prAdapter, TRUE); */
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
/* Should stop now... skip pending interrupt */
DBGLOG(INIT, INFO, "ignore pending interrupt\n");
} else {
wlanIST(prGlueInfo->prAdapter);
}
}
#endif
/* transfer ioctl to OID request */
do {
if (test_and_clear_bit(GLUE_FLAG_OID_BIT, &prGlueInfo->ulFlag)) {
/* get current prIoReq */
prIoReq = &(prGlueInfo->OidEntry);
if (prIoReq->fgRead == FALSE) {
prIoReq->rStatus = wlanSetInformation(prIoReq->prAdapter,
prIoReq->pfnOidHandler,
prIoReq->pvInfoBuf,
prIoReq->u4InfoBufLen,
prIoReq->pu4QryInfoLen);
} else {
prIoReq->rStatus = wlanQueryInformation(prIoReq->prAdapter,
prIoReq->pfnOidHandler,
prIoReq->pvInfoBuf,
prIoReq->u4InfoBufLen,
prIoReq->pu4QryInfoLen);
}
if (prIoReq->rStatus != WLAN_STATUS_PENDING) {
/* complete ONLY if there are waiters */
if (!completion_done(&prGlueInfo->rPendComp))
complete(&prGlueInfo->rPendComp);
else
DBGLOG(INIT, WARN, "SKIP multiple OID complete!\n");
} else {
wlanoidTimeoutCheck(prGlueInfo->prAdapter,
prIoReq->pfnOidHandler,
prIoReq->u4Timeout);
}
}
} while (FALSE);
/*
*
* if TX request, clear the TXREQ flag. TXREQ set by kalSetEvent/GlueSetEvent
* indicates the following requests occur
*
*/
if (test_and_clear_bit(GLUE_FLAG_TXREQ_BIT, &prGlueInfo->ulFlag))
kalProcessTxReq(prGlueInfo, &fgNeedHwAccess);
#if CFG_SUPPORT_MULTITHREAD
/* Process RX */
if (test_and_clear_bit(GLUE_FLAG_RX_BIT, &prGlueInfo->ulFlag))
nicRxProcessRFBs(prGlueInfo->prAdapter);
if (test_and_clear_bit(GLUE_FLAG_TX_CMD_DONE_BIT, &prGlueInfo->ulFlag))
wlanTxCmdDoneMthread(prGlueInfo->prAdapter);
#endif
/* Process RX, In linux, we don't need to free sk_buff by ourself */
/* In linux, we don't need to free sk_buff by ourself */
/* In linux, we don't do reset */
#if CFG_SUPPORT_MULTITHREAD
#else
if (fgNeedHwAccess == TRUE)
wlanReleasePowerControl(prGlueInfo->prAdapter);
#endif
/* handle cnmTimer time out */
if (test_and_clear_bit(GLUE_FLAG_TIMEOUT_BIT, &prGlueInfo->ulFlag))
wlanTimerTimeoutCheck(prGlueInfo->prAdapter);
#if CFG_SUPPORT_SDIO_READ_WRITE_PATTERN
if (prGlueInfo->fgEnSdioTestPattern == TRUE)
kalSetEvent(prGlueInfo);
#endif
}
#if 0
if (fgNeedHwAccess == TRUE)
wlanReleasePowerControl(prGlueInfo->prAdapter);
#endif
/* flush the pending TX packets */
if (GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum) > 0)
kalFlushPendingTxPackets(prGlueInfo);
/* flush pending security frames */
if (GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum) > 0)
kalClearSecurityFrames(prGlueInfo);
/* remove pending oid */
wlanReleasePendingOid(prGlueInfo->prAdapter, 0);
complete(&prGlueInfo->rHaltComp);
#if defined(CONFIG_ANDROID) && (CFG_ENABLE_WAKE_LOCK)
if (KAL_WAKE_LOCK_ACTIVE(prGlueInfo->prAdapter, &rTxThreadWakeLock))
KAL_WAKE_UNLOCK(prGlueInfo->prAdapter, &rTxThreadWakeLock);
KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &rTxThreadWakeLock);
#endif
DBGLOG(INIT, INFO, "%s:%u stopped!\n", KAL_GET_CURRENT_THREAD_NAME(), KAL_GET_CURRENT_THREAD_ID());
return 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to check if card is removed
*
* \param pvGlueInfo Pointer of GLUE Data Structure
*
* \retval TRUE: card is removed
* FALSE: card is still attached
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalIsCardRemoved(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return FALSE;
/* Linux MMC doesn't have removal notification yet */
}
#ifdef CONFIG_IDME
#define IDME_MACADDR "/proc/idme/mac_addr"
static int idme_get_mac_addr(unsigned char *mac_addr, size_t addr_len)
{
unsigned char buf[IFHWADDRLEN * 2 + 1] = {""}, str[3] = {""};
int i, mac[IFHWADDRLEN];
mm_segment_t old_fs;
struct file *f;
size_t len;
if (!mac_addr || addr_len < IFHWADDRLEN) {
DBGLOG(INIT, ERROR, "invalid mac_addr ptr or buf\n");
return -1;
}
f = filp_open(IDME_MACADDR, O_RDONLY, 0);
if (IS_ERR(f)) {
DBGLOG(INIT, ERROR, "can't open mac addr file\n");
return -1;
}
old_fs = get_fs();
set_fs(get_ds());
f->f_op->read(f, buf, IFHWADDRLEN * 2, &f->f_pos);
filp_close(f, NULL);
set_fs(old_fs);
if (strlen(buf) != IFHWADDRLEN * 2)
goto bailout;
for (i = 0; i < IFHWADDRLEN; i++) {
str[0] = buf[i * 2];
str[1] = buf[i * 2 + 1];
if (!isxdigit(str[0]) || !isxdigit(str[1]))
goto bailout;
len = sscanf(str, "%02x", &mac[i]);
if (len != 1)
goto bailout;
}
for (i = 0; i < IFHWADDRLEN; i++)
mac_addr[i] = (unsigned char)mac[i];
return 0;
bailout:
DBGLOG(INIT, ERROR, "wrong mac addr %02x %02x\n", buf[0], buf[1]);
return -1;
}
#endif
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to send command to firmware for overriding netweork address
*
* \param pvGlueInfo Pointer of GLUE Data Structure
* \retval TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalRetrieveNetworkAddress(IN P_GLUE_INFO_T prGlueInfo, IN OUT PARAM_MAC_ADDRESS *prMacAddr)
{
P_ADAPTER_T prAdapter;
ASSERT(prGlueInfo);
prAdapter = prGlueInfo->prAdapter;
/* Get MAC address override from wlan feature option */
prGlueInfo->fgIsMacAddrOverride = prAdapter->rWifiVar.ucMacAddrOverride;
wlanHwAddrToBin(prAdapter->rWifiVar.aucMacAddrStr, prGlueInfo->rMacAddrOverride);
#ifdef CONFIG_IDME
if (prMacAddr && 0 == idme_get_mac_addr((unsigned char *)prMacAddr, sizeof(PARAM_MAC_ADDRESS))) {
DBGLOG(INIT, INFO, "use IDME mac addr\n");
return TRUE;
}
#endif
if (prGlueInfo->fgIsMacAddrOverride == FALSE) {
UINT_32 i;
BOOLEAN fgIsReadError = FALSE;
#if !defined(CONFIG_X86)
for (i = 0; i < MAC_ADDR_LEN; i += 2) {
if (kalCfgDataRead16(prGlueInfo,
OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucMacAddress) + i,
(PUINT_16) (((PUINT_8) prMacAddr) + i)) == FALSE) {
fgIsReadError = TRUE;
break;
}
}
#else
/* x86 Linux doesn't need to override network address so far */
/*return FALSE;*/
/*Modify for Linux PC support NVRAM Setting*/
for (i = 0; i < MAC_ADDR_LEN; i += 2) {
if (kalCfgDataRead16(prGlueInfo,
OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucMacAddress) + i,
(PUINT_16) (((PUINT_8) prMacAddr) + i)) == FALSE) {
fgIsReadError = TRUE;
break;
}
}
#endif
#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)
/* retrieve buffer mode efuse */
if ((prAdapter->fgIsSupportPowerOnSendBufferModeCMD == TRUE) &&
(prAdapter->rWifiVar.ucEfuseBufferModeCal == TRUE)) {
if (wlanExtractBufferBin(prAdapter) == WLAN_STATUS_SUCCESS) {
UINT_32 u4BinOffset = prAdapter->u4EfuseMacAddrOffset;
/* Update MAC address */
kalMemCopy(prMacAddr, &uacEEPROMImage[u4BinOffset], MAC_ADDR_LEN);
fgIsReadError = FALSE;
} else {
fgIsReadError = TRUE;
}
}
#endif
/* return retrieve result */
if (fgIsReadError == TRUE)
return FALSE;
else
return TRUE;
} else {
COPY_MAC_ADDR(prMacAddr, prGlueInfo->rMacAddrOverride);
return TRUE;
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to flush pending TX packets in glue layer
*
* \param pvGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalFlushPendingTxPackets(IN P_GLUE_INFO_T prGlueInfo)
{
P_QUE_T prTxQue;
P_QUE_ENTRY_T prQueueEntry;
PVOID prPacket;
ASSERT(prGlueInfo);
prTxQue = &(prGlueInfo->rTxQueue);
if (GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum) == 0)
return;
if (HAL_IS_TX_DIRECT()) {
nicTxDirectClearSkbQ(prGlueInfo->prAdapter);
} else {
GLUE_SPIN_LOCK_DECLARATION();
while (TRUE) {
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
QUEUE_REMOVE_HEAD(prTxQue, prQueueEntry, P_QUE_ENTRY_T);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE);
if (prQueueEntry == NULL)
break;
prPacket = GLUE_GET_PKT_DESCRIPTOR(prQueueEntry);
kalSendComplete(prGlueInfo, prPacket, WLAN_STATUS_NOT_ACCEPTED);
}
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is get indicated media state
*
* \param pvGlueInfo Pointer of GLUE Data Structure
*
* \retval
*/
/*----------------------------------------------------------------------------*/
ENUM_PARAM_MEDIA_STATE_T kalGetMediaStateIndicated(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return prGlueInfo->eParamMediaStateIndicated;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to set indicated media state
*
* \param pvGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalSetMediaStateIndicated(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_PARAM_MEDIA_STATE_T eParamMediaStateIndicate)
{
ASSERT(prGlueInfo);
prGlueInfo->eParamMediaStateIndicated = eParamMediaStateIndicate;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to clear pending OID staying in command queue
*
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalOidCmdClearance(IN P_GLUE_INFO_T prGlueInfo)
{
P_QUE_T prCmdQue;
QUE_T rTempCmdQue;
P_QUE_T prTempCmdQue = &rTempCmdQue;
QUE_T rReturnCmdQue;
P_QUE_T prReturnCmdQue = &rReturnCmdQue;
P_QUE_ENTRY_T prQueueEntry = (P_QUE_ENTRY_T) NULL;
P_CMD_INFO_T prCmdInfo = (P_CMD_INFO_T) NULL;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
QUEUE_INITIALIZE(prReturnCmdQue);
prCmdQue = &prGlueInfo->