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->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) {
if (((P_CMD_INFO_T) prQueueEntry)->fgIsOid) {
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
break;
}
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);
if (prCmdInfo) {
if (prCmdInfo->pfCmdTimeoutHandler)
prCmdInfo->pfCmdTimeoutHandler(prGlueInfo->prAdapter, prCmdInfo);
else
kalOidComplete(prGlueInfo, prCmdInfo->fgSetQuery, 0, WLAN_STATUS_NOT_ACCEPTED);
prGlueInfo->u4OidCompleteFlag = 1;
cmdBufFreeCmdInfo(prGlueInfo->prAdapter, prCmdInfo);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to insert command into prCmdQueue
*
* \param prGlueInfo Pointer of GLUE Data Structure
* prQueueEntry Pointer of queue entry to be inserted
*
* \retval none
*/
/*----------------------------------------------------------------------------*/
VOID kalEnqueueCommand(IN P_GLUE_INFO_T prGlueInfo, IN P_QUE_ENTRY_T prQueueEntry)
{
P_QUE_T prCmdQue;
P_CMD_INFO_T prCmdInfo;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
ASSERT(prQueueEntry);
prCmdQue = &prGlueInfo->rCmdQueue;
prCmdInfo = (P_CMD_INFO_T) prQueueEntry;
DBGLOG(INIT, INFO, "EN-Q CMD TYPE[%u] ID[0x%02X] SEQ[%u] to CMD Q\n",
prCmdInfo->eCmdType, prCmdInfo->ucCID, prCmdInfo->ucCmdSeqNum);
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
QUEUE_INSERT_TAIL(prCmdQue, prQueueEntry);
GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_CMD_QUE);
}
/*----------------------------------------------------------------------------*/
/*!
* @brief Handle EVENT_ID_ASSOC_INFO event packet by indicating to OS with
* proper information
*
* @param pvGlueInfo Pointer of GLUE Data Structure
* @param prAssocInfo Pointer of EVENT_ID_ASSOC_INFO Packet
*
* @return none
*/
/*----------------------------------------------------------------------------*/
VOID kalHandleAssocInfo(IN P_GLUE_INFO_T prGlueInfo, IN P_EVENT_ASSOC_INFO prAssocInfo)
{
/* to do */
}
/*----------------------------------------------------------------------------*/
/*!
* * @brief Notify OS with SendComplete event of the specific packet. Linux should
* * free packets here.
* *
* * @param pvGlueInfo Pointer of GLUE Data Structure
* * @param pvPacket Pointer of Packet Handle
* * @param status Status Code for OS upper layer
* *
* * @return none
*/
/*----------------------------------------------------------------------------*/
/* / Todo */
VOID kalSecurityFrameSendComplete(IN P_GLUE_INFO_T prGlueInfo, IN PVOID pvPacket, IN WLAN_STATUS rStatus)
{
ASSERT(pvPacket);
/* dev_kfree_skb((struct sk_buff *) pvPacket); */
kalSendCompleteAndAwakeQueue(prGlueInfo, pvPacket);
GLUE_DEC_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum);
}
UINT_32 kalGetTxPendingFrameCount(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return (UINT_32) (GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingFrameNum));
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to retrieve the number of pending commands
* (including MMPDU, 802.1X and command packets)
*
* \param prGlueInfo Pointer of GLUE Data Structure
*
* \retval
*/
/*----------------------------------------------------------------------------*/
UINT_32 kalGetTxPendingCmdCount(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return (UINT_32)GLUE_GET_REF_CNT(prGlueInfo->i4TxPendingCmdNum);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Timer Initialization Procedure
*
* \param[in] prGlueInfo Pointer to GLUE Data Structure
* \param[in] prTimerHandler Pointer to timer handling function, whose only
* argument is "prAdapter"
*
* \retval none
*
*/
/*----------------------------------------------------------------------------*/
/* static struct timer_list tickfn; */
VOID kalOsTimerInitialize(IN P_GLUE_INFO_T prGlueInfo, IN PVOID prTimerHandler)
{
ASSERT(prGlueInfo);
init_timer(&(prGlueInfo->tickfn));
prGlueInfo->tickfn.function = prTimerHandler;
prGlueInfo->tickfn.data = (unsigned long)prGlueInfo;
}
/* Todo */
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is called to set the time to do the time out check.
*
* \param[in] prGlueInfo Pointer to GLUE Data Structure
* \param[in] rInterval Time out interval from current time.
*
* \retval TRUE Success.
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalSetTimer(IN P_GLUE_INFO_T prGlueInfo, IN UINT_32 u4Interval)
{
ASSERT(prGlueInfo);
if (HAL_IS_RX_DIRECT(prGlueInfo->prAdapter)) {
mod_timer(&prGlueInfo->tickfn, jiffies + u4Interval * HZ / MSEC_PER_SEC);
} else {
del_timer_sync(&(prGlueInfo->tickfn));
prGlueInfo->tickfn.expires = jiffies + u4Interval * HZ / MSEC_PER_SEC;
add_timer(&(prGlueInfo->tickfn));
}
return TRUE; /* success */
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is called to cancel
*
* \param[in] prGlueInfo Pointer to GLUE Data Structure
*
* \retval TRUE : Timer has been canceled
* FALAE : Timer doens't exist
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalCancelTimer(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
clear_bit(GLUE_FLAG_TIMEOUT_BIT, &prGlueInfo->ulFlag);
if (del_timer_sync(&(prGlueInfo->tickfn)) >= 0)
return TRUE;
else
return FALSE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is a callback function for scanning done
*
* \param[in] prGlueInfo Pointer to GLUE Data Structure
*
* \retval none
*
*/
/*----------------------------------------------------------------------------*/
VOID kalScanDone(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_KAL_NETWORK_TYPE_INDEX_T eNetTypeIdx, IN WLAN_STATUS status)
{
ASSERT(prGlueInfo);
scanReportBss2Cfg80211(prGlueInfo->prAdapter, BSS_TYPE_INFRASTRUCTURE, NULL);
/* check for system configuration for generating error message on scan list */
wlanCheckSystemConfiguration(prGlueInfo->prAdapter);
kalIndicateStatusAndComplete(prGlueInfo, WLAN_STATUS_SCAN_COMPLETE, NULL, 0);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief This routine is used to generate a random number
*
* \param none
*
* \retval UINT_32
*/
/*----------------------------------------------------------------------------*/
UINT_32 kalRandomNumber(VOID)
{
UINT_32 number = 0;
get_random_bytes(&number, 4);
return number;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief command timeout call-back function
*
* \param[in] prGlueInfo Pointer to the GLUE data structure.
*
* \retval (none)
*/
/*----------------------------------------------------------------------------*/
VOID kalTimeoutHandler(unsigned long arg)
{
P_GLUE_INFO_T prGlueInfo = (P_GLUE_INFO_T) arg;
ASSERT(prGlueInfo);
/* Notify tx thread for timeout event */
set_bit(GLUE_FLAG_TIMEOUT_BIT, &prGlueInfo->ulFlag);
wake_up_interruptible(&prGlueInfo->waitq);
}
VOID kalSetEvent(P_GLUE_INFO_T pr)
{
set_bit(GLUE_FLAG_TXREQ_BIT, &pr->ulFlag);
wake_up_interruptible(&pr->waitq);
}
VOID kalSetIntEvent(P_GLUE_INFO_T pr)
{
KAL_WAKE_LOCK(pr->prAdapter, &pr->rIntrWakeLock);
set_bit(GLUE_FLAG_INT_BIT, &pr->ulFlag);
/* when we got interrupt, we wake up servie thread */
#if CFG_SUPPORT_MULTITHREAD
wake_up_interruptible(&pr->waitq_hif);
#else
wake_up_interruptible(&pr->waitq);
#endif
}
#if CFG_SUPPORT_MULTITHREAD
VOID kalSetTxEvent2Hif(P_GLUE_INFO_T pr)
{
if (!pr->hif_thread)
return;
KAL_WAKE_LOCK_TIMEOUT(pr->prAdapter, &pr->rTimeoutWakeLock,
MSEC_TO_JIFFIES(pr->prAdapter->rWifiVar.u4WakeLockThreadWakeup));
set_bit(GLUE_FLAG_HIF_TX_BIT, &pr->ulFlag);
wake_up_interruptible(&pr->waitq_hif);
}
VOID kalSetFwOwnEvent2Hif(P_GLUE_INFO_T pr)
{
if (!pr->hif_thread)
return;
KAL_WAKE_LOCK_TIMEOUT(pr->prAdapter, &pr->rTimeoutWakeLock,
MSEC_TO_JIFFIES(pr->prAdapter->rWifiVar.u4WakeLockThreadWakeup));
set_bit(GLUE_FLAG_HIF_FW_OWN_BIT, &pr->ulFlag);
wake_up_interruptible(&pr->waitq_hif);
}
VOID kalSetTxEvent2Rx(P_GLUE_INFO_T pr)
{
if (!pr->rx_thread)
return;
KAL_WAKE_LOCK_TIMEOUT(pr->prAdapter, &pr->rTimeoutWakeLock,
MSEC_TO_JIFFIES(pr->prAdapter->rWifiVar.u4WakeLockThreadWakeup));
set_bit(GLUE_FLAG_RX_TO_OS_BIT, &pr->ulFlag);
wake_up_interruptible(&pr->waitq_rx);
}
VOID kalSetTxCmdEvent2Hif(P_GLUE_INFO_T pr)
{
if (!pr->hif_thread)
return;
KAL_WAKE_LOCK_TIMEOUT(pr->prAdapter, &pr->rTimeoutWakeLock,
MSEC_TO_JIFFIES(pr->prAdapter->rWifiVar.u4WakeLockThreadWakeup));
set_bit(GLUE_FLAG_HIF_TX_CMD_BIT, &pr->ulFlag);
wake_up_interruptible(&pr->waitq_hif);
}
#endif
/*----------------------------------------------------------------------------*/
/*!
* \brief to check if configuration file (NVRAM/Registry) exists
*
* \param[in]
* prGlueInfo
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalIsConfigurationExist(IN P_GLUE_INFO_T prGlueInfo)
{
#if !defined(CONFIG_X86)
ASSERT(prGlueInfo);
return prGlueInfo->fgNvramAvailable;
#else
/* there is no configuration data for x86-linux */
/*return FALSE;*/
/*Modify for Linux PC support NVRAM Setting*/
return prGlueInfo->fgNvramAvailable;
#endif
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve Registry information
*
* \param[in]
* prGlueInfo
*
* \return
* Pointer of REG_INFO_T
*/
/*----------------------------------------------------------------------------*/
P_REG_INFO_T kalGetConfiguration(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return &(prGlueInfo->rRegInfo);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve version information of corresponding configuration file
*
* \param[in]
* prGlueInfo
*
* \param[out]
* pu2Part1CfgOwnVersion
* pu2Part1CfgPeerVersion
* pu2Part2CfgOwnVersion
* pu2Part2CfgPeerVersion
*
* \return
* NONE
*/
/*----------------------------------------------------------------------------*/
VOID
kalGetConfigurationVersion(IN P_GLUE_INFO_T prGlueInfo,
OUT PUINT_16 pu2Part1CfgOwnVersion,
OUT PUINT_16 pu2Part1CfgPeerVersion,
OUT PUINT_16 pu2Part2CfgOwnVersion, OUT PUINT_16 pu2Part2CfgPeerVersion)
{
ASSERT(prGlueInfo);
ASSERT(pu2Part1CfgOwnVersion);
ASSERT(pu2Part1CfgPeerVersion);
ASSERT(pu2Part2CfgOwnVersion);
ASSERT(pu2Part2CfgPeerVersion);
kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, u2Part1OwnVersion), pu2Part1CfgOwnVersion);
kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, u2Part1PeerVersion), pu2Part1CfgPeerVersion);
kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, u2Part2OwnVersion), pu2Part2CfgOwnVersion);
kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, u2Part2PeerVersion), pu2Part2CfgPeerVersion);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to check if the WPS is active or not
*
* \param[in]
* prGlueInfo
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalWSCGetActiveState(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return prGlueInfo->fgWpsActive;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief update RSSI and LinkQuality to GLUE layer
*
* \param[in]
* prGlueInfo
* eNetTypeIdx
* cRssi
* cLinkQuality
*
* \return
* None
*/
/*----------------------------------------------------------------------------*/
VOID
kalUpdateRSSI(IN P_GLUE_INFO_T prGlueInfo,
IN ENUM_KAL_NETWORK_TYPE_INDEX_T eNetTypeIdx, IN INT_8 cRssi, IN INT_8 cLinkQuality)
{
struct iw_statistics *pStats = (struct iw_statistics *)NULL;
ASSERT(prGlueInfo);
switch (eNetTypeIdx) {
case KAL_NETWORK_TYPE_AIS_INDEX:
pStats = (struct iw_statistics *)(&(prGlueInfo->rIwStats));
break;
#if CFG_ENABLE_WIFI_DIRECT
#if CFG_SUPPORT_P2P_RSSI_QUERY
case KAL_NETWORK_TYPE_P2P_INDEX:
pStats = (struct iw_statistics *)(&(prGlueInfo->rP2pIwStats));
break;
#endif
#endif
default:
break;
}
if (pStats) {
pStats->qual.qual = cLinkQuality;
pStats->qual.noise = 0;
pStats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_NOISE_UPDATED;
pStats->qual.level = 0x100 + cRssi;
pStats->qual.updated |= IW_QUAL_LEVEL_UPDATED;
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Pre-allocate I/O buffer
*
* \param[in]
* none
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalInitIOBuffer(BOOLEAN is_pre_alloc)
{
UINT_32 u4Size;
/* not pre-allocation for all memory usage */
if (!is_pre_alloc) {
pvIoBuffer = NULL;
return FALSE;
}
/* pre-allocation for all memory usage */
if (HIF_TX_COALESCING_BUFFER_SIZE > HIF_RX_COALESCING_BUFFER_SIZE)
u4Size = HIF_TX_COALESCING_BUFFER_SIZE;
else
u4Size = HIF_RX_COALESCING_BUFFER_SIZE;
u4Size += HIF_EXTRA_IO_BUFFER_SIZE;
pvIoBuffer = kmalloc(u4Size, GFP_KERNEL);
if (pvIoBuffer) {
pvIoBufferSize = u4Size;
pvIoBufferUsage = 0;
return TRUE;
}
return FALSE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Free pre-allocated I/O buffer
*
* \param[in]
* none
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID kalUninitIOBuffer(VOID)
{
kfree(pvIoBuffer);
pvIoBuffer = (PVOID) NULL;
pvIoBufferSize = 0;
pvIoBufferUsage = 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Dispatch pre-allocated I/O buffer
*
* \param[in]
* u4AllocSize
*
* \return
* PVOID for pointer of pre-allocated I/O buffer
*/
/*----------------------------------------------------------------------------*/
PVOID kalAllocateIOBuffer(IN UINT_32 u4AllocSize)
{
PVOID ret = (PVOID) NULL;
if (pvIoBuffer) {
if (u4AllocSize <= (pvIoBufferSize - pvIoBufferUsage)) {
ret = (PVOID) &(((PUINT_8) (pvIoBuffer))[pvIoBufferUsage]);
pvIoBufferUsage += u4AllocSize;
}
} else {
/* fault tolerance */
ret = (PVOID) kalMemAlloc(u4AllocSize, PHY_MEM_TYPE);
}
return ret;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief Release all dispatched I/O buffer
*
* \param[in]
* none
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID kalReleaseIOBuffer(IN PVOID pvAddr, IN UINT_32 u4Size)
{
if (pvIoBuffer) {
pvIoBufferUsage -= u4Size;
} else {
/* fault tolerance */
kalMemFree(pvAddr, PHY_MEM_TYPE, u4Size);
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief
*
* \param[in] prAdapter Pointer of ADAPTER_T
*
* \return none
*/
/*----------------------------------------------------------------------------*/
VOID
kalGetChannelList(IN P_GLUE_INFO_T prGlueInfo,
IN ENUM_BAND_T eSpecificBand,
IN UINT_8 ucMaxChannelNum, IN PUINT_8 pucNumOfChannel, IN P_RF_CHANNEL_INFO_T paucChannelList)
{
rlmDomainGetChnlList(prGlueInfo->prAdapter, eSpecificBand, FALSE,
ucMaxChannelNum, pucNumOfChannel, paucChannelList);
}
/*----------------------------------------------------------------------------*/
/*!
* \brief
*
* \param[in] prAdapter Pointer of ADAPTER_T
*
* \return none
*/
/*----------------------------------------------------------------------------*/
BOOL kalIsAPmode(IN P_GLUE_INFO_T prGlueInfo)
{
#if 0 /* Marked for MT6630 (New ucBssIndex) */
#if CFG_ENABLE_WIFI_DIRECT
if (IS_NET_ACTIVE(prGlueInfo->prAdapter, NETWORK_TYPE_P2P_INDEX) &&
p2pFuncIsAPMode(prGlueInfo->prAdapter->rWifiVar.prP2pFsmInfo))
return TRUE;
#endif
#endif
return FALSE;
}
#if CFG_SUPPORT_802_11W
/*----------------------------------------------------------------------------*/
/*!
* \brief to check if the MFP is active or not
*
* \param[in]
* prGlueInfo
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
UINT_32 kalGetMfpSetting(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return prGlueInfo->rWpaInfo.u4Mfp;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to check if the RSN IE CAP setting from supplicant
*
* \param[in]
* prGlueInfo
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
UINT_8 kalGetRsnIeMfpCap(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return prGlueInfo->rWpaInfo.ucRSNMfpCap;
}
#endif
struct file *kalFileOpen(const char *path, int flags, int rights)
{
struct file *filp = NULL;
mm_segment_t oldfs;
int err = 0;
oldfs = get_fs();
set_fs(get_ds());
filp = filp_open(path, flags, rights);
set_fs(oldfs);
if (IS_ERR(filp)) {
err = PTR_ERR(filp);
return NULL;
}
return filp;
}
VOID kalFileClose(struct file *file)
{
filp_close(file, NULL);
}
UINT_32 kalFileRead(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_read(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
UINT_32 kalFileWrite(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
UINT_32 kalWriteToFile(const PUINT_8 pucPath, BOOLEAN fgDoAppend, PUINT_8 pucData, UINT_32 u4Size)
{
struct file *file = NULL;
UINT_32 ret = 0; /* size been written */
UINT_32 u4Flags = 0;
if (fgDoAppend)
u4Flags = O_APPEND;
file = kalFileOpen(pucPath, O_WRONLY | O_CREAT | u4Flags, S_IRWXU);
if (file) {
ret = kalFileWrite(file, 0, pucData, u4Size);
kalFileClose(file);
}
return ret;
}
INT_32 kalReadToFile(const PUINT_8 pucPath, PUINT_8 pucData, UINT_32 u4Size, PUINT_32 pu4ReadSize)
{
struct file *file = NULL;
INT_32 ret = -1;
UINT_32 u4ReadSize = 0;
DBGLOG(INIT, INFO, "kalReadToFile() path %s\n", pucPath);
file = kalFileOpen(pucPath, O_RDONLY, 0);
if ((file != NULL) && !IS_ERR(file)) {
u4ReadSize = kalFileRead(file, 0, pucData, u4Size);
kalFileClose(file);
if (pu4ReadSize)
*pu4ReadSize = u4ReadSize;
ret = 0;
}
return ret;
}
UINT_32 kalCheckPath(const PUINT_8 pucPath)
{
struct file *file = NULL;
UINT_32 u4Flags = 0;
file = kalFileOpen(pucPath, O_WRONLY | O_CREAT | u4Flags, S_IRWXU);
if (!file)
return -1;
kalFileClose(file);
return 1;
}
UINT_32 kalTrunkPath(const PUINT_8 pucPath)
{
struct file *file = NULL;
UINT_32 u4Flags = O_TRUNC;
file = kalFileOpen(pucPath, O_WRONLY | O_CREAT | u4Flags, S_IRWXU);
if (!file)
return -1;
kalFileClose(file);
return 1;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief read request firmware file binary to pucData
*
* \param[in] pucPath file name
* \param[out] pucData Request file output buffer
* \param[in] u4Size read size
* \param[out] pu4ReadSize real read size
* \param[in] dev
*
* \return
* 0 success
* >0 fail
*/
/*----------------------------------------------------------------------------*/
INT_32 kalRequestFirmware(const PUINT_8 pucPath, PUINT_8 pucData, UINT_32 u4Size,
PUINT_32 pu4ReadSize, struct device *dev)
{
const struct firmware *fw;
int ret = 0;
/*
* 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, pucPath, dev);
if (ret != 0) {
DBGLOG(INIT, INFO, "kalRequestFirmware %s Fail, errno[%d]!!\n", pucPath, ret);
pucData = NULL;
*pu4ReadSize = 0;
release_firmware(fw);
return ret;
}
DBGLOG(INIT, INFO, "kalRequestFirmware(): %s OK\n", pucPath);
if (fw->size < u4Size)
u4Size = fw->size;
memcpy(pucData, fw->data, u4Size);
if (pu4ReadSize)
*pu4ReadSize = u4Size;
release_firmware(fw);
return ret;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate BSS-INFO to NL80211 as scanning result
*
* \param[in]
* prGlueInfo
* pucBeaconProbeResp
* u4FrameLen
*
*
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID
kalIndicateBssInfo(IN P_GLUE_INFO_T prGlueInfo,
IN PUINT_8 pucBeaconProbeResp,
IN UINT_32 u4FrameLen, IN UINT_8 ucChannelNum, IN INT_32 i4SignalStrength)
{
struct wiphy *wiphy;
struct ieee80211_channel *prChannel = NULL;
ASSERT(prGlueInfo);
wiphy = priv_to_wiphy(prGlueInfo);
/* search through channel entries */
if (ucChannelNum <= 14) {
prChannel =
ieee80211_get_channel(wiphy, ieee80211_channel_to_frequency(ucChannelNum, KAL_BAND_2GHZ));
} else {
prChannel =
ieee80211_get_channel(wiphy, ieee80211_channel_to_frequency(ucChannelNum, KAL_BAND_5GHZ));
}
if (prChannel != NULL && prGlueInfo->fgIsRegistered == TRUE) {
struct cfg80211_bss *bss;
#if CFG_SUPPORT_TSF_USING_BOOTTIME
struct ieee80211_mgmt *prMgmtFrame = (struct ieee80211_mgmt *)pucBeaconProbeResp;
prMgmtFrame->u.beacon.timestamp = kalGetBootTime();
#endif
/* indicate to NL80211 subsystem */
bss = cfg80211_inform_bss_frame(wiphy,
prChannel,
(struct ieee80211_mgmt *)pucBeaconProbeResp,
u4FrameLen, i4SignalStrength * 100, GFP_KERNEL);
if (!bss) {
/* ToDo:: DBGLOG */
DBGLOG(REQ, WARN, "cfg80211_inform_bss_frame() returned with NULL\n");
} else
cfg80211_put_bss(wiphy, bss);
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate channel ready
*
* \param[in]
* prGlueInfo
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID
kalReadyOnChannel(IN P_GLUE_INFO_T prGlueInfo,
IN UINT_64 u8Cookie,
IN ENUM_BAND_T eBand, IN ENUM_CHNL_EXT_T eSco, IN UINT_8 ucChannelNum, IN UINT_32 u4DurationMs)
{
struct ieee80211_channel *prChannel = NULL;
enum nl80211_channel_type rChannelType;
/* ucChannelNum = wlanGetChannelNumberByNetwork(prGlueInfo->prAdapter, NETWORK_TYPE_AIS_INDEX); */
if (prGlueInfo->fgIsRegistered == TRUE) {
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));
}
if (!prChannel) {
DBGLOG(INIT, ERROR, "ieee80211_get_channel fail!\n");
return;
}
switch (eSco) {
case CHNL_EXT_SCN:
rChannelType = NL80211_CHAN_NO_HT;
break;
case CHNL_EXT_SCA:
rChannelType = NL80211_CHAN_HT40MINUS;
break;
case CHNL_EXT_SCB:
rChannelType = NL80211_CHAN_HT40PLUS;
break;
case CHNL_EXT_RES:
default:
rChannelType = NL80211_CHAN_HT20;
break;
}
cfg80211_ready_on_channel(prGlueInfo->prDevHandler->ieee80211_ptr,
u8Cookie, prChannel, u4DurationMs, GFP_KERNEL);
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate channel expiration
*
* \param[in]
* prGlueInfo
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID
kalRemainOnChannelExpired(IN P_GLUE_INFO_T prGlueInfo,
IN UINT_64 u8Cookie, IN ENUM_BAND_T eBand, IN ENUM_CHNL_EXT_T eSco, IN UINT_8 ucChannelNum)
{
struct ieee80211_channel *prChannel = NULL;
enum nl80211_channel_type rChannelType;
ucChannelNum =
wlanGetChannelNumberByNetwork(prGlueInfo->prAdapter, prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex);
if (prGlueInfo->fgIsRegistered == TRUE) {
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));
}
if (!prChannel) {
DBGLOG(INIT, ERROR, "ieee80211_get_channel fail!\n");
return;
}
switch (eSco) {
case CHNL_EXT_SCN:
rChannelType = NL80211_CHAN_NO_HT;
break;
case CHNL_EXT_SCA:
rChannelType = NL80211_CHAN_HT40MINUS;
break;
case CHNL_EXT_SCB:
rChannelType = NL80211_CHAN_HT40PLUS;
break;
case CHNL_EXT_RES:
default:
rChannelType = NL80211_CHAN_HT20;
break;
}
cfg80211_remain_on_channel_expired(prGlueInfo->prDevHandler->ieee80211_ptr,
u8Cookie, prChannel, GFP_KERNEL);
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate Mgmt tx status
*
* \param[in]
* prGlueInfo
*
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID
kalIndicateMgmtTxStatus(IN P_GLUE_INFO_T prGlueInfo,
IN UINT_64 u8Cookie, IN BOOLEAN fgIsAck, IN PUINT_8 pucFrameBuf, IN UINT_32 u4FrameLen)
{
do {
if ((prGlueInfo == NULL)
|| (pucFrameBuf == NULL)
|| (u4FrameLen == 0)) {
DBGLOG(AIS, TRACE,
"Unexpected pointer PARAM. 0x%lx, 0x%lx, %ld.", prGlueInfo, pucFrameBuf, u4FrameLen);
ASSERT(FALSE);
break;
}
cfg80211_mgmt_tx_status(prGlueInfo->prDevHandler->ieee80211_ptr,
u8Cookie, pucFrameBuf, u4FrameLen, fgIsAck, GFP_KERNEL);
} while (FALSE);
} /* kalIndicateMgmtTxStatus */
VOID kalIndicateRxMgmtFrame(IN P_GLUE_INFO_T prGlueInfo, IN P_SW_RFB_T prSwRfb)
{
INT_32 i4Freq = 0;
UINT_8 ucChnlNum = 0;
do {
if ((prGlueInfo == NULL) || (prSwRfb == NULL)) {
ASSERT(FALSE);
break;
}
ucChnlNum = (UINT_8) HAL_RX_STATUS_GET_CHNL_NUM(prSwRfb->prRxStatus);
i4Freq = nicChannelNum2Freq(ucChnlNum) / 1000;
#if (KERNEL_VERSION(3, 18, 0) <= CFG80211_VERSION_CODE)
cfg80211_rx_mgmt(prGlueInfo->prDevHandler->ieee80211_ptr,
i4Freq, /* in MHz */
RCPI_TO_dBm((UINT_8) nicRxGetRcpiValueFromRxv(RCPI_MODE_WF0, prSwRfb)),
prSwRfb->pvHeader, prSwRfb->u2PacketLen,
NL80211_RXMGMT_FLAG_ANSWERED);
#elif (KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE)
cfg80211_rx_mgmt(prGlueInfo->prDevHandler->ieee80211_ptr, i4Freq, /* in MHz */
RCPI_TO_dBm((UINT_8)
nicRxGetRcpiValueFromRxv(RCPI_MODE_WF0, prSwRfb)),
prSwRfb->pvHeader, prSwRfb->u2PacketLen,
NL80211_RXMGMT_FLAG_ANSWERED,
GFP_ATOMIC);
#else
cfg80211_rx_mgmt(prGlueInfo->prDevHandler->ieee80211_ptr, i4Freq, /* in MHz */
RCPI_TO_dBm((UINT_8)
nicRxGetRcpiValueFromRxv(RCPI_MODE_WF0, prSwRfb)),
prSwRfb->pvHeader, prSwRfb->u2PacketLen,
GFP_ATOMIC);
#endif
} while (FALSE);
} /* kalIndicateRxMgmtFrame */
#if CFG_SUPPORT_SDIO_READ_WRITE_PATTERN
/*----------------------------------------------------------------------------*/
/*!
* \brief To configure SDIO test pattern mode
*
* \param[in]
* prGlueInfo
* fgEn
* fgRead
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalSetSdioTestPattern(IN P_GLUE_INFO_T prGlueInfo, IN BOOLEAN fgEn, IN BOOLEAN fgRead)
{
const UINT_8 aucPattern[] = {
0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55,
0xaa, 0x55, 0x80, 0x80, 0x80, 0x7f, 0x80, 0x80,
0x80, 0x7f, 0x7f, 0x7f, 0x80, 0x7f, 0x7f, 0x7f,
0x40, 0x40, 0x40, 0xbf, 0x40, 0x40, 0x40, 0xbf,
0xbf, 0xbf, 0x40, 0xbf, 0xbf, 0xbf, 0x20, 0x20,
0x20, 0xdf, 0x20, 0x20, 0x20, 0xdf, 0xdf, 0xdf,
0x20, 0xdf, 0xdf, 0xdf, 0x10, 0x10, 0x10, 0xef,
0x10, 0x10, 0x10, 0xef, 0xef, 0xef, 0x10, 0xef,
0xef, 0xef, 0x08, 0x08, 0x08, 0xf7, 0x08, 0x08,
0x08, 0xf7, 0xf7, 0xf7, 0x08, 0xf7, 0xf7, 0xf7,
0x04, 0x04, 0x04, 0xfb, 0x04, 0x04, 0x04, 0xfb,
0xfb, 0xfb, 0x04, 0xfb, 0xfb, 0xfb, 0x02, 0x02,
0x02, 0xfd, 0x02, 0x02, 0x02, 0xfd, 0xfd, 0xfd,
0x02, 0xfd, 0xfd, 0xfd, 0x01, 0x01, 0x01, 0xfe,
0x01, 0x01, 0x01, 0xfe, 0xfe, 0xfe, 0x01, 0xfe,
0xfe, 0xfe, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff
};
UINT_32 i;
ASSERT(prGlueInfo);
/* access to MCR_WTMCR to engage PRBS mode */
prGlueInfo->fgEnSdioTestPattern = fgEn;
prGlueInfo->fgSdioReadWriteMode = fgRead;
if (fgRead == FALSE) {
/* fill buffer for data to be written */
for (i = 0; i < sizeof(aucPattern); i++)
prGlueInfo->aucSdioTestBuffer[i] = aucPattern[i];
}
return TRUE;
}
#endif
#if (CFG_MET_PACKET_TRACE_SUPPORT == 1)
#define PROC_MET_PROF_CTRL "met_ctrl"
#define PROC_MET_PROF_PORT "met_port"
struct proc_dir_entry *pMetProcDir;
void *pMetGlobalData;
#endif
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate scheduled scan results are avilable
*
* \param[in]
* prGlueInfo
*
* \return
* None
*/
/*----------------------------------------------------------------------------*/
VOID kalSchedScanResults(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
cfg80211_sched_scan_results(priv_to_wiphy(prGlueInfo));
}
/*----------------------------------------------------------------------------*/
/*!
* \brief To indicate scheduled scan has been stopped
*
* \param[in]
* prGlueInfo
*
* \return
* None
*/
/*----------------------------------------------------------------------------*/
VOID kalSchedScanStopped(IN P_GLUE_INFO_T prGlueInfo)
{
/* DBGLOG(SCN, INFO, ("-->kalSchedScanStopped\n" )); */
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prGlueInfo);
#if 1
/* 1. reset first for newly incoming request */
GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
if (prGlueInfo->prSchedScanRequest != NULL)
prGlueInfo->prSchedScanRequest = NULL;
GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV);
#endif
DBGLOG(SCN, INFO, "cfg80211_sched_scan_stopped send event\n");
/* 2. indication to cfg80211 */
/* 20150205 change cfg80211_sched_scan_stopped to work queue to use K thread to send event instead of Tx thread
* due to sched_scan_mtx dead lock issue by Tx thread serves oid cmds and send event in the same time
*/
DBGLOG(SCN, INFO, "start work queue to send event\n");
schedule_delayed_work(&sched_workq, 0);
DBGLOG(SCN, INFO, "main_thread return from kalSchedScanStoppped\n");
}
BOOLEAN
kalGetIPv4Address(IN struct net_device *prDev,
IN UINT_32 u4MaxNumOfAddr, OUT PUINT_8 pucIpv4Addrs, OUT PUINT_32 pu4NumOfIpv4Addr)
{
UINT_32 u4NumIPv4 = 0;
UINT_32 u4AddrLen = IPV4_ADDR_LEN;
struct in_ifaddr *prIfa;
/* 4 <1> Sanity check of netDevice */
if (!prDev || !(prDev->ip_ptr) || !((struct in_device *)(prDev->ip_ptr))->ifa_list) {
DBGLOG(INIT, INFO, "IPv4 address is not available for dev(0x%p)\n", prDev);
*pu4NumOfIpv4Addr = 0;
return FALSE;
}
prIfa = ((struct in_device *)(prDev->ip_ptr))->ifa_list;
/* 4 <2> copy the IPv4 address */
while ((u4NumIPv4 < u4MaxNumOfAddr) && prIfa) {
kalMemCopy(&pucIpv4Addrs[u4NumIPv4 * u4AddrLen], &prIfa->ifa_local, u4AddrLen);
prIfa = prIfa->ifa_next;
DBGLOG(INIT, INFO,
"IPv4 addr [%u][" IPV4STR "]\n", u4NumIPv4, IPV4TOSTR(&pucIpv4Addrs[u4NumIPv4 * u4AddrLen]));
u4NumIPv4++;
}
*pu4NumOfIpv4Addr = u4NumIPv4;
return TRUE;
}
#if IS_ENABLED(CONFIG_IPV6)
BOOLEAN
kalGetIPv6Address(IN struct net_device *prDev,
IN UINT_32 u4MaxNumOfAddr, OUT PUINT_8 pucIpv6Addrs, OUT PUINT_32 pu4NumOfIpv6Addr)
{
UINT_32 u4NumIPv6 = 0;
UINT_32 u4AddrLen = IPV6_ADDR_LEN;
struct inet6_ifaddr *prIfa;
/* 4 <1> Sanity check of netDevice */
if (!prDev || !(prDev->ip6_ptr)) {
DBGLOG(INIT, INFO, "IPv6 address is not available for dev(0x%p)\n", prDev);
*pu4NumOfIpv6Addr = 0;
return FALSE;
}
/* 4 <2> copy the IPv6 address */
LIST_FOR_EACH_IPV6_ADDR(prIfa, prDev->ip6_ptr) {
kalMemCopy(&pucIpv6Addrs[u4NumIPv6 * u4AddrLen], &prIfa->addr, u4AddrLen);
DBGLOG(INIT, INFO,
"IPv6 addr [%u][" IPV6STR "]\n", u4NumIPv6, IPV6TOSTR(&pucIpv6Addrs[u4NumIPv6 * u4AddrLen]));
if ((u4NumIPv6 + 1) >= u4MaxNumOfAddr)
break;
u4NumIPv6++;
}
*pu4NumOfIpv6Addr = u4NumIPv6;
return TRUE;
}
#endif /* IS_ENABLED(CONFIG_IPV6) */
VOID
kalSetNetAddress(IN P_GLUE_INFO_T prGlueInfo,
IN UINT_8 ucBssIdx,
IN PUINT_8 pucIPv4Addr, IN UINT_32 u4NumIPv4Addr, IN PUINT_8 pucIPv6Addr, IN UINT_32 u4NumIPv6Addr)
{
WLAN_STATUS rStatus = WLAN_STATUS_FAILURE;
UINT_32 u4SetInfoLen = 0;
UINT_32 u4Len = OFFSET_OF(PARAM_NETWORK_ADDRESS_LIST, arAddress);
P_PARAM_NETWORK_ADDRESS_LIST prParamNetAddrList;
P_PARAM_NETWORK_ADDRESS prParamNetAddr;
UINT_32 i, u4AddrLen;
/* 4 <1> Calculate buffer size */
/* IPv4 */
u4Len += (((sizeof(PARAM_NETWORK_ADDRESS) - 1) + IPV4_ADDR_LEN) * u4NumIPv4Addr);
/* IPv6 */
u4Len += (((sizeof(PARAM_NETWORK_ADDRESS) - 1) + IPV6_ADDR_LEN) * u4NumIPv6Addr);
/* 4 <2> Allocate buffer */
prParamNetAddrList = (P_PARAM_NETWORK_ADDRESS_LIST) kalMemAlloc(u4Len, VIR_MEM_TYPE);
if (!prParamNetAddrList) {
DBGLOG(INIT, WARN, "Fail to alloc buffer for setting BSS[%u] network address!\n", ucBssIdx);
return;
}
/* 4 <3> Fill up network address */
prParamNetAddrList->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP;
prParamNetAddrList->u4AddressCount = 0;
prParamNetAddrList->ucBssIdx = ucBssIdx;
/* 4 <3.1> Fill up IPv4 address */
u4AddrLen = IPV4_ADDR_LEN;
prParamNetAddr = prParamNetAddrList->arAddress;
for (i = 0; i < u4NumIPv4Addr; i++) {
prParamNetAddr->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP;
prParamNetAddr->u2AddressLength = u4AddrLen;
kalMemCopy(prParamNetAddr->aucAddress, &pucIPv4Addr[i * u4AddrLen], u4AddrLen);
prParamNetAddr = (P_PARAM_NETWORK_ADDRESS) ((ULONG) prParamNetAddr +
(ULONG) (u4AddrLen +
OFFSET_OF(PARAM_NETWORK_ADDRESS, aucAddress)));
}
prParamNetAddrList->u4AddressCount += u4NumIPv4Addr;
/* 4 <3.2> Fill up IPv6 address */
u4AddrLen = IPV6_ADDR_LEN;
for (i = 0; i < u4NumIPv6Addr; i++) {
prParamNetAddr->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP;
prParamNetAddr->u2AddressLength = u4AddrLen;
kalMemCopy(prParamNetAddr->aucAddress, &pucIPv6Addr[i * u4AddrLen], u4AddrLen);
prParamNetAddr = (P_PARAM_NETWORK_ADDRESS) ((ULONG) prParamNetAddr +
(ULONG) (u4AddrLen +
OFFSET_OF(PARAM_NETWORK_ADDRESS, aucAddress)));
}
prParamNetAddrList->u4AddressCount += u4NumIPv6Addr;
/* 4 <4> IOCTL to main_thread */
rStatus = kalIoctl(prGlueInfo,
wlanoidSetNetworkAddress,
(PVOID) prParamNetAddrList, u4Len, FALSE, FALSE, TRUE, &u4SetInfoLen);
if (rStatus != WLAN_STATUS_SUCCESS)
DBGLOG(REQ, WARN, "%s: Fail to set network address\n", __func__);
kalMemFree(prParamNetAddrList, VIR_MEM_TYPE, u4Len);
}
VOID kalSetNetAddressFromInterface(IN P_GLUE_INFO_T prGlueInfo, IN struct net_device *prDev, IN BOOLEAN fgSet)
{
UINT_32 u4NumIPv4, u4NumIPv6;
UINT_8 pucIPv4Addr[IPV4_ADDR_LEN * CFG_PF_ARP_NS_MAX_NUM], pucIPv6Addr[IPV6_ADDR_LEN * CFG_PF_ARP_NS_MAX_NUM];
P_NETDEV_PRIVATE_GLUE_INFO prNetDevPrivate = (P_NETDEV_PRIVATE_GLUE_INFO) NULL;
prNetDevPrivate = (P_NETDEV_PRIVATE_GLUE_INFO) netdev_priv(prDev);
if (prNetDevPrivate->prGlueInfo != prGlueInfo)
DBGLOG(REQ, WARN, "%s: unexpected prGlueInfo(0x%p)!\n", __func__, prNetDevPrivate->prGlueInfo);
u4NumIPv4 = 0;
u4NumIPv6 = 0;
if (fgSet) {
kalGetIPv4Address(prDev, CFG_PF_ARP_NS_MAX_NUM, pucIPv4Addr, &u4NumIPv4);
kalGetIPv6Address(prDev, CFG_PF_ARP_NS_MAX_NUM, pucIPv6Addr, &u4NumIPv6);
}
if (u4NumIPv4 + u4NumIPv6 > CFG_PF_ARP_NS_MAX_NUM) {
if (u4NumIPv4 >= CFG_PF_ARP_NS_MAX_NUM) {
u4NumIPv4 = CFG_PF_ARP_NS_MAX_NUM;
u4NumIPv6 = 0;
} else {
u4NumIPv6 = CFG_PF_ARP_NS_MAX_NUM - u4NumIPv4;
}
}
kalSetNetAddress(prGlueInfo, prNetDevPrivate->ucBssIdx, pucIPv4Addr, u4NumIPv4, pucIPv6Addr, u4NumIPv6);
}
#if CFG_MET_PACKET_TRACE_SUPPORT
BOOLEAN kalMetCheckProfilingPacket(IN P_GLUE_INFO_T prGlueInfo, IN P_NATIVE_PACKET prPacket)
{
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;
u4PacketLen = prSkb->len;
if (u4PacketLen < ETHER_HEADER_LEN) {
DBGLOG(INIT, WARN, "Invalid Ether packet length: %lu\n", u4PacketLen);
return FALSE;
}
aucLookAheadBuf = prSkb->data;
/* 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) {
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:
{
PUINT_8 pucIpHdr = pucNextProtocol;
UINT_8 ucIpVersion;
/* IPv4 header length check */
if (u4PacketLen < (ucEthTypeLenOffset + ETHER_TYPE_LEN + IPV4_HDR_LEN)) {
DBGLOG(INIT, WARN, "Invalid IPv4 packet length: %lu\n", u4PacketLen);
return FALSE;
}
/* 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;
}
if (pucIpHdr[IPV4_HDR_IP_PROTOCOL_OFFSET] == IP_PROTOCOL_UDP) {
PUINT_8 pucUdpHdr = &pucIpHdr[IPV4_HDR_LEN];
UINT_16 u2UdpDstPort;
UINT_16 u2UdpSrcPort;
/* Get UDP DST port */
WLAN_GET_FIELD_BE16(&pucUdpHdr[UDP_HDR_DST_PORT_OFFSET], &u2UdpDstPort);
/* Get UDP SRC port */
WLAN_GET_FIELD_BE16(&pucUdpHdr[UDP_HDR_SRC_PORT_OFFSET], &u2UdpSrcPort);
if (u2UdpSrcPort == prGlueInfo->u2MetUdpPort) {
UINT_16 u2IpId;
/* Store IP ID for Tag */
WLAN_GET_FIELD_BE16(&pucIpHdr[IPV4_HDR_IP_IDENTIFICATION_OFFSET], &u2IpId);
#if 0
DBGLOG(INIT, INFO, "TX PKT PROTOCOL[0x%x] UDP DST port[%u] IP_ID[%u]\n",
pucIpHdr[IPV4_HDR_IP_PROTOCOL_OFFSET], u2UdpDstPort, u2IpId);
#endif
GLUE_SET_PKT_IP_ID(prPacket, u2IpId);
return TRUE;
}
}
}
break;
default:
break;
}
return FALSE;
}
static unsigned long __read_mostly tracing_mark_write_addr;
static int __mt_find_tracing_mark_write_symbol_fn(void *prData, const char *pcNameBuf,
struct module *prModule, unsigned long ulAddress)
{
if (strcmp(pcNameBuf, "tracing_mark_write") == 0) {
tracing_mark_write_addr = ulAddress;
return 1;
}
return 0;
}
static inline void __mt_update_tracing_mark_write_addr(void)
{
if (unlikely(tracing_mark_write_addr == 0))
kallsyms_on_each_symbol(__mt_find_tracing_mark_write_symbol_fn, NULL);
}
VOID kalMetTagPacket(IN P_GLUE_INFO_T prGlueInfo, IN P_NATIVE_PACKET prPacket, IN ENUM_TX_PROFILING_TAG_T eTag)
{
if (!prGlueInfo->fgMetProfilingEn)
return;
switch (eTag) {
case TX_PROF_TAG_OS_TO_DRV:
if (kalMetCheckProfilingPacket(prGlueInfo, prPacket)) {
/* trace_printk("S|%d|%s|%d\n", current->pid, "WIFI-CHIP", GLUE_GET_PKT_IP_ID(prPacket)); */
__mt_update_tracing_mark_write_addr();
#if 0 /* #ifdef CONFIG_TRACING */ /* #if CFG_MET_PACKET_TRACE_SUPPORT */
event_trace_printk(tracing_mark_write_addr, "S|%d|%s|%d\n", current->tgid, "WIFI-CHIP",
GLUE_GET_PKT_IP_ID(prPacket));
#endif
GLUE_SET_PKT_FLAG_PROF_MET(prPacket);
}
break;
case TX_PROF_TAG_DRV_TX_DONE:
if (GLUE_GET_PKT_IS_PROF_MET(prPacket)) {
/* trace_printk("F|%d|%s|%d\n", current->pid, "WIFI-CHIP", GLUE_GET_PKT_IP_ID(prPacket)); */
__mt_update_tracing_mark_write_addr();
#if 0 /* #ifdef CONFIG_TRACING */ /* #if CFG_MET_PACKET_TRACE_SUPPORT */
event_trace_printk(tracing_mark_write_addr, "F|%d|%s|%d\n", current->tgid, "WIFI-CHIP",
GLUE_GET_PKT_IP_ID(prPacket));
#endif
}
break;
case TX_PROF_TAG_MAC_TX_DONE:
break;
default:
break;
}
}
VOID kalMetInit(IN P_GLUE_INFO_T prGlueInfo)
{
prGlueInfo->fgMetProfilingEn = FALSE;
prGlueInfo->u2MetUdpPort = 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief The PROC function for adjusting Debug Level to turn on/off debugging message.
*
* \param[in] file pointer to file.
* \param[in] buffer Buffer from user space.
* \param[in] count Number of characters to write
* \param[in] data Pointer to the private data structure.
*
* \return number of characters write from User Space.
*/
/*----------------------------------------------------------------------------*/
#if 0
static ssize_t kalMetWriteProcfs(struct file *file, const char __user *buffer, size_t count, loff_t *off)
{
char acBuf[128 + 1]; /* + 1 for "\0" */
UINT_32 u4CopySize;
int u16MetUdpPort;
int u8MetProfEnable;
IN P_GLUE_INFO_T prGlueInfo;
ssize_t result;
u4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1);
result = copy_from_user(acBuf, buffer, u4CopySize);
acBuf[u4CopySize] = '\0';
if (sscanf(acBuf, " %d %d", &u8MetProfEnable, &u16MetUdpPort) == 2)
DBGLOG(INIT, INFO, "MET_PROF: Write MET PROC Enable=%d UDP_PORT=%d\n", u8MetProfEnable, u16MetUdpPort);
if (pMetGlobalData != NULL) {
prGlueInfo = (P_GLUE_INFO_T) pMetGlobalData;
prGlueInfo->fgMetProfilingEn = (BOOLEAN) u8MetProfEnable;
prGlueInfo->u2MetUdpPort = (UINT_16) u16MetUdpPort;
}
return count;
}
#endif
static ssize_t kalMetCtrlWriteProcfs(struct file *file, const char __user *buffer, size_t count, loff_t *off)
{
char acBuf[128 + 1]; /* + 1 for "\0" */
UINT_32 u4CopySize;
int u8MetProfEnable;
ssize_t result;
IN P_GLUE_INFO_T prGlueInfo;
u4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1);
result = copy_from_user(acBuf, buffer, u4CopySize);
acBuf[u4CopySize] = '\0';
if (sscanf(acBuf, " %d", &u8MetProfEnable) == 1)
DBGLOG(INIT, INFO, "MET_PROF: Write MET PROC Enable=%d\n", u8MetProfEnable);
if (pMetGlobalData != NULL) {
prGlueInfo = (P_GLUE_INFO_T) pMetGlobalData;
prGlueInfo->fgMetProfilingEn = (UINT_8) u8MetProfEnable;
}
return count;
}
static ssize_t kalMetPortWriteProcfs(struct file *file, const char __user *buffer, size_t count, loff_t *off)
{
char acBuf[128 + 1]; /* + 1 for "\0" */
UINT_32 u4CopySize;
int u16MetUdpPort;
ssize_t result;
IN P_GLUE_INFO_T prGlueInfo;
u4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1);
result = copy_from_user(acBuf, buffer, u4CopySize);
acBuf[u4CopySize] = '\0';
if (sscanf(acBuf, " %d", &u16MetUdpPort) == 1)
DBGLOG(INIT, INFO, "MET_PROF: Write MET PROC UDP_PORT=%d\n", u16MetUdpPort);
if (pMetGlobalData != NULL) {
prGlueInfo = (P_GLUE_INFO_T) pMetGlobalData;
prGlueInfo->u2MetUdpPort = (UINT_16) u16MetUdpPort;
}
return count;
}
#if 0
const struct file_operations rMetProcFops = {
.write = kalMetWriteProcfs
};
#endif
const struct file_operations rMetProcCtrlFops = {
.write = kalMetCtrlWriteProcfs
};
const struct file_operations rMetProcPortFops = {
.write = kalMetPortWriteProcfs
};
int kalMetInitProcfs(IN P_GLUE_INFO_T prGlueInfo)
{
/* struct proc_dir_entry *pMetProcDir; */
if (init_net.proc_net == (struct proc_dir_entry *)NULL) {
DBGLOG(INIT, INFO, "init proc fs fail: proc_net == NULL\n");
return -ENOENT;
}
/*
* Directory: Root (/proc/net/wlan0)
*/
pMetProcDir = proc_mkdir(prGlueInfo->prDevHandler->name,
init_net.proc_net);
if (pMetProcDir == NULL)
return -ENOENT;
/*
* /proc/net/wlan0
* |-- met_ctrl (PROC_MET_PROF_CTRL)
*/
/* proc_create(PROC_MET_PROF_CTRL, 0x0644, pMetProcDir, &rMetProcFops); */
proc_create(PROC_MET_PROF_CTRL, 0, pMetProcDir, &rMetProcCtrlFops);
proc_create(PROC_MET_PROF_PORT, 0, pMetProcDir, &rMetProcPortFops);
pMetGlobalData = (void *)prGlueInfo;
return 0;
}
int kalMetRemoveProcfs(void)
{
if (init_net.proc_net == (struct proc_dir_entry *)NULL) {
DBGLOG(INIT, WARN, "remove proc fs fail: proc_net == NULL\n");
return -ENOENT;
}
remove_proc_entry(PROC_MET_PROF_CTRL, pMetProcDir);
remove_proc_entry(PROC_MET_PROF_PORT, pMetProcDir);
/* remove root directory (proc/net/wlan0) */
remove_proc_entry("wlan0", init_net.proc_net);
/* clear MetGlobalData */
pMetGlobalData = NULL;
return 0;
}
#endif
#if CFG_SUPPORT_AGPS_ASSIST
BOOLEAN kalIndicateAgpsNotify(P_ADAPTER_T prAdapter, UINT_8 cmd, PUINT_8 data, UINT_16 dataLen)
{
#ifdef CONFIG_NL80211_TESTMODE
P_GLUE_INFO_T prGlueInfo = prAdapter->prGlueInfo;
struct sk_buff *skb = cfg80211_testmode_alloc_event_skb(priv_to_wiphy(prGlueInfo),
dataLen, GFP_KERNEL);
/* DBGLOG(CCX, INFO, ("WLAN_STATUS_AGPS_NOTIFY, cmd=%d\n", cmd)); */
if (unlikely(nla_put(skb, MTK_ATTR_AGPS_CMD, sizeof(cmd), &cmd) < 0))
goto nla_put_failure;
if (dataLen > 0 && data && unlikely(nla_put(skb, MTK_ATTR_AGPS_DATA, dataLen, data) < 0))
goto nla_put_failure;
if (unlikely(nla_put(skb, MTK_ATTR_AGPS_IFINDEX, sizeof(UINT_32), &prGlueInfo->prDevHandler->ifindex) < 0))
goto nla_put_failure;
/* currently, the ifname maybe wlan0, p2p0, so the maximum name length will be 5 bytes */
if (unlikely(nla_put(skb, MTK_ATTR_AGPS_IFNAME, 5, prGlueInfo->prDevHandler->name) < 0))
goto nla_put_failure;
cfg80211_testmode_event(skb, GFP_KERNEL);
return TRUE;
nla_put_failure:
kfree_skb(skb);
return FALSE;
#else
return FALSE;
#endif
}
#endif
UINT_64 kalGetBootTime(void)
{
struct timespec ts;
UINT_64 bootTime = 0;
#if KERNEL_VERSION(2, 6, 39) <= LINUX_VERSION_CODE
get_monotonic_boottime(&ts);
#else
ts = ktime_to_timespec(ktime_get());
#endif
bootTime = ts.tv_sec;
bootTime *= USEC_PER_SEC;
bootTime += ts.tv_nsec / NSEC_PER_USEC;
return bootTime;
}
#if CFG_ASSERT_DUMP
WLAN_STATUS kalOpenCorDumpFile(BOOLEAN fgIsN9)
{
/* Move open-op to kalWriteCorDumpFile(). Empty files only */
UINT_32 ret;
PUINT_8 apucFileName;
if (fgIsN9)
apucFileName = apucCorDumpN9FileName;
else
apucFileName = apucCorDumpCr4FileName;
ret = kalTrunkPath(apucFileName);
return (ret >= 0)?WLAN_STATUS_SUCCESS:WLAN_STATUS_FAILURE;
}
WLAN_STATUS kalWriteCorDumpFile(PUINT_8 pucBuffer, UINT_16 u2Size, BOOLEAN fgIsN9)
{
UINT_32 ret;
PUINT_8 apucFileName;
if (fgIsN9)
apucFileName = apucCorDumpN9FileName;
else
apucFileName = apucCorDumpCr4FileName;
ret = kalWriteToFile(apucFileName, TRUE, pucBuffer, u2Size);
return (ret >= 0)?WLAN_STATUS_SUCCESS:WLAN_STATUS_FAILURE;
}
WLAN_STATUS kalCloseCorDumpFile(BOOLEAN fgIsN9)
{
/* Move close-op to kalWriteCorDumpFile(). Do nothing here */
return WLAN_STATUS_SUCCESS;
}
#endif
#if CFG_WOW_SUPPORT
#ifdef CFG_USE_LINUX_GPIO_GLUE
static irqreturn_t wowHandler(int irq, void *data)
{
disable_irq_nosync(irq);
return IRQ_WAKE_THREAD;
}
static irqreturn_t wowThreadFn(int irq, void *data)
{
DBGLOG(INIT, ERROR, "WOW wakup interrupt come in!!\n");
return IRQ_HANDLED;
}
INT_32 wlanWowIrqEnable(IN struct GPIO_GLUE_INFO *prGpioGlueInfo)
{
struct GPIO_CTRL_INFO *wowPinInfo = NULL;
INT_32 ret = 0;
if (prGpioGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s parameter check fail\n", __func__);
return -EINVAL;
}
wowPinInfo = &prGpioGlueInfo->pinInfo[WLAN_GPIO_WOW_PIN];
if (wowPinInfo->irq <= 0) {
DBGLOG(INIT, ERROR, "%s no IRQ specified\n", __func__);
return -EINVAL;
}
enable_irq(wowPinInfo->irq);
ret = enable_irq_wake(wowPinInfo->irq);
if (ret != 0) {
DBGLOG(INIT, ERROR, "%s enable_irq_wake(%d) fail(%d)!\n",
__func__, wowPinInfo->irq, ret);
} else {
DBGLOG(INIT, STATE, "%s enable_irq_wake(%d) success!\n",
__func__, wowPinInfo->irq);
}
return ret;
}
INT_32 wlanWowIrqDisable(IN struct GPIO_GLUE_INFO *prGpioGlueInfo)
{
struct GPIO_CTRL_INFO *wowPinInfo = NULL;
INT_32 ret = 0;
if (prGpioGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s parameter check fail\n", __func__);
return -EINVAL;
}
wowPinInfo = &prGpioGlueInfo->pinInfo[WLAN_GPIO_WOW_PIN];
if (wowPinInfo->irq <= 0) {
DBGLOG(INIT, ERROR, "%s no IRQ specified\n", __func__);
return -EINVAL;
}
DBGLOG(INIT, STATE, "%s get wow irq(%d) suscessfully\n",
__func__, wowPinInfo->irq);
disable_irq(wowPinInfo->irq);
DBGLOG(INIT, STATE, "%s disable_irq success!\n", __func__);
return ret;
}
INT_32 wlanWowIrqInit(IN struct GPIO_GLUE_INFO *prGpioGlueInfo)
{
struct GPIO_CTRL_INFO *wowPinInfo = NULL;
INT_32 ret = 0;
if (prGpioGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s parameter check fail\n", __func__);
return -EINVAL;
}
wowPinInfo = &prGpioGlueInfo->pinInfo[WLAN_GPIO_WOW_PIN];
if (wowPinInfo->irq <= 0) {
DBGLOG(INIT, ERROR, "%s no IRQ specified\n", __func__);
return -EINVAL;
}
ret = request_threaded_irq(wowPinInfo->irq,
wowHandler, wowThreadFn,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
wowPinInfo->pinName,
NULL);
if (ret) {
DBGLOG(INIT, ERROR, "%s failed to request IRQ\n", __func__);
return ret;
}
disable_irq(wowPinInfo->irq);
DBGLOG(INIT, STATE, "%s disable_irq(%d) !\n",
__func__, wowPinInfo->irq);
return ret;
}
INT_32 wlanWowIrqFree(IN struct GPIO_GLUE_INFO *prGpioGlueInfo)
{
struct GPIO_CTRL_INFO *wowPinInfo = NULL;
INT_32 ret = 0;
if (prGpioGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s prGpioGlueInfo is NULL\n", __func__);
return -EINVAL;
}
wowPinInfo = &prGpioGlueInfo->pinInfo[WLAN_GPIO_WOW_PIN];
if (wowPinInfo->irq <= 0) {
DBGLOG(INIT, ERROR, "%s no IRQ specified\n", __func__);
return -EINVAL;
}
free_irq(wowPinInfo->irq, NULL);
DBGLOG(INIT, STATE, "%s free_irq(%d) !\n",
__func__, wowPinInfo->irq);
return ret;
}
#endif /* CFG_USE_LINUX_GPIO_GLUE */
VOID kalWowUninit(IN P_GLUE_INFO_T prGlueInfo)
{
if (prGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s prGlueInfo is NULL\n", __func__);
return;
}
kalMemZero(&prGlueInfo->prAdapter->rWowCtrl.stWowPort, sizeof(WOW_PORT_T));
#ifdef CFG_USE_LINUX_GPIO_GLUE
wlanWowIrqFree(prGlueInfo->prGpioGlueInfo);
DBGLOG(INIT, STATE, "%s wow module uninitialized end!\n", __func__);
#endif /* CFG_USE_LINUX_GPIO_GLUE */
}
VOID kalWowInit(IN P_GLUE_INFO_T prGlueInfo)
{
if (prGlueInfo == NULL) {
DBGLOG(INIT, ERROR, "%s prGlueInfo is NULL\n", __func__);
return;
}
kalMemZero(&prGlueInfo->prAdapter->rWowCtrl.stWowPort,
sizeof(WOW_PORT_T));
prGlueInfo->prAdapter->rWowCtrl.ucReason = INVALID_WOW_WAKE_UP_REASON;
#ifdef CFG_USE_LINUX_GPIO_GLUE
wlanWowIrqInit(prGlueInfo->prGpioGlueInfo);
DBGLOG(INIT, STATE, "%s wow module initialized end!\n", __func__);
#endif /* CFG_USE_LINUX_GPIO_GLUE */
}
VOID kalWowCmdEventSetCb(IN P_ADAPTER_T prAdapter, IN P_CMD_INFO_T prCmdInfo, IN PUINT_8 pucEventBuf)
{
ASSERT(prAdapter);
ASSERT(prCmdInfo);
if (prCmdInfo->ucCID == CMD_ID_SET_PF_CAPABILITY) {
DBGLOG(INIT, STATE, "CMD_ID_SET_PF_CAPABILITY cmd submitted\n");
prAdapter->fgSetPfCapabilityDone = TRUE;
}
if (prCmdInfo->ucCID == CMD_ID_SET_WOWLAN) {
DBGLOG(INIT, STATE, "CMD_ID_SET_WOWLAN cmd submitted\n");
prAdapter->fgSetWowDone = TRUE;
}
}
VOID kalWowProcess(IN P_GLUE_INFO_T prGlueInfo, UINT_8 enable)
{
CMD_WOWLAN_PARAM_T rCmdWowlanParam;
CMD_PACKET_FILTER_CAP_T rCmdPacket_Filter_Cap;
CMD_FW_LOG_2_HOST_CTRL_T rFwLog2HostCtrl;
P_WOW_CTRL_T pWOW_CTRL = &prGlueInfo->prAdapter->rWowCtrl;
WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
UINT_32 ii, u4BufLen, wait = 0;
kalMemZero(&rCmdWowlanParam, sizeof(CMD_WOWLAN_PARAM_T));
kalMemZero(&rFwLog2HostCtrl, sizeof(rFwLog2HostCtrl));
kalMemZero(&rCmdPacket_Filter_Cap, sizeof(CMD_PACKET_FILTER_CAP_T));
prGlueInfo->prAdapter->fgSetPfCapabilityDone = FALSE;
prGlueInfo->prAdapter->fgSetWowDone = FALSE;
DBGLOG(PF, INFO, "PF, pAd AIS ucBssIndex=%d, ucOwnMacIndex=%d, band=%d\n",
prGlueInfo->prAdapter->prAisBssInfo->ucBssIndex, prGlueInfo->prAdapter->prAisBssInfo->ucOwnMacIndex,
prGlueInfo->prAdapter->prAisBssInfo->eDBDCBand);
DBGLOG(PF, INFO, "profile wow=%d, GpioInterval=%d\n",
prGlueInfo->prAdapter->rWifiVar.ucWow, prGlueInfo->prAdapter->rWowCtrl.astWakeHif[0].u4GpioInterval);
/* Kernel log timestamp correction */
if (prGlueInfo->prAdapter->rWifiVar.ucN9Log2HostCtrl > 0) {
rFwLog2HostCtrl.ucMcuDest = 0;
rFwLog2HostCtrl.ucFwLog2HostCtrl = prGlueInfo->prAdapter->rWifiVar.ucN9Log2HostCtrl;
rStatus = kalIoctl(prGlueInfo,
wlanoidSetFwLog2Host,
&rFwLog2HostCtrl, sizeof(CMD_FW_LOG_2_HOST_CTRL_T),
TRUE, TRUE, TRUE, &u4BufLen);
}
/* add band info */
rCmdWowlanParam.ucDbdcBand = (UINT_8)prGlueInfo->prAdapter->prAisBssInfo->eDBDCBand;
rCmdPacket_Filter_Cap.packet_cap_type |= PACKETF_CAP_TYPE_MAGIC;
/* 20160627 Bennett: if receive BMC magic, PF search by bssid index, which is different with OM index */
/* After discussion, enable all bssid bits */
/* rCmdPacket_Filter_Cap.ucBssid |= BIT(prGlueInfo->prAdapter->prAisBssInfo->ucOwnMacIndex); */
rCmdPacket_Filter_Cap.ucBssid |= BITS(0, 3);
if (enable)
rCmdPacket_Filter_Cap.usEnableBits |= PACKETF_CAP_TYPE_MAGIC;
else
rCmdPacket_Filter_Cap.usEnableBits &= ~PACKETF_CAP_TYPE_MAGIC;
rStatus = wlanSendSetQueryCmd(prGlueInfo->prAdapter,
CMD_ID_SET_PF_CAPABILITY,
TRUE,
FALSE,
FALSE,
kalWowCmdEventSetCb,
nicOidCmdTimeoutCommon,
sizeof(CMD_PACKET_FILTER_CAP_T),
(PUINT_8)&rCmdPacket_Filter_Cap,
NULL,
0);
/* ARP offload */
wlanSetSuspendMode(prGlueInfo, enable);
/* p2pSetSuspendMode(prGlueInfo, TRUE); */
/* wake up reason reset to default only when enter wow mode */
if (enable)
pWOW_CTRL->ucReason = INVALID_WOW_WAKE_UP_REASON;
/* Let WOW enable/disable as last command, so we can back/restore DMA classify filter in FW */
rCmdWowlanParam.ucScenarioID = pWOW_CTRL->ucScenarioId;
rCmdWowlanParam.ucBlockCount = pWOW_CTRL->ucBlockCount;
kalMemCopy(&rCmdWowlanParam.astWakeHif[0], &pWOW_CTRL->astWakeHif[0], sizeof(WOW_WAKE_HIF_T));
/* copy UDP/TCP port setting */
kalMemCopy(&rCmdWowlanParam.stWowPort, &prGlueInfo->prAdapter->rWowCtrl.stWowPort, sizeof(WOW_PORT_T));
DBGLOG(PF, INFO, "Cmd: IPV4/UDP=%d, IPV4/TCP=%d, IPV6/UDP=%d, IPV6/TCP=%d\n",
rCmdWowlanParam.stWowPort.ucIPv4UdpPortCnt, rCmdWowlanParam.stWowPort.ucIPv4TcpPortCnt,
rCmdWowlanParam.stWowPort.ucIPv6UdpPortCnt, rCmdWowlanParam.stWowPort.ucIPv6TcpPortCnt);
for (ii = 0; ii < rCmdWowlanParam.stWowPort.ucIPv4UdpPortCnt; ii++)
DBGLOG(PF, INFO, "IPV4/UDP port[%d]=%d\n", ii, rCmdWowlanParam.stWowPort.ausIPv4UdpPort[ii]);
for (ii = 0; ii < rCmdWowlanParam.stWowPort.ucIPv4TcpPortCnt; ii++)
DBGLOG(PF, INFO, "IPV4/TCP port[%d]=%d\n", ii, rCmdWowlanParam.stWowPort.ausIPv4TcpPort[ii]);
for (ii = 0; ii < rCmdWowlanParam.stWowPort.ucIPv6UdpPortCnt; ii++)
DBGLOG(PF, INFO, "IPV6/UDP port[%d]=%d\n", ii, rCmdWowlanParam.stWowPort.ausIPv6UdpPort[ii]);
for (ii = 0; ii < rCmdWowlanParam.stWowPort.ucIPv6TcpPortCnt; ii++)
DBGLOG(PF, INFO, "IPV6/TCP port[%d]=%d\n", ii, rCmdWowlanParam.stWowPort.ausIPv6TcpPort[ii]);
/* GPIO parameter is necessary in suspend/resume */
if (enable == 1) {
rCmdWowlanParam.ucCmd = PM_WOWLAN_REQ_START;
rCmdWowlanParam.ucDetectType = WOWLAN_DETECT_TYPE_MAGIC | WOWLAN_DETECT_TYPE_ONLY_PHONE_SUSPEND;
rCmdWowlanParam.u2FilterFlag = WOWLAN_FF_DROP_ALL | WOWLAN_FF_SEND_MAGIC_TO_HOST |
WOWLAN_FF_ALLOW_1X | WOWLAN_FF_ALLOW_ARP_REQ2ME;
} else {
rCmdWowlanParam.ucCmd = PM_WOWLAN_REQ_STOP;
}
rStatus = wlanSendSetQueryCmd(prGlueInfo->prAdapter,
CMD_ID_SET_WOWLAN,
TRUE,
FALSE,
FALSE,
kalWowCmdEventSetCb,
nicOidCmdTimeoutCommon,
sizeof(CMD_WOWLAN_PARAM_T),
(PUINT_8)&rCmdWowlanParam,
NULL,
0);
while (1) {
kalMsleep(5);
if (wait > 100) {
DBGLOG(INIT, ERROR, "WoW process timeout\n\n");
break;
}
if ((prGlueInfo->prAdapter->fgSetPfCapabilityDone == TRUE)
&& (prGlueInfo->prAdapter->fgSetWowDone == TRUE)) {
DBGLOG(INIT, STATE, "WoW process done\n\n");
break;
}
wait++;
}
#ifdef CFG_USE_LINUX_GPIO_GLUE
if (enable == 1)
wlanWowIrqEnable(prGlueInfo->prGpioGlueInfo);
#endif
}
#endif
VOID kalFreeTxMsduWorker(struct work_struct *work)
{
P_GLUE_INFO_T prGlueInfo;
P_ADAPTER_T prAdapter;
QUE_T rTmpQue;
P_QUE_T prTmpQue = &rTmpQue;
P_MSDU_INFO_T prMsduInfo;
if (g_u4HaltFlag)
return;
prGlueInfo = ENTRY_OF(work, GLUE_INFO_T, rTxMsduFreeWork);
prAdapter = prGlueInfo->prAdapter;
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT)
return;
KAL_ACQUIRE_MUTEX(prAdapter, MUTEX_TX_DATA_DONE_QUE);
QUEUE_MOVE_ALL(prTmpQue, &prAdapter->rTxDataDoneQueue);
KAL_RELEASE_MUTEX(prAdapter, MUTEX_TX_DATA_DONE_QUE);
while (QUEUE_IS_NOT_EMPTY(prTmpQue)) {
QUEUE_REMOVE_HEAD(prTmpQue, prMsduInfo, P_MSDU_INFO_T);
nicTxFreePacket(prAdapter, prMsduInfo, FALSE);
nicTxReturnMsduInfo(prAdapter, prMsduInfo);
}
}
VOID kalFreeTxMsdu(P_ADAPTER_T prAdapter, P_MSDU_INFO_T prMsduInfo)
{
KAL_ACQUIRE_MUTEX(prAdapter, MUTEX_TX_DATA_DONE_QUE);
QUEUE_INSERT_TAIL(&prAdapter->rTxDataDoneQueue, (P_QUE_ENTRY_T) prMsduInfo);
KAL_RELEASE_MUTEX(prAdapter, MUTEX_TX_DATA_DONE_QUE);
schedule_work(&prAdapter->prGlueInfo->rTxMsduFreeWork);
}
VOID kalInitDevWakeup(P_ADAPTER_T prAdapter, struct device *prDev)
{
/*
* The remote wakeup function will be disabled after first time resume,
* we need to call device_init_wakeup() to notify usbcore that we
* support wakeup function, so usbcore will re-enable our remote wakeup
* function before entering suspend.
*/
if (prAdapter->rWifiVar.ucWow)
device_init_wakeup(prDev, TRUE);
}