summaryrefslogtreecommitdiff
path: root/drivers/bluetooth/sd8797/bt/bt_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bluetooth/sd8797/bt/bt_main.c')
-rw-r--r--drivers/bluetooth/sd8797/bt/bt_main.c2371
1 files changed, 2371 insertions, 0 deletions
diff --git a/drivers/bluetooth/sd8797/bt/bt_main.c b/drivers/bluetooth/sd8797/bt/bt_main.c
new file mode 100644
index 000000000000..078f91ba8d52
--- /dev/null
+++ b/drivers/bluetooth/sd8797/bt/bt_main.c
@@ -0,0 +1,2371 @@
+/** @file bt_main.c
+ *
+ * @brief This file contains the major functions in BlueTooth
+ * driver. It includes init, exit, open, close and main
+ * thread etc..
+ *
+ * Copyright (C) 2007-2012, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available along with the File in the gpl.txt file or by writing to
+ * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+/**
+ * @mainpage M-BT Linux Driver
+ *
+ * @section overview_sec Overview
+ *
+ * The M-BT is a Linux reference driver for Marvell Bluetooth chipset.
+ *
+ * @section copyright_sec Copyright
+ *
+ * Copyright (C) 2007-2012, Marvell International Ltd.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/wlan_plat.h>
+
+#include "bt_drv.h"
+#include "mbt_char.h"
+#include "bt_sdio.h"
+
+/** Version */
+#define VERSION "M2614110"
+
+/** Driver version */
+static char mbt_driver_version[] = "SD8797-%s-" VERSION "-(" "FP" FPNUM ")"
+#ifdef DEBUG_LEVEL2
+ "-dbg"
+#endif
+ " ";
+
+/** Declare and initialize fw_version */
+static char fw_version[32] = "0.0.0.p0";
+
+#define AID_SYSTEM 1000 /* system server */
+
+#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
+
+/** Define module name */
+#define MODULE_NAME "bt_fm_nfc"
+
+/** Declaration of chardev class */
+static struct class *chardev_class;
+
+/** Interface specific variables */
+static int mbtchar_minor = 0;
+static int fmchar_minor = 0;
+static int nfcchar_minor = 0;
+static int debugchar_minor = 0;
+
+/** Default Driver mode */
+static int drv_mode = (DRV_MODE_BT | DRV_MODE_FM | DRV_MODE_NFC);
+
+/** BT interface name */
+static char *bt_name = NULL;
+/** FM interface name */
+static char *fm_name = NULL;
+/** NFC interface name */
+static char *nfc_name = NULL;
+/** BT debug interface name */
+static char *debug_name = NULL;
+
+/** Firmware flag */
+static int fw = 1;
+/** default powermode */
+static int psmode = 1;
+/** Init config file (MAC address, register etc.) */
+static char *init_cfg = NULL;
+/** Calibration config file (MAC address, init powe etc.) */
+static char *cal_cfg = NULL;
+/** Init MAC address */
+static char *bt_mac = NULL;
+
+/** Setting mbt_drvdbg value based on DEBUG level */
+#ifdef DEBUG_LEVEL1
+#ifdef DEBUG_LEVEL2
+#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT)
+#else
+#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR)
+#endif /* DEBUG_LEVEL2 */
+u32 mbt_drvdbg = DEFAULT_DEBUG_MASK;
+#endif
+
+#ifdef SDIO_SUSPEND_RESUME
+/** PM keep power */
+int mbt_pm_keep_power = 1;
+#endif
+
+static int debug_intf = 1;
+
+/** Enable minicard power-up/down */
+int minicard_pwrup = 1;
+/** Pointer to struct with control hooks */
+struct wifi_platform_data *bt_control_data = NULL;
+
+/**
+ * @brief Alloc bt device
+ *
+ * @return pointer to structure mbt_dev or NULL
+ */
+struct mbt_dev *
+alloc_mbt_dev(void)
+{
+ struct mbt_dev *mbt_dev;
+ ENTER();
+
+ mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL);
+ if (!mbt_dev) {
+ LEAVE();
+ return NULL;
+ }
+
+ LEAVE();
+ return mbt_dev;
+}
+
+/**
+ * @brief Alloc fm device
+ *
+ * @return pointer to structure fm_dev or NULL
+ */
+struct fm_dev *
+alloc_fm_dev(void)
+{
+ struct fm_dev *fm_dev;
+ ENTER();
+
+ fm_dev = kzalloc(sizeof(struct fm_dev), GFP_KERNEL);
+ if (!fm_dev) {
+ LEAVE();
+ return NULL;
+ }
+
+ LEAVE();
+ return fm_dev;
+}
+
+/**
+ * @brief Alloc nfc device
+ *
+ * @return pointer to structure nfc_dev or NULL
+ */
+struct nfc_dev *
+alloc_nfc_dev(void)
+{
+ struct nfc_dev *nfc_dev;
+ ENTER();
+
+ nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
+ if (!nfc_dev) {
+ LEAVE();
+ return NULL;
+ }
+
+ LEAVE();
+ return nfc_dev;
+}
+
+/**
+ * @brief Alloc debug device
+ *
+ * @return pointer to structure debug_level or NULL
+ */
+struct debug_dev *
+alloc_debug_dev(void)
+{
+ struct debug_dev *debug_dev;
+ ENTER();
+
+ debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL);
+ if (!debug_dev) {
+ LEAVE();
+ return NULL;
+ }
+
+ LEAVE();
+ return debug_dev;
+}
+
+/**
+ * @brief Frees m_dev
+ *
+ * @return N/A
+ */
+void
+free_m_dev(struct m_dev *m_dev)
+{
+ ENTER();
+ kfree(m_dev->dev_pointer);
+ LEAVE();
+}
+
+/**
+ * @brief This function verify the received event pkt
+ *
+ * Event format:
+ * +--------+--------+--------+--------+--------+
+ * | Event | Length | ncmd | Opcode |
+ * +--------+--------+--------+--------+--------+
+ * | 1-byte | 1-byte | 1-byte | 2-byte |
+ * +--------+--------+--------+--------+--------+
+ *
+ * @param priv A pointer to bt_private structure
+ * @param skb A pointer to rx skb
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+check_evtpkt(bt_private * priv, struct sk_buff *skb)
+{
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data;
+ struct hci_ev_cmd_complete *ec;
+ u16 opcode, ocf;
+ u8 ret = BT_STATUS_SUCCESS;
+ ENTER();
+ if (!priv->bt_dev.sendcmdflag) {
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+ ec = (struct hci_ev_cmd_complete *)
+ (skb->data + HCI_EVENT_HDR_SIZE);
+ opcode = __le16_to_cpu(ec->opcode);
+ ocf = hci_opcode_ocf(opcode);
+ PRINTM(CMD, "BT: CMD_COMPLTE ocf=0x%x, send_cmd_ocf=0x%x\n",
+ ocf, priv->bt_dev.send_cmd_ocf);
+ if (ocf != priv->bt_dev.send_cmd_ocf) {
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ switch (ocf) {
+ case BT_CMD_MODULE_CFG_REQ:
+ case BT_CMD_BLE_DEEP_SLEEP:
+ case BT_CMD_CONFIG_MAC_ADDR:
+ case BT_CMD_CSU_WRITE_REG:
+ case BT_CMD_LOAD_CONFIG_DATA:
+ case BT_CMD_AUTO_SLEEP_MODE:
+ case BT_CMD_HOST_SLEEP_CONFIG:
+ case BT_CMD_SDIO_PULL_CFG_REQ:
+ case BT_CMD_RESET:
+ priv->bt_dev.sendcmdflag = FALSE;
+ priv->adapter->cmd_complete = TRUE;
+ wake_up_interruptible(&priv->adapter->cmd_wait_q);
+ break;
+ case BT_CMD_GET_FW_VERSION:
+ {
+ u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE +
+ sizeof(struct hci_ev_cmd_complete) +
+ 1);
+ snprintf(fw_version, sizeof(fw_version),
+ "%u.%u.%u.p%u", pos[2], pos[1], pos[0],
+ pos[3]);
+ priv->bt_dev.sendcmdflag = FALSE;
+ priv->adapter->cmd_complete = TRUE;
+ wake_up_interruptible(&priv->adapter->
+ cmd_wait_q);
+ break;
+ }
+#ifdef SDIO_SUSPEND_RESUME
+ case FM_CMD:
+ {
+ u8 *pos =
+ (skb->data + HCI_EVENT_HDR_SIZE +
+ sizeof(struct hci_ev_cmd_complete) +
+ 1);
+ if (*pos == FM_SET_INTR_MASK) {
+ priv->bt_dev.sendcmdflag = FALSE;
+ priv->adapter->cmd_complete = TRUE;
+ wake_up_interruptible(&priv->adapter->
+ cmd_wait_q);
+ }
+ }
+ break;
+#endif
+ case BT_CMD_HOST_SLEEP_ENABLE:
+ priv->bt_dev.sendcmdflag = FALSE;
+ break;
+ default:
+ ret = BT_STATUS_FAILURE;
+ break;
+ }
+ }
+exit:
+ if (ret == BT_STATUS_SUCCESS)
+ kfree_skb(skb);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function process the received event
+ *
+ * Event format:
+ * +--------+--------+--------+--------+-----+
+ * | EC | Length | Data |
+ * +--------+--------+--------+--------+-----+
+ * | 1-byte | 1-byte | n-byte |
+ * +--------+--------+--------+--------+-----+
+ *
+ * @param priv A pointer to bt_private structure
+ * @param skb A pointer to rx skb
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_process_event(bt_private * priv, struct sk_buff *skb)
+{
+ u8 ret = BT_STATUS_SUCCESS;
+ struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+ BT_EVENT *pEvent;
+
+ ENTER();
+ pEvent = (BT_EVENT *) skb->data;
+ if (pEvent->EC != 0xff) {
+ PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pEvent->EC);
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ switch (pEvent->data[0]) {
+ case BT_CMD_AUTO_SLEEP_MODE:
+ if (pEvent->data[2] == BT_STATUS_SUCCESS) {
+ if (pEvent->data[1] == BT_PS_ENABLE)
+ priv->adapter->psmode = 1;
+ else
+ priv->adapter->psmode = 0;
+ PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name,
+ (priv->adapter->psmode) ? "Enable" : "Disable");
+
+ } else {
+ PRINTM(CMD, "BT: PS Mode Command Fail %s\n",
+ m_dev->name);
+ }
+ break;
+ case BT_CMD_HOST_SLEEP_CONFIG:
+ if (pEvent->data[3] == BT_STATUS_SUCCESS) {
+ PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n",
+ m_dev->name, pEvent->data[1], pEvent->data[2]);
+ } else {
+ PRINTM(CMD, "BT: %s: HSCFG Command Fail\n",
+ m_dev->name);
+ }
+ break;
+ case BT_CMD_HOST_SLEEP_ENABLE:
+ if (pEvent->data[1] == BT_STATUS_SUCCESS) {
+ priv->adapter->hs_state = HS_ACTIVATED;
+ if (priv->adapter->suspend_fail == FALSE) {
+#ifdef SDIO_SUSPEND_RESUME
+#ifdef MMC_PM_KEEP_POWER
+#ifdef MMC_PM_FUNC_SUSPENDED
+ bt_is_suspended(priv);
+#endif
+#endif
+#endif
+ wake_up_interruptible(&priv->adapter->
+ cmd_wait_q);
+ }
+ if (priv->adapter->psmode)
+ priv->adapter->ps_state = PS_SLEEP;
+ PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n",
+ m_dev->name);
+
+ } else {
+ PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name);
+ }
+ break;
+ case BT_CMD_MODULE_CFG_REQ:
+ if ((priv->bt_dev.sendcmdflag == TRUE) &&
+ ((pEvent->data[1] == MODULE_BRINGUP_REQ)
+ || (pEvent->data[1] == MODULE_SHUTDOWN_REQ))) {
+ if (pEvent->data[1] == MODULE_BRINGUP_REQ) {
+ PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+ (pEvent->data[2] && (pEvent->data[2] !=
+ MODULE_CFG_RESP_ALREADY_UP))
+ ? "Bring up Fail" : "Bring up success");
+ priv->bt_dev.devType = pEvent->data[3];
+ PRINTM(CMD, "devType:%s\n",
+ (pEvent->data[3] ==
+ DEV_TYPE_AMP) ? "AMP controller" :
+ "BR/EDR controller");
+ priv->bt_dev.devFeature = pEvent->data[4];
+ PRINTM(CMD,
+ "devFeature: %s, %s, %s, %s, %s\n",
+ ((pEvent->
+ data[4] & DEV_FEATURE_BT) ?
+ "BT Feature" : "No BT Feature"),
+ ((pEvent->
+ data[4] & DEV_FEATURE_BTAMP) ?
+ "BTAMP Feature" : "No BTAMP Feature"),
+ ((pEvent->
+ data[4] & DEV_FEATURE_BLE) ?
+ "BLE Feature" : "No BLE Feature"),
+ ((pEvent->
+ data[4] & DEV_FEATURE_FM) ?
+ "FM Feature" : "No FM Feature"),
+ ((pEvent->
+ data[4] & DEV_FEATURE_NFC) ?
+ "NFC Feature" : "No NFC Feature"));
+ }
+ if (pEvent->data[1] == MODULE_SHUTDOWN_REQ) {
+ PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+ (pEvent->
+ data[2]) ? "Shut down Fail" :
+ "Shut down success");
+
+ }
+ if (pEvent->data[2]) {
+ priv->bt_dev.sendcmdflag = FALSE;
+ priv->adapter->cmd_complete = TRUE;
+ wake_up_interruptible(&priv->adapter->
+ cmd_wait_q);
+ }
+ } else {
+ PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n");
+ ret = BT_STATUS_FAILURE;
+ }
+ break;
+ case BT_EVENT_POWER_STATE:
+ if (pEvent->data[1] == BT_PS_SLEEP)
+ priv->adapter->ps_state = PS_SLEEP;
+ PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name,
+ (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE");
+
+ break;
+ case BT_CMD_SDIO_PULL_CFG_REQ:
+ if (pEvent->data[pEvent->length - 1] == BT_STATUS_SUCCESS)
+ PRINTM(CMD, "BT: %s: SDIO pull configuration success\n",
+ m_dev->name);
+
+ else {
+ PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n",
+ m_dev->name);
+
+ }
+ break;
+ default:
+ PRINTM(CMD, "BT: Unknown Event=%d %s\n", pEvent->data[0],
+ m_dev->name);
+ ret = BT_STATUS_FAILURE;
+ break;
+ }
+exit:
+ if (ret == BT_STATUS_SUCCESS)
+ kfree_skb(skb);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function shows debug info for timeout of command sending.
+ *
+ * @param adapter A pointer to bt_adapter
+ * @param cmd Timeout command id
+ *
+ * @return N/A
+ */
+static void
+bt_cmd_timeout_func(bt_adapter * adapter, u16 cmd)
+{
+ ENTER();
+
+ adapter->num_cmd_timeout++;
+
+ PRINTM(ERROR, "Version = %s\n", adapter->drv_ver);
+ PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd);
+ PRINTM(ERROR, "Number of command timeout = %d\n",
+ adapter->num_cmd_timeout);
+ PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter);
+ PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode);
+ PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state);
+ PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state);
+ PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip);
+ PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail);
+ PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended);
+ PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries);
+ PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete);
+ PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv);
+ PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done);
+ PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg);
+ PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending);
+
+ LEAVE();
+}
+
+/**
+ * @brief This function send reset cmd to firmware
+ *
+ * @param priv A pointer to bt_private structure
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_reset_command(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_HCI_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_HCI_CMD *) skb->tail;
+ pCmd->ocf_ogf = (RESET_OGF << 10) | BT_CMD_RESET;
+ pCmd->length = 0x00;
+ pCmd->cmd_type = 0x00;
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ skb_put(skb, 3);
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_RESET;
+ priv->adapter->cmd_complete = FALSE;
+ PRINTM(CMD, "Queue Reset Command(0x%x)\n", pCmd->ocf_ogf);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: Reset timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_RESET);
+ } else {
+ PRINTM(CMD, "BT: Reset Command done\n");
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sends module cfg cmd to firmware
+ *
+ * Command format:
+ * +--------+--------+--------+--------+--------+--------+--------+
+ * | OCF OGF | Length | Data |
+ * +--------+--------+--------+--------+--------+--------+--------+
+ * | 2-byte | 1-byte | 4-byte |
+ * +--------+--------+--------+--------+--------+--------+--------+
+ *
+ * @param priv A pointer to bt_private structure
+ * @param subcmd sub command
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_module_cfg_cmd(bt_private * priv, int subcmd)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "BT: No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ;
+ pCmd->length = 1;
+ pCmd->data[0] = subcmd;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_MODULE_CFG_REQ;
+ priv->adapter->cmd_complete = FALSE;
+ PRINTM(CMD, "Queue module cfg Command(0x%x)\n", pCmd->ocf_ogf);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ /*
+ On some Android platforms certain delay is needed for HCI daemon to
+ remove this module and close itself gracefully. Otherwise it hangs.
+ This 10ms delay is a workaround for such platforms as the root cause
+ has not been found yet. */
+ mdelay(10);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: module_cfg_cmd (0x%x): "
+ "timeout sendcmdflag=%d\n",
+ subcmd, priv->bt_dev.sendcmdflag);
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_MODULE_CFG_REQ);
+ } else {
+ PRINTM(CMD, "BT: module cfg Command done\n");
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function enables power save mode
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_ps(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE;
+ if (priv->bt_dev.psmode)
+ pCmd->data[0] = BT_PS_ENABLE;
+ else
+ pCmd->data[0] = BT_PS_DISABLE;
+ if (priv->bt_dev.idle_timeout) {
+ pCmd->length = 3;
+ pCmd->data[1] = (u8) (priv->bt_dev.idle_timeout & 0x00ff);
+ pCmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8;
+ } else {
+ pCmd->length = 1;
+ }
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", pCmd->ocf_ogf,
+ pCmd->data[0]);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_AUTO_SLEEP_MODE;
+ priv->adapter->cmd_complete = FALSE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: psmode timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_AUTO_SLEEP_MODE);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sends hscfg command
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_hscfg_cmd(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG;
+ pCmd->length = 2;
+ pCmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8;
+ pCmd->data[1] = (u8) (priv->bt_dev.gpio_gap & 0x00ff);
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n",
+ pCmd->ocf_ogf, pCmd->data[0], pCmd->data[1]);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_HOST_SLEEP_CONFIG;
+ priv->adapter->cmd_complete = FALSE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: HSCFG timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_HOST_SLEEP_CONFIG);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sends sdio pull ctrl command
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_send_sdio_pull_ctrl_cmd(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ;
+ pCmd->length = 4;
+ pCmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff);
+ pCmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8;
+ pCmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16;
+ pCmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ PRINTM(CMD,
+ "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n",
+ pCmd->ocf_ogf, pCmd->data[1], pCmd->data[0],
+ pCmd->data[3], pCmd->data[2]);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_SDIO_PULL_CFG_REQ;
+ priv->adapter->cmd_complete = FALSE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_SDIO_PULL_CFG_REQ);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+#ifdef SDIO_SUSPEND_RESUME
+/**
+ * @brief This function set FM interrupt mask
+ *
+ * @param priv A pointer to bt_private structure
+ *
+ * @param priv FM interrupt mask value
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+fm_set_intr_mask(bt_private * priv, u32 mask)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | FM_CMD;
+ pCmd->length = 0x05;
+ pCmd->data[0] = FM_SET_INTR_MASK;
+ memcpy(&pCmd->data[1], &mask, sizeof(mask));
+ PRINTM(CMD, "FM set intr mask=0x%x\n", mask);
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[FM_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = FM_CMD;
+ priv->adapter->cmd_complete = FALSE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+ priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "FM: set intr mask=%d timeout\n", (int)mask);
+ bt_cmd_timeout_func(priv->adapter, FM_CMD);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+#endif
+
+/**
+ * @brief This function enables host sleep
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_enable_hs(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ priv->adapter->suspend_fail = FALSE;
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE;
+ pCmd->length = 0;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_HOST_SLEEP_ENABLE;
+ PRINTM(CMD, "Queue hs enable Command(0x%x)\n", pCmd->ocf_ogf);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->hs_state,
+ WAIT_UNTIL_HS_STATE_CHANGED)) {
+ PRINTM(MSG, "BT: Enable host sleep timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_HOST_SLEEP_ENABLE);
+ }
+ OS_INT_DISABLE;
+ if ((priv->adapter->hs_state == HS_ACTIVATED) ||
+ (priv->adapter->is_suspended == TRUE)) {
+ OS_INT_RESTORE;
+ PRINTM(MSG, "BT: suspend success! skip=%d\n",
+ priv->adapter->hs_skip);
+ } else {
+ priv->adapter->suspend_fail = TRUE;
+ OS_INT_RESTORE;
+ priv->adapter->hs_skip++;
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG,
+ "BT: suspend skipped! "
+ "state=%d skip=%d ps_state= %d WakeupTries=%d\n",
+ priv->adapter->hs_state, priv->adapter->hs_skip,
+ priv->adapter->ps_state, priv->adapter->WakeupTries);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sets ble deepsleep mode
+ *
+ * @param priv A pointer to bt_private structure
+ * @param mode TRUE/FALSE
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_ble_deepsleep(bt_private * priv, int mode)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_BLE_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_BLE_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP;
+ pCmd->length = 1;
+ pCmd->deepsleep = mode;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_BLE_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_BLE_DEEP_SLEEP;
+ priv->adapter->cmd_complete = FALSE;
+ PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, pCmd->ocf_ogf);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_BLE_DEEP_SLEEP);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function gets FW version
+ *
+ * @param priv A pointer to bt_private structure
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_get_fw_version(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_HCI_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_HCI_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION;
+ pCmd->length = 0x01;
+ pCmd->cmd_type = 0x00;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, 4);
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_GET_FW_VERSION;
+ priv->adapter->cmd_complete = FALSE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q,
+ priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: Get FW version: timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_GET_FW_VERSION);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sets mac address
+ *
+ * @param priv A pointer to bt_private structure
+ * @param mac A pointer to mac address
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_set_mac_address(bt_private * priv, u8 * mac)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_HCI_CMD *pCmd;
+ int i = 0;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_HCI_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR;
+ pCmd->length = 8;
+ pCmd->cmd_type = MRVL_VENDOR_PKT;
+ pCmd->cmd_len = 6;
+ for (i = 0; i < 6; i++)
+ pCmd->data[i] = mac[5 - i];
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_HCI_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_CONFIG_MAC_ADDR;
+ priv->adapter->cmd_complete = FALSE;
+ PRINTM(CMD, "BT: Set mac addr %02x:%02x:%02x:%02x:%02x:%02x (0x%x)\n",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], pCmd->ocf_ogf);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(MSG, "BT: Set mac addr: timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_CONFIG_MAC_ADDR);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function load the calibrate data
+ *
+ * @param priv A pointer to bt_private structure
+ * @param config_data A pointer to calibrate data
+ * @param mac A pointer to mac address
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_load_cal_data(bt_private * priv, u8 * config_data, u8 * mac)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CMD *pCmd;
+ int i = 0;
+ // u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF
+ // 0x01 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE
+ // 0xC0 0xC6 0x2D 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xF0};
+
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA;
+ pCmd->length = 0x20;
+ pCmd->data[0] = 0x00;
+ pCmd->data[1] = 0x00;
+ pCmd->data[2] = 0x00;
+ pCmd->data[3] = 0x1C;
+ // swip cal-data byte
+ for (i = 4; i < 32; i++) {
+ pCmd->data[i] = config_data[(i / 4) * 8 - 1 - i];
+ }
+ if (mac != NULL) {
+ pCmd->data[2] = 0x01; // skip checksum
+ for (i = 24; i < 30; i++)
+ pCmd->data[i] = mac[29 - i];
+ }
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_LOAD_CONFIG_DATA;
+ priv->adapter->cmd_complete = FALSE;
+
+ DBG_HEXDUMP(DAT_D, "calirate data: ", pCmd->data, 32);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(ERROR, "BT: Load calibrate data: timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_LOAD_CONFIG_DATA);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function writes value to CSU registers
+ *
+ * @param priv A pointer to bt_private structure
+ * @param type reg type
+ * @param offset register address
+ * @param value register value to write
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_write_reg(bt_private * priv, u8 type, u32 offset, u16 value)
+{
+ struct sk_buff *skb = NULL;
+ u8 ret = BT_STATUS_SUCCESS;
+ BT_CSU_CMD *pCmd;
+ ENTER();
+ skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC);
+ if (skb == NULL) {
+ PRINTM(WARN, "No free skb\n");
+ ret = BT_STATUS_FAILURE;
+ goto exit;
+ }
+ pCmd = (BT_CSU_CMD *) skb->data;
+ pCmd->ocf_ogf = (VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG;
+ pCmd->length = 7;
+ pCmd->type = type;
+ pCmd->offset[0] = (offset & 0x000000ff);
+ pCmd->offset[1] = (offset & 0x0000ff00) >> 8;
+ pCmd->offset[2] = (offset & 0x00ff0000) >> 16;
+ pCmd->offset[3] = (offset & 0xff000000) >> 24;
+ pCmd->value[0] = (value & 0x00ff);
+ pCmd->value[1] = (value & 0xff00) >> 8;
+ bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
+ skb_put(skb, sizeof(BT_CSU_CMD));
+ skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ]));
+ skb_queue_head(&priv->adapter->tx_queue, skb);
+ priv->bt_dev.sendcmdflag = TRUE;
+ priv->bt_dev.send_cmd_ocf = BT_CMD_CSU_WRITE_REG;
+ priv->adapter->cmd_complete = FALSE;
+ PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n",
+ type, offset, value);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (!os_wait_interruptible_timeout
+ (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete,
+ WAIT_UNTIL_CMD_RESP)) {
+ ret = BT_STATUS_FAILURE;
+ PRINTM(ERROR, "BT: Set CSU reg timeout:\n");
+ bt_cmd_timeout_func(priv->adapter, BT_CMD_CSU_WRITE_REG);
+ }
+exit:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function used to restore tx_queue
+ *
+ * @param priv A pointer to bt_private structure
+ * @return N/A
+ */
+void
+bt_restore_tx_queue(bt_private * priv)
+{
+ struct sk_buff *skb = NULL;
+ while (!skb_queue_empty(&priv->adapter->pending_queue)) {
+ skb = skb_dequeue(&priv->adapter->pending_queue);
+ skb_queue_tail(&priv->adapter->tx_queue, skb);
+ }
+ wake_up_interruptible(&priv->MainThread.waitQ);
+}
+
+/**
+ * @brief This function used to send command to firmware
+ *
+ * Command format:
+ * +--------+--------+--------+--------+--------+--------+--------+
+ * | OCF OGF | Length | Data |
+ * +--------+--------+--------+--------+--------+--------+--------+
+ * | 2-byte | 1-byte | 4-byte |
+ * +--------+--------+--------+--------+--------+--------+--------+
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+bt_prepare_command(bt_private * priv)
+{
+ u8 ret = BT_STATUS_SUCCESS;
+ ENTER();
+ if (priv->bt_dev.hscfgcmd) {
+ priv->bt_dev.hscfgcmd = 0;
+ ret = bt_send_hscfg_cmd(priv);
+ }
+ if (priv->bt_dev.pscmd) {
+ priv->bt_dev.pscmd = 0;
+ ret = bt_enable_ps(priv);
+ }
+ if (priv->bt_dev.sdio_pull_ctrl) {
+ priv->bt_dev.sdio_pull_ctrl = 0;
+ ret = bt_send_sdio_pull_ctrl_cmd(priv);
+ }
+ if (priv->bt_dev.hscmd) {
+ priv->bt_dev.hscmd = 0;
+ if (priv->bt_dev.hsmode)
+ ret = bt_enable_hs(priv);
+ else {
+ ret = sbi_wakeup_firmware(priv);
+ priv->adapter->hs_state = HS_DEACTIVATED;
+ }
+ }
+ LEAVE();
+ return ret;
+}
+
+/** @brief This function processes a single packet
+ *
+ * @param priv A pointer to bt_private structure
+ * @param skb A pointer to skb which includes TX packet
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+SendSinglePacket(bt_private * priv, struct sk_buff *skb)
+{
+ int ret;
+ ENTER();
+ if (!skb || !skb->data) {
+ LEAVE();
+ return BT_STATUS_FAILURE;
+ }
+
+ if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) {
+ PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len,
+ BT_UPLD_SIZE);
+ LEAVE();
+ return BT_STATUS_FAILURE;
+ }
+ if (skb_headroom(skb) < BT_HEADER_LEN) {
+ struct sk_buff *tmp = skb;
+ skb = skb_realloc_headroom(skb, BT_HEADER_LEN);
+ if (!skb) {
+ PRINTM(ERROR, "TX error: realloc_headroom failed %d\n",
+ BT_HEADER_LEN);
+ skb = tmp;
+ LEAVE();
+ return BT_STATUS_FAILURE;
+ }
+ kfree_skb(tmp);
+ }
+ /* This is SDIO specific header length: byte[3][2][1], * type: byte[0]
+ (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */
+ skb_push(skb, BT_HEADER_LEN);
+ skb->data[0] = (skb->len & 0x0000ff);
+ skb->data[1] = (skb->len & 0x00ff00) >> 8;
+ skb->data[2] = (skb->len & 0xff0000) >> 16;
+ skb->data[3] = bt_cb(skb)->pkt_type;
+ if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT)
+ PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n",
+ *((u16 *) & skb->data[4]), skb->len);
+ ret = sbi_host_to_card(priv, skb->data, skb->len);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function initializes the adapter structure
+ * and set default value to the member of adapter.
+ *
+ * @param priv A pointer to bt_private structure
+ * @return N/A
+ */
+static void
+bt_init_adapter(bt_private * priv)
+{
+ ENTER();
+ skb_queue_head_init(&priv->adapter->tx_queue);
+ skb_queue_head_init(&priv->adapter->pending_queue);
+ priv->adapter->tx_lock = FALSE;
+ priv->adapter->ps_state = PS_AWAKE;
+ priv->adapter->suspend_fail = FALSE;
+ priv->adapter->is_suspended = FALSE;
+ priv->adapter->hs_skip = 0;
+ priv->adapter->num_cmd_timeout = 0;
+ init_waitqueue_head(&priv->adapter->cmd_wait_q);
+ LEAVE();
+}
+
+/**
+ * @brief This function initializes firmware
+ *
+ * @param priv A pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_fw(bt_private * priv)
+{
+ int ret = BT_STATUS_SUCCESS;
+ ENTER();
+ if (fw == 0) {
+ sd_enable_host_int(priv);
+ goto done;
+ }
+ sd_disable_host_int(priv);
+ if (sbi_download_fw(priv)) {
+ PRINTM(ERROR, " FW failed to be download!\n");
+ ret = BT_STATUS_FAILURE;
+ goto done;
+ }
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function frees the structure of adapter
+ *
+ * @param priv A pointer to bt_private structure
+ * @return N/A
+ */
+void
+bt_free_adapter(bt_private * priv)
+{
+ bt_adapter *Adapter = priv->adapter;
+ ENTER();
+ skb_queue_purge(&priv->adapter->tx_queue);
+ /* Free the adapter object itself */
+ kfree(Adapter);
+ priv->adapter = NULL;
+
+ LEAVE();
+}
+
+/**
+ * @brief This function handles the wrapper_dev ioctl
+ *
+ * @param hev A pointer to wrapper_dev structure
+ * @cmd ioctl cmd
+ * @arg argument
+ * @return -ENOIOCTLCMD
+ */
+static int
+mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, unsigned long arg)
+{
+ ENTER();
+ LEAVE();
+ return -ENOIOCTLCMD;
+}
+
+/**
+ * @brief This function handles wrapper device destruct
+ *
+ * @param m_dev A pointer to m_dev structure
+ *
+ * @return N/A
+ */
+static void
+mdev_destruct(struct m_dev *m_dev)
+{
+ ENTER();
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief This function handles the wrapper device transmit
+ *
+ * @param m_dev A pointer to m_dev structure
+ * @param skb A pointer to sk_buff structure
+ *
+ * @return BT_STATUS_SUCCESS or other error no.
+ */
+static int
+mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb)
+{
+ bt_private *priv = NULL;
+
+ ENTER();
+ if (!m_dev || !m_dev->driver_data) {
+ PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n");
+ LEAVE();
+ return -ENODEV;
+ }
+ priv = (bt_private *) m_dev->driver_data;
+ if (!test_bit(HCI_RUNNING, &m_dev->flags)) {
+ PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n",
+ m_dev->flags);
+ LEAVE();
+ return -EBUSY;
+ }
+ switch (bt_cb(skb)->pkt_type) {
+ case HCI_COMMAND_PKT:
+ m_dev->stat.cmd_tx++;
+ break;
+ case HCI_ACLDATA_PKT:
+ m_dev->stat.acl_tx++;
+ break;
+ case HCI_SCODATA_PKT:
+ m_dev->stat.sco_tx++;
+ break;
+ }
+
+ if (m_dev->dev_type == DEBUG_TYPE) {
+ /* remember the ogf_ocf */
+ priv->debug_device_pending = 1;
+ priv->debug_ocf_ogf[0] = skb->data[0];
+ priv->debug_ocf_ogf[1] = skb->data[1];
+ PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x \n",
+ priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]);
+ }
+
+ if (priv->adapter->tx_lock == TRUE)
+ skb_queue_tail(&priv->adapter->pending_queue, skb);
+ else
+ skb_queue_tail(&priv->adapter->tx_queue, skb);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function flushes the transmit queue
+ *
+ * @param m_dev A pointer to m_dev structure
+ *
+ * @return BT_STATUS_SUCCESS
+ */
+static int
+mdev_flush(struct m_dev *m_dev)
+{
+ bt_private *priv = (bt_private *) m_dev->driver_data;
+ ENTER();
+ skb_queue_purge(&priv->adapter->tx_queue);
+ skb_queue_purge(&priv->adapter->pending_queue);
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function closes the wrapper device
+ *
+ * @param m_dev A pointer to m_dev structure
+ *
+ * @return BT_STATUS_SUCCESS
+ */
+static int
+mdev_close(struct m_dev *m_dev)
+{
+ ENTER();
+ mdev_req_lock(m_dev);
+ if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) {
+ mdev_req_unlock(m_dev);
+ LEAVE();
+ return 0;
+ }
+
+ if (m_dev->flush)
+ m_dev->flush(m_dev);
+ /* wait up pending read and unregister char dev */
+ wake_up_interruptible(&m_dev->req_wait_q);
+ /* Drop queues */
+ skb_queue_purge(&m_dev->rx_q);
+
+ if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) {
+ mdev_req_unlock(m_dev);
+ LEAVE();
+ return 0;
+ }
+ module_put(THIS_MODULE);
+ m_dev->flags = 0;
+ mdev_req_unlock(m_dev);
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function opens the wrapper device
+ *
+ * @param m_dev A pointer to m_dev structure
+ *
+ * @return BT_STATUS_SUCCESS or other
+ */
+static int
+mdev_open(struct m_dev *m_dev)
+{
+ ENTER();
+
+ if (try_module_get(THIS_MODULE) == 0)
+ return BT_STATUS_FAILURE;
+
+ set_bit(HCI_RUNNING, &m_dev->flags);
+
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function queries the wrapper device
+ *
+ * @param m_dev A pointer to m_dev structure
+ * @param arg arguement
+ *
+ * @return BT_STATUS_SUCCESS or other
+ */
+void
+mdev_query(struct m_dev *m_dev, unsigned long arg)
+{
+ struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer;
+
+ ENTER();
+ if (copy_to_user((void *)arg, &mbt_dev->type, sizeof(mbt_dev->type)))
+ PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n");
+
+ LEAVE();
+}
+
+/**
+ * @brief This function initializes the wrapper device
+ *
+ * @param m_dev A pointer to m_dev structure
+ *
+ * @return BT_STATUS_SUCCESS or other
+ */
+void
+init_m_dev(struct m_dev *m_dev)
+{
+ m_dev->dev_pointer = NULL;
+ m_dev->driver_data = NULL;
+ m_dev->dev_type = 0;
+ m_dev->spec_type = 0;
+ skb_queue_head_init(&m_dev->rx_q);
+ init_waitqueue_head(&m_dev->req_wait_q);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
+ init_MUTEX(&m_dev->req_lock);
+#else
+ sema_init(&m_dev->req_lock, 1);
+#endif
+ memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats));
+ m_dev->open = mdev_open;
+ m_dev->close = mdev_close;
+ m_dev->flush = mdev_flush;
+ m_dev->send = mdev_send_frame;
+ m_dev->destruct = mdev_destruct;
+ m_dev->ioctl = mdev_ioctl;
+ m_dev->query = mdev_query;
+ m_dev->owner = THIS_MODULE;
+
+}
+
+/**
+ * @brief This function handles the major job in bluetooth driver.
+ * it handles the event generated by firmware, rx data received
+ * from firmware and tx data sent from kernel.
+ *
+ * @param data A pointer to bt_thread structure
+ * @return BT_STATUS_SUCCESS
+ */
+static int
+bt_service_main_thread(void *data)
+{
+ bt_thread *thread = data;
+ bt_private *priv = thread->priv;
+ bt_adapter *Adapter = priv->adapter;
+ wait_queue_t wait;
+ struct sk_buff *skb;
+ ENTER();
+ bt_activate_thread(thread);
+ init_waitqueue_entry(&wait, current);
+ current->flags |= PF_NOFREEZE;
+
+ for (;;) {
+ add_wait_queue(&thread->waitQ, &wait);
+ OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE);
+ if (priv->adapter->WakeupTries ||
+ ((!priv->adapter->IntCounter) &&
+ (!priv->bt_dev.tx_dnld_rdy ||
+ skb_queue_empty(&priv->adapter->tx_queue)))) {
+ PRINTM(INFO, "Main: Thread sleeping...\n");
+ schedule();
+ }
+ OS_SET_THREAD_STATE(TASK_RUNNING);
+ remove_wait_queue(&thread->waitQ, &wait);
+ if (kthread_should_stop() || Adapter->SurpriseRemoved) {
+ PRINTM(INFO, "main-thread: break from main thread: "
+ "SurpriseRemoved=0x%x\n",
+ Adapter->SurpriseRemoved);
+ break;
+ }
+
+ PRINTM(INFO, "Main: Thread waking up...\n");
+ if (priv->adapter->IntCounter) {
+ OS_INT_DISABLE;
+ Adapter->IntCounter = 0;
+ OS_INT_RESTORE;
+ sbi_get_int_status(priv);
+ } else if ((priv->adapter->ps_state == PS_SLEEP) &&
+ !skb_queue_empty(&priv->adapter->tx_queue)) {
+ priv->adapter->WakeupTries++;
+ sbi_wakeup_firmware(priv);
+ continue;
+ }
+ if (priv->adapter->ps_state == PS_SLEEP)
+ continue;
+ if (priv->bt_dev.tx_dnld_rdy == TRUE) {
+ if (!skb_queue_empty(&priv->adapter->tx_queue)) {
+ skb = skb_dequeue(&priv->adapter->tx_queue);
+ if (skb) {
+ if (SendSinglePacket(priv, skb))
+ ((struct m_dev *)skb->dev)->
+ stat.err_tx++;
+ else
+ ((struct m_dev *)skb->dev)->
+ stat.byte_tx +=
+ skb->len;
+ kfree_skb(skb);
+ }
+ }
+ }
+ }
+ bt_deactivate_thread(thread);
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function handles the interrupt. it will change PS
+ * state if applicable. it will wake up main_thread to handle
+ * the interrupt event as well.
+ *
+ * @param m_dev A pointer to m_dev structure
+ * @return N/A
+ */
+void
+bt_interrupt(struct m_dev *m_dev)
+{
+ bt_private *priv = (bt_private *) m_dev->driver_data;
+ ENTER();
+ if (!priv || !priv->adapter) {
+ LEAVE();
+ return;
+ }
+ PRINTM(INTR, "*\n");
+ priv->adapter->ps_state = PS_AWAKE;
+ if (priv->adapter->hs_state == HS_ACTIVATED) {
+ PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name);
+ priv->adapter->hs_state = HS_DEACTIVATED;
+ }
+ priv->adapter->WakeupTries = 0;
+ priv->adapter->IntCounter++;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ LEAVE();
+}
+
+/**
+ * @brief Module configuration and register device
+ *
+ * @param priv A Pointer to bt_private structure
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+int
+sbi_register_conf_dpc(bt_private * priv)
+{
+ int ret = BT_STATUS_SUCCESS;
+ struct mbt_dev *mbt_dev = NULL;
+ struct fm_dev *fm_dev = NULL;
+ struct nfc_dev *nfc_dev = NULL;
+ struct debug_dev *debug_dev = NULL;
+ struct m_dev *m_dev = NULL;
+ int i = 0;
+ struct char_dev *char_dev = NULL;
+ char dev_file[DEV_NAME_LEN + 5];
+ unsigned char dev_type = 0;
+
+ ENTER();
+
+ priv->bt_dev.tx_dnld_rdy = TRUE;
+
+ if (drv_mode & DRV_MODE_BT) {
+ mbt_dev = alloc_mbt_dev();
+ if (!mbt_dev) {
+ PRINTM(FATAL, "Can not allocate mbt dev\n");
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+ init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ]));
+ priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE;
+ priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC;
+ priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev;
+ priv->bt_dev.m_dev[BT_SEQ].driver_data = priv;
+ priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0;
+ }
+
+ dev_type = HCI_SDIO;
+
+ if (mbt_dev)
+ mbt_dev->type = dev_type;
+
+ ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ);
+ if (ret < 0) {
+ PRINTM(FATAL, "Module cfg command send failed!\n");
+ goto done;
+ }
+ ret = bt_set_ble_deepsleep(priv, TRUE);
+ if (ret < 0) {
+ PRINTM(FATAL, "Enable BLE deepsleep failed!\n");
+ goto done;
+ }
+ if (psmode) {
+ priv->bt_dev.psmode = TRUE;
+ priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME;
+ ret = bt_enable_ps(priv);
+ if (ret < 0) {
+ PRINTM(FATAL, "Enable PS mode failed!\n");
+ goto done;
+ }
+ }
+#ifdef SDIO_SUSPEND_RESUME
+ priv->bt_dev.gpio_gap = 0xffff;
+ ret = bt_send_hscfg_cmd(priv);
+ if (ret < 0) {
+ PRINTM(FATAL, "Send HSCFG failed!\n");
+ goto done;
+ }
+#endif
+ priv->bt_dev.sdio_pull_cfg = 0xffffffff;
+ priv->bt_dev.sdio_pull_ctrl = 0;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ if (priv->bt_dev.devType == DEV_TYPE_AMP) {
+ mbt_dev->type |= HCI_BT_AMP;
+ priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE;
+ }
+ /* block all the packet from bluez */
+ if (init_cfg || cal_cfg)
+ priv->adapter->tx_lock = TRUE;
+
+ if (mbt_dev) {
+ /** init mbt_dev */
+ mbt_dev->flags = 0;
+ mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+ mbt_dev->esco_type = (ESCO_HV1);
+ mbt_dev->link_mode = (HCI_LM_ACCEPT);
+
+ mbt_dev->idle_timeout = 0;
+ mbt_dev->sniff_max_interval = 800;
+ mbt_dev->sniff_min_interval = 80;
+ for (i = 0; i < 3; i++)
+ mbt_dev->reassembly[i] = NULL;
+ atomic_set(&mbt_dev->promisc, 0);
+
+ /** alloc char dev node */
+ char_dev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ class_destroy(chardev_class);
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+ char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor;
+ if (mbt_dev->type & HCI_BT_AMP)
+ char_dev->dev_type = BT_AMP_TYPE;
+ else
+ char_dev->dev_type = BT_TYPE;
+
+ if (bt_name)
+ snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d",
+ bt_name, mbtchar_minor);
+ else
+ snprintf(mbt_dev->name, sizeof(mbt_dev->name),
+ "mbtchar%d", mbtchar_minor);
+ snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name);
+ mbtchar_minor++;
+
+ /** create BT char device node */
+ register_char_dev(char_dev, chardev_class, MODULE_NAME,
+ mbt_dev->name);
+
+ /** chmod & chown for BT char device */
+ mbtchar_chown(dev_file, AID_SYSTEM, AID_BLUETOOTH);
+ mbtchar_chmod(dev_file, 0666);
+
+ /** register m_dev to BT char device */
+ priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor;
+ char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+
+ /** create proc device */
+ snprintf(priv->bt_dev.m_dev[BT_SEQ].name,
+ sizeof(priv->bt_dev.m_dev[BT_SEQ].name),
+ mbt_dev->name);
+ bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ);
+ }
+
+ if ((drv_mode & DRV_MODE_FM) &&
+ (!(priv->bt_dev.devType == DEV_TYPE_AMP)) &&
+ (priv->bt_dev.devFeature & DEV_FEATURE_FM)) {
+
+ /** alloc fm_dev */
+ fm_dev = alloc_fm_dev();
+ if (!fm_dev) {
+ PRINTM(FATAL, "Can not allocate fm dev\n");
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+
+ /** init m_dev */
+ init_m_dev(&(priv->bt_dev.m_dev[FM_SEQ]));
+ priv->bt_dev.m_dev[FM_SEQ].dev_type = FM_TYPE;
+ priv->bt_dev.m_dev[FM_SEQ].spec_type = GENERIC_SPEC;
+ priv->bt_dev.m_dev[FM_SEQ].dev_pointer = (void *)fm_dev;
+ priv->bt_dev.m_dev[FM_SEQ].driver_data = priv;
+ priv->bt_dev.m_dev[FM_SEQ].read_continue_flag = 0;
+
+ /** create char device for FM */
+ char_dev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ class_destroy(chardev_class);
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+ char_dev->minor = FMCHAR_MINOR_BASE + fmchar_minor;
+ char_dev->dev_type = FM_TYPE;
+
+ if (fm_name)
+ snprintf(fm_dev->name, sizeof(fm_dev->name), "%s%d",
+ fm_name, fmchar_minor);
+ else
+ snprintf(fm_dev->name, sizeof(fm_dev->name),
+ "mfmchar%d", fmchar_minor);
+ snprintf(dev_file, sizeof(dev_file), "/dev/%s", fm_dev->name);
+ fmchar_minor++;
+
+ /** register char dev */
+ register_char_dev(char_dev, chardev_class,
+ MODULE_NAME, fm_dev->name);
+
+ /** chmod for FM char device */
+ mbtchar_chmod(dev_file, 0666);
+
+ /** register m_dev to FM char device */
+ priv->bt_dev.m_dev[FM_SEQ].index = char_dev->minor;
+ char_dev->m_dev = &(priv->bt_dev.m_dev[FM_SEQ]);
+
+ /** create proc device */
+ snprintf(priv->bt_dev.m_dev[FM_SEQ].name,
+ sizeof(priv->bt_dev.m_dev[FM_SEQ].name), fm_dev->name);
+ bt_proc_init(priv, &(priv->bt_dev.m_dev[FM_SEQ]), FM_SEQ);
+ }
+
+ if ((drv_mode & DRV_MODE_NFC) &&
+ (!(priv->bt_dev.devType == DEV_TYPE_AMP)) &&
+ (priv->bt_dev.devFeature & DEV_FEATURE_NFC)) {
+
+ /** alloc nfc_dev */
+ nfc_dev = alloc_nfc_dev();
+ if (!nfc_dev) {
+ PRINTM(FATAL, "Can not allocate nfc dev\n");
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+
+ /** init m_dev */
+ init_m_dev(&(priv->bt_dev.m_dev[NFC_SEQ]));
+ priv->bt_dev.m_dev[NFC_SEQ].dev_type = NFC_TYPE;
+ priv->bt_dev.m_dev[NFC_SEQ].spec_type = GENERIC_SPEC;
+ priv->bt_dev.m_dev[NFC_SEQ].dev_pointer = (void *)nfc_dev;
+ priv->bt_dev.m_dev[NFC_SEQ].driver_data = priv;
+ priv->bt_dev.m_dev[NFC_SEQ].read_continue_flag = 0;
+
+ /** create char device for NFC */
+ char_dev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ class_destroy(chardev_class);
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+ char_dev->minor = NFCCHAR_MINOR_BASE + nfcchar_minor;
+ char_dev->dev_type = NFC_TYPE;
+ if (nfc_name)
+ snprintf(nfc_dev->name, sizeof(nfc_dev->name), "%s%d",
+ nfc_name, nfcchar_minor);
+ else
+ snprintf(nfc_dev->name, sizeof(nfc_dev->name),
+ "mnfcchar%d", nfcchar_minor);
+ snprintf(dev_file, sizeof(dev_file), "/dev/%s", nfc_dev->name);
+ nfcchar_minor++;
+
+ /** register char dev */
+ register_char_dev(char_dev, chardev_class, MODULE_NAME,
+ nfc_dev->name);
+
+ /** chmod for NFC char device */
+ mbtchar_chmod(dev_file, 0666);
+
+ /** register m_dev to NFC char device */
+ priv->bt_dev.m_dev[NFC_SEQ].index = char_dev->minor;
+ char_dev->m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]);
+
+ /** create proc device */
+ snprintf(priv->bt_dev.m_dev[NFC_SEQ].name,
+ sizeof(priv->bt_dev.m_dev[NFC_SEQ].name),
+ nfc_dev->name);
+ bt_proc_init(priv, &(priv->bt_dev.m_dev[NFC_SEQ]), NFC_SEQ);
+ }
+
+ if ((debug_intf) &&
+ ((drv_mode & DRV_MODE_BT) ||
+ (drv_mode & DRV_MODE_FM) || (drv_mode & DRV_MODE_NFC))) {
+ /** alloc debug_dev */
+ debug_dev = alloc_debug_dev();
+ if (!debug_dev) {
+ PRINTM(FATAL, "Can not allocate debug dev\n");
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+
+ /** init m_dev */
+ init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ]));
+ priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE;
+ priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC;
+ priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev;
+ priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv;
+
+ /** create char device for Debug */
+ char_dev = kzalloc(sizeof(struct char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ class_destroy(chardev_class);
+ ret = -ENOMEM;
+ goto err_kmalloc;
+ }
+ char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor;
+ char_dev->dev_type = DEBUG_TYPE;
+ if (debug_name)
+ snprintf(debug_dev->name, sizeof(debug_dev->name),
+ "%s%d", debug_name, debugchar_minor);
+ else
+ snprintf(debug_dev->name, sizeof(debug_dev->name),
+ "mdebugchar%d", debugchar_minor);
+ snprintf(dev_file, sizeof(dev_file), "/dev/%s",
+ debug_dev->name);
+ debugchar_minor++;
+
+ /** register char dev */
+ register_char_dev(char_dev, chardev_class, MODULE_NAME,
+ debug_dev->name);
+
+ /** chmod for debug char device */
+ mbtchar_chmod(dev_file, 0666);
+
+ /** register m_dev to debug char device */
+ priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor;
+ char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+
+ /** create proc device */
+ snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name,
+ sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name),
+ debug_dev->name);
+ bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ);
+ }
+
+ if (init_cfg)
+ if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) {
+ PRINTM(FATAL,
+ "BT: Set user init data and param failed\n");
+ if (mbt_dev) {
+ m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+ /** unregister m_dev to char_dev */
+ m_dev->close(m_dev);
+ for (i = 0; i < 3; i++)
+ kfree_skb(mbt_dev->reassembly[i]);
+ /** unregister m_dev to char_dev */
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ ret = BT_STATUS_FAILURE;
+ goto done;
+ }
+
+ if (cal_cfg)
+ if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) {
+ PRINTM(FATAL, "BT: Set cal data failed\n");
+ if (mbt_dev) {
+ m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+ /** unregister m_dev to char_dev */
+ m_dev->close(m_dev);
+ for (i = 0; i < 3; i++)
+ kfree_skb(mbt_dev->reassembly[i]);
+ /** unregister m_dev to char_dev */
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ ret = BT_STATUS_FAILURE;
+ goto done;
+ }
+ if (init_cfg || cal_cfg) {
+ priv->adapter->tx_lock = FALSE;
+ bt_restore_tx_queue(priv);
+ }
+
+ /* Get FW version */
+ bt_get_fw_version(priv);
+ snprintf(priv->adapter->drv_ver, MAX_VER_STR_LEN,
+ mbt_driver_version, fw_version);
+
+done:
+ LEAVE();
+ return ret;
+err_kmalloc:
+ kfree(mbt_dev);
+ kfree(fm_dev);
+ kfree(nfc_dev);
+ kfree(debug_dev);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function adds the card. it will probe the
+ * card, allocate the bt_priv and initialize the device.
+ *
+ * @param card A pointer to card
+ * @return A pointer to bt_private structure
+ */
+
+bt_private *
+bt_add_card(void *card)
+{
+ bt_private *priv = NULL;
+
+ ENTER();
+
+ priv = kzalloc(sizeof(bt_private), GFP_KERNEL);
+ if (!priv) {
+ PRINTM(FATAL, "Can not allocate priv\n");
+ LEAVE();
+ return NULL;
+ }
+
+ /* allocate buffer for bt_adapter */
+ if (!(priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL))) {
+ PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n");
+ goto err_kmalloc;
+ }
+
+ bt_init_adapter(priv);
+
+ PRINTM(INFO, "Starting kthread...\n");
+ priv->MainThread.priv = priv;
+ spin_lock_init(&priv->driver_lock);
+
+ bt_create_thread(bt_service_main_thread, &priv->MainThread,
+ "bt_main_service");
+
+ /* wait for mainthread to up */
+ while (!priv->MainThread.pid)
+ os_sched_timeout(1);
+
+ priv->bt_dev.card = card;
+
+ ((struct sdio_mmc_card *)card)->priv = priv;
+ priv->adapter->sd_ireg = 0;
+ /*
+ * Register the device. Fillup the private data structure with
+ * relevant information from the card and request for the required
+ * IRQ.
+ */
+ if (sbi_register_dev(priv) < 0) {
+ PRINTM(FATAL, "Failed to register bt device!\n");
+ goto err_registerdev;
+ }
+ if (bt_init_fw(priv)) {
+ PRINTM(FATAL, "BT Firmware Init Failed\n");
+ goto err_init_fw;
+ }
+ LEAVE();
+ return priv;
+
+err_init_fw:
+ PRINTM(INFO, "Unregister device\n");
+ sbi_unregister_dev(priv);
+err_registerdev:
+ ((struct sdio_mmc_card *)card)->priv = NULL;
+ /* Stop the thread servicing the interrupts */
+ priv->adapter->SurpriseRemoved = TRUE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ while (priv->MainThread.pid)
+ os_sched_timeout(1);
+err_kmalloc:
+ if (priv->adapter)
+ bt_free_adapter(priv);
+ kfree(priv);
+ LEAVE();
+ return NULL;
+}
+
+/**
+ * @brief This function removes the card.
+ *
+ * @param card A pointer to card
+ * @return BT_STATUS_SUCCESS
+ */
+int
+bt_remove_card(void *card)
+{
+ struct m_dev *m_dev = NULL;
+ bt_private *priv = (bt_private *) card;
+ int i;
+
+ ENTER();
+ if (!priv) {
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+ }
+ if (!priv->adapter->SurpriseRemoved) {
+ bt_send_reset_command(priv);
+ bt_send_module_cfg_cmd(priv, MODULE_SHUTDOWN_REQ);
+ /* Disable interrupts on the card */
+ sd_disable_host_int(priv);
+ priv->adapter->SurpriseRemoved = TRUE;
+ }
+ wake_up_interruptible(&priv->adapter->cmd_wait_q);
+ priv->adapter->SurpriseRemoved = TRUE;
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ while (priv->MainThread.pid) {
+ os_sched_timeout(1);
+ wake_up_interruptible(&priv->MainThread.waitQ);
+ }
+
+ bt_proc_remove(priv);
+
+ PRINTM(INFO, "Unregister device\n");
+ sbi_unregister_dev(priv);
+
+ if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) {
+ m_dev = &(priv->bt_dev.m_dev[BT_SEQ]);
+ if (m_dev->spec_type == IANYWHERE_SPEC) {
+ if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0))
+ mbtchar_minor--;
+ for (i = 0; i < 3; i++)
+ kfree_skb(((struct mbt_dev *)
+ (m_dev->dev_pointer))->
+ reassembly[i]);
+ /** unregister m_dev to char_dev */
+ if (chardev_class)
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ }
+ if (priv->bt_dev.m_dev[FM_SEQ].dev_pointer) {
+ m_dev = &(priv->bt_dev.m_dev[FM_SEQ]);
+ if ((drv_mode & DRV_MODE_FM) && (fmchar_minor > 0))
+ fmchar_minor--;
+ /** unregister m_dev to char_dev */
+ if (chardev_class)
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ if (priv->bt_dev.m_dev[NFC_SEQ].dev_pointer) {
+ m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]);
+ if ((drv_mode & DRV_MODE_NFC) && (nfcchar_minor > 0))
+ nfcchar_minor--;
+ /** unregister m_dev to char_dev */
+ if (chardev_class)
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) {
+ m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]);
+ if ((debug_intf) && (debugchar_minor > 0))
+ debugchar_minor--;
+ /** unregister m_dev to char_dev */
+ if (chardev_class)
+ chardev_cleanup_one(m_dev, chardev_class);
+ free_m_dev(m_dev);
+ }
+ PRINTM(INFO, "Free Adapter\n");
+ bt_free_adapter(priv);
+ kfree(priv);
+
+ LEAVE();
+ return BT_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function sets card detect
+ *
+ * @param on card detect status
+ * @return 0
+ */
+static int
+bt_set_carddetect(int on)
+{
+ PRINTM(MSG, "%s = %d\n", __FUNCTION__, on);
+ if (bt_control_data && bt_control_data->set_carddetect)
+ bt_control_data->set_carddetect(on);
+
+ return 0;
+}
+
+/**
+ * @brief This function sets power
+ *
+ * @param on power status
+ * @return 0
+ */
+static int
+bt_set_power(int on, unsigned long msec)
+{
+ PRINTM(MSG, "%s = %d\n", __FUNCTION__, on);
+ if (bt_control_data && bt_control_data->set_power)
+ bt_control_data->set_power(on);
+
+ if (msec)
+ mdelay(msec);
+ return 0;
+}
+
+/**
+ * @brief This function probes the platform-level device
+ *
+ * @param pdev pointer to struct platform_device
+ * @return 0
+ */
+static int
+bt_probe(struct platform_device *pdev)
+{
+ struct wifi_platform_data *bt_ctrl =
+ (struct wifi_platform_data *)(pdev->dev.platform_data);
+
+ ENTER();
+
+ bt_control_data = bt_ctrl;
+ bt_set_power(1, 0); /* Power On */
+ bt_set_carddetect(1); /* CardDetect (0->1) */
+
+ LEAVE();
+ return 0;
+}
+
+/**
+ * @brief This function removes the platform-level device
+ *
+ * @param pdev pointer to struct platform_device
+ * @return 0
+ */
+static int
+bt_remove(struct platform_device *pdev)
+{
+ struct wifi_platform_data *bt_ctrl =
+ (struct wifi_platform_data *)(pdev->dev.platform_data);
+
+ ENTER();
+
+ bt_control_data = bt_ctrl;
+ bt_set_power(0, 0); /* Power Off */
+ bt_set_carddetect(0); /* CardDetect (1->0) */
+
+ LEAVE();
+ return 0;
+}
+
+static struct platform_driver bt_device = {
+ .probe = bt_probe,
+ .remove = bt_remove,
+ .driver = {
+ .name = "mrvl_bt",
+ }
+};
+
+/**
+ * @brief This function registers the platform-level device to the bus driver
+ *
+ * @return 0--success, failure otherwise
+ */
+static int
+bt_add_dev(void)
+{
+ int ret = 0;
+
+ ENTER();
+
+ if (minicard_pwrup)
+ ret = platform_driver_register(&bt_device);
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function deregisters the platform-level device
+ *
+ * @return N/A
+ */
+static void
+bt_del_dev(void)
+{
+ ENTER();
+
+ if (minicard_pwrup)
+ platform_driver_unregister(&bt_device);
+
+ LEAVE();
+}
+
+/**
+ * @brief This function initializes module.
+ *
+ * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE
+ */
+static int
+bt_init_module(void)
+{
+ int ret = BT_STATUS_SUCCESS;
+ ENTER();
+ bt_root_proc_init();
+ if (ret)
+ goto done;
+
+ /** create char device class */
+ chardev_class = class_create(THIS_MODULE, MODULE_NAME);
+ if (IS_ERR(chardev_class)) {
+ PRINTM(ERROR, "Unable to allocate class\n");
+ bt_root_proc_remove();
+ ret = PTR_ERR(chardev_class);
+ goto done;
+ }
+
+ bt_add_dev();
+ if (sbi_register() == NULL) {
+ ret = BT_STATUS_FAILURE;
+ goto done;
+ }
+done:
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function cleans module
+ *
+ * @return N/A
+ */
+static void
+bt_exit_module(void)
+{
+ ENTER();
+ printk(KERN_ERR "+++++ enter func bt_exit_module() +++++\n");
+
+ sbi_unregister();
+
+ bt_root_proc_remove();
+ bt_del_dev();
+ class_destroy(chardev_class);
+ LEAVE();
+}
+
+module_init(bt_init_module);
+module_exit(bt_exit_module);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+module_param(fw, int, 1);
+MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware");
+module_param(psmode, int, 1);
+MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode");
+#ifdef DEBUG_LEVEL1
+module_param(mbt_drvdbg, uint, 0);
+MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL");
+#endif
+#ifdef SDIO_SUSPEND_RESUME
+module_param(mbt_pm_keep_power, int, 1);
+MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power");
+#endif
+module_param(init_cfg, charp, 0);
+MODULE_PARM_DESC(init_cfg, "BT init config file name");
+module_param(cal_cfg, charp, 0);
+MODULE_PARM_DESC(cal_cfg, "BT calibrate file name");
+module_param(bt_mac, charp, 0);
+MODULE_PARM_DESC(bt_mac, "BT init mac address");
+module_param(minicard_pwrup, int, 0);
+MODULE_PARM_DESC(minicard_pwrup,
+ "1: Driver load clears PDn/Rst, unload sets (default); 0: Don't do this.");
+module_param(drv_mode, int, 0);
+MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE; Bit 1: FM; Bit 2: NFC");
+module_param(bt_name, charp, 0);
+MODULE_PARM_DESC(bt_name, "BT interface name");
+module_param(fm_name, charp, 0);
+MODULE_PARM_DESC(fm_name, "FM interface name");
+module_param(nfc_name, charp, 0);
+MODULE_PARM_DESC(nfc_name, "NFC interface name");
+module_param(debug_intf, int, 1);
+MODULE_PARM_DESC(debug_intf,
+ "1: Enable debug interface; 0: Disable debug interface ");
+module_param(debug_name, charp, 0);
+MODULE_PARM_DESC(debug_name, "Debug interface name");