All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: hci_uart: Add protocol support for Nokia UART devices
@ 2015-04-09 19:56 Marcel Holtmann
  0 siblings, 0 replies; only message in thread
From: Marcel Holtmann @ 2015-04-09 19:56 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: sre, pavel, pali.rohar

WARNING: This driver is not complete. It only contains the H4+ protocol
part and not the serial line or GPIO setup.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
 drivers/bluetooth/Kconfig     |  12 ++
 drivers/bluetooth/Makefile    |   1 +
 drivers/bluetooth/hci_ldisc.c |   6 +
 drivers/bluetooth/hci_nok.c   | 269 ++++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/hci_uart.h  |   8 +-
 5 files changed, 295 insertions(+), 1 deletion(-)
 create mode 100644 drivers/bluetooth/hci_nok.c

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index ed5c2738bea2..efc8115b3bbe 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -135,6 +135,18 @@ config BT_HCIUART_BCM
 
 	  Say Y here to compile support for Broadcom protocol.
 
+config BT_HCIUART_NOK
+	bool "Nokia protocol support"
+	depends on BT_HCIUART
+	select BT_HCIUART_H4
+	select BT_BCM
+	help
+	  The Nokia protocol support enables Bluetooth HCI over serial
+	  port interface for Bluetooth devices in Nokia products. This
+	  is sometimes also referenced H4+ UART protocol.
+
+	  Say Y here to compile support for Nokia protocol.
+
 config BT_HCIBCM203X
 	tristate "HCI BCM203x USB driver"
 	depends on USB
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index dd0d9c40b999..2d748e291629 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -33,6 +33,7 @@ hci_uart-$(CONFIG_BT_HCIUART_ATH3K)	+= hci_ath.o
 hci_uart-$(CONFIG_BT_HCIUART_3WIRE)	+= hci_h5.o
 hci_uart-$(CONFIG_BT_HCIUART_INTEL)	+= hci_intel.o
 hci_uart-$(CONFIG_BT_HCIUART_BCM)	+= hci_bcm.o
+hci_uart-$(CONFIG_BT_HCIUART_NOK)	+= hci_nok.o
 hci_uart-objs				:= $(hci_uart-y)
 
 ccflags-y += -D__CHECK_ENDIAN__
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 5c9a73f02664..8d9849579866 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -684,6 +684,9 @@ static int __init hci_uart_init(void)
 #ifdef CONFIG_BT_HCIUART_BCM
 	bcm_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_NOK
+	nok_init();
+#endif
 
 	return 0;
 }
@@ -710,6 +713,9 @@ static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_BCM
 	bcm_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_NOK
