From cec2a9de58044b9e212cb2540c006aa93e44641c Mon Sep 17 00:00:00 2001 From: Asriel Dreemurr Date: Sun, 14 Jul 2019 19:30:38 +0700 Subject: [PATCH] linux-xiaomi-santoni: add hci smd for bluetooth support (!420) Signed-off-by: Danct12 --- device/linux-xiaomi-santoni/APKBUILD | 4 +- device/linux-xiaomi-santoni/bt-hci-smd.patch | 758 ++++++++++++++++++ .../config-xiaomi-santoni.aarch64 | 1 + 3 files changed, 762 insertions(+), 1 deletion(-) create mode 100644 device/linux-xiaomi-santoni/bt-hci-smd.patch diff --git a/device/linux-xiaomi-santoni/APKBUILD b/device/linux-xiaomi-santoni/APKBUILD index a72e5fb98..69dc45561 100644 --- a/device/linux-xiaomi-santoni/APKBUILD +++ b/device/linux-xiaomi-santoni/APKBUILD @@ -23,6 +23,7 @@ source=" $pkgname-$_commit.tar.gz::https://github.com/bitrvmpd/${_repository}/archive/${_commit}.tar.gz fix-xorg-mdss-update-fb-info.patch disable-substream-runtime-errors.patch + bt-hci-smd.patch $_config " builddir="$srcdir/${_repository}-${_commit}" @@ -73,4 +74,5 @@ package() { sha512sums="d7a290e676aa40d35289cedbad5dd077accff4e1a2467223765a79f101ff4c1313315cbffd574ad74c648c4b8f5751d16a1fa1607c9939b373d3f2974a7b2326 linux-xiaomi-santoni-2dd8017f40290eb326e352cca254cd8bdd1614c9.tar.gz a277af28a718f5caf94257005393aa92d62c96110a8ec6368ff62f2e0d871b69c56c4ce00174db637204d53fa63ad3a79b8d6141713dca0878e1f06fe0379caa fix-xorg-mdss-update-fb-info.patch 14ee08e4f93befeaabfb35e6bf0b8bb5b2eecba1c21ce4498499c377c2cf4d5fccbfe85e14ca92c704d067386ec08a06805ab886f0ec7691471cf379838545aa disable-substream-runtime-errors.patch -685668bb733c485167c5944aec6c749904933312f6a930f27ee279bdea2c7ddfc4fc46ed9cd3cf6469fc3ef798ab832bda35f86a8befff4bade85b6de6de9332 config-xiaomi-santoni.aarch64" +f5850f712ce7aa42ada1971fc5f428ac8b9f5eef77d8bfb09bb24e0636e0b70ae78009466ed4962bd257721126577a756114e2ee2a70d4a925dd57e0ec7bcfa6 bt-hci-smd.patch +753b1517980a6aa4bc1ccea6d013cb5cf74cd85a92a279a5fff2f13f0930a9266305d9efdd06ce4fe7baf76eb47118fe95b855a458e0457d91041da9b8ef0897 config-xiaomi-santoni.aarch64" diff --git a/device/linux-xiaomi-santoni/bt-hci-smd.patch b/device/linux-xiaomi-santoni/bt-hci-smd.patch new file mode 100644 index 000000000..6673aebca --- /dev/null +++ b/device/linux-xiaomi-santoni/bt-hci-smd.patch @@ -0,0 +1,758 @@ +From 08188f95f81aa4c716184eb40e08873e810ad089 Mon Sep 17 00:00:00 2001 +From: Adam Pigg +Date: Sat, 1 Jul 2017 09:04:43 +0100 +Subject: [PATCH] BLUETOOTH: Add and port the BT HCI SMD driver + +Change-Id: I19e3cd2c7043d192590e4f81af6f8a18c9513d5e +--- + drivers/bluetooth/Kconfig | 10 + + drivers/bluetooth/Makefile | 1 + + drivers/bluetooth/hci_smd.c | 678 +++++++++++++++++++++++++++++++ + include/net/bluetooth/hci.h | 1 + + include/net/bluetooth/hci_core.h | 3 + + 5 files changed, 693 insertions(+) + create mode 100644 drivers/bluetooth/hci_smd.c + +diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig +index d3d007014679..95903015e45e 100644 +--- a/drivers/bluetooth/Kconfig ++++ b/drivers/bluetooth/Kconfig +@@ -2,6 +2,16 @@ + menu "Bluetooth device drivers" + depends on BT + ++config BT_HCISMD ++ tristate "HCI SMD driver" ++ help ++ Bluetooth HCI SMD driver. ++ This driver is required if you want to use Bluetoth device with ++ SMD interface. ++ ++ Say Y here to compile support for Bluetooth USB devices into the ++ kernel or say M to compile is as a module (hci_smd). ++ + config BT_HCIBTUSB + tristate "HCI USB driver" + depends on USB +diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile +index d2e424bd273d..008ba6830e43 100644 +--- a/drivers/bluetooth/Makefile ++++ b/drivers/bluetooth/Makefile +@@ -2,6 +2,7 @@ + # Makefile for the Linux Bluetooth HCI device drivers. + # + ++obj-$(CONFIG_BT_HCISMD) += hci_smd.o + obj-$(CONFIG_BT_HCIVHCI) += hci_vhci.o + obj-$(CONFIG_BT_HCIUART) += hci_uart.o + obj-$(CONFIG_BT_HCIBCM203X) += bcm203x.o +diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c +new file mode 100644 +index 000000000000..8e9a7505ff1f +--- /dev/null ++++ b/drivers/bluetooth/hci_smd.c +@@ -0,0 +1,678 @@ ++/* ++ * HCI_SMD (HCI Shared Memory Driver) is Qualcomm's Shared memory driver ++ * for the BT HCI protocol. ++ * ++ * Copyright (c) 2000-2001, 2011-2012 The Linux Foundation. All rights reserved. ++ * Copyright (C) 2002-2003 Maxim Krasnyansky ++ * Copyright (C) 2004-2006 Marcel Holtmann ++ * ++ * This file is based on drivers/bluetooth/hci_vhci.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 ++ * as published by the Free Software Foundation ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define EVENT_CHANNEL "APPS_RIVA_BT_CMD" ++#define DATA_CHANNEL "APPS_RIVA_BT_ACL" ++/* release wakelock in 500ms, not immediately, because higher layers ++ * don't always take wakelocks when they should ++ * This is derived from the implementation for UART transport ++ */ ++ ++#define RX_Q_MONITOR (500) /* 500 milli second */ ++#define HCI_REGISTER_SET 0 ++ ++/* SSR state machine to take care of back to back SSR requests ++ * and handling the incomming BT on/off,Airplane mode toggling and ++ * also spuriour SMD open notification while one SSr is in progress ++ */ ++#define STATE_SSR_ON 0x1 ++#define STATE_SSR_START 0x02 ++#define STATE_SSR_CHANNEL_OPEN_PENDING 0x04 ++#define STATE_SSR_PENDING_INIT 0x08 ++#define STATE_SSR_COMPLETE 0x00 ++#define STATE_SSR_OFF STATE_SSR_COMPLETE ++ ++static int ssr_state = STATE_SSR_OFF; ++ ++ ++static int hcismd_set; ++static DEFINE_SEMAPHORE(hci_smd_enable); ++ ++static int restart_in_progress; ++ ++static int hcismd_set_enable(const char *val, struct kernel_param *kp); ++module_param_call(hcismd_set, hcismd_set_enable, NULL, &hcismd_set, 0644); ++ ++static void hci_dev_smd_open(struct work_struct *worker); ++static void hci_dev_restart(struct work_struct *worker); ++ ++struct hci_smd_data { ++ struct hci_dev *hdev; ++ unsigned long flags; ++ struct smd_channel *event_channel; ++ struct smd_channel *data_channel; ++ struct wake_lock wake_lock_tx; ++ struct wake_lock wake_lock_rx; ++ struct timer_list rx_q_timer; ++ struct tasklet_struct rx_task; ++}; ++static struct hci_smd_data hs; ++ ++/* Rx queue monitor timer function */ ++static int is_rx_q_empty(unsigned long arg) ++{ ++ struct hci_dev *hdev = (struct hci_dev *) arg; ++ struct sk_buff_head *list_ = &hdev->rx_q; ++ struct sk_buff *list = ((struct sk_buff *)list_)->next; ++ BT_DBG("%s Rx timer triggered", hdev->name); ++ ++ if (list == (struct sk_buff *)list_) { ++ BT_DBG("%s RX queue empty", hdev->name); ++ return 1; ++ } else{ ++ BT_DBG("%s RX queue not empty", hdev->name); ++ return 0; ++ } ++} ++ ++static void release_lock(void) ++{ ++ struct hci_smd_data *hsmd = &hs; ++ BT_DBG("Releasing Rx Lock"); ++ if (is_rx_q_empty((unsigned long)hsmd->hdev) && ++ wake_lock_active(&hs.wake_lock_rx)) ++ wake_unlock(&hs.wake_lock_rx); ++} ++ ++/* Rx timer callback function */ ++static void schedule_timer(unsigned long arg) ++{ ++ struct hci_dev *hdev = (struct hci_dev *) arg; ++ struct hci_smd_data *hsmd = &hs; ++ BT_DBG("%s Schedule Rx timer", hdev->name); ++ ++ if (is_rx_q_empty(arg) && wake_lock_active(&hs.wake_lock_rx)) { ++ BT_DBG("%s RX queue empty", hdev->name); ++ /* ++ * Since the queue is empty, its ideal ++ * to release the wake lock on Rx ++ */ ++ wake_unlock(&hs.wake_lock_rx); ++ } else{ ++ BT_DBG("%s RX queue not empty", hdev->name); ++ /* ++ * Restart the timer to monitor whether the Rx queue is ++ * empty for releasing the Rx wake lock ++ */ ++ mod_timer(&hsmd->rx_q_timer, ++ jiffies + msecs_to_jiffies(RX_Q_MONITOR)); ++ } ++} ++ ++static int hci_smd_open(struct hci_dev *hdev) ++{ ++ set_bit(HCI_RUNNING, &hdev->flags); ++ return 0; ++} ++ ++ ++static int hci_smd_close(struct hci_dev *hdev) ++{ ++ if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) ++ return 0; ++ else ++ return -EPERM; ++} ++ ++#if 0 ++static void hci_smd_destruct(struct hci_dev *hdev) ++{ ++ if (NULL != hdev->driver_data) ++ kfree(hdev->driver_data); ++} ++#endif ++ ++static void hci_smd_recv_data(void) ++{ ++ int len = 0; ++ int rc = 0; ++ struct sk_buff *skb = NULL; ++ struct hci_smd_data *hsmd = &hs; ++ wake_lock(&hs.wake_lock_rx); ++ ++ len = smd_read_avail(hsmd->data_channel); ++ if (len > HCI_MAX_FRAME_SIZE) { ++ BT_ERR("Frame larger than the allowed size, flushing frame"); ++ smd_read(hsmd->data_channel, NULL, len); ++ goto out_data; ++ } ++ ++ if (len <= 0) ++ goto out_data; ++ ++ skb = bt_skb_alloc(len, GFP_ATOMIC); ++ if (!skb) { ++ BT_ERR("Error in allocating socket buffer"); ++ smd_read(hsmd->data_channel, NULL, len); ++ goto out_data; ++ } ++ ++ rc = smd_read(hsmd->data_channel, skb_put(skb, len), len); ++ if (rc < len) { ++ BT_ERR("Error in reading from the channel"); ++ goto out_data; ++ } ++ ++ skb->dev = (void *)hsmd->hdev; ++ bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; ++ skb_orphan(skb); ++ ++ rc = hci_recv_frame(hsmd->hdev, skb); ++ if (rc < 0) { ++ BT_ERR("Error in passing the packet to HCI Layer"); ++ /* ++ * skb is getting freed in hci_recv_frame, making it ++ * to null to avoid multiple access ++ */ ++ skb = NULL; ++ goto out_data; ++ } ++ ++ /* ++ * Start the timer to monitor whether the Rx queue is ++ * empty for releasing the Rx wake lock ++ */ ++ BT_DBG("Rx Timer is starting"); ++ mod_timer(&hsmd->rx_q_timer, ++ jiffies + msecs_to_jiffies(RX_Q_MONITOR)); ++ ++out_data: ++ release_lock(); ++ if (rc) ++ kfree_skb(skb); ++} ++ ++static void hci_smd_recv_event(void) ++{ ++ int len = 0; ++ int rc = 0; ++ struct sk_buff *skb = NULL; ++ struct hci_smd_data *hsmd = &hs; ++ wake_lock(&hs.wake_lock_rx); ++ ++ len = smd_read_avail(hsmd->event_channel); ++ if (len > HCI_MAX_FRAME_SIZE) { ++ BT_ERR("Frame larger than the allowed size, flushing frame"); ++ rc = smd_read(hsmd->event_channel, NULL, len); ++ goto out_event; ++ } ++ ++ while (len > 0) { ++ skb = bt_skb_alloc(len, GFP_ATOMIC); ++ if (!skb) { ++ BT_ERR("Error in allocating socket buffer"); ++ smd_read(hsmd->event_channel, NULL, len); ++ goto out_event; ++ } ++ ++ rc = smd_read(hsmd->event_channel, skb_put(skb, len), len); ++ if (rc < len) { ++ BT_ERR("Error in reading from the event channel"); ++ goto out_event; ++ } ++ ++ skb->dev = (void *)hsmd->hdev; ++ bt_cb(skb)->pkt_type = HCI_EVENT_PKT; ++ ++ skb_orphan(skb); ++ ++ rc = hci_recv_frame(hsmd->hdev, skb); ++ if (rc < 0) { ++ BT_ERR("Error in passing the packet to HCI Layer"); ++ /* ++ * skb is getting freed in hci_recv_frame, making it ++ * to null to avoid multiple access ++ */ ++ skb = NULL; ++ goto out_event; ++ } ++ ++ len = smd_read_avail(hsmd->event_channel); ++ /* ++ * Start the timer to monitor whether the Rx queue is ++ * empty for releasing the Rx wake lock ++ */ ++ BT_DBG("Rx Timer is starting"); ++ mod_timer(&hsmd->rx_q_timer, ++ jiffies + msecs_to_jiffies(RX_Q_MONITOR)); ++ } ++out_event: ++ release_lock(); ++ if (rc) ++ kfree_skb(skb); ++} ++ ++static int hci_smd_send_frame(struct hci_dev *hdev, struct sk_buff *skb) ++{ ++ int len; ++ int avail; ++ int ret = 0; ++ wake_lock(&hs.wake_lock_tx); ++ ++ switch (bt_cb(skb)->pkt_type) { ++ case HCI_COMMAND_PKT: ++ avail = smd_write_avail(hs.event_channel); ++ if (!avail) { ++ BT_ERR("No space available for smd frame"); ++ ret = -ENOSPC; ++ } ++ len = smd_write(hs.event_channel, skb->data, skb->len); ++ if (len < skb->len) { ++ BT_ERR("Failed to write Command %d", len); ++ ret = -ENODEV; ++ } ++ break; ++ case HCI_ACLDATA_PKT: ++ case HCI_SCODATA_PKT: ++ avail = smd_write_avail(hs.data_channel); ++ if (!avail) { ++ BT_ERR("No space available for smd frame"); ++ ret = -ENOSPC; ++ } ++ len = smd_write(hs.data_channel, skb->data, skb->len); ++ if (len < skb->len) { ++ BT_ERR("Failed to write Data %d", len); ++ ret = -ENODEV; ++ } ++ break; ++ default: ++ BT_ERR("Uknown packet type"); ++ ret = -ENODEV; ++ break; ++ } ++ ++ kfree_skb(skb); ++ wake_unlock(&hs.wake_lock_tx); ++ return ret; ++} ++ ++static void hci_smd_rx(unsigned long arg) ++{ ++ struct hci_smd_data *hsmd = &hs; ++ ++ while ((smd_read_avail(hsmd->event_channel) > 0) || ++ (smd_read_avail(hsmd->data_channel) > 0)) { ++ hci_smd_recv_event(); ++ hci_smd_recv_data(); ++ } ++} ++ ++static void hci_smd_notify_event(void *data, unsigned int event) ++{ ++ struct hci_dev *hdev = hs.hdev; ++ struct hci_smd_data *hsmd = &hs; ++ struct work_struct *reset_worker; ++ struct work_struct *open_worker; ++ ++ int len = 0; ++ ++ if (!hdev) { ++ BT_ERR("Frame for unknown HCI device (hdev=NULL)"); ++ return; ++ } ++ ++ switch (event) { ++ case SMD_EVENT_DATA: ++ len = smd_read_avail(hsmd->event_channel); ++ if (len > 0) ++ tasklet_hi_schedule(&hs.rx_task); ++ else if (len < 0) ++ BT_ERR("Failed to read event from smd %d", len); ++ ++ break; ++ case SMD_EVENT_OPEN: ++ BT_INFO("opening HCI-SMD channel :%s", EVENT_CHANNEL); ++ BT_DBG("SSR state is : %x", ssr_state); ++ if ((ssr_state == STATE_SSR_OFF) || ++ (ssr_state == STATE_SSR_CHANNEL_OPEN_PENDING)) { ++ ++ hci_smd_open(hdev); ++ open_worker = kzalloc(sizeof(*open_worker), GFP_ATOMIC); ++ if (!open_worker) { ++ BT_ERR("Out of memory"); ++ break; ++ } ++ if (ssr_state == STATE_SSR_CHANNEL_OPEN_PENDING) { ++ ssr_state = STATE_SSR_PENDING_INIT; ++ BT_INFO("SSR state is : %x", ssr_state); ++ } ++ INIT_WORK(open_worker, hci_dev_smd_open); ++ schedule_work(open_worker); ++ ++ } ++ break; ++ case SMD_EVENT_CLOSE: ++ BT_INFO("Closing HCI-SMD channel :%s", EVENT_CHANNEL); ++ BT_DBG("SSR state is : %x", ssr_state); ++ if ((ssr_state == STATE_SSR_OFF) || ++ (ssr_state == (STATE_SSR_PENDING_INIT))) { ++ ++ hci_smd_close(hdev); ++ reset_worker = kzalloc(sizeof(*reset_worker), ++ GFP_ATOMIC); ++ if (!reset_worker) { ++ BT_ERR("Out of memory"); ++ break; ++ } ++ ssr_state = STATE_SSR_ON; ++ BT_INFO("SSR state is : %x", ssr_state); ++ INIT_WORK(reset_worker, hci_dev_restart); ++ schedule_work(reset_worker); ++ ++ } else if (ssr_state & STATE_SSR_ON) { ++ BT_ERR("SSR state is : %x", ssr_state); ++ } ++ ++ break; ++ default: ++ break; ++ } ++} ++ ++static void hci_smd_notify_data(void *data, unsigned int event) ++{ ++ struct hci_dev *hdev = hs.hdev; ++ struct hci_smd_data *hsmd = &hs; ++ int len = 0; ++ ++ if (!hdev) { ++ BT_ERR("Frame for unknown HCI device (hdev=NULL)"); ++ return; ++ } ++ ++ switch (event) { ++ case SMD_EVENT_DATA: ++ len = smd_read_avail(hsmd->data_channel); ++ if (len > 0) ++ tasklet_hi_schedule(&hs.rx_task); ++ else if (len < 0) ++ BT_ERR("Failed to read data from smd %d", len); ++ break; ++ case SMD_EVENT_OPEN: ++ BT_INFO("opening HCI-SMD channel :%s", DATA_CHANNEL); ++ hci_smd_open(hdev); ++ break; ++ case SMD_EVENT_CLOSE: ++ BT_INFO("Closing HCI-SMD channel :%s", DATA_CHANNEL); ++ hci_smd_close(hdev); ++ break; ++ default: ++ break; ++ } ++ ++} ++ ++static int hci_smd_hci_register_dev(struct hci_smd_data *hsmd) ++{ ++ struct hci_dev *hdev; ++ ++ if (hsmd->hdev) ++ hdev = hsmd->hdev; ++ else { ++ BT_ERR("hdev is NULL"); ++ return 0; ++ } ++ /* Allow the incomming SSR even the prev one at PENDING INIT STATE ++ * since clenup need to be started again from the beging and ignore ++ * or bypass the prev one ++ */ ++ if ((ssr_state == STATE_SSR_OFF) || ++ (ssr_state == STATE_SSR_PENDING_INIT)) { ++ ++ if (test_and_set_bit(HCI_REGISTER_SET, &hsmd->flags)) { ++ BT_ERR("HCI device registered already"); ++ return 0; ++ } else ++ BT_INFO("HCI device registration is starting"); ++ if (hci_register_dev(hdev) < 0) { ++ BT_ERR("Can't register HCI device"); ++ hci_free_dev(hdev); ++ hsmd->hdev = NULL; ++ clear_bit(HCI_REGISTER_SET, &hsmd->flags); ++ return -ENODEV; ++ } ++ if (ssr_state == STATE_SSR_PENDING_INIT) { ++ ssr_state = STATE_SSR_COMPLETE; ++ BT_INFO("SSR state is : %x", ssr_state); ++ } ++ } else if (ssr_state) ++ BT_ERR("Registration called in invalid context"); ++ return 0; ++} ++ ++static int hci_smd_register_smd(struct hci_smd_data *hsmd) ++{ ++ struct hci_dev *hdev; ++ int rc; ++ ++ /* Initialize and register HCI device */ ++ hdev = hci_alloc_dev(); ++ if (!hdev) { ++ BT_ERR("Can't allocate HCI device"); ++ return -ENOMEM; ++ } ++ ++ hsmd->hdev = hdev; ++ hdev->bus = HCI_SMD; ++ hdev->driver_data = NULL; ++ hdev->open = hci_smd_open; ++ hdev->close = hci_smd_close; ++ hdev->send = hci_smd_send_frame; ++ //hdev->destruct = hci_smd_destruct; ++ hdev->owner = THIS_MODULE; ++ ++ ++ tasklet_init(&hsmd->rx_task, ++ hci_smd_rx, (unsigned long) hsmd); ++ /* ++ * Setup the timer to monitor whether the Rx queue is empty, ++ * to control the wake lock release ++ */ ++ setup_timer(&hsmd->rx_q_timer, schedule_timer, ++ (unsigned long) hsmd->hdev); ++ if (ssr_state == STATE_SSR_START) { ++ ssr_state = STATE_SSR_CHANNEL_OPEN_PENDING; ++ BT_INFO("SSR state is : %x", ssr_state); ++ } ++ /* Open the SMD Channel and device and register the callback function */ ++ rc = smd_named_open_on_edge(EVENT_CHANNEL, SMD_APPS_WCNSS, ++ &hsmd->event_channel, hdev, hci_smd_notify_event); ++ if (rc < 0) { ++ BT_ERR("Cannot open the command channel"); ++ hci_free_dev(hdev); ++ hsmd->hdev = NULL; ++ return -ENODEV; ++ } ++ ++ rc = smd_named_open_on_edge(DATA_CHANNEL, SMD_APPS_WCNSS, ++ &hsmd->data_channel, hdev, hci_smd_notify_data); ++ if (rc < 0) { ++ BT_ERR("Failed to open the Data channel"); ++ hci_free_dev(hdev); ++ hsmd->hdev = NULL; ++ return -ENODEV; ++ } ++ ++ /* Disable the read interrupts on the channel */ ++ smd_disable_read_intr(hsmd->event_channel); ++ smd_disable_read_intr(hsmd->data_channel); ++ return 0; ++} ++ ++static void hci_smd_deregister_dev(struct hci_smd_data *hsmd) ++{ ++ tasklet_kill(&hs.rx_task); ++ if (ssr_state) ++ BT_DBG("SSR state is : %x", ssr_state); ++ /* Though the hci_smd driver is not registered with the hci ++ * need to close the opened channels as a part of cleaup ++ */ ++ if (!test_and_clear_bit(HCI_REGISTER_SET, &hsmd->flags)) { ++ BT_ERR("HCI device un-registered already"); ++ } else { ++ BT_INFO("HCI device un-registration going on"); ++ ++ if (hsmd->hdev) { ++ hci_unregister_dev(hsmd->hdev) ; ++ hci_free_dev(hsmd->hdev); ++ hsmd->hdev = NULL; ++ } ++ } ++ smd_close(hs.event_channel); ++ smd_close(hs.data_channel); ++ ++ if (wake_lock_active(&hs.wake_lock_rx)) ++ wake_unlock(&hs.wake_lock_rx); ++ if (wake_lock_active(&hs.wake_lock_tx)) ++ wake_unlock(&hs.wake_lock_tx); ++ ++ /*Destroy the timer used to monitor the Rx queue for emptiness */ ++ if (hs.rx_q_timer.function) { ++ del_timer_sync(&hs.rx_q_timer); ++ hs.rx_q_timer.function = NULL; ++ hs.rx_q_timer.data = 0; ++ } ++} ++ ++static void hci_dev_restart(struct work_struct *worker) ++{ ++ down(&hci_smd_enable); ++ restart_in_progress = 1; ++ BT_DBG("SSR state is : %x", ssr_state); ++ ++ if (ssr_state == STATE_SSR_ON) { ++ ssr_state = STATE_SSR_START; ++ BT_INFO("SSR state is : %x", ssr_state); ++ } else { ++ BT_ERR("restart triggered in wrong context"); ++ up(&hci_smd_enable); ++ kfree(worker); ++ return; ++ } ++ hci_smd_deregister_dev(&hs); ++ hci_smd_register_smd(&hs); ++ up(&hci_smd_enable); ++ kfree(worker); ++ ++} ++ ++static void hci_dev_smd_open(struct work_struct *worker) ++{ ++ down(&hci_smd_enable); ++ if (ssr_state) ++ BT_DBG("SSR state is : %x", ssr_state); ++ ++ if ((ssr_state != STATE_SSR_OFF) && ++ (ssr_state != (STATE_SSR_PENDING_INIT))) { ++ up(&hci_smd_enable); ++ kfree(worker); ++ return; ++ } ++ ++ if (restart_in_progress == 1) { ++ /* Allow wcnss to initialize */ ++ restart_in_progress = 0; ++ msleep(10000); ++ } ++ ++ hci_smd_hci_register_dev(&hs); ++ up(&hci_smd_enable); ++ kfree(worker); ++ ++} ++ ++static int hcismd_set_enable(const char *val, struct kernel_param *kp) ++{ ++ int ret = 0; ++ ++ pr_err("hcismd_set_enable %d", hcismd_set); ++ ++ down(&hci_smd_enable); ++ ++ ret = param_set_int(val, kp); ++ ++ if (ret) ++ goto done; ++ ++ /* Ignore the all incomming register de-register requests in case of ++ * SSR is in-progress ++ */ ++ switch (hcismd_set) { ++ ++ case 1: ++ if ((hs.hdev == NULL) && (ssr_state == STATE_SSR_OFF)) ++ hci_smd_register_smd(&hs); ++ else if (ssr_state) ++ BT_ERR("SSR is in progress,state is : %x", ssr_state); ++ ++ break; ++ case 0: ++ if (ssr_state == STATE_SSR_OFF) ++ hci_smd_deregister_dev(&hs); ++ else if (ssr_state) ++ BT_ERR("SSR is in progress,state is : %x", ssr_state); ++ break; ++ default: ++ ret = -EFAULT; ++ } ++ ++done: ++ up(&hci_smd_enable); ++ return ret; ++} ++static int __init hci_smd_init(void) ++{ ++ wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND, ++ "msm_smd_Rx"); ++ wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND, ++ "msm_smd_Tx"); ++ restart_in_progress = 0; ++ ssr_state = STATE_SSR_OFF; ++ hs.hdev = NULL; ++ return 0; ++} ++module_init(hci_smd_init); ++ ++static void __exit hci_smd_exit(void) ++{ ++ wake_lock_destroy(&hs.wake_lock_rx); ++ wake_lock_destroy(&hs.wake_lock_tx); ++} ++module_exit(hci_smd_exit); ++ ++MODULE_AUTHOR("Ankur Nandwani "); ++MODULE_DESCRIPTION("Bluetooth SMD driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h +index cb8d4b314bcf..84e041e99f78 100644 +--- a/include/net/bluetooth/hci.h ++++ b/include/net/bluetooth/hci.h +@@ -59,6 +59,7 @@ + #define HCI_RS232 4 + #define HCI_PCI 5 + #define HCI_SDIO 6 ++#define HCI_SMD 7 + + /* HCI controller types */ + #define HCI_BREDR 0x00 +diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h +index b1980ccdd6ee..a2c759045bfc 100644 +--- a/include/net/bluetooth/hci_core.h ++++ b/include/net/bluetooth/hci_core.h +@@ -349,6 +349,9 @@ struct hci_dev { + struct delayed_work rpa_expired; + bdaddr_t rpa; + ++ void *driver_data; ++ struct module *owner; ++ + int (*open)(struct hci_dev *hdev); + int (*close)(struct hci_dev *hdev); + int (*flush)(struct hci_dev *hdev); diff --git a/device/linux-xiaomi-santoni/config-xiaomi-santoni.aarch64 b/device/linux-xiaomi-santoni/config-xiaomi-santoni.aarch64 index 117759d78..660b9d61d 100644 --- a/device/linux-xiaomi-santoni/config-xiaomi-santoni.aarch64 +++ b/device/linux-xiaomi-santoni/config-xiaomi-santoni.aarch64 @@ -1023,6 +1023,7 @@ CONFIG_BT_HIDP=y # # Bluetooth device drivers # +CONFIG_BT_HCISMD=y # CONFIG_BT_HCIBTUSB is not set # CONFIG_BT_HCIBTSDIO is not set # CONFIG_BT_HCIUART is not set