linux-xiaomi-santoni: add hci smd for bluetooth support (!420)
Signed-off-by: Danct12 <danct12@disroot.org>
This commit is contained in:
parent
4499ff72c7
commit
cec2a9de58
|
@ -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"
|
||||
|
|
758
device/linux-xiaomi-santoni/bt-hci-smd.patch
Normal file
758
device/linux-xiaomi-santoni/bt-hci-smd.patch
Normal file
|
@ -0,0 +1,758 @@
|
|||
From 08188f95f81aa4c716184eb40e08873e810ad089 Mon Sep 17 00:00:00 2001
|
||||
From: Adam Pigg <adam@piggz.co.uk>
|
||||
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 <maxk@qualcomm.com>
|
||||
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
|
||||
+ *
|
||||
+ * 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 <linux/module.h>
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/init.h>
|
||||
+#include <linux/errno.h>
|
||||
+#include <linux/semaphore.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/wakelock.h>
|
||||
+#include <linux/workqueue.h>
|
||||
+#include <linux/uaccess.h>
|
||||
+#include <linux/interrupt.h>
|
||||
+#include <net/bluetooth/bluetooth.h>
|
||||
+#include <net/bluetooth/hci_core.h>
|
||||
+#include <net/bluetooth/hci.h>
|
||||
+#include <soc/qcom/smd.h>
|
||||
+
|
||||
+#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 <ankurn@codeaurora.org>");
|
||||
+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);
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user