+	nok_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	err = tty_unregister_ldisc(N_HCI);
diff --git a/drivers/bluetooth/hci_nok.c b/drivers/bluetooth/hci_nok.c
new file mode 100644
index 000000000000..c54f3ba131f1
--- /dev/null
+++ b/drivers/bluetooth/hci_nok.c
@@ -0,0 +1,269 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Nokia devices
+ *
+ *  Copyright (C) 2015  Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/tty.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btbcm.h"
+#include "hci_uart.h"
+
+#define NOK_NEG_PKT	0x06
+#define NOK_ALIVE_PKT	0x07
+#define NOK_RADIO_PKT	0x08
+
+#define NOK_NEG_HDR_SIZE	1
+#define NOK_ALIVE_HDR_SIZE	1
+
+struct nok_data {
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+};
+
+static int nok_open(struct hci_uart *hu)
+{
+	struct nok_data *nok;
+
+	BT_DBG("hu %p", hu);
+
+	nok = kzalloc(sizeof(*nok), GFP_KERNEL);
+	if (!nok)
+		return -ENOMEM;
+
+	skb_queue_head_init(&nok->txq);
+
+	hu->priv = nok;
+	return 0;
+}
+
+static int nok_close(struct hci_uart *hu)
+{
+	struct nok_data *nok = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&nok->txq);
+	kfree_skb(nok->rx_skb);
+	kfree(nok);
+
+	hu->priv = NULL;
+	return 0;
+}
+
+static int nok_flush(struct hci_uart *hu)
+{
+	struct nok_data *nok = hu->priv;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&nok->txq);
+
+	return 0;
+}
+
+static int nok_setup(struct hci_uart *hu)
+{
+	const struct firmware *fw;
+	const u8 *fw_ptr;
+	size_t fw_size;
+	int err;
+
+	BT_DBG("hu %p", hu);
+
+	err = request_firmware(&fw, "nokia/bcmfw.bin", hu->tty->dev);
+	if (err < 0) {
+		BT_ERR("%s: Failed to load Nokia firmware file (%d)",
+		       hu->hdev->name, err);
+		return err;
+	}
+
+	fw_ptr = fw->data;
+	fw_size = fw->size;
+
+	while (fw_size >= 4) {
+		u16 pkt_size = get_unaligned_le16(fw_ptr);
+		u8 pkt_type = fw_ptr[2];
+		const struct hci_command_hdr *cmd;
+		u16 opcode;
+		struct sk_buff *skb;
+
+		switch (pkt_type) {
+		case HCI_COMMAND_PKT:
+		case NOK_RADIO_PKT:
+			cmd = (struct hci_command_hdr *)(fw_ptr + 3);
+			opcode = le16_to_cpu(cmd->opcode);
+
+			skb = __hci_cmd_sync(hu->hdev, opcode, cmd->plen,
+					     fw_ptr + 3 + HCI_COMMAND_HDR_SIZE,
+					     HCI_INIT_TIMEOUT);
+			if (IS_ERR(skb)) {
+				err = PTR_ERR(skb);
+				BT_ERR("%s: Firmware command %04x failed (%d)",
+				       hu->hdev->name, opcode, err);
+				goto done;
+			}
+			kfree_skb(skb);
+			break;
+		case NOK_NEG_PKT:
+		case NOK_ALIVE_PKT:
+			break;
+		}
+
+		fw_ptr += pkt_size + 2;
+		fw_size -= pkt_size + 2;
+	}
+
+	hu->hdev->set_bdaddr = btbcm_set_bdaddr;
+	set_bit(HCI_QUIRK_INVALID_BDADDR, &hu->hdev->quirks);
+
+done:
+	release_firmware(fw);
+	return err;
+}
+
+static int nok_recv_neg(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	kfree_skb(skb);
+	return 0;
+}
+
+static int nok_recv_alive(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	kfree_skb(skb);
+	return 0;
+}
+
+static int nok_recv_radio(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	/* Packets received on the dedicated radio channel are
+	 * HCI events and so feed them back into the core.
+	 */
+	bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+	return hci_recv_frame(hdev, skb);
+}
+
+#define NOK_RECV_NEG \
+	.type = NOK_NEG_PKT, \
+	.hlen = NOK_NEG_HDR_SIZE, \
+	.loff = 0, \
+	.lsize = 1, \
+	.maxlen = HCI_MAX_EVENT_SIZE
+
+#define NOK_RECV_ALIVE \
+	.type = NOK_ALIVE_PKT, \
+	.hlen = NOK_ALIVE_HDR_SIZE, \
+	.loff = 0, \
+	.lsize = 1, \
+	.maxlen = HCI_MAX_EVENT_SIZE
+
+#define NOK_RECV_RADIO \
+	.type = NOK_RADIO_PKT, \
+	.hlen = HCI_EVENT_HDR_SIZE, \
+	.loff = 1, \
+	.lsize = 1, \
+	.maxlen = HCI_MAX_EVENT_SIZE
+
+static const struct h4_recv_pkt nok_recv_pkts[] = {
+	{ H4_RECV_ACL,    .recv = hci_recv_frame },
+	{ H4_RECV_SCO,    .recv = hci_recv_frame },
+	{ H4_RECV_EVENT,  .recv = hci_recv_frame },
+	{ NOK_RECV_NEG,   .recv = nok_recv_neg   },
+	{ NOK_RECV_ALIVE, .recv = nok_recv_alive },
+	{ NOK_RECV_RADIO, .recv = nok_recv_radio },
+};
+
+static int nok_recv(struct hci_uart *hu, const void *data, int count)
+{
+	struct nok_data *nok = hu->priv;
+
+	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+		return -EUNATCH;
+
+	nok->rx_skb = h4_recv_buf(hu->hdev, nok->rx_skb, data, count,
+				  nok_recv_pkts, ARRAY_SIZE(nok_recv_pkts));
+	if (IS_ERR(nok->rx_skb)) {
+		int err = PTR_ERR(nok->rx_skb);
+		BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
+		return err;
+	}
+
+	return count;
+}
+
+static int nok_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct nok_data *bcm = hu->priv;
+	u8 pkt_type;
+
+	BT_DBG("hu %p skb %p", hu, skb);
+
+	/* Packets for the FM radio control are standard HCI commands, but
+	 * they are sent through the dedicated radio channel.
+	 */
+	if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT &&
+	    bt_cb(skb)->opcode == 0xfc15)
+		pkt_type = NOK_RADIO_PKT;
+	else
+		pkt_type = bt_cb(skb)->pkt_type;
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &pkt_type, 1);
+	skb_queue_tail(&bcm->txq, skb);
+
+	return 0;
+}
+
+static struct sk_buff *nok_dequeue(struct hci_uart *hu)
+{
+	struct nok_data *nok = hu->priv;
+
+	return skb_dequeue(&nok->txq);
+}
+
+static const struct hci_uart_proto nok_proto = {
+	.id		= HCI_UART_NOK,
+	.name		= "Nokia",
+	.open		= nok_open,
+	.close		= nok_close,
+	.flush		= nok_flush,
+	.setup		= nok_setup,
+	.recv		= nok_recv,
+	.enqueue	= nok_enqueue,
+	.dequeue	= nok_dequeue,
+};
+
+int __init nok_init(void)
+{
+	return hci_uart_register_proto(&nok_proto);
+}
+
+int __exit nok_deinit(void)
+{
+	return hci_uart_unregister_proto(&nok_proto);
+}
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 72120a5ba13c..3735c8adc6e6 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -35,7 +35,7 @@
 #define HCIUARTGETFLAGS		_IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO	8
+#define HCI_UART_MAX_PROTO	9
 
 #define HCI_UART_H4	0
 #define HCI_UART_BCSP	1
@@ -45,6 +45,7 @@
 #define HCI_UART_ATH3K	5
 #define HCI_UART_INTEL	6
 #define HCI_UART_BCM	7
+#define HCI_UART_NOK	8
 
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
@@ -160,3 +161,8 @@ int h5_deinit(void);
 int bcm_init(void);
 int bcm_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_NOK
+int nok_init(void);
+int nok_deinit(void);
+#endif
-- 
2.1.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2015-04-09 19:56 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-04-09 19:56 [PATCH] Bluetooth: hci_uart: Add protocol support for Nokia UART devices Marcel Holtmann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.