diff options
Diffstat (limited to 'drivers/staging/ath6kl/os/linux/ar6k_pal.c')
-rw-r--r-- | drivers/staging/ath6kl/os/linux/ar6k_pal.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/drivers/staging/ath6kl/os/linux/ar6k_pal.c b/drivers/staging/ath6kl/os/linux/ar6k_pal.c new file mode 100644 index 0000000..6c98a88 --- /dev/null +++ b/drivers/staging/ath6kl/os/linux/ar6k_pal.c @@ -0,0 +1,481 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2004-2010 Atheros Communications Inc. +// All rights reserved. +// +// +// +// 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" +//------------------------------------------------------------------------------ + +#include "ar6000_drv.h" +#ifdef AR6K_ENABLE_HCI_PAL +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> +#include <ar6k_pal.h> + +extern unsigned int setupbtdev; +#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...) +#endif + +/********************************** + * HCI PAL private info structure + *********************************/ +typedef struct ar6k_hci_pal_info_s{ + + unsigned long ulFlags; +#define HCI_NORMAL_MODE (1) +#define HCI_REGISTERED (1<<1) + struct hci_dev *hdev; /* BT Stack HCI dev */ + AR_SOFTC_T *ar; + +}ar6k_hci_pal_info_t; + +/*** 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_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); + 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 (AR_DEBUG_LVL_CHECK(ATH_DEBUG_HCI_DUMP)) { + A_PRINTF(">>> Send HCI %s packet len: %d\n", + (type == HCI_COMMAND_TYPE) ? "COMMAND" : "ACL", + skb->len); + if (type == HCI_COMMAND_TYPE) { + PRIN_LOG(" HCI Command: OGF:0x%X OCF:0x%X \r\n", + HCI_GET_OP_CODE(skb-data) >> 10, HCI_GET_OP_CODE(skb-data) & 0x3FF); + } + AR_DEBUG_PRINTBUF(skb->data,skb->len,"BT HCI SEND Packet Dump"); + } + + do { + if(type == HCI_COMMAND_TYPE) + { + PRIN_LOG("HCI command"); + + if (ar->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->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; + + 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->arNetDev); + } + 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); + /* 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); + } + } + + 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) +{ + A_STATUS status = A_OK; + struct hci_dev *pHciDev = NULL; + + if (!setupbtdev) { + return A_OK; + } + + 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; + SET_HCI_BUS_TYPE(pHciDev, HCI_VIRTUAL, HCI_80211); + 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; +} + +/********************************************** + * Cleanup HCI device and free HCI PAL private info + *********************************************/ +void ar6k_cleanup_hci_pal(void *ar_p) +{ + AR_SOFTC_T *ar = (AR_SOFTC_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) +{ + 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; +} + +/************************************************** + * 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; + + /* pass the received event packet up the stack */ + if (hci_recv_frame(skb) != 0) { + 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_T *ar = (AR_SOFTC_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; +} +#else /* AR6K_ENABLE_HCI_PAL */ +A_STATUS ar6k_setup_hci_pal(void *ar_p) +{ + return A_OK; +} +void ar6k_cleanup_hci_pal(void *ar_p) +{ +} +#endif /* AR6K_ENABLE_HCI_PAL */ + +#ifdef EXPORT_HCI_PAL_INTERFACE +/***************************************************** + * Register init and callback function with ar6k + * when PAL driver is a separate kernel module. + ****************************************************/ +A_STATUS ar6k_register_hci_pal(HCI_TRANSPORT_CALLBACKS *hciTransCallbacks); +static int __init pal_init_module(void) +{ + HCI_TRANSPORT_CALLBACKS hciTransCallbacks; + + hciTransCallbacks.setupTransport = ar6k_setup_hci_pal; + hciTransCallbacks.cleanupTransport = ar6k_cleanup_hci_pal; + + if(ar6k_register_hci_pal(&hciTransCallbacks) != A_OK) + return -ENODEV; + + return 0; +} + +static void __exit pal_cleanup_module(void) +{ +} + +module_init(pal_init_module); +module_exit(pal_cleanup_module); +MODULE_LICENSE("Dual BSD/GPL"); +#endif |