162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell Bluetooth driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2009, Marvell International Ltd. 662306a36Sopenharmony_ci **/ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 1162306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h> 1262306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "btmrvl_drv.h" 1562306a36Sopenharmony_ci#include "btmrvl_sdio.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define VERSION "1.0" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * This function is called by interface specific interrupt handler. 2162306a36Sopenharmony_ci * It updates Power Save & Host Sleep states, and wakes up the main 2262306a36Sopenharmony_ci * thread. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_civoid btmrvl_interrupt(struct btmrvl_private *priv) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci priv->adapter->ps_state = PS_AWAKE; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci priv->adapter->wakeup_tries = 0; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci priv->adapter->int_count++; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (priv->adapter->hs_state == HS_ACTIVATED) { 3362306a36Sopenharmony_ci BT_DBG("BT: HS DEACTIVATED in ISR!"); 3462306a36Sopenharmony_ci priv->adapter->hs_state = HS_DEACTIVATED; 3562306a36Sopenharmony_ci } 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci wake_up_interruptible(&priv->main_thread.wait_q); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_interrupt); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cibool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct hci_event_hdr *hdr = (void *) skb->data; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (hdr->evt == HCI_EV_CMD_COMPLETE) { 4662306a36Sopenharmony_ci struct hci_ev_cmd_complete *ec; 4762306a36Sopenharmony_ci u16 opcode; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci ec = (void *) (skb->data + HCI_EVENT_HDR_SIZE); 5062306a36Sopenharmony_ci opcode = __le16_to_cpu(ec->opcode); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (priv->btmrvl_dev.sendcmdflag) { 5362306a36Sopenharmony_ci priv->btmrvl_dev.sendcmdflag = false; 5462306a36Sopenharmony_ci priv->adapter->cmd_complete = true; 5562306a36Sopenharmony_ci wake_up_interruptible(&priv->adapter->cmd_wait_q); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (hci_opcode_ogf(opcode) == 0x3F) { 5862306a36Sopenharmony_ci BT_DBG("vendor event skipped: opcode=%#4.4x", 5962306a36Sopenharmony_ci opcode); 6062306a36Sopenharmony_ci kfree_skb(skb); 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return true; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_check_evtpkt); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct btmrvl_adapter *adapter = priv->adapter; 7362306a36Sopenharmony_ci struct btmrvl_event *event; 7462306a36Sopenharmony_ci int ret = 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci event = (struct btmrvl_event *) skb->data; 7762306a36Sopenharmony_ci if (event->ec != 0xff) { 7862306a36Sopenharmony_ci BT_DBG("Not Marvell Event=%x", event->ec); 7962306a36Sopenharmony_ci ret = -EINVAL; 8062306a36Sopenharmony_ci goto exit; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci switch (event->data[0]) { 8462306a36Sopenharmony_ci case BT_EVENT_AUTO_SLEEP_MODE: 8562306a36Sopenharmony_ci if (!event->data[2]) { 8662306a36Sopenharmony_ci if (event->data[1] == BT_PS_ENABLE) 8762306a36Sopenharmony_ci adapter->psmode = 1; 8862306a36Sopenharmony_ci else 8962306a36Sopenharmony_ci adapter->psmode = 0; 9062306a36Sopenharmony_ci BT_DBG("PS Mode:%s", 9162306a36Sopenharmony_ci (adapter->psmode) ? "Enable" : "Disable"); 9262306a36Sopenharmony_ci } else { 9362306a36Sopenharmony_ci BT_DBG("PS Mode command failed"); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci case BT_EVENT_HOST_SLEEP_CONFIG: 9862306a36Sopenharmony_ci if (!event->data[3]) 9962306a36Sopenharmony_ci BT_DBG("gpio=%x, gap=%x", event->data[1], 10062306a36Sopenharmony_ci event->data[2]); 10162306a36Sopenharmony_ci else 10262306a36Sopenharmony_ci BT_DBG("HSCFG command failed"); 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci case BT_EVENT_HOST_SLEEP_ENABLE: 10662306a36Sopenharmony_ci if (!event->data[1]) { 10762306a36Sopenharmony_ci adapter->hs_state = HS_ACTIVATED; 10862306a36Sopenharmony_ci if (adapter->psmode) 10962306a36Sopenharmony_ci adapter->ps_state = PS_SLEEP; 11062306a36Sopenharmony_ci wake_up_interruptible(&adapter->event_hs_wait_q); 11162306a36Sopenharmony_ci BT_DBG("HS ACTIVATED!"); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci BT_DBG("HS Enable failed"); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci break; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci case BT_EVENT_MODULE_CFG_REQ: 11862306a36Sopenharmony_ci if (priv->btmrvl_dev.sendcmdflag && 11962306a36Sopenharmony_ci event->data[1] == MODULE_BRINGUP_REQ) { 12062306a36Sopenharmony_ci BT_DBG("EVENT:%s", 12162306a36Sopenharmony_ci ((event->data[2] == MODULE_BROUGHT_UP) || 12262306a36Sopenharmony_ci (event->data[2] == MODULE_ALREADY_UP)) ? 12362306a36Sopenharmony_ci "Bring-up succeed" : "Bring-up failed"); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (event->length > 3 && event->data[3]) 12662306a36Sopenharmony_ci priv->btmrvl_dev.dev_type = HCI_AMP; 12762306a36Sopenharmony_ci else 12862306a36Sopenharmony_ci priv->btmrvl_dev.dev_type = HCI_PRIMARY; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci BT_DBG("dev_type: %d", priv->btmrvl_dev.dev_type); 13162306a36Sopenharmony_ci } else if (priv->btmrvl_dev.sendcmdflag && 13262306a36Sopenharmony_ci event->data[1] == MODULE_SHUTDOWN_REQ) { 13362306a36Sopenharmony_ci BT_DBG("EVENT:%s", (event->data[2]) ? 13462306a36Sopenharmony_ci "Shutdown failed" : "Shutdown succeed"); 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci BT_DBG("BT_CMD_MODULE_CFG_REQ resp for APP"); 13762306a36Sopenharmony_ci ret = -EINVAL; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci case BT_EVENT_POWER_STATE: 14262306a36Sopenharmony_ci if (event->data[1] == BT_PS_SLEEP) 14362306a36Sopenharmony_ci adapter->ps_state = PS_SLEEP; 14462306a36Sopenharmony_ci BT_DBG("EVENT:%s", 14562306a36Sopenharmony_ci (adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci default: 14962306a36Sopenharmony_ci BT_DBG("Unknown Event=%d", event->data[0]); 15062306a36Sopenharmony_ci ret = -EINVAL; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciexit: 15562306a36Sopenharmony_ci if (!ret) 15662306a36Sopenharmony_ci kfree_skb(skb); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_process_event); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, 16362306a36Sopenharmony_ci const void *param, u8 len) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct sk_buff *skb; 16662306a36Sopenharmony_ci struct hci_command_hdr *hdr; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (priv->surprise_removed) { 16962306a36Sopenharmony_ci BT_ERR("Card is removed"); 17062306a36Sopenharmony_ci return -EFAULT; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_KERNEL); 17462306a36Sopenharmony_ci if (!skb) { 17562306a36Sopenharmony_ci BT_ERR("No free skb"); 17662306a36Sopenharmony_ci return -ENOMEM; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci hdr = skb_put(skb, HCI_COMMAND_HDR_SIZE); 18062306a36Sopenharmony_ci hdr->opcode = cpu_to_le16(opcode); 18162306a36Sopenharmony_ci hdr->plen = len; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (len) 18462306a36Sopenharmony_ci skb_put_data(skb, param, len); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci hci_skb_pkt_type(skb) = MRVL_VENDOR_PKT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci skb_queue_head(&priv->adapter->tx_queue, skb); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci priv->btmrvl_dev.sendcmdflag = true; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci priv->adapter->cmd_complete = false; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci wake_up_interruptible(&priv->main_thread.wait_q); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, 19762306a36Sopenharmony_ci priv->adapter->cmd_complete || 19862306a36Sopenharmony_ci priv->surprise_removed, 19962306a36Sopenharmony_ci WAIT_UNTIL_CMD_RESP)) 20062306a36Sopenharmony_ci return -ETIMEDOUT; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (priv->surprise_removed) 20362306a36Sopenharmony_ci return -EFAULT; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return 0; 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciint btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int ret; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci BT_ERR("module_cfg_cmd(%x) failed", subcmd); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int btmrvl_enable_sco_routing_to_host(struct btmrvl_private *priv) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci u8 subcmd = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_ROUTE_SCO_TO_HOST, &subcmd, 1); 22662306a36Sopenharmony_ci if (ret) 22762306a36Sopenharmony_ci BT_ERR("BT_CMD_ROUTE_SCO_TO_HOST command failed: %#x", ret); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ciint btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 23562306a36Sopenharmony_ci int ret; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!card->support_pscan_win_report) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_PSCAN_WIN_REPORT_ENABLE, 24162306a36Sopenharmony_ci &subcmd, 1); 24262306a36Sopenharmony_ci if (ret) 24362306a36Sopenharmony_ci BT_ERR("PSCAN_WIN_REPORT_ENABLE command failed: %#x", ret); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return ret; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_pscan_window_reporting); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ciint btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int ret; 25262306a36Sopenharmony_ci u8 param[2]; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; 25562306a36Sopenharmony_ci param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x", 25862306a36Sopenharmony_ci param[0], param[1]); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci BT_ERR("HSCFG command failed"); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ciint btmrvl_enable_ps(struct btmrvl_private *priv) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci int ret; 27162306a36Sopenharmony_ci u8 param; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (priv->btmrvl_dev.psmode) 27462306a36Sopenharmony_ci param = BT_PS_ENABLE; 27562306a36Sopenharmony_ci else 27662306a36Sopenharmony_ci param = BT_PS_DISABLE; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); 27962306a36Sopenharmony_ci if (ret) 28062306a36Sopenharmony_ci BT_ERR("PSMODE command failed"); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_enable_ps); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ciint btmrvl_enable_hs(struct btmrvl_private *priv) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct btmrvl_adapter *adapter = priv->adapter; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); 29262306a36Sopenharmony_ci if (ret) { 29362306a36Sopenharmony_ci BT_ERR("Host sleep enable command failed"); 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, 29862306a36Sopenharmony_ci adapter->hs_state || 29962306a36Sopenharmony_ci priv->surprise_removed, 30062306a36Sopenharmony_ci WAIT_UNTIL_HS_STATE_CHANGED); 30162306a36Sopenharmony_ci if (ret < 0 || priv->surprise_removed) { 30262306a36Sopenharmony_ci BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", 30362306a36Sopenharmony_ci ret, adapter->hs_state, adapter->ps_state, 30462306a36Sopenharmony_ci adapter->wakeup_tries); 30562306a36Sopenharmony_ci } else if (!ret) { 30662306a36Sopenharmony_ci BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state, 30762306a36Sopenharmony_ci adapter->ps_state, adapter->wakeup_tries); 30862306a36Sopenharmony_ci ret = -ETIMEDOUT; 30962306a36Sopenharmony_ci } else { 31062306a36Sopenharmony_ci BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state, 31162306a36Sopenharmony_ci adapter->ps_state, adapter->wakeup_tries); 31262306a36Sopenharmony_ci ret = 0; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_enable_hs); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciint btmrvl_prepare_command(struct btmrvl_private *priv) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int ret = 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (priv->btmrvl_dev.hscfgcmd) { 32462306a36Sopenharmony_ci priv->btmrvl_dev.hscfgcmd = 0; 32562306a36Sopenharmony_ci btmrvl_send_hscfg_cmd(priv); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (priv->btmrvl_dev.pscmd) { 32962306a36Sopenharmony_ci priv->btmrvl_dev.pscmd = 0; 33062306a36Sopenharmony_ci btmrvl_enable_ps(priv); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (priv->btmrvl_dev.hscmd) { 33462306a36Sopenharmony_ci priv->btmrvl_dev.hscmd = 0; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (priv->btmrvl_dev.hsmode) { 33762306a36Sopenharmony_ci ret = btmrvl_enable_hs(priv); 33862306a36Sopenharmony_ci } else { 33962306a36Sopenharmony_ci ret = priv->hw_wakeup_firmware(priv); 34062306a36Sopenharmony_ci priv->adapter->hs_state = HS_DEACTIVATED; 34162306a36Sopenharmony_ci BT_DBG("BT: HS DEACTIVATED due to host activity!"); 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return ret; 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic int btmrvl_tx_pkt(struct btmrvl_private *priv, struct sk_buff *skb) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci int ret = 0; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!skb || !skb->data) 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!skb->len || ((skb->len + BTM_HEADER_LEN) > BTM_UPLD_SIZE)) { 35662306a36Sopenharmony_ci BT_ERR("Tx Error: Bad skb length %d : %d", 35762306a36Sopenharmony_ci skb->len, BTM_UPLD_SIZE); 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci skb_push(skb, BTM_HEADER_LEN); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* header type: byte[3] 36462306a36Sopenharmony_ci * HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor 36562306a36Sopenharmony_ci * header length: byte[2][1][0] 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci skb->data[0] = (skb->len & 0x0000ff); 36962306a36Sopenharmony_ci skb->data[1] = (skb->len & 0x00ff00) >> 8; 37062306a36Sopenharmony_ci skb->data[2] = (skb->len & 0xff0000) >> 16; 37162306a36Sopenharmony_ci skb->data[3] = hci_skb_pkt_type(skb); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (priv->hw_host_to_card) 37462306a36Sopenharmony_ci ret = priv->hw_host_to_card(priv, skb->data, skb->len); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void btmrvl_init_adapter(struct btmrvl_private *priv) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int buf_size; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci skb_queue_head_init(&priv->adapter->tx_queue); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci priv->adapter->ps_state = PS_AWAKE; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci buf_size = ALIGN_SZ(SDIO_BLOCK_SIZE, BTSDIO_DMA_ALIGN); 38862306a36Sopenharmony_ci priv->adapter->hw_regs_buf = kzalloc(buf_size, GFP_KERNEL); 38962306a36Sopenharmony_ci if (!priv->adapter->hw_regs_buf) { 39062306a36Sopenharmony_ci priv->adapter->hw_regs = NULL; 39162306a36Sopenharmony_ci BT_ERR("Unable to allocate buffer for hw_regs."); 39262306a36Sopenharmony_ci } else { 39362306a36Sopenharmony_ci priv->adapter->hw_regs = 39462306a36Sopenharmony_ci (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, 39562306a36Sopenharmony_ci BTSDIO_DMA_ALIGN); 39662306a36Sopenharmony_ci BT_DBG("hw_regs_buf=%p hw_regs=%p", 39762306a36Sopenharmony_ci priv->adapter->hw_regs_buf, priv->adapter->hw_regs); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci init_waitqueue_head(&priv->adapter->cmd_wait_q); 40162306a36Sopenharmony_ci init_waitqueue_head(&priv->adapter->event_hs_wait_q); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void btmrvl_free_adapter(struct btmrvl_private *priv) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci skb_queue_purge(&priv->adapter->tx_queue); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci kfree(priv->adapter->hw_regs_buf); 40962306a36Sopenharmony_ci kfree(priv->adapter); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci priv->adapter = NULL; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct btmrvl_private *priv = hci_get_drvdata(hdev); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci BT_DBG("type=%d, len=%d", hci_skb_pkt_type(skb), skb->len); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (priv->adapter->is_suspending || priv->adapter->is_suspended) { 42162306a36Sopenharmony_ci BT_ERR("%s: Device is suspending or suspended", __func__); 42262306a36Sopenharmony_ci return -EBUSY; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci switch (hci_skb_pkt_type(skb)) { 42662306a36Sopenharmony_ci case HCI_COMMAND_PKT: 42762306a36Sopenharmony_ci hdev->stat.cmd_tx++; 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci case HCI_ACLDATA_PKT: 43162306a36Sopenharmony_ci hdev->stat.acl_tx++; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci case HCI_SCODATA_PKT: 43562306a36Sopenharmony_ci hdev->stat.sco_tx++; 43662306a36Sopenharmony_ci break; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci skb_queue_tail(&priv->adapter->tx_queue, skb); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (!priv->adapter->is_suspended) 44262306a36Sopenharmony_ci wake_up_interruptible(&priv->main_thread.wait_q); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return 0; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int btmrvl_flush(struct hci_dev *hdev) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct btmrvl_private *priv = hci_get_drvdata(hdev); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci skb_queue_purge(&priv->adapter->tx_queue); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci return 0; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int btmrvl_close(struct hci_dev *hdev) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct btmrvl_private *priv = hci_get_drvdata(hdev); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci skb_queue_purge(&priv->adapter->tx_queue); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int btmrvl_open(struct hci_dev *hdev) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic int btmrvl_download_cal_data(struct btmrvl_private *priv, 47162306a36Sopenharmony_ci u8 *data, int len) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci data[0] = 0x00; 47662306a36Sopenharmony_ci data[1] = 0x00; 47762306a36Sopenharmony_ci data[2] = 0x00; 47862306a36Sopenharmony_ci data[3] = len; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci print_hex_dump_bytes("Calibration data: ", 48162306a36Sopenharmony_ci DUMP_PREFIX_OFFSET, data, BT_CAL_HDR_LEN + len); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, 48462306a36Sopenharmony_ci BT_CAL_HDR_LEN + len); 48562306a36Sopenharmony_ci if (ret) 48662306a36Sopenharmony_ci BT_ERR("Failed to download calibration data"); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int btmrvl_check_device_tree(struct btmrvl_private *priv) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct device_node *dt_node; 49462306a36Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 49562306a36Sopenharmony_ci u8 cal_data[BT_CAL_HDR_LEN + BT_CAL_DATA_SIZE]; 49662306a36Sopenharmony_ci int ret = 0; 49762306a36Sopenharmony_ci u16 gpio, gap; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (card->plt_of_node) { 50062306a36Sopenharmony_ci dt_node = card->plt_of_node; 50162306a36Sopenharmony_ci ret = of_property_read_u16(dt_node, "marvell,wakeup-pin", 50262306a36Sopenharmony_ci &gpio); 50362306a36Sopenharmony_ci if (ret) 50462306a36Sopenharmony_ci gpio = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ret = of_property_read_u16(dt_node, "marvell,wakeup-gap-ms", 50762306a36Sopenharmony_ci &gap); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci gap = (u8)(priv->btmrvl_dev.gpio_gap & 0x00ff); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci priv->btmrvl_dev.gpio_gap = (gpio << 8) + gap; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = of_property_read_u8_array(dt_node, "marvell,cal-data", 51462306a36Sopenharmony_ci cal_data + BT_CAL_HDR_LEN, 51562306a36Sopenharmony_ci BT_CAL_DATA_SIZE); 51662306a36Sopenharmony_ci if (ret) 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci BT_DBG("Use cal data from device tree"); 52062306a36Sopenharmony_ci ret = btmrvl_download_cal_data(priv, cal_data, 52162306a36Sopenharmony_ci BT_CAL_DATA_SIZE); 52262306a36Sopenharmony_ci if (ret) 52362306a36Sopenharmony_ci BT_ERR("Fail to download calibrate data"); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int btmrvl_setup(struct hci_dev *hdev) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct btmrvl_private *priv = hci_get_drvdata(hdev); 53262306a36Sopenharmony_ci int ret; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); 53562306a36Sopenharmony_ci if (ret) 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci priv->btmrvl_dev.gpio_gap = 0xfffe; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci btmrvl_check_device_tree(priv); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci btmrvl_enable_sco_routing_to_host(priv); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci btmrvl_pscan_window_reporting(priv, 0x01); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci priv->btmrvl_dev.psmode = 1; 54762306a36Sopenharmony_ci btmrvl_enable_ps(priv); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci btmrvl_send_hscfg_cmd(priv); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci return 0; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int btmrvl_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct sk_buff *skb; 55762306a36Sopenharmony_ci long ret; 55862306a36Sopenharmony_ci u8 buf[8]; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci buf[0] = MRVL_VENDOR_PKT; 56162306a36Sopenharmony_ci buf[1] = sizeof(bdaddr_t); 56262306a36Sopenharmony_ci memcpy(buf + 2, bdaddr, sizeof(bdaddr_t)); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci skb = __hci_cmd_sync(hdev, BT_CMD_SET_BDADDR, sizeof(buf), buf, 56562306a36Sopenharmony_ci HCI_INIT_TIMEOUT); 56662306a36Sopenharmony_ci if (IS_ERR(skb)) { 56762306a36Sopenharmony_ci ret = PTR_ERR(skb); 56862306a36Sopenharmony_ci BT_ERR("%s: changing btmrvl device address failed (%ld)", 56962306a36Sopenharmony_ci hdev->name, ret); 57062306a36Sopenharmony_ci return ret; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci kfree_skb(skb); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic bool btmrvl_wakeup(struct hci_dev *hdev) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct btmrvl_private *priv = hci_get_drvdata(hdev); 58062306a36Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return device_may_wakeup(&card->func->dev); 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci/* 58662306a36Sopenharmony_ci * This function handles the event generated by firmware, rx data 58762306a36Sopenharmony_ci * received from firmware, and tx data sent from kernel. 58862306a36Sopenharmony_ci */ 58962306a36Sopenharmony_cistatic int btmrvl_service_main_thread(void *data) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct btmrvl_thread *thread = data; 59262306a36Sopenharmony_ci struct btmrvl_private *priv = thread->priv; 59362306a36Sopenharmony_ci struct btmrvl_adapter *adapter = priv->adapter; 59462306a36Sopenharmony_ci wait_queue_entry_t wait; 59562306a36Sopenharmony_ci struct sk_buff *skb; 59662306a36Sopenharmony_ci ulong flags; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci init_waitqueue_entry(&wait, current); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci for (;;) { 60162306a36Sopenharmony_ci add_wait_queue(&thread->wait_q, &wait); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 60462306a36Sopenharmony_ci if (kthread_should_stop() || priv->surprise_removed) { 60562306a36Sopenharmony_ci BT_DBG("main_thread: break from main thread"); 60662306a36Sopenharmony_ci break; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (adapter->wakeup_tries || 61062306a36Sopenharmony_ci ((!adapter->int_count) && 61162306a36Sopenharmony_ci (!priv->btmrvl_dev.tx_dnld_rdy || 61262306a36Sopenharmony_ci skb_queue_empty(&adapter->tx_queue)))) { 61362306a36Sopenharmony_ci BT_DBG("main_thread is sleeping..."); 61462306a36Sopenharmony_ci schedule(); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci set_current_state(TASK_RUNNING); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci remove_wait_queue(&thread->wait_q, &wait); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci BT_DBG("main_thread woke up"); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (kthread_should_stop() || priv->surprise_removed) { 62462306a36Sopenharmony_ci BT_DBG("main_thread: break from main thread"); 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci spin_lock_irqsave(&priv->driver_lock, flags); 62962306a36Sopenharmony_ci if (adapter->int_count) { 63062306a36Sopenharmony_ci adapter->int_count = 0; 63162306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 63262306a36Sopenharmony_ci priv->hw_process_int_status(priv); 63362306a36Sopenharmony_ci } else if (adapter->ps_state == PS_SLEEP && 63462306a36Sopenharmony_ci !skb_queue_empty(&adapter->tx_queue)) { 63562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 63662306a36Sopenharmony_ci adapter->wakeup_tries++; 63762306a36Sopenharmony_ci priv->hw_wakeup_firmware(priv); 63862306a36Sopenharmony_ci continue; 63962306a36Sopenharmony_ci } else { 64062306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->driver_lock, flags); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (adapter->ps_state == PS_SLEEP) 64462306a36Sopenharmony_ci continue; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (!priv->btmrvl_dev.tx_dnld_rdy || 64762306a36Sopenharmony_ci priv->adapter->is_suspended) 64862306a36Sopenharmony_ci continue; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci skb = skb_dequeue(&adapter->tx_queue); 65162306a36Sopenharmony_ci if (skb) { 65262306a36Sopenharmony_ci if (btmrvl_tx_pkt(priv, skb)) 65362306a36Sopenharmony_ci priv->btmrvl_dev.hcidev->stat.err_tx++; 65462306a36Sopenharmony_ci else 65562306a36Sopenharmony_ci priv->btmrvl_dev.hcidev->stat.byte_tx += skb->len; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci kfree_skb(skb); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci return 0; 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ciint btmrvl_register_hdev(struct btmrvl_private *priv) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci struct hci_dev *hdev = NULL; 66762306a36Sopenharmony_ci struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; 66862306a36Sopenharmony_ci int ret; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci hdev = hci_alloc_dev(); 67162306a36Sopenharmony_ci if (!hdev) { 67262306a36Sopenharmony_ci BT_ERR("Can not allocate HCI device"); 67362306a36Sopenharmony_ci goto err_hdev; 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci priv->btmrvl_dev.hcidev = hdev; 67762306a36Sopenharmony_ci hci_set_drvdata(hdev, priv); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci hdev->bus = HCI_SDIO; 68062306a36Sopenharmony_ci hdev->open = btmrvl_open; 68162306a36Sopenharmony_ci hdev->close = btmrvl_close; 68262306a36Sopenharmony_ci hdev->flush = btmrvl_flush; 68362306a36Sopenharmony_ci hdev->send = btmrvl_send_frame; 68462306a36Sopenharmony_ci hdev->setup = btmrvl_setup; 68562306a36Sopenharmony_ci hdev->set_bdaddr = btmrvl_set_bdaddr; 68662306a36Sopenharmony_ci hdev->wakeup = btmrvl_wakeup; 68762306a36Sopenharmony_ci SET_HCIDEV_DEV(hdev, &card->func->dev); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci hdev->dev_type = priv->btmrvl_dev.dev_type; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci ret = hci_register_dev(hdev); 69262306a36Sopenharmony_ci if (ret < 0) { 69362306a36Sopenharmony_ci BT_ERR("Can not register HCI device"); 69462306a36Sopenharmony_ci goto err_hci_register_dev; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 69862306a36Sopenharmony_ci btmrvl_debugfs_init(hdev); 69962306a36Sopenharmony_ci#endif 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cierr_hci_register_dev: 70462306a36Sopenharmony_ci hci_free_dev(hdev); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cierr_hdev: 70762306a36Sopenharmony_ci /* Stop the thread servicing the interrupts */ 70862306a36Sopenharmony_ci kthread_stop(priv->main_thread.task); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci btmrvl_free_adapter(priv); 71162306a36Sopenharmony_ci kfree(priv); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return -ENOMEM; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_register_hdev); 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistruct btmrvl_private *btmrvl_add_card(void *card) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct btmrvl_private *priv; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 72262306a36Sopenharmony_ci if (!priv) { 72362306a36Sopenharmony_ci BT_ERR("Can not allocate priv"); 72462306a36Sopenharmony_ci goto err_priv; 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci priv->adapter = kzalloc(sizeof(*priv->adapter), GFP_KERNEL); 72862306a36Sopenharmony_ci if (!priv->adapter) { 72962306a36Sopenharmony_ci BT_ERR("Allocate buffer for btmrvl_adapter failed!"); 73062306a36Sopenharmony_ci goto err_adapter; 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci btmrvl_init_adapter(priv); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci BT_DBG("Starting kthread..."); 73662306a36Sopenharmony_ci priv->main_thread.priv = priv; 73762306a36Sopenharmony_ci spin_lock_init(&priv->driver_lock); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci init_waitqueue_head(&priv->main_thread.wait_q); 74062306a36Sopenharmony_ci priv->main_thread.task = kthread_run(btmrvl_service_main_thread, 74162306a36Sopenharmony_ci &priv->main_thread, "btmrvl_main_service"); 74262306a36Sopenharmony_ci if (IS_ERR(priv->main_thread.task)) 74362306a36Sopenharmony_ci goto err_thread; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci priv->btmrvl_dev.card = card; 74662306a36Sopenharmony_ci priv->btmrvl_dev.tx_dnld_rdy = true; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return priv; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cierr_thread: 75162306a36Sopenharmony_ci btmrvl_free_adapter(priv); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cierr_adapter: 75462306a36Sopenharmony_ci kfree(priv); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cierr_priv: 75762306a36Sopenharmony_ci return NULL; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_add_card); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ciint btmrvl_remove_card(struct btmrvl_private *priv) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct hci_dev *hdev; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci hdev = priv->btmrvl_dev.hcidev; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci wake_up_interruptible(&priv->adapter->cmd_wait_q); 76862306a36Sopenharmony_ci wake_up_interruptible(&priv->adapter->event_hs_wait_q); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci kthread_stop(priv->main_thread.task); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 77362306a36Sopenharmony_ci btmrvl_debugfs_remove(hdev); 77462306a36Sopenharmony_ci#endif 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci hci_unregister_dev(hdev); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci hci_free_dev(hdev); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci priv->btmrvl_dev.hcidev = NULL; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci btmrvl_free_adapter(priv); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci kfree(priv); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci return 0; 78762306a36Sopenharmony_ci} 78862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(btmrvl_remove_card); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 79162306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell Bluetooth driver ver " VERSION); 79262306a36Sopenharmony_ciMODULE_VERSION(VERSION); 79362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 794