aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/ath6kl/os/linux/hci_bridge.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/ath6kl/os/linux/hci_bridge.c')
-rw-r--r--drivers/staging/ath6kl/os/linux/hci_bridge.c1144
1 files changed, 1144 insertions, 0 deletions
diff --git a/drivers/staging/ath6kl/os/linux/hci_bridge.c b/drivers/staging/ath6kl/os/linux/hci_bridge.c
new file mode 100644
index 000000000000..5cdc3b85a6f6
--- /dev/null
+++ b/drivers/staging/ath6kl/os/linux/hci_bridge.c
@@ -0,0 +1,1144 @@
+//------------------------------------------------------------------------------
+// Copyright (c) 2009-2010 Atheros Corporation. 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.
+//
+//
+//------------------------------------------------------------------------------
+//==============================================================================
+// HCI bridge implementation
+//
+// Author(s): ="Atheros"
+//==============================================================================
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+#include <linux/etherdevice.h>
+#include <a_config.h>
+#include <athdefs.h>
+#include "a_types.h"
+#include "a_osapi.h"
+#include "htc_api.h"
+#include "wmi.h"
+#include "a_drv.h"
+#include "hif.h"
+#include "common_drv.h"
+#include "a_debug.h"
+#define ATH_DEBUG_HCI_BRIDGE ATH_DEBUG_MAKE_MODULE_MASK(6)
+#define ATH_DEBUG_HCI_RECV ATH_DEBUG_MAKE_MODULE_MASK(7)
+#define ATH_DEBUG_HCI_SEND ATH_DEBUG_MAKE_MODULE_MASK(8)
+#define ATH_DEBUG_HCI_DUMP ATH_DEBUG_MAKE_MODULE_MASK(9)
+#else
+#include "ar6000_drv.h"
+#endif /* EXPORT_HCI_BRIDGE_INTERFACE */
+
+#ifdef ATH_AR6K_ENABLE_GMBOX
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+#include "export_hci_transport.h"
+#else
+#include "hci_transport_api.h"
+#endif
+#include "epping_test.h"
+#include "gmboxif.h"
+#include "ar3kconfig.h"
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+ /* only build on newer kernels which have BT configured */
+#if defined(CONFIG_BT_MODULE) || defined(CONFIG_BT)
+#define CONFIG_BLUEZ_HCI_BRIDGE
+#endif
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+unsigned int ar3khcibaud = 0;
+unsigned int hciuartscale = 0;
+unsigned int hciuartstep = 0;
+
+module_param(ar3khcibaud, int, 0644);
+module_param(hciuartscale, int, 0644);
+module_param(hciuartstep, int, 0644);
+#else
+extern unsigned int ar3khcibaud;
+extern unsigned int hciuartscale;
+extern unsigned int hciuartstep;
+#endif /* EXPORT_HCI_BRIDGE_INTERFACE */
+
+typedef struct {
+ void *pHCIDev; /* HCI bridge device */
+ HCI_TRANSPORT_PROPERTIES HCIProps; /* HCI bridge props */
+ struct hci_dev *pBtStackHCIDev; /* BT Stack HCI dev */
+ A_BOOL HciNormalMode; /* Actual HCI mode enabled (non-TEST)*/
+ A_BOOL HciRegistered; /* HCI device registered with stack */
+ HTC_PACKET_QUEUE HTCPacketStructHead;
+ A_UINT8 *pHTCStructAlloc;
+ spinlock_t BridgeLock;
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ HCI_TRANSPORT_MISC_HANDLES HCITransHdl;
+#else
+ AR_SOFTC_T *ar;
+#endif /* EXPORT_HCI_BRIDGE_INTERFACE */
+} AR6K_HCI_BRIDGE_INFO;
+
+#define MAX_ACL_RECV_BUFS 16
+#define MAX_EVT_RECV_BUFS 8
+#define MAX_HCI_WRITE_QUEUE_DEPTH 32
+#define MAX_ACL_RECV_LENGTH 1200
+#define MAX_EVT_RECV_LENGTH 257
+#define TX_PACKET_RSV_OFFSET 32
+#define NUM_HTC_PACKET_STRUCTS ((MAX_ACL_RECV_BUFS + MAX_EVT_RECV_BUFS + MAX_HCI_WRITE_QUEUE_DEPTH) * 2)
+
+#define HCI_GET_OP_CODE(p) (((A_UINT16)((p)[1])) << 8) | ((A_UINT16)((p)[0]))
+
+extern unsigned int setupbtdev;
+AR3K_CONFIG_INFO ar3kconfig;
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+AR6K_HCI_BRIDGE_INFO *g_pHcidevInfo;
+#endif
+
+static A_STATUS bt_setup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo);
+static void bt_cleanup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo);
+static A_STATUS bt_register_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo);
+static A_BOOL bt_indicate_recv(AR6K_HCI_BRIDGE_INFO *pHcidevInfo,
+ HCI_TRANSPORT_PACKET_TYPE Type,
+ struct sk_buff *skb);
+static struct sk_buff *bt_alloc_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, int Length);
+static void bt_free_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, struct sk_buff *skb);
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+A_STATUS ar6000_setup_hci(void *ar);
+void ar6000_cleanup_hci(void *ar);
+A_STATUS hci_test_send(void *ar, struct sk_buff *skb);
+#else
+A_STATUS ar6000_setup_hci(AR_SOFTC_T *ar);
+void ar6000_cleanup_hci(AR_SOFTC_T *ar);
+/* HCI bridge testing */
+A_STATUS hci_test_send(AR_SOFTC_T *ar, struct sk_buff *skb);
+#endif /* EXPORT_HCI_BRIDGE_INTERFACE */
+
+#define LOCK_BRIDGE(dev) spin_lock_bh(&(dev)->BridgeLock)
+#define UNLOCK_BRIDGE(dev) spin_unlock_bh(&(dev)->BridgeLock)
+
+static inline void FreeBtOsBuf(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, void *osbuf)
+{
+ if (pHcidevInfo->HciNormalMode) {
+ bt_free_buffer(pHcidevInfo, (struct sk_buff *)osbuf);
+ } else {
+ /* in test mode, these are just ordinary netbuf allocations */
+ A_NETBUF_FREE(osbuf);
+ }
+}
+
+static void FreeHTCStruct(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, HTC_PACKET *pPacket)
+{
+ LOCK_BRIDGE(pHcidevInfo);
+ HTC_PACKET_ENQUEUE(&pHcidevInfo->HTCPacketStructHead,pPacket);
+ UNLOCK_BRIDGE(pHcidevInfo);
+}
+
+static HTC_PACKET * AllocHTCStruct(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ HTC_PACKET *pPacket = NULL;
+ LOCK_BRIDGE(pHcidevInfo);
+ pPacket = HTC_PACKET_DEQUEUE(&pHcidevInfo->HTCPacketStructHead);
+ UNLOCK_BRIDGE(pHcidevInfo);
+ return pPacket;
+}
+
+#define BLOCK_ROUND_UP_PWR2(x, align) (((int) (x) + ((align)-1)) & ~((align)-1))
+
+static void RefillRecvBuffers(AR6K_HCI_BRIDGE_INFO *pHcidevInfo,
+ HCI_TRANSPORT_PACKET_TYPE Type,
+ int NumBuffers)
+{
+ int length, i;
+ void *osBuf = NULL;
+ HTC_PACKET_QUEUE queue;
+ HTC_PACKET *pPacket;
+
+ INIT_HTC_PACKET_QUEUE(&queue);
+
+ if (Type == HCI_ACL_TYPE) {
+ if (pHcidevInfo->HciNormalMode) {
+ length = HCI_MAX_FRAME_SIZE;
+ } else {
+ length = MAX_ACL_RECV_LENGTH;
+ }
+ } else {
+ length = MAX_EVT_RECV_LENGTH;
+ }
+
+ /* add on transport head and tail room */
+ length += pHcidevInfo->HCIProps.HeadRoom + pHcidevInfo->HCIProps.TailRoom;
+ /* round up to the required I/O padding */
+ length = BLOCK_ROUND_UP_PWR2(length,pHcidevInfo->HCIProps.IOBlockPad);
+
+ for (i = 0; i < NumBuffers; i++) {
+
+ if (pHcidevInfo->HciNormalMode) {
+ osBuf = bt_alloc_buffer(pHcidevInfo,length);
+ } else {
+ osBuf = A_NETBUF_ALLOC(length);
+ }
+
+ if (NULL == osBuf) {
+ break;
+ }
+
+ pPacket = AllocHTCStruct(pHcidevInfo);
+ if (NULL == pPacket) {
+ FreeBtOsBuf(pHcidevInfo,osBuf);
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to alloc HTC struct \n"));
+ break;
+ }
+
+ SET_HTC_PACKET_INFO_RX_REFILL(pPacket,osBuf,A_NETBUF_DATA(osBuf),length,Type);
+ /* add to queue */
+ HTC_PACKET_ENQUEUE(&queue,pPacket);
+ }
+
+ if (i > 0) {
+ HCI_TransportAddReceivePkts(pHcidevInfo->pHCIDev, &queue);
+ }
+}
+
+#define HOST_INTEREST_ITEM_ADDRESS(ar, item) \
+ (((ar)->arTargetType == TARGET_TYPE_AR6002) ? AR6002_HOST_INTEREST_ITEM_ADDRESS(item) : \
+ (((ar)->arTargetType == TARGET_TYPE_AR6003) ? AR6003_HOST_INTEREST_ITEM_ADDRESS(item) : 0))
+static A_STATUS ar6000_hci_transport_ready(HCI_TRANSPORT_HANDLE HCIHandle,
+ HCI_TRANSPORT_PROPERTIES *pProps,
+ void *pContext)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+ A_STATUS status;
+ A_UINT32 address, hci_uart_pwr_mgmt_params;
+// AR3K_CONFIG_INFO ar3kconfig;
+
+ pHcidevInfo->pHCIDev = HCIHandle;
+
+ A_MEMCPY(&pHcidevInfo->HCIProps,pProps,sizeof(*pProps));
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE,("HCI ready (hci:0x%lX, headroom:%d, tailroom:%d blockpad:%d) \n",
+ (unsigned long)HCIHandle,
+ pHcidevInfo->HCIProps.HeadRoom,
+ pHcidevInfo->HCIProps.TailRoom,
+ pHcidevInfo->HCIProps.IOBlockPad));
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ A_ASSERT((pProps->HeadRoom + pProps->TailRoom) <= (struct net_device *)(pHcidevInfo->HCITransHdl.netDevice)->hard_header_len);
+#else
+ A_ASSERT((pProps->HeadRoom + pProps->TailRoom) <= pHcidevInfo->ar->arNetDev->hard_header_len);
+#endif
+
+ /* provide buffers */
+ RefillRecvBuffers(pHcidevInfo, HCI_ACL_TYPE, MAX_ACL_RECV_BUFS);
+ RefillRecvBuffers(pHcidevInfo, HCI_EVENT_TYPE, MAX_EVT_RECV_BUFS);
+
+ do {
+ /* start transport */
+ status = HCI_TransportStart(pHcidevInfo->pHCIDev);
+
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (!pHcidevInfo->HciNormalMode) {
+ /* in test mode, no need to go any further */
+ break;
+ }
+
+ // The delay is required when AR6K is driving the BT reset line
+ // where time is needed after the BT chip is out of reset (HCI_TransportStart)
+ // and before the first HCI command is issued (AR3KConfigure)
+ // FIXME
+ // The delay should be configurable and be only applied when AR6K driving the BT
+ // reset line. This could be done by some module parameter or based on some HW config
+ // info. For now apply 100ms delay blindly
+ A_MDELAY(100);
+
+ A_MEMZERO(&ar3kconfig,sizeof(ar3kconfig));
+ ar3kconfig.pHCIDev = pHcidevInfo->pHCIDev;
+ ar3kconfig.pHCIProps = &pHcidevInfo->HCIProps;
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ ar3kconfig.pHIFDevice = (HIF_DEVICE *)(pHcidevInfo->HCITransHdl.hifDevice);
+#else
+ ar3kconfig.pHIFDevice = pHcidevInfo->ar->arHifDevice;
+#endif
+ ar3kconfig.pBtStackHCIDev = pHcidevInfo->pBtStackHCIDev;
+
+ if (ar3khcibaud != 0) {
+ /* user wants ar3k baud rate change */
+ ar3kconfig.Flags |= AR3K_CONFIG_FLAG_SET_AR3K_BAUD;
+ ar3kconfig.Flags |= AR3K_CONFIG_FLAG_AR3K_BAUD_CHANGE_DELAY;
+ ar3kconfig.AR3KBaudRate = ar3khcibaud;
+ }
+
+ if ((hciuartscale != 0) || (hciuartstep != 0)) {
+ /* user wants to tune HCI bridge UART scale/step values */
+ ar3kconfig.AR6KScale = (A_UINT16)hciuartscale;
+ ar3kconfig.AR6KStep = (A_UINT16)hciuartstep;
+ ar3kconfig.Flags |= AR3K_CONFIG_FLAG_SET_AR6K_SCALE_STEP;
+ }
+
+ /* Fetch the address of the hi_hci_uart_pwr_mgmt_params instance in the host interest area */
+ address = TARG_VTOP(pHcidevInfo->ar->arTargetType,
+ HOST_INTEREST_ITEM_ADDRESS(pHcidevInfo->ar, hi_hci_uart_pwr_mgmt_params));
+ status = ar6000_ReadRegDiag(pHcidevInfo->ar->arHifDevice, &address, &hci_uart_pwr_mgmt_params);
+ if (A_OK == status) {
+ ar3kconfig.PwrMgmtEnabled = (hci_uart_pwr_mgmt_params & 0x1);
+ ar3kconfig.IdleTimeout = (hci_uart_pwr_mgmt_params & 0xFFFF0000) >> 16;
+ ar3kconfig.WakeupTimeout = (hci_uart_pwr_mgmt_params & 0xFF00) >> 8;
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: failed to read hci_uart_pwr_mgmt_params! \n"));
+ }
+ /* configure the AR3K device */
+ memcpy(ar3kconfig.bdaddr,pHcidevInfo->ar->bdaddr,6);
+ status = AR3KConfigure(&ar3kconfig);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ /* Make sure both AR6K and AR3K have power management enabled */
+ if (ar3kconfig.PwrMgmtEnabled) {
+ status = HCI_TransportEnablePowerMgmt(pHcidevInfo->pHCIDev, TRUE);
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: failed to enable TLPM for AR6K! \n"));
+ }
+ }
+
+ status = bt_register_hci(pHcidevInfo);
+
+ } while (FALSE);
+
+ return status;
+}
+
+static void ar6000_hci_transport_failure(void *pContext, A_STATUS Status)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: transport failure! \n"));
+
+ if (pHcidevInfo->HciNormalMode) {
+ /* TODO .. */
+ }
+}
+
+static void ar6000_hci_transport_removed(void *pContext)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE, ("HCI Bridge: transport removed. \n"));
+
+ A_ASSERT(pHcidevInfo->pHCIDev != NULL);
+
+ HCI_TransportDetach(pHcidevInfo->pHCIDev);
+ bt_cleanup_hci(pHcidevInfo);
+ pHcidevInfo->pHCIDev = NULL;
+}
+
+static void ar6000_hci_send_complete(void *pContext, HTC_PACKET *pPacket)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+ void *osbuf = pPacket->pPktContext;
+ A_ASSERT(osbuf != NULL);
+ A_ASSERT(pHcidevInfo != NULL);
+
+ if (A_FAILED(pPacket->Status)) {
+ if ((pPacket->Status != A_ECANCELED) && (pPacket->Status != A_NO_RESOURCE)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: Send Packet Failed: %d \n",pPacket->Status));
+ }
+ }
+
+ FreeHTCStruct(pHcidevInfo,pPacket);
+ FreeBtOsBuf(pHcidevInfo,osbuf);
+
+}
+
+static void ar6000_hci_pkt_recv(void *pContext, HTC_PACKET *pPacket)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+ struct sk_buff *skb;
+
+ A_ASSERT(pHcidevInfo != NULL);
+ skb = (struct sk_buff *)pPacket->pPktContext;
+ A_ASSERT(skb != NULL);
+
+ do {
+
+ if (A_FAILED(pPacket->Status)) {
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_RECV,
+ ("HCI Bridge, packet received type : %d len:%d \n",
+ HCI_GET_PACKET_TYPE(pPacket),pPacket->ActualLength));
+
+ /* set the actual buffer position in the os buffer, HTC recv buffers posted to HCI are set
+ * to fill the front of the buffer */
+ A_NETBUF_PUT(skb,pPacket->ActualLength + pHcidevInfo->HCIProps.HeadRoom);
+ A_NETBUF_PULL(skb,pHcidevInfo->HCIProps.HeadRoom);
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_HCI_DUMP)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,("<<< Recv HCI %s packet len:%d \n",
+ (HCI_GET_PACKET_TYPE(pPacket) == HCI_EVENT_TYPE) ? "EVENT" : "ACL",
+ skb->len));
+ AR_DEBUG_PRINTBUF(skb->data, skb->len,"BT HCI RECV Packet Dump");
+ }
+
+ if (pHcidevInfo->HciNormalMode) {
+ /* indicate the packet */
+ if (bt_indicate_recv(pHcidevInfo,HCI_GET_PACKET_TYPE(pPacket),skb)) {
+ /* bt stack accepted the packet */
+ skb = NULL;
+ }
+ break;
+ }
+
+ /* for testing, indicate packet to the network stack */
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ skb->dev = (struct net_device *)(pHcidevInfo->HCITransHdl.netDevice);
+ if ((((struct net_device *)pHcidevInfo->HCITransHdl.netDevice)->flags & IFF_UP) == IFF_UP) {
+ skb->protocol = eth_type_trans(skb, (struct net_device *)(pHcidevInfo->HCITransHdl.netDevice));
+#else
+ skb->dev = pHcidevInfo->ar->arNetDev;
+ if ((pHcidevInfo->ar->arNetDev->flags & IFF_UP) == IFF_UP) {
+ skb->protocol = eth_type_trans(skb, pHcidevInfo->ar->arNetDev);
+#endif
+ netif_rx(skb);
+ skb = NULL;
+ }
+
+ } while (FALSE);
+
+ FreeHTCStruct(pHcidevInfo,pPacket);
+
+ if (skb != NULL) {
+ /* packet was not accepted, free it */
+ FreeBtOsBuf(pHcidevInfo,skb);
+ }
+
+}
+
+static void ar6000_hci_pkt_refill(void *pContext, HCI_TRANSPORT_PACKET_TYPE Type, int BuffersAvailable)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+ int refillCount;
+
+ if (Type == HCI_ACL_TYPE) {
+ refillCount = MAX_ACL_RECV_BUFS - BuffersAvailable;
+ } else {
+ refillCount = MAX_EVT_RECV_BUFS - BuffersAvailable;
+ }
+
+ if (refillCount > 0) {
+ RefillRecvBuffers(pHcidevInfo,Type,refillCount);
+ }
+
+}
+
+static HCI_SEND_FULL_ACTION ar6000_hci_pkt_send_full(void *pContext, HTC_PACKET *pPacket)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)pContext;
+ HCI_SEND_FULL_ACTION action = HCI_SEND_FULL_KEEP;
+
+ if (!pHcidevInfo->HciNormalMode) {
+ /* for epping testing, check packet tag, some epping packets are
+ * special and cannot be dropped */
+ if (HTC_GET_TAG_FROM_PKT(pPacket) == AR6K_DATA_PKT_TAG) {
+ action = HCI_SEND_FULL_DROP;
+ }
+ }
+
+ return action;
+}
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+A_STATUS ar6000_setup_hci(void *ar)
+#else
+A_STATUS ar6000_setup_hci(AR_SOFTC_T *ar)
+#endif
+{
+ HCI_TRANSPORT_CONFIG_INFO config;
+ A_STATUS status = A_OK;
+ int i;
+ HTC_PACKET *pPacket;
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo;
+
+
+ do {
+
+ pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)A_MALLOC(sizeof(AR6K_HCI_BRIDGE_INFO));
+
+ if (NULL == pHcidevInfo) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ A_MEMZERO(pHcidevInfo, sizeof(AR6K_HCI_BRIDGE_INFO));
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ g_pHcidevInfo = pHcidevInfo;
+ pHcidevInfo->HCITransHdl = *(HCI_TRANSPORT_MISC_HANDLES *)ar;
+#else
+ ar->hcidev_info = pHcidevInfo;
+ pHcidevInfo->ar = ar;
+#endif
+ spin_lock_init(&pHcidevInfo->BridgeLock);
+ INIT_HTC_PACKET_QUEUE(&pHcidevInfo->HTCPacketStructHead);
+
+ ar->exitCallback = AR3KConfigureExit;
+
+ status = bt_setup_hci(pHcidevInfo);
+ if (A_FAILED(status)) {
+ break;
+ }
+
+ if (pHcidevInfo->HciNormalMode) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE, ("HCI Bridge: running in normal mode... \n"));
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE, ("HCI Bridge: running in test mode... \n"));
+ }
+
+ pHcidevInfo->pHTCStructAlloc = (A_UINT8 *)A_MALLOC((sizeof(HTC_PACKET)) * NUM_HTC_PACKET_STRUCTS);
+
+ if (NULL == pHcidevInfo->pHTCStructAlloc) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ pPacket = (HTC_PACKET *)pHcidevInfo->pHTCStructAlloc;
+ for (i = 0; i < NUM_HTC_PACKET_STRUCTS; i++,pPacket++) {
+ FreeHTCStruct(pHcidevInfo,pPacket);
+ }
+
+ A_MEMZERO(&config,sizeof(HCI_TRANSPORT_CONFIG_INFO));
+ config.ACLRecvBufferWaterMark = MAX_ACL_RECV_BUFS / 2;
+ config.EventRecvBufferWaterMark = MAX_EVT_RECV_BUFS / 2;
+ config.MaxSendQueueDepth = MAX_HCI_WRITE_QUEUE_DEPTH;
+ config.pContext = pHcidevInfo;
+ config.TransportFailure = ar6000_hci_transport_failure;
+ config.TransportReady = ar6000_hci_transport_ready;
+ config.TransportRemoved = ar6000_hci_transport_removed;
+ config.pHCISendComplete = ar6000_hci_send_complete;
+ config.pHCIPktRecv = ar6000_hci_pkt_recv;
+ config.pHCIPktRecvRefill = ar6000_hci_pkt_refill;
+ config.pHCISendFull = ar6000_hci_pkt_send_full;
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ pHcidevInfo->pHCIDev = HCI_TransportAttach(pHcidevInfo->HCITransHdl.htcHandle, &config);
+#else
+ pHcidevInfo->pHCIDev = HCI_TransportAttach(ar->arHtcTarget, &config);
+#endif
+
+ if (NULL == pHcidevInfo->pHCIDev) {
+ status = A_ERROR;
+ }
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ if (pHcidevInfo != NULL) {
+ if (NULL == pHcidevInfo->pHCIDev) {
+ /* GMBOX may not be present in older chips */
+ /* just return success */
+ status = A_OK;
+ }
+ }
+ ar6000_cleanup_hci(ar);
+ }
+
+ return status;
+}
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+void ar6000_cleanup_hci(void *ar)
+#else
+void ar6000_cleanup_hci(AR_SOFTC_T *ar)
+#endif
+{
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = g_pHcidevInfo;
+#else
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)ar->hcidev_info;
+#endif
+
+ if (pHcidevInfo != NULL) {
+ bt_cleanup_hci(pHcidevInfo);
+
+ if (pHcidevInfo->pHCIDev != NULL) {
+ HCI_TransportStop(pHcidevInfo->pHCIDev);
+ HCI_TransportDetach(pHcidevInfo->pHCIDev);
+ pHcidevInfo->pHCIDev = NULL;
+ }
+
+ if (pHcidevInfo->pHTCStructAlloc != NULL) {
+ A_FREE(pHcidevInfo->pHTCStructAlloc);
+ pHcidevInfo->pHTCStructAlloc = NULL;
+ }
+
+ A_FREE(pHcidevInfo);
+#ifndef EXPORT_HCI_BRIDGE_INTERFACE
+ ar->hcidev_info = NULL;
+#endif
+ }
+
+
+}
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+A_STATUS hci_test_send(void *ar, struct sk_buff *skb)
+#else
+A_STATUS hci_test_send(AR_SOFTC_T *ar, struct sk_buff *skb)
+#endif
+{
+ int status = A_OK;
+ int length;
+ EPPING_HEADER *pHeader;
+ HTC_PACKET *pPacket;
+ HTC_TX_TAG htc_tag = AR6K_DATA_PKT_TAG;
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = g_pHcidevInfo;
+#else
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)ar->hcidev_info;
+#endif
+
+ do {
+
+ if (NULL == pHcidevInfo) {
+ status = A_ERROR;
+ break;
+ }
+
+ if (NULL == pHcidevInfo->pHCIDev) {
+ status = A_ERROR;
+ break;
+ }
+
+ if (pHcidevInfo->HciNormalMode) {
+ /* this interface cannot run when normal WMI is running */
+ status = A_ERROR;
+ break;
+ }
+
+ pHeader = (EPPING_HEADER *)A_NETBUF_DATA(skb);
+
+ if (!IS_EPPING_PACKET(pHeader)) {
+ status = A_EINVAL;
+ break;
+ }
+
+ if (IS_EPING_PACKET_NO_DROP(pHeader)) {
+ htc_tag = AR6K_CONTROL_PKT_TAG;
+ }
+
+ length = sizeof(EPPING_HEADER) + pHeader->DataLength;
+
+ pPacket = AllocHTCStruct(pHcidevInfo);
+ if (NULL == pPacket) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ SET_HTC_PACKET_INFO_TX(pPacket,
+ skb,
+ A_NETBUF_DATA(skb),
+ length,
+ HCI_ACL_TYPE, /* send every thing out as ACL */
+ htc_tag);
+
+ HCI_TransportSendPkt(pHcidevInfo->pHCIDev,pPacket,FALSE);
+ pPacket = NULL;
+
+ } while (FALSE);
+
+ return status;
+}
+
+void ar6000_set_default_ar3kconfig(AR_SOFTC_T *ar, void *ar3kconfig)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)ar->hcidev_info;
+ AR3K_CONFIG_INFO *config = (AR3K_CONFIG_INFO *)ar3kconfig;
+
+ config->pHCIDev = pHcidevInfo->pHCIDev;
+ config->pHCIProps = &pHcidevInfo->HCIProps;
+ config->pHIFDevice = ar->arHifDevice;
+ config->pBtStackHCIDev = pHcidevInfo->pBtStackHCIDev;
+ config->Flags |= AR3K_CONFIG_FLAG_SET_AR3K_BAUD;
+ config->AR3KBaudRate = 115200;
+}
+
+#ifdef CONFIG_BLUEZ_HCI_BRIDGE
+/*** BT Stack Entrypoints *******/
+
+/*
+ * bt_open - open a handle to the device
+*/
+static int bt_open(struct hci_dev *hdev)
+{
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: 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)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: bt_close - enter\n"));
+ clear_bit(HCI_RUNNING, &hdev->flags);
+ return 0;
+}
+
+/*
+ * bt_send_frame - send data frames
+*/
+static int bt_send_frame(struct sk_buff *skb)
+{
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+ HCI_TRANSPORT_PACKET_TYPE type;
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo;
+ HTC_PACKET *pPacket;
+ A_STATUS status = A_OK;
+ struct sk_buff *txSkb = NULL;
+
+ if (!hdev) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("HCI Bridge: bt_send_frame - no device\n"));
+ return -ENODEV;
+ }
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: bt_send_frame - not open\n"));
+ return -EBUSY;
+ }
+
+ pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)hdev->driver_data;
+ A_ASSERT(pHcidevInfo != NULL);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_SEND, ("+bt_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 bridge */
+ kfree_skb(skb);
+ return 0;
+ default:
+ A_ASSERT(FALSE);
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_HCI_DUMP)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,(">>> Send HCI %s packet len: %d\n",
+ (type == HCI_COMMAND_TYPE) ? "COMMAND" : "ACL",
+ skb->len));
+ if (type == HCI_COMMAND_TYPE) {
+ A_UINT16 opcode = HCI_GET_OP_CODE(skb->data);
+ AR_DEBUG_PRINTF(ATH_DEBUG_ANY,(" HCI Command: OGF:0x%X OCF:0x%X \r\n",
+ opcode >> 10, opcode & 0x3FF));
+ }
+ AR_DEBUG_PRINTBUF(skb->data,skb->len,"BT HCI SEND Packet Dump");
+ }
+
+ do {
+
+ txSkb = bt_skb_alloc(TX_PACKET_RSV_OFFSET + pHcidevInfo->HCIProps.HeadRoom +
+ pHcidevInfo->HCIProps.TailRoom + skb->len,
+ GFP_ATOMIC);
+
+ if (txSkb == NULL) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ bt_cb(txSkb)->pkt_type = bt_cb(skb)->pkt_type;
+ txSkb->dev = (void *)pHcidevInfo->pBtStackHCIDev;
+ skb_reserve(txSkb, TX_PACKET_RSV_OFFSET + pHcidevInfo->HCIProps.HeadRoom);
+ A_MEMCPY(txSkb->data, skb->data, skb->len);
+ skb_put(txSkb,skb->len);
+
+ pPacket = AllocHTCStruct(pHcidevInfo);
+ if (NULL == pPacket) {
+ status = A_NO_MEMORY;
+ break;
+ }
+
+ /* HCI packet length here doesn't include the 1-byte transport header which
+ * will be handled by the HCI transport layer. Enough headroom has already
+ * been reserved above for the transport header
+ */
+ SET_HTC_PACKET_INFO_TX(pPacket,
+ txSkb,
+ txSkb->data,
+ txSkb->len,
+ type,
+ AR6K_CONTROL_PKT_TAG); /* HCI packets cannot be dropped */
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_SEND, ("HCI Bridge: bt_send_frame skb:0x%lX \n",(unsigned long)txSkb));
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_SEND, ("HCI Bridge: type:%d, Total Length:%d Bytes \n",
+ type, txSkb->len));
+
+ status = HCI_TransportSendPkt(pHcidevInfo->pHCIDev,pPacket,FALSE);
+ pPacket = NULL;
+ txSkb = NULL;
+
+ } while (FALSE);
+
+ if (txSkb != NULL) {
+ kfree_skb(txSkb);
+ }
+
+ kfree_skb(skb);
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_SEND, ("-bt_send_frame \n"));
+ return 0;
+}
+
+/*
+ * bt_ioctl - ioctl processing
+*/
+static int bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: bt_ioctl - enter\n"));
+ return -ENOIOCTLCMD;
+}
+
+/*
+ * bt_flush - flush outstandingbpackets
+*/
+static int bt_flush(struct hci_dev *hdev)
+{
+ AR6K_HCI_BRIDGE_INFO *pHcidevInfo;
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: bt_flush - enter\n"));
+
+ pHcidevInfo = (AR6K_HCI_BRIDGE_INFO *)hdev->driver_data;
+
+ /* TODO??? */
+
+ return 0;
+}
+
+
+/*
+ * bt_destruct -
+*/
+static void bt_destruct(struct hci_dev *hdev)
+{
+ AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("HCI Bridge: bt_destruct - enter\n"));
+ /* nothing to do here */
+}
+
+static A_STATUS bt_setup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ A_STATUS status = A_OK;
+ struct hci_dev *pHciDev = NULL;
+ HIF_DEVICE_OS_DEVICE_INFO osDevInfo;
+
+ if (!setupbtdev) {
+ return A_OK;
+ }
+
+ do {
+
+ A_MEMZERO(&osDevInfo,sizeof(osDevInfo));
+ /* get the underlying OS device */
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+ status = ar6000_get_hif_dev((HIF_DEVICE *)(pHcidevInfo->HCITransHdl.hifDevice),
+ &osDevInfo);
+#else
+ status = HIFConfigureDevice(pHcidevInfo->ar->arHifDevice,
+ HIF_DEVICE_GET_OS_DEVICE,
+ &osDevInfo,
+ sizeof(osDevInfo));
+#endif
+
+ if (A_FAILED(status)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to OS device info from HIF\n"));
+ break;
+ }
+
+ /* allocate a BT HCI struct for this device */
+ pHciDev = hci_alloc_dev();
+ if (NULL == pHciDev) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge - failed to allocate bt struct \n"));
+ status = A_NO_MEMORY;
+ break;
+ }
+ /* save the device, we'll register this later */
+ pHcidevInfo->pBtStackHCIDev = pHciDev;
+ SET_HCIDEV_DEV(pHciDev,osDevInfo.pOSDevice);
+ SET_HCI_BUS_TYPE(pHciDev, HCI_VIRTUAL, HCI_BREDR);
+ pHciDev->driver_data = pHcidevInfo;
+ pHciDev->open = bt_open;
+ pHciDev->close = bt_close;
+ pHciDev->send = bt_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 */
+ pHcidevInfo->HciNormalMode = TRUE;
+
+ } while (FALSE);
+
+ if (A_FAILED(status)) {
+ bt_cleanup_hci(pHcidevInfo);
+ }
+
+ return status;
+}
+
+static void bt_cleanup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ int err;
+
+ if (pHcidevInfo->HciRegistered) {
+ pHcidevInfo->HciRegistered = FALSE;
+ clear_bit(HCI_RUNNING, &pHcidevInfo->pBtStackHCIDev->flags);
+ clear_bit(HCI_UP, &pHcidevInfo->pBtStackHCIDev->flags);
+ clear_bit(HCI_INIT, &pHcidevInfo->pBtStackHCIDev->flags);
+ A_ASSERT(pHcidevInfo->pBtStackHCIDev != NULL);
+ /* unregister */
+ if ((err = hci_unregister_dev(pHcidevInfo->pBtStackHCIDev)) < 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: failed to unregister with bluetooth %d\n",err));
+ }
+ }
+
+ if (pHcidevInfo->pBtStackHCIDev != NULL) {
+ kfree(pHcidevInfo->pBtStackHCIDev);
+ pHcidevInfo->pBtStackHCIDev = NULL;
+ }
+}
+
+static A_STATUS bt_register_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ int err;
+ A_STATUS status = A_OK;
+
+ do {
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE, ("HCI Bridge: registering HCI... \n"));
+ A_ASSERT(pHcidevInfo->pBtStackHCIDev != NULL);
+ /* mark that we are registered */
+ pHcidevInfo->HciRegistered = TRUE;
+ if ((err = hci_register_dev(pHcidevInfo->pBtStackHCIDev)) < 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: failed to register with bluetooth %d\n",err));
+ pHcidevInfo->HciRegistered = FALSE;
+ status = A_ERROR;
+ break;
+ }
+
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_BRIDGE, ("HCI Bridge: HCI registered \n"));
+
+ } while (FALSE);
+
+ return status;
+}
+
+static A_BOOL bt_indicate_recv(AR6K_HCI_BRIDGE_INFO *pHcidevInfo,
+ HCI_TRANSPORT_PACKET_TYPE Type,
+ struct sk_buff *skb)
+{
+ A_UINT8 btType;
+ int len;
+ A_BOOL success = FALSE;
+ BT_HCI_EVENT_HEADER *pEvent;
+
+ do {
+
+ if (!test_bit(HCI_RUNNING, &pHcidevInfo->pBtStackHCIDev->flags)) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_WARN, ("HCI Bridge: bt_indicate_recv - not running\n"));
+ break;
+ }
+
+ switch (Type) {
+ case HCI_ACL_TYPE:
+ btType = HCI_ACLDATA_PKT;
+ break;
+ case HCI_EVENT_TYPE:
+ btType = HCI_EVENT_PKT;
+ break;
+ default:
+ btType = 0;
+ A_ASSERT(FALSE);
+ break;
+ }
+
+ if (0 == btType) {
+ break;
+ }
+
+ /* set the final type */
+ bt_cb(skb)->pkt_type = btType;
+ /* set dev */
+ skb->dev = (void *)pHcidevInfo->pBtStackHCIDev;
+ len = skb->len;
+
+ if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_HCI_RECV)) {
+ if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) {
+ pEvent = (BT_HCI_EVENT_HEADER *)skb->data;
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_RECV, ("BT HCI EventCode: %d, len:%d \n",
+ pEvent->EventCode, pEvent->ParamLength));
+ }
+ }
+
+ /* pass receive packet up the stack */
+ if (hci_recv_frame(skb) != 0) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("HCI Bridge: hci_recv_frame failed \n"));
+ break;
+ } else {
+ AR_DEBUG_PRINTF(ATH_DEBUG_HCI_RECV,
+ ("HCI Bridge: Indicated RCV of type:%d, Length:%d \n",btType,len));
+ }
+
+ success = TRUE;
+
+ } while (FALSE);
+
+ return success;
+}
+
+static struct sk_buff* bt_alloc_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, int Length)
+{
+ struct sk_buff *skb;
+ /* in normal HCI mode we need to alloc from the bt core APIs */
+ skb = bt_skb_alloc(Length, GFP_ATOMIC);
+ if (NULL == skb) {
+ AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to alloc bt sk_buff \n"));
+ }
+ return skb;
+}
+
+static void bt_free_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, struct sk_buff *skb)
+{
+ kfree_skb(skb);
+}
+
+#else // { CONFIG_BLUEZ_HCI_BRIDGE
+
+ /* stubs when we only want to test the HCI bridging Interface without the HT stack */
+static A_STATUS bt_setup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ return A_OK;
+}
+static void bt_cleanup_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+
+}
+static A_STATUS bt_register_hci(AR6K_HCI_BRIDGE_INFO *pHcidevInfo)
+{
+ A_ASSERT(FALSE);
+ return A_ERROR;
+}
+
+static A_BOOL bt_indicate_recv(AR6K_HCI_BRIDGE_INFO *pHcidevInfo,
+ HCI_TRANSPORT_PACKET_TYPE Type,
+ struct sk_buff *skb)
+{
+ A_ASSERT(FALSE);
+ return FALSE;
+}
+
+static struct sk_buff* bt_alloc_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, int Length)
+{
+ A_ASSERT(FALSE);
+ return NULL;
+}
+static void bt_free_buffer(AR6K_HCI_BRIDGE_INFO *pHcidevInfo, struct sk_buff *skb)
+{
+ A_ASSERT(FALSE);
+}
+
+#endif // } CONFIG_BLUEZ_HCI_BRIDGE
+
+#else // { ATH_AR6K_ENABLE_GMBOX
+
+ /* stubs when GMBOX support is not needed */
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+A_STATUS ar6000_setup_hci(void *ar)
+#else
+A_STATUS ar6000_setup_hci(AR_SOFTC_T *ar)
+#endif
+{
+ return A_OK;
+}
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+void ar6000_cleanup_hci(void *ar)
+#else
+void ar6000_cleanup_hci(AR_SOFTC_T *ar)
+#endif
+{
+ return;
+}
+
+#ifndef EXPORT_HCI_BRIDGE_INTERFACE
+void ar6000_set_default_ar3kconfig(AR_SOFTC_T *ar, void *ar3kconfig)
+{
+ return;
+}
+#endif
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+int hci_test_send(void *ar, struct sk_buff *skb)
+#else
+int hci_test_send(AR_SOFTC_T *ar, struct sk_buff *skb)
+#endif
+{
+ return -EOPNOTSUPP;
+}
+
+#endif // } ATH_AR6K_ENABLE_GMBOX
+
+
+#ifdef EXPORT_HCI_BRIDGE_INTERFACE
+static int __init
+hcibridge_init_module(void)
+{
+ A_STATUS status;
+ HCI_TRANSPORT_CALLBACKS hciTransCallbacks;
+
+ hciTransCallbacks.setupTransport = ar6000_setup_hci;
+ hciTransCallbacks.cleanupTransport = ar6000_cleanup_hci;
+
+ status = ar6000_register_hci_transport(&hciTransCallbacks);
+ if(status != A_OK)
+ return -ENODEV;
+
+ return 0;
+}
+
+static void __exit
+hcibridge_cleanup_module(void)
+{
+}
+
+module_init(hcibridge_init_module);
+module_exit(hcibridge_cleanup_module);
+MODULE_LICENSE("Dual BSD/GPL");
+#endif