Index: chromeos/drivers/ath6kl/os/linux/ar6k_pal.c |
diff --git a/chromeos/drivers/ath6kl/os/linux/ar6k_pal.c b/chromeos/drivers/ath6kl/os/linux/ar6k_pal.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6c98a8817aed88107c5b9d687faa54f8cb48187d |
--- /dev/null |
+++ b/chromeos/drivers/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 |