479 lines
13 KiB
C
479 lines
13 KiB
C
|
//------------------------------------------------------------------------------
|
||
|
// ISC License (ISC)
|
||
|
//
|
||
|
// Copyright (c) 2004-2010, The Linux Foundation
|
||
|
// All rights reserved.
|
||
|
// Software was previously licensed under ISC license by Qualcomm Atheros, Inc.
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||
|
// purpose with or without fee is hereby granted, provided that the above
|
||
|
// copyright notice and this permission notice appear in all copies.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
// Author(s): ="Atheros"
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
/* PAL Transport driver for AR6003 */
|
||
|
|
||
|
#include "ar6000_drv.h"
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <net/bluetooth/bluetooth.h>
|
||
|
#include <net/bluetooth/hci_core.h>
|
||
|
#include <ar6k_pal.h>
|
||
|
|
||
|
extern unsigned int setupbtdev;
|
||
|
extern unsigned int loghci;
|
||
|
|
||
|
#define bt_check_bit(val, bit) (val & bit)
|
||
|
#define bt_set_bit(val, bit) (val |= bit)
|
||
|
#define bt_clear_bit(val, bit) (val &= ~bit)
|
||
|
|
||
|
/* export ATH_AR6K_DEBUG_HCI_PAL=yes in host/localmake.linux.inc
|
||
|
* to enable debug information */
|
||
|
#ifdef HCIPAL_DEBUG
|
||
|
#define PRIN_LOG(format, args...) printk(KERN_ALERT "%s:%d - %s Msg:" format "\n",__FUNCTION__, __LINE__, __FILE__, ## args)
|
||
|
#else
|
||
|
#define PRIN_LOG(format, args...) do {} while(0)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*** BT Stack Entrypoints *******/
|
||
|
/***************************************
|
||
|
* bt_open - open a handle to the device
|
||
|
***************************************/
|
||
|
static int bt_open(struct hci_dev *hdev)
|
||
|
{
|
||
|
PRIN_LOG("HCI PAL: bt_open - enter - x\n");
|
||
|
set_bit(HCI_RUNNING, &hdev->flags);
|
||
|
set_bit(HCI_UP, &hdev->flags);
|
||
|
set_bit(HCI_INIT, &hdev->flags);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/***************************************
|
||
|
* bt_close - close handle to the device
|
||
|
***************************************/
|
||
|
static int bt_close(struct hci_dev *hdev)
|
||
|
{
|
||
|
PRIN_LOG("HCI PAL: bt_close - enter\n");
|
||
|
clear_bit(HCI_RUNNING, &hdev->flags);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*****************************
|
||
|
* bt_ioctl - ioctl processing
|
||
|
*****************************/
|
||
|
static int bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
|
||
|
{
|
||
|
PRIN_LOG("HCI PAL: bt_ioctl - enter\n");
|
||
|
return -ENOIOCTLCMD;
|
||
|
}
|
||
|
|
||
|
/**************************************
|
||
|
* bt_flush - flush outstanding packets
|
||
|
**************************************/
|
||
|
static int bt_flush(struct hci_dev *hdev)
|
||
|
{
|
||
|
PRIN_LOG("HCI PAL: bt_flush - enter\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/***************
|
||
|
* bt_destruct
|
||
|
***************/
|
||
|
static void bt_destruct(struct hci_dev *hdev)
|
||
|
{
|
||
|
PRIN_LOG("HCI PAL: bt_destruct - enter\n");
|
||
|
/* nothing to do here */
|
||
|
}
|
||
|
|
||
|
/****************************************************
|
||
|
* Invoked from bluetooth stack via hdev->send()
|
||
|
* to send the packet out via ar6k to PAL firmware.
|
||
|
*
|
||
|
* For HCI command packet wmi_send_hci_cmd() is invoked.
|
||
|
* wmi_send_hci_cmd adds WMI_CMD_HDR and sends the packet
|
||
|
* to PAL firmware.
|
||
|
*
|
||
|
* For HCI ACL data packet wmi_data_hdr_add is invoked
|
||
|
* to add WMI_DATA_HDR to the packet. ar6000_acl_data_tx
|
||
|
* is then invoked to send the packet to PAL firmware.
|
||
|
******************************************************/
|
||
|
static int btpal_send_frame(struct sk_buff *skb)
|
||
|
{
|
||
|
struct hci_dev *hdev = (struct hci_dev *)skb->dev;
|
||
|
HCI_TRANSPORT_PACKET_TYPE type;
|
||
|
ar6k_hci_pal_info_t *pHciPalInfo;
|
||
|
A_STATUS status = A_OK;
|
||
|
struct sk_buff *txSkb = NULL;
|
||
|
AR_SOFTC_DEV_T *ar;
|
||
|
|
||
|
if (!hdev) {
|
||
|
PRIN_LOG("HCI PAL: btpal_send_frame - no device\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
if (!test_bit(HCI_RUNNING, &hdev->flags)) {
|
||
|
PRIN_LOG("HCI PAL: btpal_send_frame - not open\n");
|
||
|
return -EBUSY;
|
||
|
}
|
||
|
|
||
|
pHciPalInfo = (ar6k_hci_pal_info_t *)hdev->driver_data;
|
||
|
A_ASSERT(pHciPalInfo != NULL);
|
||
|
if (pHciPalInfo == NULL)
|
||
|
return -1;
|
||
|
|
||
|
ar = pHciPalInfo->ar;
|
||
|
|
||
|
PRIN_LOG("+btpal_send_frame type: %d \n",bt_cb(skb)->pkt_type);
|
||
|
type = HCI_COMMAND_TYPE;
|
||
|
|
||
|
switch (bt_cb(skb)->pkt_type) {
|
||
|
case HCI_COMMAND_PKT:
|
||
|
type = HCI_COMMAND_TYPE;
|
||
|
hdev->stat.cmd_tx++;
|
||
|
break;
|
||
|
|
||
|
case HCI_ACLDATA_PKT:
|
||
|
type = HCI_ACL_TYPE;
|
||
|
hdev->stat.acl_tx++;
|
||
|
break;
|
||
|
|
||
|
case HCI_SCODATA_PKT:
|
||
|
/* we don't support SCO over the pal */
|
||
|
kfree_skb(skb);
|
||
|
return 0;
|
||
|
default:
|
||
|
A_ASSERT(FALSE);
|
||
|
kfree_skb(skb);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if(loghci) {
|
||
|
A_PRINTF(">>> Send HCI %s packet len: %d\n",
|
||
|
(type == HCI_COMMAND_TYPE) ? "COMMAND" : "ACL",
|
||
|
skb->len);
|
||
|
if (type == HCI_COMMAND_TYPE) {
|
||
|
A_PRINTF("HCI Command: OGF:0x%X OCF:0x%X \r\n",
|
||
|
(HCI_GET_OP_CODE(skb->data)) >> 10,
|
||
|
(HCI_GET_OP_CODE(skb->data)) & 0x3FF);
|
||
|
}
|
||
|
DebugDumpBytes(skb->data,skb->len,"BT HCI SEND Packet Dump");
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
if(type == HCI_COMMAND_TYPE)
|
||
|
{
|
||
|
PRIN_LOG("HCI command");
|
||
|
|
||
|
if (ar->arSoftc->arWmiReady == FALSE)
|
||
|
{
|
||
|
PRIN_LOG("WMI not ready ");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (wmi_send_hci_cmd(ar->arWmi, skb->data, skb->len) != A_OK)
|
||
|
{
|
||
|
PRIN_LOG("send hci cmd error");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if(type == HCI_ACL_TYPE)
|
||
|
{
|
||
|
void *osbuf;
|
||
|
|
||
|
PRIN_LOG("ACL data");
|
||
|
if (ar->arSoftc->arWmiReady == FALSE)
|
||
|
{
|
||
|
PRIN_LOG("WMI not ready");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* need to add WMI header so allocate a skb with more space */
|
||
|
txSkb = bt_skb_alloc(TX_PACKET_RSV_OFFSET + WMI_MAX_TX_META_SZ +
|
||
|
sizeof(WMI_DATA_HDR) + skb->len,
|
||
|
GFP_ATOMIC);
|
||
|
|
||
|
if (txSkb == NULL) {
|
||
|
status = A_NO_MEMORY;
|
||
|
PRIN_LOG("No memory");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
bt_cb(txSkb)->pkt_type = bt_cb(skb)->pkt_type;
|
||
|
txSkb->dev = (void *)pHciPalInfo->hdev;
|
||
|
skb_reserve(txSkb, TX_PACKET_RSV_OFFSET + WMI_MAX_TX_META_SZ + sizeof(WMI_DATA_HDR));
|
||
|
A_MEMCPY(txSkb->data, skb->data, skb->len);
|
||
|
skb_put(txSkb,skb->len);
|
||
|
/* Add WMI packet type */
|
||
|
osbuf = (void *)txSkb;
|
||
|
|
||
|
PRIN_LOG("\nAdd WMI header");
|
||
|
if (wmi_data_hdr_add(ar->arWmi, osbuf, DATA_MSGTYPE, 0, WMI_DATA_HDR_DATA_TYPE_ACL,0,NULL) != A_OK) {
|
||
|
PRIN_LOG("XIOCTL_ACL_DATA - wmi_data_hdr_add failed\n");
|
||
|
} else {
|
||
|
/* Send data buffer over HTC */
|
||
|
PRIN_LOG("acl data tx");
|
||
|
ar6000_acl_data_tx(osbuf, ar);
|
||
|
}
|
||
|
txSkb = NULL;
|
||
|
}
|
||
|
} while (FALSE);
|
||
|
|
||
|
if (txSkb != NULL) {
|
||
|
PRIN_LOG("Free skb");
|
||
|
kfree_skb(txSkb);
|
||
|
}
|
||
|
kfree_skb(skb);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************
|
||
|
* Unregister HCI device and free HCI device info
|
||
|
***********************************************/
|
||
|
static void bt_cleanup_hci_pal(ar6k_hci_pal_info_t *pHciPalInfo)
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
if (bt_check_bit(pHciPalInfo->ulFlags, HCI_REGISTERED)) {
|
||
|
bt_clear_bit(pHciPalInfo->ulFlags, HCI_REGISTERED);
|
||
|
clear_bit(HCI_RUNNING, &pHciPalInfo->hdev->flags);
|
||
|
clear_bit(HCI_UP, &pHciPalInfo->hdev->flags);
|
||
|
clear_bit(HCI_INIT, &pHciPalInfo->hdev->flags);
|
||
|
A_ASSERT(pHciPalInfo->hdev != NULL);
|
||
|
#ifdef CONFIG_BT
|
||
|
/* unregister */
|
||
|
PRIN_LOG("Unregister PAL device");
|
||
|
if ((err = hci_unregister_dev(pHciPalInfo->hdev)) < 0) {
|
||
|
PRIN_LOG("HCI PAL: failed to unregister with bluetooth %d\n",err);
|
||
|
}
|
||
|
#else
|
||
|
(void)&err;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (pHciPalInfo->hdev != NULL) {
|
||
|
kfree(pHciPalInfo->hdev);
|
||
|
pHciPalInfo->hdev = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************
|
||
|
* Allocate HCI device and store in PAL private info structure.
|
||
|
*********************************************************/
|
||
|
static A_STATUS bt_setup_hci_pal(ar6k_hci_pal_info_t *pHciPalInfo)
|
||
|
{
|
||
|
#ifdef CONFIG_BT
|
||
|
A_STATUS status = A_OK;
|
||
|
struct hci_dev *pHciDev = NULL;
|
||
|
|
||
|
#if 0 //This condition is not required for PAL
|
||
|
if (!setupbtdev) {
|
||
|
return A_OK;
|
||
|
}
|
||
|
#endif
|
||
|
do {
|
||
|
/* allocate a BT HCI struct for this device */
|
||
|
pHciDev = hci_alloc_dev();
|
||
|
if (NULL == pHciDev) {
|
||
|
PRIN_LOG("HCI PAL driver - failed to allocate BT HCI struct \n");
|
||
|
status = A_NO_MEMORY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* save the device, we'll register this later */
|
||
|
pHciPalInfo->hdev = pHciDev;
|
||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
||
|
SET_HCI_BUS_TYPE(pHciDev, HCI_VIRTUAL, HCI_80211);
|
||
|
#else
|
||
|
SET_HCI_BUS_TYPE(pHciDev, HCI_VIRTUAL, HCI_AMP);
|
||
|
#endif
|
||
|
pHciDev->driver_data = pHciPalInfo;
|
||
|
pHciDev->open = bt_open;
|
||
|
pHciDev->close = bt_close;
|
||
|
pHciDev->send = btpal_send_frame;
|
||
|
pHciDev->ioctl = bt_ioctl;
|
||
|
pHciDev->flush = bt_flush;
|
||
|
pHciDev->destruct = bt_destruct;
|
||
|
pHciDev->owner = THIS_MODULE;
|
||
|
/* driver is running in normal BT mode */
|
||
|
PRIN_LOG("Normal mode enabled");
|
||
|
bt_set_bit(pHciPalInfo->ulFlags, HCI_NORMAL_MODE);
|
||
|
|
||
|
} while (FALSE);
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
bt_cleanup_hci_pal(pHciPalInfo);
|
||
|
}
|
||
|
return status;
|
||
|
#else
|
||
|
(void)&bt_open;
|
||
|
(void)&bt_close;
|
||
|
(void)&btpal_send_frame;
|
||
|
(void)&bt_ioctl;
|
||
|
(void)&bt_flush;
|
||
|
(void)&bt_destruct;
|
||
|
bt_cleanup_hci_pal(pHciPalInfo);
|
||
|
return A_ENOTSUP;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**********************************************
|
||
|
* Cleanup HCI device and free HCI PAL private info
|
||
|
*********************************************/
|
||
|
void ar6k_cleanup_hci_pal(void *ar_p)
|
||
|
{
|
||
|
AR_SOFTC_DEV_T *ar = (AR_SOFTC_DEV_T *)ar_p;
|
||
|
ar6k_hci_pal_info_t *pHciPalInfo = (ar6k_hci_pal_info_t *)ar->hcipal_info;
|
||
|
|
||
|
if (pHciPalInfo != NULL) {
|
||
|
bt_cleanup_hci_pal(pHciPalInfo);
|
||
|
A_FREE(pHciPalInfo);
|
||
|
ar->hcipal_info = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/****************************
|
||
|
* Register HCI device
|
||
|
****************************/
|
||
|
static A_BOOL ar6k_pal_transport_ready(void *pHciPal)
|
||
|
{
|
||
|
#ifdef CONFIG_BT
|
||
|
ar6k_hci_pal_info_t *pHciPalInfo = (ar6k_hci_pal_info_t *)pHciPal;
|
||
|
|
||
|
PRIN_LOG("HCI device transport ready");
|
||
|
if(pHciPalInfo == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
if (hci_register_dev(pHciPalInfo->hdev) < 0) {
|
||
|
PRIN_LOG("Can't register HCI device");
|
||
|
hci_free_dev(pHciPalInfo->hdev);
|
||
|
return FALSE;
|
||
|
}
|
||
|
PRIN_LOG("HCI device registered");
|
||
|
pHciPalInfo->ulFlags |= HCI_REGISTERED;
|
||
|
return TRUE;
|
||
|
#else
|
||
|
return FALSE;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/**************************************************
|
||
|
* Called from ar6k driver when command or ACL data
|
||
|
* packet is received. Pass the packet to bluetooth
|
||
|
* stack via hci_recv_frame.
|
||
|
**************************************************/
|
||
|
A_BOOL ar6k_pal_recv_pkt(void *pHciPal, void *osbuf)
|
||
|
{
|
||
|
struct sk_buff *skb = (struct sk_buff *)osbuf;
|
||
|
ar6k_hci_pal_info_t *pHciPalInfo;
|
||
|
A_BOOL success = FALSE;
|
||
|
A_UINT8 btType = 0;
|
||
|
pHciPalInfo = (ar6k_hci_pal_info_t *)pHciPal;
|
||
|
|
||
|
do {
|
||
|
|
||
|
/* if normal mode is not enabled pass on to the stack
|
||
|
* by returning failure */
|
||
|
if(!(pHciPalInfo->ulFlags & HCI_NORMAL_MODE))
|
||
|
{
|
||
|
PRIN_LOG("Normal mode not enabled");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!test_bit(HCI_RUNNING, &pHciPalInfo->hdev->flags)) {
|
||
|
PRIN_LOG("HCI PAL: HCI - not running\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(*((short *)A_NETBUF_DATA(skb)) == WMI_ACL_DATA_EVENTID)
|
||
|
btType = HCI_ACLDATA_PKT;
|
||
|
else
|
||
|
btType = HCI_EVENT_PKT;
|
||
|
/* pull 4 bytes which contains WMI packet type */
|
||
|
A_NETBUF_PULL(skb, sizeof(int));
|
||
|
bt_cb(skb)->pkt_type = btType;
|
||
|
skb->dev = (void *)pHciPalInfo->hdev;
|
||
|
|
||
|
if(loghci) {
|
||
|
AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("<<< Recv HCI %s packet len:%d \n",
|
||
|
(btType == HCI_EVENT_TYPE) ? "EVENT" : "ACL",
|
||
|
skb->len));
|
||
|
DebugDumpBytes(skb->data, skb->len,"BT HCI RECV Packet Dump");
|
||
|
}
|
||
|
/* pass the received event packet up the stack */
|
||
|
#ifdef CONFIG_BT
|
||
|
if (hci_recv_frame(skb) != 0) {
|
||
|
#else
|
||
|
if (1) {
|
||
|
#endif
|
||
|
PRIN_LOG("HCI PAL: hci_recv_frame failed \n");
|
||
|
break;
|
||
|
} else {
|
||
|
PRIN_LOG("HCI PAL: Indicated RCV of type:%d, Length:%d \n",HCI_EVENT_PKT, skb->len);
|
||
|
}
|
||
|
PRIN_LOG("hci recv success");
|
||
|
success = TRUE;
|
||
|
}while(FALSE);
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
/**********************************************************
|
||
|
* HCI PAL init function called from ar6k when it is loaded..
|
||
|
* Allocates PAL private info, stores the same in ar6k private info.
|
||
|
* Registers a HCI device.
|
||
|
* Registers packet receive callback function with ar6k
|
||
|
**********************************************************/
|
||
|
A_STATUS ar6k_setup_hci_pal(void *ar_p)
|
||
|
{
|
||
|
A_STATUS status = A_OK;
|
||
|
ar6k_hci_pal_info_t *pHciPalInfo;
|
||
|
ar6k_pal_config_t ar6k_pal_config;
|
||
|
AR_SOFTC_DEV_T *ar = (AR_SOFTC_DEV_T *)ar_p;
|
||
|
|
||
|
do {
|
||
|
|
||
|
pHciPalInfo = (ar6k_hci_pal_info_t *)A_MALLOC(sizeof(ar6k_hci_pal_info_t));
|
||
|
|
||
|
if (NULL == pHciPalInfo) {
|
||
|
status = A_NO_MEMORY;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
A_MEMZERO(pHciPalInfo, sizeof(ar6k_hci_pal_info_t));
|
||
|
ar->hcipal_info = pHciPalInfo;
|
||
|
pHciPalInfo->ar = ar;
|
||
|
|
||
|
status = bt_setup_hci_pal(pHciPalInfo);
|
||
|
if (A_FAILED(status)) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(bt_check_bit(pHciPalInfo->ulFlags, HCI_NORMAL_MODE))
|
||
|
PRIN_LOG("HCI PAL: running in normal mode... \n");
|
||
|
else
|
||
|
PRIN_LOG("HCI PAL: running in test mode... \n");
|
||
|
|
||
|
ar6k_pal_config.fpar6k_pal_recv_pkt = ar6k_pal_recv_pkt;
|
||
|
register_pal_cb(&ar6k_pal_config);
|
||
|
ar6k_pal_transport_ready(ar->hcipal_info);
|
||
|
} while (FALSE);
|
||
|
|
||
|
if (A_FAILED(status)) {
|
||
|
ar6k_cleanup_hci_pal(ar);
|
||
|
}
|
||
|
return status;
|
||
|
}
|