blob: a708472834fc22811406e060cd36810e2e68f99f [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: @(#) gl_bow.c@@
*/
/*! \file gl_bow.c
* \brief Main routines of Linux driver interface for 802.11 PAL (BT 3.0 + HS)
*
* This file contains the main routines of Linux driver for MediaTek Inc. 802.11
* Wireless LAN Adapters.
*/
/*******************************************************************************
* 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 "debug.h"
#include "wlan_lib.h"
#include "gl_wext.h"
#include "precomp.h"
#include <linux/poll.h>
#include "bss.h"
#if CFG_ENABLE_BT_OVER_WIFI
/*******************************************************************************
* C O N S T A N T S
********************************************************************************
*/
/* @FIXME if there is command/event with payload length > 28 */
#define MAX_BUFFER_SIZE (64)
/*******************************************************************************
* D A T A T Y P E S
********************************************************************************
*/
/*******************************************************************************
* P U B L I C D A T A
********************************************************************************
*/
UINT_32 g_u4PrevSysTime;
UINT_32 g_u4CurrentSysTime;
UINT_32 g_arBowRevPalPacketTime[32];
/*******************************************************************************
* P R I V A T E D A T A
********************************************************************************
*/
/* forward declarations */
static ssize_t bow_ampc_read(IN struct file *filp, IN char __user *buf, IN size_t size, IN OUT loff_t *ppos);
static ssize_t bow_ampc_write(IN struct file *filp, OUT const char __user *buf, IN size_t size, IN OUT loff_t *ppos);
static long bow_ampc_ioctl(IN struct file *filp, IN unsigned int cmd, IN OUT unsigned long arg);
static unsigned int bow_ampc_poll(IN struct file *filp, IN poll_table * wait);
static int bow_ampc_open(IN struct inode *inodep, IN struct file *filp);
static int bow_ampc_release(IN struct inode *inodep, IN struct file *filp);
/* character file operations */
static const struct file_operations bow_ampc_fops = {
/* .owner = THIS_MODULE, */
.read = bow_ampc_read,
.write = bow_ampc_write,
.unlocked_ioctl = bow_ampc_ioctl,
.poll = bow_ampc_poll,
.open = bow_ampc_open,
.release = bow_ampc_release,
};
/*******************************************************************************
* 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
********************************************************************************
*/
/*----------------------------------------------------------------------------*/
/*!
* \brief Register for character device to communicate with 802.11 PAL
*
* \param[in] prGlueInfo Pointer to glue info
*
* \return TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN glRegisterAmpc(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
DBGLOG(BOW, INFO, "Register for character device to communicate with 802.11 PAL.\n");
if (prGlueInfo->rBowInfo.fgIsRegistered == TRUE)
return FALSE;
#if 0
/* 1. allocate major number dynamically */
if (alloc_chrdev_region(&(prGlueInfo->rBowInfo.u4DeviceNumber), 0, /* first minor number */
1, /* number */
GLUE_BOW_DEVICE_NAME) != 0)
return FALSE;
#endif
#if 1
#if defined(CONFIG_AMPC_CDEV_NUM)
prGlueInfo->rBowInfo.u4DeviceNumber = MKDEV(CONFIG_AMPC_CDEV_NUM, 0);
#else
prGlueInfo->rBowInfo.u4DeviceNumber = MKDEV(226, 0);
#endif
if (register_chrdev_region(prGlueInfo->rBowInfo.u4DeviceNumber, 1, /* number */
GLUE_BOW_DEVICE_NAME) != 0)
return FALSE;
#endif
/* 2. spin-lock initialization */
/* spin_lock_init(&(prGlueInfo->rBowInfo.rSpinLock)); */
/* 3. initialize kfifo */
#if 0
prGlueInfo->rBowInfo.prKfifo = kfifo_alloc(GLUE_BOW_KFIFO_DEPTH,
GFP_KERNEL,
&(prGlueInfo->rBowInfo.rSpinLock));
#endif
if ((kfifo_alloc((struct kfifo *)&(prGlueInfo->rBowInfo.rKfifo), GLUE_BOW_KFIFO_DEPTH, GFP_KERNEL)))
goto fail_kfifo_alloc;
/* if(prGlueInfo->rBowInfo.prKfifo == NULL) */
if (&(prGlueInfo->rBowInfo.rKfifo) == NULL)
goto fail_kfifo_alloc;
/* 4. initialize cdev */
cdev_init(&(prGlueInfo->rBowInfo.cdev), &bow_ampc_fops);
/* prGlueInfo->rBowInfo.cdev.owner = THIS_MODULE; */
prGlueInfo->rBowInfo.cdev.ops = &bow_ampc_fops;
/* 5. add character device */
if (cdev_add(&(prGlueInfo->rBowInfo.cdev), prGlueInfo->rBowInfo.u4DeviceNumber, 1))
goto fail_cdev_add;
/* 6. in queue initialization */
init_waitqueue_head(&(prGlueInfo->rBowInfo.outq));
/* 7. finish */
prGlueInfo->rBowInfo.fgIsRegistered = TRUE;
return TRUE;
fail_cdev_add:
kfifo_free(&(prGlueInfo->rBowInfo.rKfifo));
/* kfifo_free(prGlueInfo->rBowInfo.prKfifo); */
fail_kfifo_alloc:
unregister_chrdev_region(prGlueInfo->rBowInfo.u4DeviceNumber, 1);
return FALSE;
} /* end of glRegisterAmpc */
/*----------------------------------------------------------------------------*/
/*!
* \brief Unregister character device for communicating with 802.11 PAL
*
* \param[in] prGlueInfo Pointer to glue info
*
* \return TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN glUnregisterAmpc(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
DBGLOG(BOW, INFO, "Unregister character device for communicating with 802.11 PAL.\n");
if (prGlueInfo->rBowInfo.fgIsRegistered == FALSE)
return FALSE;
prGlueInfo->rBowInfo.fgIsRegistered = FALSE;
/* 1. free netdev if necessary */
#if CFG_BOW_SEPARATE_DATA_PATH
kalUninitBowDevice(prGlueInfo);
#endif
/* 2. removal of character device */
cdev_del(&(prGlueInfo->rBowInfo.cdev));
/* 3. free kfifo */
/* kfifo_free(prGlueInfo->rBowInfo.prKfifo); */
kfifo_free(&(prGlueInfo->rBowInfo.rKfifo));
/* prGlueInfo->rBowInfo.prKfifo = NULL; */
/* prGlueInfo->rBowInfo.rKfifo = NULL; */
/* 4. free device number */
unregister_chrdev_region(prGlueInfo->rBowInfo.u4DeviceNumber, 1);
return TRUE;
} /* end of glUnregisterAmpc */
/*----------------------------------------------------------------------------*/
/*!
* \brief read handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static ssize_t bow_ampc_read(IN struct file *filp, IN char __user *buf, IN size_t size, IN OUT loff_t *ppos)
{
UINT_8 aucBuffer[MAX_BUFFER_SIZE];
ssize_t retval;
P_GLUE_INFO_T prGlueInfo;
prGlueInfo = (P_GLUE_INFO_T) (filp->private_data);
ASSERT(prGlueInfo);
DBGLOG(BOW, INFO, "BoW EVENT read.\n");
if ((prGlueInfo->rBowInfo.fgIsRegistered == FALSE) || (prGlueInfo->ulFlag & GLUE_FLAG_HALT))
return -EFAULT;
/* size check */
/* if(kfifo_len(prGlueInfo->rBowInfo.prKfifo) >= size) */
if (kfifo_len(&(prGlueInfo->rBowInfo.rKfifo)) >= size)
retval = size;
else
retval = kfifo_len(&(prGlueInfo->rBowInfo.rKfifo));
/* retval = kfifo_len(prGlueInfo->rBowInfo.prKfifo); */
/* kfifo_get(prGlueInfo->rBowInfo.prKfifo, aucBuffer, retval); */
/* kfifo_out(prGlueInfo->rBowInfo.prKfifo, aucBuffer, retval); */
if (!(kfifo_out(&(prGlueInfo->rBowInfo.rKfifo), aucBuffer, retval)))
retval = -EIO;
if (copy_to_user(buf, aucBuffer, retval))
retval = -EIO;
return retval;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief write handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static ssize_t bow_ampc_write(IN struct file *filp, OUT const char __user *buf, IN size_t size, IN OUT loff_t *ppos)
{
UINT_8 i;
UINT_8 aucBuffer[MAX_BUFFER_SIZE];
P_AMPC_COMMAND prCmd;
P_GLUE_INFO_T prGlueInfo;
prGlueInfo = (P_GLUE_INFO_T) (filp->private_data);
ASSERT(prGlueInfo);
if ((prGlueInfo->rBowInfo.fgIsRegistered == FALSE) || (prGlueInfo->ulFlag & GLUE_FLAG_HALT))
return -EFAULT;
if (size > MAX_BUFFER_SIZE)
return -EINVAL;
else if (copy_from_user(aucBuffer, buf, size))
return -EIO;
DBGLOG(BOW, EVENT, "AMP driver CMD buffer size : %d.\n", size);
for (i = 0; i < MAX_BUFFER_SIZE; i++)
DBGLOG(BOW, EVENT, "AMP write content : 0x%x.\n", aucBuffer[i]);
DBGLOG(BOW, EVENT, "BoW CMD write.\n");
prCmd = (P_AMPC_COMMAND) aucBuffer;
DBGLOG(BOW, EVENT, "AMP write content payload length : %d.\n", prCmd->rHeader.u2PayloadLength);
DBGLOG(BOW, EVENT, "AMP write content header length : %d.\n", sizeof(AMPC_COMMAND_HEADER_T));
/* size check */
if (prCmd->rHeader.u2PayloadLength + sizeof(AMPC_COMMAND_HEADER_T) != size) {
DBGLOG(BOW, EVENT, "Wrong CMD total length.\n");
return -EINVAL;
}
if (wlanbowHandleCommand(prGlueInfo->prAdapter, prCmd) == WLAN_STATUS_SUCCESS)
return size;
else
return -EINVAL;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief ioctl handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static long bow_ampc_ioctl(IN struct file *filp, IN unsigned int cmd, IN OUT unsigned long arg)
{
int err = 0;
P_GLUE_INFO_T prGlueInfo;
prGlueInfo = (P_GLUE_INFO_T) (filp->private_data);
ASSERT(prGlueInfo);
if ((prGlueInfo->rBowInfo.fgIsRegistered == FALSE) || (prGlueInfo->ulFlag & GLUE_FLAG_HALT))
return -EFAULT;
/* permission check */
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
if (err)
return -EFAULT;
/* no ioctl is implemented yet */
return 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief ioctl handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static unsigned int bow_ampc_poll(IN struct file *filp, IN poll_table * wait)
{
unsigned int retval;
P_GLUE_INFO_T prGlueInfo;
prGlueInfo = (P_GLUE_INFO_T) (filp->private_data);
ASSERT(prGlueInfo);
if ((prGlueInfo->rBowInfo.fgIsRegistered == FALSE) || (prGlueInfo->ulFlag & GLUE_FLAG_HALT))
return -EFAULT;
poll_wait(filp, &prGlueInfo->rBowInfo.outq, wait);
retval = (POLLOUT | POLLWRNORM); /* always accepts incoming command packets */
/* DBGLOG(BOW, EVENT, ("bow_ampc_pol, POLLOUT | POLLWRNORM, %x\n", retval)); */
/* if(kfifo_len(prGlueInfo->rBowInfo.prKfifo) > 0) */
if (kfifo_len(&(prGlueInfo->rBowInfo.rKfifo)) > 0) {
retval |= (POLLIN | POLLRDNORM);
/* DBGLOG(BOW, EVENT, ("bow_ampc_pol, POLLIN | POLLRDNORM, %x\n", retval)); */
}
return retval;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief open handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static int bow_ampc_open(IN struct inode *inodep, IN struct file *filp)
{
P_GLUE_INFO_T prGlueInfo;
P_GL_BOW_INFO prBowInfo;
DBGLOG(BOW, INFO, "in %s\n", __func__);
prBowInfo = container_of(inodep->i_cdev, GL_BOW_INFO, cdev);
ASSERT(prBowInfo);
prGlueInfo = container_of(prBowInfo, GLUE_INFO_T, rBowInfo);
ASSERT(prGlueInfo);
/* set-up private data */
filp->private_data = prGlueInfo;
return 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief close handler for character device to communicate with 802.11 PAL
*
* \param[in]
* \return
* Follows Linux Character Device Interface
*
*/
/*----------------------------------------------------------------------------*/
static int bow_ampc_release(IN struct inode *inodep, IN struct file *filp)
{
P_GLUE_INFO_T prGlueInfo;
prGlueInfo = (P_GLUE_INFO_T) (filp->private_data);
DBGLOG(BOW, INFO, "in %s\n", __func__);
ASSERT(prGlueInfo);
return 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to indicate event for Bluetooth over Wi-Fi
*
* \param[in]
* prGlueInfo
* prEvent
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID kalIndicateBOWEvent(IN P_GLUE_INFO_T prGlueInfo, IN P_AMPC_EVENT prEvent)
{
size_t u4AvailSize, u4EventSize;
ASSERT(prGlueInfo);
ASSERT(prEvent);
/* check device */
if ((prGlueInfo->rBowInfo.fgIsRegistered == FALSE) || (prGlueInfo->ulFlag & GLUE_FLAG_HALT))
return;
#if 0
u4AvailSize = GLUE_BOW_KFIFO_DEPTH - kfifo_len(prGlueInfo->rBowInfo.prKfifo);
#endif
u4AvailSize = GLUE_BOW_KFIFO_DEPTH - kfifo_len(&(prGlueInfo->rBowInfo.rKfifo));
u4EventSize = prEvent->rHeader.u2PayloadLength + sizeof(AMPC_EVENT_HEADER_T);
/* check kfifo availability */
if (u4AvailSize < u4EventSize) {
DBGLOG(BOW, EVENT, "[bow] no space for event: %d/%d\n", u4EventSize, u4AvailSize);
return;
}
/* queue into kfifo */
/* kfifo_put(prGlueInfo->rBowInfo.prKfifo, (PUINT_8)prEvent, u4EventSize); */
/* kfifo_in(prGlueInfo->rBowInfo.prKfifo, (PUINT_8)prEvent, u4EventSize); */
kfifo_in(&(prGlueInfo->rBowInfo.rKfifo), (PUINT_8) prEvent, u4EventSize);
wake_up_interruptible(&(prGlueInfo->rBowInfo.outq));
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve Bluetooth-over-Wi-Fi state from glue layer
*
* \param[in]
* prGlueInfo
* rPeerAddr
* \return
* ENUM_BOW_DEVICE_STATE
*/
/*----------------------------------------------------------------------------*/
ENUM_BOW_DEVICE_STATE kalGetBowState(IN P_GLUE_INFO_T prGlueInfo, IN UINT_8 aucPeerAddress[6])
{
UINT_8 i;
ASSERT(prGlueInfo);
DBGLOG(BOW, EVENT, "kalGetBowState.\n");
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (EQUAL_MAC_ADDR(prGlueInfo->rBowInfo.arPeerAddr, aucPeerAddress) == 0) {
DBGLOG(BOW, EVENT,
"kalGetBowState, aucPeerAddress %x, %x:%x:%x:%x:%x:%x.\n", i,
aucPeerAddress[0], aucPeerAddress[1], aucPeerAddress[2],
aucPeerAddress[3], aucPeerAddress[4], aucPeerAddress[5]);
DBGLOG(BOW, EVENT,
"kalGetBowState, prGlueInfo->rBowInfo.aeState %x, %x.\n", i,
prGlueInfo->rBowInfo.aeState[i]);
return prGlueInfo->rBowInfo.aeState[i];
}
}
return BOW_DEVICE_STATE_DISCONNECTED;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to set Bluetooth-over-Wi-Fi state in glue layer
*
* \param[in]
* prGlueInfo
* eBowState
* rPeerAddr
* \return
* none
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalSetBowState(IN P_GLUE_INFO_T prGlueInfo, IN ENUM_BOW_DEVICE_STATE eBowState, IN UINT_8 aucPeerAddress[6])
{
UINT_8 i;
ASSERT(prGlueInfo);
DBGLOG(BOW, EVENT, "kalSetBowState, aucPeerAddress, %x:%x:%x:%x:%x:%x.\n",
aucPeerAddress[0],
aucPeerAddress[1], aucPeerAddress[2], aucPeerAddress[3], aucPeerAddress[4], aucPeerAddress[5]);
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (EQUAL_MAC_ADDR(prGlueInfo->rBowInfo.arPeerAddr, aucPeerAddress) == 0) {
prGlueInfo->rBowInfo.aeState[i] = eBowState;
DBGLOG(BOW, EVENT,
"kalSetBowState, aucPeerAddress %x, %x:%x:%x:%x:%x:%x.\n", i,
aucPeerAddress[0], aucPeerAddress[1], aucPeerAddress[2],
aucPeerAddress[3], aucPeerAddress[4], aucPeerAddress[5]);
DBGLOG(BOW, EVENT,
"kalSetBowState, prGlueInfo->rBowInfo.aeState %x, %x.\n", i,
prGlueInfo->rBowInfo.aeState[i]);
return TRUE;
}
}
return FALSE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve Bluetooth-over-Wi-Fi global state
*
* \param[in]
* prGlueInfo
*
* \return
* BOW_DEVICE_STATE_DISCONNECTED
* in case there is no BoW connection or
* BoW connection under initialization
*
* BOW_DEVICE_STATE_STARTING
* in case there is no BoW connection but
* some BoW connection under initialization
*
* BOW_DEVICE_STATE_CONNECTED
* in case there is any BoW connection available
*/
/*----------------------------------------------------------------------------*/
ENUM_BOW_DEVICE_STATE kalGetBowGlobalState(IN P_GLUE_INFO_T prGlueInfo)
{
UINT_32 i;
ASSERT(prGlueInfo);
/* Henry, can reduce this logic to indentify state change */
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (prGlueInfo->rBowInfo.aeState[i] == BOW_DEVICE_STATE_CONNECTED)
return BOW_DEVICE_STATE_CONNECTED;
}
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (prGlueInfo->rBowInfo.aeState[i] == BOW_DEVICE_STATE_STARTING)
return BOW_DEVICE_STATE_STARTING;
}
return BOW_DEVICE_STATE_DISCONNECTED;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve Bluetooth-over-Wi-Fi operating frequency
*
* \param[in]
* prGlueInfo
*
* \return
* in unit of KHz
*/
/*----------------------------------------------------------------------------*/
UINT_32 kalGetBowFreqInKHz(IN P_GLUE_INFO_T prGlueInfo)
{
ASSERT(prGlueInfo);
return prGlueInfo->rBowInfo.u4FreqInKHz;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to retrieve Bluetooth-over-Wi-Fi role
*
* \param[in]
* prGlueInfo
*
* \return
* 0: Responder
* 1: Initiator
*/
/*----------------------------------------------------------------------------*/
UINT_8 kalGetBowRole(IN P_GLUE_INFO_T prGlueInfo, IN PARAM_MAC_ADDRESS rPeerAddr)
{
UINT_32 i;
ASSERT(prGlueInfo);
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (EQUAL_MAC_ADDR(prGlueInfo->rBowInfo.arPeerAddr[i], rPeerAddr) == 0)
return prGlueInfo->rBowInfo.aucRole[i];
}
return 0;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to set Bluetooth-over-Wi-Fi role
*
* \param[in]
* prGlueInfo
* ucRole
* 0: Responder
* 1: Initiator
* \return
* none
*/
/*----------------------------------------------------------------------------*/
VOID kalSetBowRole(IN P_GLUE_INFO_T prGlueInfo, IN UINT_8 ucRole, IN PARAM_MAC_ADDRESS rPeerAddr)
{
UINT_32 i;
ASSERT(prGlueInfo);
ASSERT(ucRole <= 1);
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (EQUAL_MAC_ADDR(prGlueInfo->rBowInfo.arPeerAddr[i], rPeerAddr) == 0) {
/* Henry, 0 : Responder, 1 : Initiator */
prGlueInfo->rBowInfo.aucRole[i] = ucRole;
}
}
}
/*----------------------------------------------------------------------------*/
/*!
* \brief to get available Bluetooth-over-Wi-Fi physical link number
*
* \param[in]
* prGlueInfo
* \return
* UINT_32
* how many physical links are aviailable
*/
/*----------------------------------------------------------------------------*/
UINT_8 kalGetBowAvailablePhysicalLinkCount(IN P_GLUE_INFO_T prGlueInfo)
{
UINT_8 i;
UINT_8 ucLinkCount = 0;
ASSERT(prGlueInfo);
for (i = 0; i < CFG_BOW_PHYSICAL_LINK_NUM; i++) {
if (prGlueInfo->rBowInfo.aeState[i] == BOW_DEVICE_STATE_DISCONNECTED)
ucLinkCount++;
}
#if 0
DBGLOG(BOW, EVENT, "kalGetBowAvailablePhysicalLinkCount, ucLinkCount, %c.\n", ucLinkCount);
#endif
return ucLinkCount;
}
/*----------------------------------------------------------------------------*/
/*!
* @brief This inline function is to extract some packet information for BoW
*
* @param prGlueInfo Pointer to the glue structure
* @param prNdisPacket Packet descriptor
* @param pfgIs1X 802.1x packet or not
*
* @retval TRUE Success to extract information
* @retval FALSE Fail to extract correct information
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalBowFrameClassifier(IN P_GLUE_INFO_T prGlueInfo, IN P_NATIVE_PACKET prPacket, OUT PBOOLEAN pfgIs1X)
{
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_8 aucLLC[] = ETH_LLC;
UINT_8 aucSnapBtOui[] = ETH_SNAP_BT_SIG_OUI;
UINT_8 ucMinLength = ucEthTypeLenOffset + ETHER_TYPE_LEN + ETH_LLC_LEN + ETH_SNAP_LEN;
DEBUGFUNC("kalQoSFrameClassifierAndPacketInfo");
u4PacketLen = prSkb->len;
if (u4PacketLen < ETHER_HEADER_LEN) {
DBGLOG(INIT, WARN, "Invalid Ether packet length: %lu\n", u4PacketLen);
return FALSE;
}
aucLookAheadBuf = prSkb->data;
*pfgIs1X = FALSE;
/* 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 */
if (u2EtherTypeLen > ETH_802_3_MAX_LEN) {
/* Not BoW frame */
return FALSE;
}
/* 4 <4> Check for PAL (BT over Wi-Fi) */
/* BoW LLC/SNAP header check */
if (u4PacketLen >= ucMinLength &&
!kalMemCmp(pucNextProtocol, aucLLC, ETH_LLC_LEN) &&
!kalMemCmp(pucNextProtocol + ETH_LLC_LEN, aucSnapBtOui, ETH_SNAP_OUI_LEN)) {
UINT_16 u2LocalCode;
WLAN_GET_FIELD_BE16(pucNextProtocol + ETH_LLC_LEN + ETH_SNAP_OUI_LEN, &u2LocalCode);
if (u2LocalCode == BOW_PROTOCOL_ID_SECURITY_FRAME)
*pfgIs1X = TRUE;
return TRUE;
}
return FALSE;
} /* end of kalBoWFrameClassifier() */
#if CFG_BOW_SEPARATE_DATA_PATH
/* Net Device Hooks */
/*----------------------------------------------------------------------------*/
/*!
* \brief A function for net_device open (ifup)
*
* \param[in] prDev Pointer to struct net_device.
*
* \retval 0 The execution succeeds.
* \retval < 0 The execution failed.
*/
/*----------------------------------------------------------------------------*/
static int bowOpen(IN struct net_device *prDev)
{
P_GLUE_INFO_T prGlueInfo = NULL;
P_ADAPTER_T prAdapter = NULL;
ASSERT(prDev);
prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
ASSERT(prGlueInfo);
prAdapter = prGlueInfo->prAdapter;
ASSERT(prAdapter);
/* 2. carrier on & start TX queue */
netif_carrier_on(prDev);
netif_tx_start_all_queues(prDev);
return 0; /* success */
}
/*----------------------------------------------------------------------------*/
/*!
* \brief A function for net_device stop (ifdown)
*
* \param[in] prDev Pointer to struct net_device.
*
* \retval 0 The execution succeeds.
* \retval < 0 The execution failed.
*/
/*----------------------------------------------------------------------------*/
static int bowStop(IN struct net_device *prDev)
{
P_GLUE_INFO_T prGlueInfo = NULL;
P_ADAPTER_T prAdapter = NULL;
ASSERT(prDev);
prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
ASSERT(prGlueInfo);
prAdapter = prGlueInfo->prAdapter;
ASSERT(prAdapter);
/* 1. stop TX queue */
netif_tx_stop_all_queues(prDev);
/* 2. turn of carrier */
if (netif_carrier_ok(prDev))
netif_carrier_off(prDev);
return 0;
};
#if 0
/*----------------------------------------------------------------------------*/
/*!
* \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
*
* \retval NETDEV_TX_OK - on success.
* \retval NETDEV_TX_BUSY - on failure, packet will be discarded by upper layer.
*/
/*----------------------------------------------------------------------------*/
static int bowHardStartXmit(IN struct sk_buff *prSkb, IN struct net_device *prDev)
{
P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
P_QUE_ENTRY_T prQueueEntry = NULL;
P_QUE_T prTxQueue = NULL;
UINT_16 u2QueueIdx = 0;
UINT_8 ucDSAP, ucSSAP, ucControl;
UINT_8 aucOUI[3];
PUINT_8 aucLookAheadBuf = NULL;
UINT_8 ucBssIndex;
GLUE_SPIN_LOCK_DECLARATION();
ASSERT(prSkb);
ASSERT(prDev);
ASSERT(prGlueInfo);
aucLookAheadBuf = prSkb->data;
ucDSAP = *(PUINT_8) &aucLookAheadBuf[ETH_LLC_OFFSET];
ucSSAP = *(PUINT_8) &aucLookAheadBuf[ETH_LLC_OFFSET + 1];
ucControl = *(PUINT_8) &aucLookAheadBuf[ETH_LLC_OFFSET + 2];
aucOUI[0] = *(PUINT_8) &aucLookAheadBuf[ETH_SNAP_OFFSET];
aucOUI[1] = *(PUINT_8) &aucLookAheadBuf[ETH_SNAP_OFFSET + 1];
aucOUI[2] = *(PUINT_8) &aucLookAheadBuf[ETH_SNAP_OFFSET + 2];
if (!(ucDSAP == ETH_LLC_DSAP_SNAP &&
ucSSAP == ETH_LLC_SSAP_SNAP &&
ucControl == ETH_LLC_CONTROL_UNNUMBERED_INFORMATION &&
aucOUI[0] == ETH_SNAP_BT_SIG_OUI_0 &&
aucOUI[1] == ETH_SNAP_BT_SIG_OUI_1 && aucOUI[2] == ETH_SNAP_BT_SIG_OUI_2) || (prSkb->len > 1514)) {
dev_kfree_skb(prSkb);
return NETDEV_TX_OK;
}
if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) {
DBGLOG(BOW, TRACE, "GLUE_FLAG_HALT skip tx\n");
dev_kfree_skb(prSkb);
return NETDEV_TX_OK;
}
GLUE_SET_PKT_FLAG_PAL(prSkb);
ucBssIndex = wlanGetBssIdxByNetInterface(prGlueInfo, NET_DEV_BOW_IDX);
GLUE_SET_PKT_BSS_IDX(prSkb, ucBssIndex);
prQueueEntry = (P_QUE_ENTRY_T) GLUE_GET_PKT_QUEUE_ENTRY(prSkb);
prTxQueue = &prGlueInfo->rTxQueue;
if (wlanProcessSecurityFrame(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb) == FALSE) {
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]);
if (prGlueInfo->ai4TxPendingFrameNumPerQueue[ucBssIndex][u2QueueIdx] >=
CFG_TX_STOP_NETIF_PER_QUEUE_THRESHOLD) {
netif_stop_subqueue(prDev, u2QueueIdx);
}
} else {
GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum);
}
kalSetEvent(prGlueInfo);
/* For Linux, we'll always return OK FLAG, because we'll free this skb by ourself */
return NETDEV_TX_OK;
}
#else
/*----------------------------------------------------------------------------*/
/*!
* \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
*
* \retval NETDEV_TX_OK - on success.
* \retval NETDEV_TX_BUSY - on failure, packet will be discarded by upper layer.
*/
/*----------------------------------------------------------------------------*/
static int bowHardStartXmit(IN struct sk_buff *prSkb, IN struct net_device *prDev)
{
P_NETDEV_PRIVATE_GLUE_INFO prNetDevPrivate = (P_NETDEV_PRIVATE_GLUE_INFO) NULL;
P_GLUE_INFO_T prGlueInfo = NULL;
UINT_8 ucBssIndex;
BOOLEAN fgIs1x;
ASSERT(prSkb);
ASSERT(prDev);
#if 1
prNetDevPrivate = (P_NETDEV_PRIVATE_GLUE_INFO) netdev_priv(prDev);
prGlueInfo = prNetDevPrivate->prGlueInfo;
ucBssIndex = prNetDevPrivate->ucBssIdx;
#else
prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev));
ucBssIndex = wlanGetBssIdxByNetInterface(prGlueInfo, NET_DEV_BOW_IDX);
#endif
kalResetPacket(prGlueInfo, (P_NATIVE_PACKET) prSkb);
/* Discard frames not generated by PAL */
/* Parsing BOW frame info */
if (!kalBowFrameClassifier(prGlueInfo, (P_NATIVE_PACKET) prSkb, &fgIs1x)) {
/* Cannot extract packet */
DBGLOG(BOW, INFO, "Invalid BOW frame, skip Tx\n");
dev_kfree_skb(prSkb);
return NETDEV_TX_OK;
}
if (fgIs1x)
GLUE_SET_PKT_FLAG(prSkb, ENUM_PKT_1X);
if (kalHardStartXmit(prSkb, prDev, prGlueInfo, ucBssIndex) == WLAN_STATUS_SUCCESS) {
/* Successfully enqueue to Tx queue */
/* Successfully enqueue to Tx queue */
}
/* For Linux, we'll always return OK FLAG, because we'll free this skb by ourself */
return NETDEV_TX_OK;
}
#endif
/* callbacks for netdevice */
static const struct net_device_ops bow_netdev_ops = {
.ndo_open = bowOpen,
.ndo_stop = bowStop,
.ndo_start_xmit = bowHardStartXmit,
};
/*----------------------------------------------------------------------------*/
/*!
* \brief initialize net device for Bluetooth-over-Wi-Fi
*
* \param[in]
* prGlueInfo
* prDevName
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalInitBowDevice(IN P_GLUE_INFO_T prGlueInfo, IN const char *prDevName)
{
P_ADAPTER_T prAdapter;
P_GL_HIF_INFO_T prHif;
PARAM_MAC_ADDRESS rMacAddr;
P_NETDEV_PRIVATE_GLUE_INFO prNetDevPriv = (P_NETDEV_PRIVATE_GLUE_INFO) NULL;
ASSERT(prGlueInfo);
ASSERT(prGlueInfo->rBowInfo.fgIsRegistered == TRUE);
prAdapter = prGlueInfo->prAdapter;
ASSERT(prAdapter);
prHif = &prGlueInfo->rHifInfo;
ASSERT(prHif);
if (prGlueInfo->rBowInfo.fgIsNetRegistered == FALSE) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
prGlueInfo->rBowInfo.prDevHandler =
alloc_netdev_mq(sizeof(P_GLUE_INFO_T), prDevName,
NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM);
#else
prGlueInfo->rBowInfo.prDevHandler =
alloc_netdev_mq(sizeof(P_GLUE_INFO_T), prDevName, ether_setup, CFG_MAX_TXQ_NUM);
#endif
if (!prGlueInfo->rBowInfo.prDevHandler)
return FALSE;
/* 1. setup netdev */
/* 1.1 Point to shared glue structure */
/* *((P_GLUE_INFO_T *) netdev_priv(prGlueInfo->rBowInfo.prDevHandler)) = prGlueInfo; */
prNetDevPriv = (P_NETDEV_PRIVATE_GLUE_INFO) netdev_priv(prGlueInfo->rBowInfo.prDevHandler);
prNetDevPriv->prGlueInfo = prGlueInfo;
/* 1.2 fill hardware address */
COPY_MAC_ADDR(rMacAddr, prAdapter->rMyMacAddr);
rMacAddr[0] |= 0x2; /* change to local administrated address */
kalMemCopy(prGlueInfo->rBowInfo.prDevHandler->dev_addr, rMacAddr, ETH_ALEN);
kalMemCopy(prGlueInfo->rBowInfo.prDevHandler->perm_addr,
prGlueInfo->rBowInfo.prDevHandler->dev_addr, ETH_ALEN);
/* 1.3 register callback functions */
prGlueInfo->rBowInfo.prDevHandler->needed_headroom += NIC_TX_HEAD_ROOM;
prGlueInfo->rBowInfo.prDevHandler->netdev_ops = &bow_netdev_ops;
#if defined(_HIF_SDIO) && (MTK_WCN_HIF_SDIO == 0)
SET_NETDEV_DEV(prGlueInfo->rBowInfo.prDevHandler, &(prHif->func->dev));
#endif
register_netdev(prGlueInfo->rBowInfo.prDevHandler);
/* 2. net device initialize */
netif_carrier_off(prGlueInfo->rBowInfo.prDevHandler);
netif_tx_stop_all_queues(prGlueInfo->rBowInfo.prDevHandler);
/* 2.1 bind NetDev pointer to NetDev index */
wlanBindBssIdxToNetInterface(prGlueInfo, bowInit(prAdapter),
(PVOID) prGlueInfo->rBowInfo.prDevHandler);
prNetDevPriv->ucBssIdx = prAdapter->rWifiVar.rBowFsmInfo.ucBssIndex;
/* wlanBindNetInterface(prGlueInfo, NET_DEV_BOW_IDX, */
/* (PVOID)prGlueInfo->rBowInfo.prDevHandler); */
/* 3. finish */
prGlueInfo->rBowInfo.fgIsNetRegistered = TRUE;
}
return TRUE;
}
/*----------------------------------------------------------------------------*/
/*!
* \brief uninitialize net device for Bluetooth-over-Wi-Fi
*
* \param[in]
* prGlueInfo
*
* \return
* TRUE
* FALSE
*/
/*----------------------------------------------------------------------------*/
BOOLEAN kalUninitBowDevice(IN P_GLUE_INFO_T prGlueInfo)
{
P_ADAPTER_T prAdapter;
ASSERT(prGlueInfo);
prAdapter = prGlueInfo->prAdapter;
ASSERT(prAdapter);
/* ASSERT(prGlueInfo->rBowInfo.fgIsRegistered == TRUE); */
if (prGlueInfo->rBowInfo.fgIsNetRegistered == TRUE) {
prGlueInfo->rBowInfo.fgIsNetRegistered = FALSE;
bowUninit(prAdapter);
if (netif_carrier_ok(prGlueInfo->rBowInfo.prDevHandler))
netif_carrier_off(prGlueInfo->rBowInfo.prDevHandler);
netif_tx_stop_all_queues(prGlueInfo->rBowInfo.prDevHandler);
/* netdevice unregistration & free */
unregister_netdev(prGlueInfo->rBowInfo.prDevHandler);
free_netdev(prGlueInfo->rBowInfo.prDevHandler);
prGlueInfo->rBowInfo.prDevHandler = NULL;
return TRUE;
} else {
return FALSE;
}
}
#endif /* CFG_BOW_SEPARATE_DATA_PATH */
#endif /* CFG_ENABLE_BT_OVER_WIFI */