All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v11 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure.
@ 2016-05-06 15:31 Amitkumar Karwar
  2016-05-06 15:31 ` [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it Amitkumar Karwar
                   ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-05-06 15:31 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat, Amitkumar Karwar

From: Ganapathi Bhat <gbhat@marvell.com>

The new callback is used to prepare the device before HCI becomes
ready. One can use this to download firmware if the download process
doesn't use HCI commands. Also recv_for_prepare callback is
introduced for receiving data from devices during prepare phase.

Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
---
 drivers/bluetooth/hci_ldisc.c | 11 ++++++++++-
 drivers/bluetooth/hci_uart.h  |  3 +++
 2 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 49b3e1e..b4ee682 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -551,8 +551,11 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 	if (!hu || tty != hu->tty)
 		return;
 
-	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
+	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+		if (hu->proto->recv_for_prepare)
+			hu->proto->recv_for_prepare(hu, data, count);
 		return;
+	}
 
 	/* It does not need a lock here as it is already protected by a mutex in
 	 * tty caller
@@ -639,6 +642,12 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
 		return err;
 
 	hu->proto = p;
+	if (p->prepare) {
+		err = p->prepare(hu);
+		if (err)
+			return err;
+	}
+
 	set_bit(HCI_UART_PROTO_READY, &hu->flags);
 
 	err = hci_uart_register_dev(hu);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 839bad1..17ba3b4 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -67,8 +67,11 @@ struct hci_uart_proto {
 	int (*close)(struct hci_uart *hu);
 	int (*flush)(struct hci_uart *hu);
 	int (*setup)(struct hci_uart *hu);
+	int (*prepare)(struct hci_uart *hu);
 	int (*set_baudrate)(struct hci_uart *hu, unsigned int speed);
 	int (*recv)(struct hci_uart *hu, const void *data, int len);
+	int (*recv_for_prepare)(struct hci_uart *hu, const void *data,
+				int len);
 	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
 	struct sk_buff *(*dequeue)(struct hci_uart *hu);
 };
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it
  2016-05-06 15:31 [PATCH v11 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Amitkumar Karwar
@ 2016-05-06 15:31 ` Amitkumar Karwar
  2016-06-24  6:01   ` [v11, " Jeffy Chen
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
  2016-06-24  6:01 ` [v11, 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Jeffy Chen
  2 siblings, 1 reply; 15+ messages in thread
From: Amitkumar Karwar @ 2016-05-06 15:31 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat, Amitkumar Karwar

From: Ganapathi Bhat <gbhat@marvell.com>

The hdev struct might not have initialized in protocol receive handler.
This patch adds necessary checks.

Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
---
 drivers/bluetooth/hci_ldisc.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index b4ee682..047e786 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -154,7 +154,9 @@ restart:
 
 		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 		len = tty->ops->write(tty, skb->data, skb->len);
-		hdev->stat.byte_tx += len;
+
+		if (hdev)
+			hdev->stat.byte_tx += len;
 
 		skb_pull(skb, len);
 		if (skb->len) {
@@ -349,7 +351,7 @@ void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
 	/* tty_set_termios() return not checked as it is always 0 */
 	tty_set_termios(tty, &ktermios);
 
-	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
+	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev ? hu->hdev->name : "",
 	       tty->termios.c_ispeed, tty->termios.c_ospeed);
 }
 
-- 
1.8.1.4


^ permalink raw reply related	[flat|nested] 15+ messages in thread

* [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 [PATCH v11 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Amitkumar Karwar
  2016-05-06 15:31 ` [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it Amitkumar Karwar
@ 2016-05-06 15:31 ` Amitkumar Karwar
  2016-05-10 13:59   ` Amitkumar Karwar
                     ` (4 more replies)
  2016-06-24  6:01 ` [v11, 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Jeffy Chen
  2 siblings, 5 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-05-06 15:31 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat, Amitkumar Karwar

From: Ganapathi Bhat <gbhat@marvell.com>

This patch implement firmware download feature for
Marvell Bluetooth devices. If firmware is already
downloaded, it will skip downloading.

Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
---
v2: Fixed compilation warning reported by kbuild test robot
v3: Addressed review comments from Marcel Holtmann
    a) Removed vendor specific code from hci_ldisc.c
    b) Get rid of static forward declaration
    c) Removed unnecessary heavy nesting
    d) Git rid of module parameter and global variables
    e) Add logic to pick right firmware image
v4: Addresses review comments from Alan
    a) Use existing kernel helper APIs instead of writing own.
    b) Replace mdelay() with msleep()
v5: Addresses review comments from Loic Poulain
    a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
    b) Used static functions where required and removed forward delcarations
    c) Edited comments for the function hci_uart_recv_data
    d) Made HCI_UART_DNLD_FW flag a part of driver private data
v6: Addresses review comments from Loic Poulain
    a) Used skb instead of array to store firmware data during download
    b) Used hci_uart_tx_wakeup and enqueued packets instead of tty write
    c) Used GFP_KERNEL instead of GFP_ATOMIC
v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change resolves
    errors reported by kbuild test robot.
v8: Addressed review comments from Marcel Holtmann
    a) Removed unnecessary memory allocation failure messages
    b) Get rid of btmrvl.h header file and add definitions in hci_mrvl.c file
v9: Addressed review comments from Marcel Holtmann
    a) Moved firmware download code from setup to prepare handler.
    b) Change messages from bt_dev_*->BT_*, as hdev isn't available during firmware
     download.
v10: Addressed review comments from Marcel Holtmann
    a) Added new callback recv_for_prepare to receive data from device
     during prepare phase
    b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive callback is
     added for the same purpose
    c) Used kernel API to handle unaligned data
    d) Moved mrvl_set_baud functionality inside setup callback
v11: Write data through ldisc in mrvl_send_ack() instead of directly calling
    write method(One Thousand Gnomes).
---
 drivers/bluetooth/Kconfig     |  11 +
 drivers/bluetooth/Makefile    |   1 +
 drivers/bluetooth/hci_ldisc.c |   6 +
 drivers/bluetooth/hci_mrvl.c  | 543 ++++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/hci_uart.h  |   8 +-
 5 files changed, 568 insertions(+), 1 deletion(-)
 create mode 100644 drivers/bluetooth/hci_mrvl.c

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index cf50fd2..daafd0c 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
 
 	  Say Y here to compile support for Intel AG6XX protocol.
 
+config BT_HCIUART_MRVL
+	bool "Marvell protocol support"
+	depends on BT_HCIUART
+	select BT_HCIUART_H4
+	help
+	  Marvell is serial protocol for communication between Bluetooth
+	  device and host. This protocol is required for most Marvell Bluetooth
+	  devices with UART interface.
+
+	  Say Y here to compile support for HCI MRVL protocol.
+
 config BT_HCIBCM203X
 	tristate "HCI BCM203x USB driver"
 	depends on USB
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 9c18939..364dbb6 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -37,6 +37,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL)	+= hci_intel.o
 hci_uart-$(CONFIG_BT_HCIUART_BCM)	+= hci_bcm.o
 hci_uart-$(CONFIG_BT_HCIUART_QCA)	+= hci_qca.o
 hci_uart-$(CONFIG_BT_HCIUART_AG6XX)	+= hci_ag6xx.o
+hci_uart-$(CONFIG_BT_HCIUART_MRVL)	+= hci_mrvl.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 047e786..4896b6f 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -821,6 +821,9 @@ static int __init hci_uart_init(void)
 #ifdef CONFIG_BT_HCIUART_AG6XX
 	ag6xx_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_MRVL
+	mrvl_init();
+#endif
 
 	return 0;
 }
@@ -856,6 +859,9 @@ static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_AG6XX
 	ag6xx_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_MRVL
+	mrvl_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	err = tty_unregister_ldisc(N_HCI);
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
new file mode 100644
index 0000000..2686901
--- /dev/null
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -0,0 +1,543 @@
+/* Bluetooth HCI UART driver for Marvell devices
+ *
+ * Copyright (C) 2016, Marvell International Ltd.
+ *
+ *  Acknowledgements:
+ *  This file is based on hci_h4.c, which was written
+ *  by Maxim Krasnyansky and Marcel Holtmann.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/firmware.h>
+#include <linux/tty.h>
+#include <asm/unaligned.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+
+struct fw_data {
+	wait_queue_head_t init_wait_q;
+	u8 wait_fw;
+	int next_len;
+	u8 five_bytes[5];
+	u8 next_index;
+	u8 last_ack;
+	u8 expected_ack;
+	struct ktermios old_termios;
+	u8 chip_id;
+	u8 chip_rev;
+	struct sk_buff *skb;
+};
+
+#define MRVL_HELPER_NAME	"mrvl/helper_uart_3000000.bin"
+#define MRVL_8997_CHIP_ID	0x50
+#define MRVL_8997_FW_NAME	"mrvl/uart8997_bt.bin"
+#define MRVL_MAX_FW_BLOCK_SIZE	1024
+#define MRVL_MAX_RETRY_SEND	12
+#define MRVL_DNLD_DELAY		100
+#define MRVL_ACK		0x5A
+#define MRVL_NAK		0xBF
+#define MRVL_HDR_REQ_FW		0xA5
+#define MRVL_HDR_CHIP_VER	0xAA
+#define MRVL_HCI_OP_SET_BAUD	0xFC09
+#define MRVL_FW_HDR_LEN		5
+#define MRVL_WAIT_TIMEOUT	msecs_to_jiffies(12000)
+
+struct mrvl_data {
+	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
+	struct fw_data *fwdata;
+};
+
+static int get_cts(struct hci_uart *hu)
+{
+	struct tty_struct *tty = hu->tty;
+	u32 state =  tty->ops->tiocmget(tty);
+
+	if (state & TIOCM_CTS) {
+		bt_dev_dbg(hu->hdev, "CTS is low");
+		return 1;
+	}
+	bt_dev_dbg(hu->hdev, "CTS is high");
+
+	return 0;
+}
+
+/* Initialize protocol */
+static int mrvl_open(struct hci_uart *hu)
+{
+	struct mrvl_data *mrvl;
+
+	bt_dev_dbg(hu->hdev, "hu %p", hu);
+
+	mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
+	if (!mrvl)
+		return -ENOMEM;
+
+	skb_queue_head_init(&mrvl->txq);
+	hu->priv = mrvl;
+
+	return 0;
+}
+
+/* Flush protocol data */
+static int mrvl_flush(struct hci_uart *hu)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	bt_dev_dbg(hu->hdev, "hu %p", hu);
+
+	skb_queue_purge(&mrvl->txq);
+
+	return 0;
+}
+
+/* Close protocol */
+static int mrvl_close(struct hci_uart *hu)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	bt_dev_dbg(hu->hdev, "hu %p", hu);
+
+	skb_queue_purge(&mrvl->txq);
+	kfree_skb(mrvl->rx_skb);
+	hu->priv = NULL;
+	kfree(mrvl);
+
+	return 0;
+}
+
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+	skb_queue_tail(&mrvl->txq, skb);
+
+	return 0;
+}
+
+static const struct h4_recv_pkt mrvl_recv_pkts[] = {
+	{ H4_RECV_ACL,   .recv = hci_recv_frame },
+	{ H4_RECV_SCO,   .recv = hci_recv_frame },
+	{ H4_RECV_EVENT, .recv = hci_recv_frame },
+};
+
+/* Send ACK/NAK to the device */
+static void mrvl_send_ack(struct hci_uart *hu, unsigned char ack)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	struct sk_buff *skb;
+
+	skb = bt_skb_alloc(sizeof(ack), GFP_KERNEL);
+	if (!skb)
+		return;
+
+	memcpy(skb->data, &ack, sizeof(ack));
+	skb_put(skb, sizeof(ack));
+	skb_queue_head(&mrvl->txq, skb);
+	hci_uart_tx_wakeup(hu);
+}
+
+/* Validate the feedback data from device */
+static void mrvl_pkt_complete(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	struct fw_data *fw_data = mrvl->fwdata;
+	u16 lhs, rhs;
+
+	lhs = get_unaligned_le16(skb->data + 1);
+	rhs = get_unaligned_le16(skb->data + 3);
+	if ((lhs ^ rhs) == 0xffff) {
+		mrvl_send_ack(hu, MRVL_ACK);
+		fw_data->wait_fw = 1;
+		fw_data->next_len = lhs;
+		/* Firmware download is done, send the last ack */
+		if (!lhs)
+			fw_data->last_ack = 1;
+
+		if (fw_data->expected_ack == MRVL_HDR_CHIP_VER) {
+			fw_data->chip_id = skb->data[1];
+			fw_data->chip_rev = skb->data[2];
+		}
+		wake_up_interruptible(&fw_data->init_wait_q);
+	} else {
+		mrvl_send_ack(hu, MRVL_NAK);
+	}
+}
+
+/* This function receives data from the uart device during firmware download.
+ * Driver expects 5 bytes of data as per the protocal in the below format:
+ * <HEADER><BYTE_1><BYTE_2><BYTE_3><BYTE_4>
+ * BYTE_3 and BYTE_4 are compliment of BYTE_1 an BYTE_2. Data can come in chunks
+ * of any length. If length received is < 5, accumulate the data in an array,
+ * until we have a sequence of 5 bytes, starting with the expected HEADER. If
+ * the length received is > 5  bytes, then get the first 5 bytes, starting with
+ * the HEADER and process the same, ignoring the rest of the bytes as per the
+ * protocal.
+ */
+static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
+					    struct sk_buff *skb,
+					    u8 *buf, int count)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	struct fw_data *fw_data = mrvl->fwdata;
+	int i = 0, len;
+
+	if (!skb) {
+		while (buf[i] != fw_data->expected_ack && i < count)
+			i++;
+		if (i == count)
+			return ERR_PTR(-EILSEQ);
+
+		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);
+	}
+
+	if (!skb)
+		return ERR_PTR(-ENOMEM);
+
+	len = count - i;
+	memcpy(skb_put(skb, len), &buf[i], len);
+
+	if (skb->len == MRVL_FW_HDR_LEN) {
+		mrvl_pkt_complete(hu, skb);
+		kfree_skb(skb);
+		skb = NULL;
+	}
+
+	return skb;
+}
+
+/* Receive firmware feedback data */
+static int mrvl_recv_for_prepare(struct hci_uart *hu, const void *data,
+				 int count)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	mrvl->fwdata->skb = mrvl_process_fw_data(hu, mrvl->fwdata->skb,
+						 (u8 *)data, count);
+	if (IS_ERR(mrvl->fwdata->skb)) {
+		int err = PTR_ERR(mrvl->fwdata->skb);
+
+		bt_dev_err(hu->hdev,
+			   "Receive firmware data failed (%d)", err);
+		mrvl->fwdata->skb = NULL;
+		return err;
+	}
+
+	return 0;
+}
+
+/* Receive data */
+static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+		return -EUNATCH;
+
+	mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
+				   mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts));
+	if (IS_ERR(mrvl->rx_skb)) {
+		int err = PTR_ERR(mrvl->rx_skb);
+
+		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
+		mrvl->rx_skb = NULL;
+		return err;
+	}
+
+	return count;
+}
+
+static struct sk_buff *mrvl_dequeue(struct hci_uart *hu)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	return skb_dequeue(&mrvl->txq);
+}
+
+static int mrvl_init_fw_data(struct hci_uart *hu)
+{
+	struct fw_data *fwdata;
+	struct mrvl_data *mrvl = hu->priv;
+
+	fwdata = kzalloc(sizeof(*fwdata), GFP_KERNEL);
+	if (!fwdata)
+		return -ENOMEM;
+
+	mrvl->fwdata = fwdata;
+	init_waitqueue_head(&fwdata->init_wait_q);
+
+	return 0;
+}
+
+/* Wait for the header from device */
+static int mrvl_wait_for_hdr(struct hci_uart *hu, u8 header)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	struct fw_data *fw_data = mrvl->fwdata;
+
+	fw_data->expected_ack = header;
+	fw_data->wait_fw = 0;
+
+	if (!wait_event_interruptible_timeout(fw_data->init_wait_q,
+					      fw_data->wait_fw,
+					      MRVL_WAIT_TIMEOUT)) {
+		BT_ERR("TIMEOUT, waiting for:0x%x", fw_data->expected_ack);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Send bytes to device */
+static int mrvl_send_data(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct mrvl_data *mrvl = hu->priv;
+
+	skb_queue_head(&mrvl->txq, skb);
+	hci_uart_tx_wakeup(hu);
+
+	if (mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW) == -1)
+		return -1;
+
+	return 0;
+}
+
+/* Download firmware to the device */
+static int mrvl_dnld_fw(struct hci_uart *hu, const char *file_name)
+{
+	const struct firmware *fw = NULL;
+	struct sk_buff *skb = NULL;
+	int offset = 0;
+	int ret, tx_len;
+	struct mrvl_data *mrvl = hu->priv;
+	struct fw_data *fw_data = mrvl->fwdata;
+
+	ret = request_firmware(&fw, file_name, hu->tty->dev);
+	if (ret < 0) {
+		BT_ERR("request_firmware() failed");
+		return -1;
+	}
+
+	BT_INFO("Downloading FW (%d bytes)", (u16)fw->size);
+
+	fw_data->last_ack = 0;
+
+	while (1) {
+		if ((offset >= fw->size) || (fw_data->last_ack))
+			break;
+		tx_len = fw_data->next_len;
+		if ((fw->size - offset) < tx_len)
+			tx_len = fw->size - offset;
+
+		skb = bt_skb_alloc(MRVL_MAX_FW_BLOCK_SIZE, GFP_KERNEL);
+		if (!skb) {
+			ret = -1;
+			goto done;
+		}
+
+		memcpy(skb->data, &fw->data[offset], tx_len);
+		skb_put(skb, tx_len);
+		if (mrvl_send_data(hu, skb) != 0) {
+			BT_ERR("Fail to download firmware");
+			ret = -1;
+			goto done;
+		}
+		offset += tx_len;
+	}
+
+	BT_INFO("Downloaded %d byte firmware", offset);
+done:
+	release_firmware(fw);
+
+	return ret;
+}
+
+/* Set the baud rate */
+static int mrvl_set_dev_baud(struct hci_uart *hu)
+{
+	struct hci_dev *hdev = hu->hdev;
+	struct sk_buff *skb;
+	static const u8 baud_param[] = { 0xc0, 0xc6, 0x2d, 0x00 };
+	int err;
+
+	skb = __hci_cmd_sync(hdev, MRVL_HCI_OP_SET_BAUD, sizeof(baud_param),
+			     baud_param, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(hu->hdev, "Set device baudrate failed (%d)", err);
+		return err;
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+
+/* Reset device */
+static int mrvl_reset(struct hci_uart *hu)
+{
+	struct hci_dev *hdev = hu->hdev;
+	struct sk_buff *skb;
+	int err;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(hu->hdev, "Reset device failed (%d)", err);
+		return err;
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+
+static int mrvl_get_fw_name(struct hci_uart *hu, char *fw_name)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	struct fw_data *fw_data = mrvl->fwdata;
+
+	if (mrvl_wait_for_hdr(hu, MRVL_HDR_CHIP_VER) != 0) {
+		BT_ERR("Could not read chip id and revision");
+		return -1;
+	}
+
+	BT_DBG("chip_id=0x%x, chip_rev=0x%x",
+	       fw_data->chip_id, fw_data->chip_rev);
+
+	switch (fw_data->chip_id) {
+	case MRVL_8997_CHIP_ID:
+		memcpy(fw_name, MRVL_8997_FW_NAME, sizeof(MRVL_8997_FW_NAME));
+		return 0;
+	default:
+		BT_ERR("Invalid chip id");
+		return -1;
+	}
+}
+
+/* Download helper and firmare to device */
+static int hci_uart_dnld_fw(struct hci_uart *hu)
+{
+	struct tty_struct *tty = hu->tty;
+	struct ktermios new_termios;
+	struct ktermios old_termios;
+	char fw_name[128];
+	int ret;
+
+	old_termios = tty->termios;
+
+	if (get_cts(hu)) {
+		BT_INFO("fw is running");
+		return 0;
+	}
+
+	hci_uart_set_baudrate(hu, 115200);
+	hci_uart_set_flow_control(hu, true);
+
+	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
+	if (ret)
+		goto fail;
+
+	ret = mrvl_dnld_fw(hu, MRVL_HELPER_NAME);
+	if (ret)
+		goto fail;
+
+	msleep(MRVL_DNLD_DELAY);
+
+	hci_uart_set_baudrate(hu, 3000000);
+	hci_uart_set_flow_control(hu, false);
+
+	ret = mrvl_get_fw_name(hu, fw_name);
+	if (ret)
+		goto fail;
+
+	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
+	if (ret)
+		goto fail;
+
+	ret = mrvl_dnld_fw(hu, fw_name);
+	if (ret)
+		goto fail;
+
+	msleep(MRVL_DNLD_DELAY);
+fail:
+	/* restore uart settings */
+	new_termios = tty->termios;
+	tty->termios.c_cflag = old_termios.c_cflag;
+	tty_set_termios(tty, &new_termios);
+
+	return ret;
+}
+
+static int mrvl_setup(struct hci_uart *hu)
+{
+	int err;
+
+	hci_uart_set_baudrate(hu, 115200);
+	hci_uart_set_flow_control(hu, false);
+
+	err = mrvl_reset(hu);
+	if (!err) {
+		err = mrvl_set_dev_baud(hu);
+		if (err)
+			return -1;
+	}
+
+	hci_uart_set_baudrate(hu, 3000000);
+	hci_uart_set_flow_control(hu, false);
+	msleep(MRVL_DNLD_DELAY);
+
+	return 0;
+}
+
+static int mrvl_prepare(struct hci_uart *hu)
+{
+	struct mrvl_data *mrvl = hu->priv;
+	int err;
+
+	err = mrvl_init_fw_data(hu);
+	if (!err)
+		err = hci_uart_dnld_fw(hu);
+
+	kfree(mrvl->fwdata);
+	return err;
+}
+
+static const struct hci_uart_proto mrvlp = {
+	.id		= HCI_UART_MRVL,
+	.name		= "MRVL",
+	.open		= mrvl_open,
+	.close		= mrvl_close,
+	.recv		= mrvl_recv,
+	.enqueue	= mrvl_enqueue,
+	.dequeue	= mrvl_dequeue,
+	.flush		= mrvl_flush,
+	.setup		= mrvl_setup,
+	.prepare	= mrvl_prepare,
+	.recv_for_prepare	= mrvl_recv_for_prepare,
+};
+
+int __init mrvl_init(void)
+{
+	return hci_uart_register_proto(&mrvlp);
+}
+
+int __exit mrvl_deinit(void)
+{
+	return hci_uart_unregister_proto(&mrvlp);
+}
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 17ba3b4..8c53b50 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	10
+#define HCI_UART_MAX_PROTO	11
 
 #define HCI_UART_H4	0
 #define HCI_UART_BCSP	1
@@ -47,6 +47,7 @@
 #define HCI_UART_BCM	7
 #define HCI_UART_QCA	8
 #define HCI_UART_AG6XX	9
+#define HCI_UART_MRVL	10
 
 #define HCI_UART_RAW_DEVICE	0
 #define HCI_UART_RESET_ON_INIT	1
@@ -192,3 +193,8 @@ int qca_deinit(void);
 int ag6xx_init(void);
 int ag6xx_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_MRVL
+int mrvl_init(void);
+int mrvl_deinit(void);
+#endif
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* RE: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
@ 2016-05-10 13:59   ` Amitkumar Karwar
  2016-05-23  6:06   ` Amitkumar Karwar
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-05-10 13:59 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat

Hi Marcel,

> From: Amitkumar Karwar [mailto:akarwar@marvell.com]
> Sent: Friday, May 06, 2016 9:02 PM
> To: linux-bluetooth@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat; Amitkumar Karwar
> Subject: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download
> for Marvell
> 
> From: Ganapathi Bhat <gbhat@marvell.com>
> 
> This patch implement firmware download feature for Marvell Bluetooth
> devices. If firmware is already downloaded, it will skip downloading.
> 
> Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> ---
> v2: Fixed compilation warning reported by kbuild test robot
> v3: Addressed review comments from Marcel Holtmann
>     a) Removed vendor specific code from hci_ldisc.c
>     b) Get rid of static forward declaration
>     c) Removed unnecessary heavy nesting
>     d) Git rid of module parameter and global variables
>     e) Add logic to pick right firmware image
> v4: Addresses review comments from Alan
>     a) Use existing kernel helper APIs instead of writing own.
>     b) Replace mdelay() with msleep()
> v5: Addresses review comments from Loic Poulain
>     a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
>     b) Used static functions where required and removed forward
> delcarations
>     c) Edited comments for the function hci_uart_recv_data
>     d) Made HCI_UART_DNLD_FW flag a part of driver private data
> v6: Addresses review comments from Loic Poulain
>     a) Used skb instead of array to store firmware data during download
>     b) Used hci_uart_tx_wakeup and enqueued packets instead of tty write
>     c) Used GFP_KERNEL instead of GFP_ATOMIC
> v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change
> resolves
>     errors reported by kbuild test robot.
> v8: Addressed review comments from Marcel Holtmann
>     a) Removed unnecessary memory allocation failure messages
>     b) Get rid of btmrvl.h header file and add definitions in hci_mrvl.c
> file
> v9: Addressed review comments from Marcel Holtmann
>     a) Moved firmware download code from setup to prepare handler.
>     b) Change messages from bt_dev_*->BT_*, as hdev isn't available
> during firmware
>      download.
> v10: Addressed review comments from Marcel Holtmann
>     a) Added new callback recv_for_prepare to receive data from device
>      during prepare phase
>     b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive
> callback is
>      added for the same purpose
>     c) Used kernel API to handle unaligned data
>     d) Moved mrvl_set_baud functionality inside setup callback
> v11: Write data through ldisc in mrvl_send_ack() instead of directly
> calling
>     write method(One Thousand Gnomes).

Could you please take this patch if there are no further comments/suggestions for improvement?

Regards,
Amitkumar

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
  2016-05-10 13:59   ` Amitkumar Karwar
@ 2016-05-23  6:06   ` Amitkumar Karwar
  2016-06-14  6:35   ` Amitkumar Karwar
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-05-23  6:06 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Cathy Luo

Hi Marcel,
 
> > From: Amitkumar Karwar [mailto:akarwar@marvell.com]
> > Sent: Friday, May 06, 2016 9:02 PM
> > To: linux-bluetooth@vger.kernel.org
> > Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat; Amitkumar Karwar
> > Subject: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware
> > download for Marvell
> >
> > From: Ganapathi Bhat <gbhat@marvell.com>
> >
> > This patch implement firmware download feature for Marvell Bluetooth
> > devices. If firmware is already downloaded, it will skip downloading.
> >
> > Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> > Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> > ---
> > v2: Fixed compilation warning reported by kbuild test robot
> > v3: Addressed review comments from Marcel Holtmann
> >     a) Removed vendor specific code from hci_ldisc.c
> >     b) Get rid of static forward declaration
> >     c) Removed unnecessary heavy nesting
> >     d) Git rid of module parameter and global variables
> >     e) Add logic to pick right firmware image
> > v4: Addresses review comments from Alan
> >     a) Use existing kernel helper APIs instead of writing own.
> >     b) Replace mdelay() with msleep()
> > v5: Addresses review comments from Loic Poulain
> >     a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
> >     b) Used static functions where required and removed forward
> > delcarations
> >     c) Edited comments for the function hci_uart_recv_data
> >     d) Made HCI_UART_DNLD_FW flag a part of driver private data
> > v6: Addresses review comments from Loic Poulain
> >     a) Used skb instead of array to store firmware data during
> download
> >     b) Used hci_uart_tx_wakeup and enqueued packets instead of tty
> write
> >     c) Used GFP_KERNEL instead of GFP_ATOMIC
> > v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change
> > resolves
> >     errors reported by kbuild test robot.
> > v8: Addressed review comments from Marcel Holtmann
> >     a) Removed unnecessary memory allocation failure messages
> >     b) Get rid of btmrvl.h header file and add definitions in
> > hci_mrvl.c file
> > v9: Addressed review comments from Marcel Holtmann
> >     a) Moved firmware download code from setup to prepare handler.
> >     b) Change messages from bt_dev_*->BT_*, as hdev isn't available
> > during firmware
> >      download.
> > v10: Addressed review comments from Marcel Holtmann
> >     a) Added new callback recv_for_prepare to receive data from device
> >      during prepare phase
> >     b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive
> > callback is
> >      added for the same purpose
> >     c) Used kernel API to handle unaligned data
> >     d) Moved mrvl_set_baud functionality inside setup callback
> > v11: Write data through ldisc in mrvl_send_ack() instead of directly
> > calling
> >     write method(One Thousand Gnomes).
> 

Could you please take this patch?

Regards,
Amitkumar

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
  2016-05-10 13:59   ` Amitkumar Karwar
  2016-05-23  6:06   ` Amitkumar Karwar
@ 2016-06-14  6:35   ` Amitkumar Karwar
  2016-06-21  3:47   ` [v11,3/3] " Caesar Wang
  2016-06-24  6:02   ` Jeffy Chen
  4 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-06-14  6:35 UTC (permalink / raw)
  To: linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Cathy Luo

Hi Marcel,

> From: Amitkumar Karwar
> Sent: Monday, May 23, 2016 11:37 AM
> To: 'linux-bluetooth@vger.kernel.org'
> Cc: 'linux-kernel@vger.kernel.org'; Ganapathi Bhat; Cathy Luo
> Subject: RE: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware
> download for Marvell
> 
> Hi Marcel,
> 
> > > From: Amitkumar Karwar [mailto:akarwar@marvell.com]
> > > Sent: Friday, May 06, 2016 9:02 PM
> > > To: linux-bluetooth@vger.kernel.org
> > > Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat; Amitkumar Karwar
> > > Subject: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware
> > > download for Marvell
> > >
> > > From: Ganapathi Bhat <gbhat@marvell.com>
> > >
> > > This patch implement firmware download feature for Marvell Bluetooth
> > > devices. If firmware is already downloaded, it will skip
> downloading.
> > >
> > > Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> > > Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> > > ---
> > > v2: Fixed compilation warning reported by kbuild test robot
> > > v3: Addressed review comments from Marcel Holtmann
> > >     a) Removed vendor specific code from hci_ldisc.c
> > >     b) Get rid of static forward declaration
> > >     c) Removed unnecessary heavy nesting
> > >     d) Git rid of module parameter and global variables
> > >     e) Add logic to pick right firmware image
> > > v4: Addresses review comments from Alan
> > >     a) Use existing kernel helper APIs instead of writing own.
> > >     b) Replace mdelay() with msleep()
> > > v5: Addresses review comments from Loic Poulain
> > >     a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
> > >     b) Used static functions where required and removed forward
> > > delcarations
> > >     c) Edited comments for the function hci_uart_recv_data
> > >     d) Made HCI_UART_DNLD_FW flag a part of driver private data
> > > v6: Addresses review comments from Loic Poulain
> > >     a) Used skb instead of array to store firmware data during
> > download
> > >     b) Used hci_uart_tx_wakeup and enqueued packets instead of tty
> > write
> > >     c) Used GFP_KERNEL instead of GFP_ATOMIC
> > > v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change
> > > resolves
> > >     errors reported by kbuild test robot.
> > > v8: Addressed review comments from Marcel Holtmann
> > >     a) Removed unnecessary memory allocation failure messages
> > >     b) Get rid of btmrvl.h header file and add definitions in
> > > hci_mrvl.c file
> > > v9: Addressed review comments from Marcel Holtmann
> > >     a) Moved firmware download code from setup to prepare handler.
> > >     b) Change messages from bt_dev_*->BT_*, as hdev isn't available
> > > during firmware
> > >      download.
> > > v10: Addressed review comments from Marcel Holtmann
> > >     a) Added new callback recv_for_prepare to receive data from
> device
> > >      during prepare phase
> > >     b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive
> > > callback is
> > >      added for the same purpose
> > >     c) Used kernel API to handle unaligned data
> > >     d) Moved mrvl_set_baud functionality inside setup callback
> > > v11: Write data through ldisc in mrvl_send_ack() instead of directly
> > > calling
> > >     write method(One Thousand Gnomes).
> >
> 
> Could you please take this patch?
> 

This has been pending for a while.
Please let me know your comment.

Regards,
Amitkumar

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
                     ` (2 preceding siblings ...)
  2016-06-14  6:35   ` Amitkumar Karwar
@ 2016-06-21  3:47   ` Caesar Wang
  2016-06-24  6:02   ` Jeffy Chen
  4 siblings, 0 replies; 15+ messages in thread
From: Caesar Wang @ 2016-06-21  3:47 UTC (permalink / raw)
  To: Amitkumar Karwar, linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat


On 2016年05月06日 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <gbhat@marvell.com>
>
> This patch implement firmware download feature for
> Marvell Bluetooth devices. If firmware is already
> downloaded, it will skip downloading.
>
> Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Tested-by: Caesar Wang <wxt@rock-chips.com>

Tested on chromeos4.4. so you can free add my test tag:
I don't find the cover-letter, so add it in here.

Cc: linux-rockchip@lists.infradead.org,
  that's interesting in this series patches.


> ---
> v2: Fixed compilation warning reported by kbuild test robot
> v3: Addressed review comments from Marcel Holtmann
>      a) Removed vendor specific code from hci_ldisc.c
>      b) Get rid of static forward declaration
>      c) Removed unnecessary heavy nesting
>      d) Git rid of module parameter and global variables
>      e) Add logic to pick right firmware image
> v4: Addresses review comments from Alan
>      a) Use existing kernel helper APIs instead of writing own.
>      b) Replace mdelay() with msleep()
> v5: Addresses review comments from Loic Poulain
>      a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
>      b) Used static functions where required and removed forward delcarations
>      c) Edited comments for the function hci_uart_recv_data
>      d) Made HCI_UART_DNLD_FW flag a part of driver private data
> v6: Addresses review comments from Loic Poulain
>      a) Used skb instead of array to store firmware data during download
>      b) Used hci_uart_tx_wakeup and enqueued packets instead of tty write
>      c) Used GFP_KERNEL instead of GFP_ATOMIC
> v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change resolves
>      errors reported by kbuild test robot.
> v8: Addressed review comments from Marcel Holtmann
>      a) Removed unnecessary memory allocation failure messages
>      b) Get rid of btmrvl.h header file and add definitions in hci_mrvl.c file
> v9: Addressed review comments from Marcel Holtmann
>      a) Moved firmware download code from setup to prepare handler.
>      b) Change messages from bt_dev_*->BT_*, as hdev isn't available during firmware
>       download.
> v10: Addressed review comments from Marcel Holtmann
>      a) Added new callback recv_for_prepare to receive data from device
>       during prepare phase
>      b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive callback is
>       added for the same purpose
>      c) Used kernel API to handle unaligned data
>      d) Moved mrvl_set_baud functionality inside setup callback
> v11: Write data through ldisc in mrvl_send_ack() instead of directly calling
>      write method(One Thousand Gnomes).
> ---
>   drivers/bluetooth/Kconfig     |  11 +
>   drivers/bluetooth/Makefile    |   1 +
>   drivers/bluetooth/hci_ldisc.c |   6 +
>   drivers/bluetooth/hci_mrvl.c  | 543 ++++++++++++++++++++++++++++++++++++++++++
>   drivers/bluetooth/hci_uart.h  |   8 +-
>   5 files changed, 568 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/bluetooth/hci_mrvl.c
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index cf50fd2..daafd0c 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
>   
>   	  Say Y here to compile support for Intel AG6XX protocol.
>   
> +config BT_HCIUART_MRVL
> +	bool "Marvell protocol support"
> +	depends on BT_HCIUART
> +	select BT_HCIUART_H4
> +	help
> +	  Marvell is serial protocol for communication between Bluetooth
> +	  device and host. This protocol is required for most Marvell Bluetooth
> +	  devices with UART interface.
> +
> +	  Say Y here to compile support for HCI MRVL protocol.
> +
>   config BT_HCIBCM203X
>   	tristate "HCI BCM203x USB driver"
>   	depends on USB
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index 9c18939..364dbb6 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -37,6 +37,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL)	+= hci_intel.o
>   hci_uart-$(CONFIG_BT_HCIUART_BCM)	+= hci_bcm.o
>   hci_uart-$(CONFIG_BT_HCIUART_QCA)	+= hci_qca.o
>   hci_uart-$(CONFIG_BT_HCIUART_AG6XX)	+= hci_ag6xx.o
> +hci_uart-$(CONFIG_BT_HCIUART_MRVL)	+= hci_mrvl.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 047e786..4896b6f 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -821,6 +821,9 @@ static int __init hci_uart_init(void)
>   #ifdef CONFIG_BT_HCIUART_AG6XX
>   	ag6xx_init();
>   #endif
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +	mrvl_init();
> +#endif
>   
>   	return 0;
>   }
> @@ -856,6 +859,9 @@ static void __exit hci_uart_exit(void)
>   #ifdef CONFIG_BT_HCIUART_AG6XX
>   	ag6xx_deinit();
>   #endif
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +	mrvl_deinit();
> +#endif
>   
>   	/* Release tty registration of line discipline */
>   	err = tty_unregister_ldisc(N_HCI);
> diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
> new file mode 100644
> index 0000000..2686901
> --- /dev/null
> +++ b/drivers/bluetooth/hci_mrvl.c
> @@ -0,0 +1,543 @@
> +/* Bluetooth HCI UART driver for Marvell devices
> + *
> + * Copyright (C) 2016, Marvell International Ltd.
> + *
> + *  Acknowledgements:
> + *  This file is based on hci_h4.c, which was written
> + *  by Maxim Krasnyansky and Marcel Holtmann.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License").  You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available on the worldwide web at
> + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/tty.h>
> +#include <asm/unaligned.h>
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include "hci_uart.h"
> +
> +struct fw_data {
> +	wait_queue_head_t init_wait_q;
> +	u8 wait_fw;
> +	int next_len;
> +	u8 five_bytes[5];
> +	u8 next_index;
> +	u8 last_ack;
> +	u8 expected_ack;
> +	struct ktermios old_termios;
> +	u8 chip_id;
> +	u8 chip_rev;
> +	struct sk_buff *skb;
> +};
> +
> +#define MRVL_HELPER_NAME	"mrvl/helper_uart_3000000.bin"
> +#define MRVL_8997_CHIP_ID	0x50
> +#define MRVL_8997_FW_NAME	"mrvl/uart8997_bt.bin"
> +#define MRVL_MAX_FW_BLOCK_SIZE	1024
> +#define MRVL_MAX_RETRY_SEND	12
> +#define MRVL_DNLD_DELAY		100
> +#define MRVL_ACK		0x5A
> +#define MRVL_NAK		0xBF
> +#define MRVL_HDR_REQ_FW		0xA5
> +#define MRVL_HDR_CHIP_VER	0xAA
> +#define MRVL_HCI_OP_SET_BAUD	0xFC09
> +#define MRVL_FW_HDR_LEN		5
> +#define MRVL_WAIT_TIMEOUT	msecs_to_jiffies(12000)
> +
> +struct mrvl_data {
> +	struct sk_buff *rx_skb;
> +	struct sk_buff_head txq;
> +	struct fw_data *fwdata;
> +};
> +
> +static int get_cts(struct hci_uart *hu)
> +{
> +	struct tty_struct *tty = hu->tty;
> +	u32 state =  tty->ops->tiocmget(tty);
> +
> +	if (state & TIOCM_CTS) {
> +		bt_dev_dbg(hu->hdev, "CTS is low");
> +		return 1;
> +	}
> +	bt_dev_dbg(hu->hdev, "CTS is high");
> +
> +	return 0;
> +}
> +
> +/* Initialize protocol */
> +static int mrvl_open(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
> +	if (!mrvl)
> +		return -ENOMEM;
> +
> +	skb_queue_head_init(&mrvl->txq);
> +	hu->priv = mrvl;
> +
> +	return 0;
> +}
> +
> +/* Flush protocol data */
> +static int mrvl_flush(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	skb_queue_purge(&mrvl->txq);
> +
> +	return 0;
> +}
> +
> +/* Close protocol */
> +static int mrvl_close(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	skb_queue_purge(&mrvl->txq);
> +	kfree_skb(mrvl->rx_skb);
> +	hu->priv = NULL;
> +	kfree(mrvl);
> +
> +	return 0;
> +}
> +
> +/* Enqueue frame for transmittion (padding, crc, etc) */
> +static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
> +
> +	/* Prepend skb with frame type */
> +	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
> +	skb_queue_tail(&mrvl->txq, skb);
> +
> +	return 0;
> +}
> +
> +static const struct h4_recv_pkt mrvl_recv_pkts[] = {
> +	{ H4_RECV_ACL,   .recv = hci_recv_frame },
> +	{ H4_RECV_SCO,   .recv = hci_recv_frame },
> +	{ H4_RECV_EVENT, .recv = hci_recv_frame },
> +};
> +
> +/* Send ACK/NAK to the device */
> +static void mrvl_send_ack(struct hci_uart *hu, unsigned char ack)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct sk_buff *skb;
> +
> +	skb = bt_skb_alloc(sizeof(ack), GFP_KERNEL);
> +	if (!skb)
> +		return;
> +
> +	memcpy(skb->data, &ack, sizeof(ack));
> +	skb_put(skb, sizeof(ack));
> +	skb_queue_head(&mrvl->txq, skb);
> +	hci_uart_tx_wakeup(hu);
> +}
> +
> +/* Validate the feedback data from device */
> +static void mrvl_pkt_complete(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +	u16 lhs, rhs;
> +
> +	lhs = get_unaligned_le16(skb->data + 1);
> +	rhs = get_unaligned_le16(skb->data + 3);
> +	if ((lhs ^ rhs) == 0xffff) {
> +		mrvl_send_ack(hu, MRVL_ACK);
> +		fw_data->wait_fw = 1;
> +		fw_data->next_len = lhs;
> +		/* Firmware download is done, send the last ack */
> +		if (!lhs)
> +			fw_data->last_ack = 1;
> +
> +		if (fw_data->expected_ack == MRVL_HDR_CHIP_VER) {
> +			fw_data->chip_id = skb->data[1];
> +			fw_data->chip_rev = skb->data[2];
> +		}
> +		wake_up_interruptible(&fw_data->init_wait_q);
> +	} else {
> +		mrvl_send_ack(hu, MRVL_NAK);
> +	}
> +}
> +
> +/* This function receives data from the uart device during firmware download.
> + * Driver expects 5 bytes of data as per the protocal in the below format:
> + * <HEADER><BYTE_1><BYTE_2><BYTE_3><BYTE_4>
> + * BYTE_3 and BYTE_4 are compliment of BYTE_1 an BYTE_2. Data can come in chunks
> + * of any length. If length received is < 5, accumulate the data in an array,
> + * until we have a sequence of 5 bytes, starting with the expected HEADER. If
> + * the length received is > 5  bytes, then get the first 5 bytes, starting with
> + * the HEADER and process the same, ignoring the rest of the bytes as per the
> + * protocal.
> + */
> +static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
> +					    struct sk_buff *skb,
> +					    u8 *buf, int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +	int i = 0, len;
> +
> +	if (!skb) {
> +		while (buf[i] != fw_data->expected_ack && i < count)
> +			i++;
> +		if (i == count)
> +			return ERR_PTR(-EILSEQ);
> +
> +		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);
> +	}
> +
> +	if (!skb)
> +		return ERR_PTR(-ENOMEM);
> +
> +	len = count - i;
> +	memcpy(skb_put(skb, len), &buf[i], len);
> +
> +	if (skb->len == MRVL_FW_HDR_LEN) {
> +		mrvl_pkt_complete(hu, skb);
> +		kfree_skb(skb);
> +		skb = NULL;
> +	}
> +
> +	return skb;
> +}
> +
> +/* Receive firmware feedback data */
> +static int mrvl_recv_for_prepare(struct hci_uart *hu, const void *data,
> +				 int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	mrvl->fwdata->skb = mrvl_process_fw_data(hu, mrvl->fwdata->skb,
> +						 (u8 *)data, count);
> +	if (IS_ERR(mrvl->fwdata->skb)) {
> +		int err = PTR_ERR(mrvl->fwdata->skb);
> +
> +		bt_dev_err(hu->hdev,
> +			   "Receive firmware data failed (%d)", err);
> +		mrvl->fwdata->skb = NULL;
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Receive data */
> +static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
> +		return -EUNATCH;
> +
> +	mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
> +				   mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts));
> +	if (IS_ERR(mrvl->rx_skb)) {
> +		int err = PTR_ERR(mrvl->rx_skb);
> +
> +		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
> +		mrvl->rx_skb = NULL;
> +		return err;
> +	}
> +
> +	return count;
> +}
> +
> +static struct sk_buff *mrvl_dequeue(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	return skb_dequeue(&mrvl->txq);
> +}
> +
> +static int mrvl_init_fw_data(struct hci_uart *hu)
> +{
> +	struct fw_data *fwdata;
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	fwdata = kzalloc(sizeof(*fwdata), GFP_KERNEL);
> +	if (!fwdata)
> +		return -ENOMEM;
> +
> +	mrvl->fwdata = fwdata;
> +	init_waitqueue_head(&fwdata->init_wait_q);
> +
> +	return 0;
> +}
> +
> +/* Wait for the header from device */
> +static int mrvl_wait_for_hdr(struct hci_uart *hu, u8 header)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	fw_data->expected_ack = header;
> +	fw_data->wait_fw = 0;
> +
> +	if (!wait_event_interruptible_timeout(fw_data->init_wait_q,
> +					      fw_data->wait_fw,
> +					      MRVL_WAIT_TIMEOUT)) {
> +		BT_ERR("TIMEOUT, waiting for:0x%x", fw_data->expected_ack);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Send bytes to device */
> +static int mrvl_send_data(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	skb_queue_head(&mrvl->txq, skb);
> +	hci_uart_tx_wakeup(hu);
> +
> +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW) == -1)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +/* Download firmware to the device */
> +static int mrvl_dnld_fw(struct hci_uart *hu, const char *file_name)
> +{
> +	const struct firmware *fw = NULL;
> +	struct sk_buff *skb = NULL;
> +	int offset = 0;
> +	int ret, tx_len;
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	ret = request_firmware(&fw, file_name, hu->tty->dev);
> +	if (ret < 0) {
> +		BT_ERR("request_firmware() failed");
> +		return -1;
> +	}
> +
> +	BT_INFO("Downloading FW (%d bytes)", (u16)fw->size);
> +
> +	fw_data->last_ack = 0;
> +
> +	while (1) {
> +		if ((offset >= fw->size) || (fw_data->last_ack))
> +			break;
> +		tx_len = fw_data->next_len;
> +		if ((fw->size - offset) < tx_len)
> +			tx_len = fw->size - offset;
> +
> +		skb = bt_skb_alloc(MRVL_MAX_FW_BLOCK_SIZE, GFP_KERNEL);
> +		if (!skb) {
> +			ret = -1;
> +			goto done;
> +		}
> +
> +		memcpy(skb->data, &fw->data[offset], tx_len);
> +		skb_put(skb, tx_len);
> +		if (mrvl_send_data(hu, skb) != 0) {
> +			BT_ERR("Fail to download firmware");
> +			ret = -1;
> +			goto done;
> +		}
> +		offset += tx_len;
> +	}
> +
> +	BT_INFO("Downloaded %d byte firmware", offset);
> +done:
> +	release_firmware(fw);
> +
> +	return ret;
> +}
> +
> +/* Set the baud rate */
> +static int mrvl_set_dev_baud(struct hci_uart *hu)
> +{
> +	struct hci_dev *hdev = hu->hdev;
> +	struct sk_buff *skb;
> +	static const u8 baud_param[] = { 0xc0, 0xc6, 0x2d, 0x00 };
> +	int err;
> +
> +	skb = __hci_cmd_sync(hdev, MRVL_HCI_OP_SET_BAUD, sizeof(baud_param),
> +			     baud_param, HCI_INIT_TIMEOUT);
> +	if (IS_ERR(skb)) {
> +		err = PTR_ERR(skb);
> +		bt_dev_err(hu->hdev, "Set device baudrate failed (%d)", err);
> +		return err;
> +	}
> +	kfree_skb(skb);
> +
> +	return 0;
> +}
> +
> +/* Reset device */
> +static int mrvl_reset(struct hci_uart *hu)
> +{
> +	struct hci_dev *hdev = hu->hdev;
> +	struct sk_buff *skb;
> +	int err;
> +
> +	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
> +	if (IS_ERR(skb)) {
> +		err = PTR_ERR(skb);
> +		bt_dev_err(hu->hdev, "Reset device failed (%d)", err);
> +		return err;
> +	}
> +	kfree_skb(skb);
> +
> +	return 0;
> +}
> +
> +static int mrvl_get_fw_name(struct hci_uart *hu, char *fw_name)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_CHIP_VER) != 0) {
> +		BT_ERR("Could not read chip id and revision");
> +		return -1;
> +	}
> +
> +	BT_DBG("chip_id=0x%x, chip_rev=0x%x",
> +	       fw_data->chip_id, fw_data->chip_rev);
> +
> +	switch (fw_data->chip_id) {
> +	case MRVL_8997_CHIP_ID:
> +		memcpy(fw_name, MRVL_8997_FW_NAME, sizeof(MRVL_8997_FW_NAME));
> +		return 0;
> +	default:
> +		BT_ERR("Invalid chip id");
> +		return -1;
> +	}
> +}
> +
> +/* Download helper and firmare to device */
> +static int hci_uart_dnld_fw(struct hci_uart *hu)
> +{
> +	struct tty_struct *tty = hu->tty;
> +	struct ktermios new_termios;
> +	struct ktermios old_termios;
> +	char fw_name[128];
> +	int ret;
> +
> +	old_termios = tty->termios;
> +
> +	if (get_cts(hu)) {
> +		BT_INFO("fw is running");
> +		return 0;
> +	}
> +
> +	hci_uart_set_baudrate(hu, 115200);
> +	hci_uart_set_flow_control(hu, true);
> +
> +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_dnld_fw(hu, MRVL_HELPER_NAME);
> +	if (ret)
> +		goto fail;
> +
> +	msleep(MRVL_DNLD_DELAY);
> +
> +	hci_uart_set_baudrate(hu, 3000000);
> +	hci_uart_set_flow_control(hu, false);
> +
> +	ret = mrvl_get_fw_name(hu, fw_name);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_dnld_fw(hu, fw_name);
> +	if (ret)
> +		goto fail;
> +
> +	msleep(MRVL_DNLD_DELAY);
> +fail:
> +	/* restore uart settings */
> +	new_termios = tty->termios;
> +	tty->termios.c_cflag = old_termios.c_cflag;
> +	tty_set_termios(tty, &new_termios);
> +
> +	return ret;
> +}
> +
> +static int mrvl_setup(struct hci_uart *hu)
> +{
> +	int err;
> +
> +	hci_uart_set_baudrate(hu, 115200);
> +	hci_uart_set_flow_control(hu, false);
> +
> +	err = mrvl_reset(hu);
> +	if (!err) {
> +		err = mrvl_set_dev_baud(hu);
> +		if (err)
> +			return -1;
> +	}
> +
> +	hci_uart_set_baudrate(hu, 3000000);
> +	hci_uart_set_flow_control(hu, false);
> +	msleep(MRVL_DNLD_DELAY);
> +
> +	return 0;
> +}
> +
> +static int mrvl_prepare(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	int err;
> +
> +	err = mrvl_init_fw_data(hu);
> +	if (!err)
> +		err = hci_uart_dnld_fw(hu);
> +
> +	kfree(mrvl->fwdata);
> +	return err;
> +}
> +
> +static const struct hci_uart_proto mrvlp = {
> +	.id		= HCI_UART_MRVL,
> +	.name		= "MRVL",
> +	.open		= mrvl_open,
> +	.close		= mrvl_close,
> +	.recv		= mrvl_recv,
> +	.enqueue	= mrvl_enqueue,
> +	.dequeue	= mrvl_dequeue,
> +	.flush		= mrvl_flush,
> +	.setup		= mrvl_setup,
> +	.prepare	= mrvl_prepare,
> +	.recv_for_prepare	= mrvl_recv_for_prepare,
> +};
> +
> +int __init mrvl_init(void)
> +{
> +	return hci_uart_register_proto(&mrvlp);
> +}
> +
> +int __exit mrvl_deinit(void)
> +{
> +	return hci_uart_unregister_proto(&mrvlp);
> +}
> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
> index 17ba3b4..8c53b50 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	10
> +#define HCI_UART_MAX_PROTO	11
>   
>   #define HCI_UART_H4	0
>   #define HCI_UART_BCSP	1
> @@ -47,6 +47,7 @@
>   #define HCI_UART_BCM	7
>   #define HCI_UART_QCA	8
>   #define HCI_UART_AG6XX	9
> +#define HCI_UART_MRVL	10
>   
>   #define HCI_UART_RAW_DEVICE	0
>   #define HCI_UART_RESET_ON_INIT	1
> @@ -192,3 +193,8 @@ int qca_deinit(void);
>   int ag6xx_init(void);
>   int ag6xx_deinit(void);
>   #endif
> +
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +int mrvl_init(void);
> +int mrvl_deinit(void);
> +#endif
>
>

-- 
caesar wang | software engineer | wxt@rock-chip.com

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v11, 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure.
  2016-05-06 15:31 [PATCH v11 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Amitkumar Karwar
  2016-05-06 15:31 ` [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it Amitkumar Karwar
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
@ 2016-06-24  6:01 ` Jeffy Chen
  2 siblings, 0 replies; 15+ messages in thread
From: Jeffy Chen @ 2016-06-24  6:01 UTC (permalink / raw)
  To: Amitkumar Karwar, linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat

On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <gbhat@marvell.com>
>
> The new callback is used to prepare the device before HCI becomes
> ready. One can use this to download firmware if the download process
> doesn't use HCI commands. Also recv_for_prepare callback is
> introduced for receiving data from devices during prepare phase.
>
> Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> ---
>   drivers/bluetooth/hci_ldisc.c | 11 ++++++++++-
>   drivers/bluetooth/hci_uart.h  |  3 +++
>   2 files changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index 49b3e1e..b4ee682 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -551,8 +551,11 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
>   	if (!hu || tty != hu->tty)
>   		return;
>   
> -	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags))
> +	if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
> +		if (hu->proto->recv_for_prepare)
> +			hu->proto->recv_for_prepare(hu, data, count);
>   		return;
> +	}
>   
>   	/* It does not need a lock here as it is already protected by a mutex in
>   	 * tty caller
> @@ -639,6 +642,12 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id)
>   		return err;
>   
>   	hu->proto = p;
> +	if (p->prepare) {
> +		err = p->prepare(hu);
> +		if (err)
> +			return err;
> +	}
> +
>   	set_bit(HCI_UART_PROTO_READY, &hu->flags);
>   
>   	err = hci_uart_register_dev(hu);
> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
> index 839bad1..17ba3b4 100644
> --- a/drivers/bluetooth/hci_uart.h
> +++ b/drivers/bluetooth/hci_uart.h
> @@ -67,8 +67,11 @@ struct hci_uart_proto {
>   	int (*close)(struct hci_uart *hu);
>   	int (*flush)(struct hci_uart *hu);
>   	int (*setup)(struct hci_uart *hu);
> +	int (*prepare)(struct hci_uart *hu);
>   	int (*set_baudrate)(struct hci_uart *hu, unsigned int speed);
>   	int (*recv)(struct hci_uart *hu, const void *data, int len);
> +	int (*recv_for_prepare)(struct hci_uart *hu, const void *data,
> +				int len);
>   	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
>   	struct sk_buff *(*dequeue)(struct hci_uart *hu);
>   };
>
>
This serial of patches work well on my device...

Tested-by: Jeffy Chen <jeffy.chen@rock-chips.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v11, 2/3] Bluetooth: hci_uart: check if hdev is present before using it
  2016-05-06 15:31 ` [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it Amitkumar Karwar
@ 2016-06-24  6:01   ` Jeffy Chen
  0 siblings, 0 replies; 15+ messages in thread
From: Jeffy Chen @ 2016-06-24  6:01 UTC (permalink / raw)
  To: Amitkumar Karwar, linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat

On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <gbhat@marvell.com>
>
> The hdev struct might not have initialized in protocol receive handler.
> This patch adds necessary checks.
>
> Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> ---
>   drivers/bluetooth/hci_ldisc.c | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
> index b4ee682..047e786 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -154,7 +154,9 @@ restart:
>   
>   		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
>   		len = tty->ops->write(tty, skb->data, skb->len);
> -		hdev->stat.byte_tx += len;
> +
> +		if (hdev)
> +			hdev->stat.byte_tx += len;
>   
>   		skb_pull(skb, len);
>   		if (skb->len) {
> @@ -349,7 +351,7 @@ void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
>   	/* tty_set_termios() return not checked as it is always 0 */
>   	tty_set_termios(tty, &ktermios);
>   
> -	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
> +	BT_DBG("%s: New tty speeds: %d/%d", hu->hdev ? hu->hdev->name : "",
>   	       tty->termios.c_ispeed, tty->termios.c_ospeed);
>   }
>   
>
>
This serial of patches work well on my device...

Tested-by: Jeffy Chen <jeffy.chen@rock-chips.com>


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
                     ` (3 preceding siblings ...)
  2016-06-21  3:47   ` [v11,3/3] " Caesar Wang
@ 2016-06-24  6:02   ` Jeffy Chen
  2016-06-27  6:28       ` Amitkumar Karwar
  4 siblings, 1 reply; 15+ messages in thread
From: Jeffy Chen @ 2016-06-24  6:02 UTC (permalink / raw)
  To: Amitkumar Karwar, linux-bluetooth; +Cc: linux-kernel, Ganapathi Bhat

On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <gbhat@marvell.com>
>
> This patch implement firmware download feature for
> Marvell Bluetooth devices. If firmware is already
> downloaded, it will skip downloading.
>
> Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> ---
> v2: Fixed compilation warning reported by kbuild test robot
> v3: Addressed review comments from Marcel Holtmann
>      a) Removed vendor specific code from hci_ldisc.c
>      b) Get rid of static forward declaration
>      c) Removed unnecessary heavy nesting
>      d) Git rid of module parameter and global variables
>      e) Add logic to pick right firmware image
> v4: Addresses review comments from Alan
>      a) Use existing kernel helper APIs instead of writing own.
>      b) Replace mdelay() with msleep()
> v5: Addresses review comments from Loic Poulain
>      a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
>      b) Used static functions where required and removed forward delcarations
>      c) Edited comments for the function hci_uart_recv_data
>      d) Made HCI_UART_DNLD_FW flag a part of driver private data
> v6: Addresses review comments from Loic Poulain
>      a) Used skb instead of array to store firmware data during download
>      b) Used hci_uart_tx_wakeup and enqueued packets instead of tty write
>      c) Used GFP_KERNEL instead of GFP_ATOMIC
> v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change resolves
>      errors reported by kbuild test robot.
> v8: Addressed review comments from Marcel Holtmann
>      a) Removed unnecessary memory allocation failure messages
>      b) Get rid of btmrvl.h header file and add definitions in hci_mrvl.c file
> v9: Addressed review comments from Marcel Holtmann
>      a) Moved firmware download code from setup to prepare handler.
>      b) Change messages from bt_dev_*->BT_*, as hdev isn't available during firmware
>       download.
> v10: Addressed review comments from Marcel Holtmann
>      a) Added new callback recv_for_prepare to receive data from device
>       during prepare phase
>      b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive callback is
>       added for the same purpose
>      c) Used kernel API to handle unaligned data
>      d) Moved mrvl_set_baud functionality inside setup callback
> v11: Write data through ldisc in mrvl_send_ack() instead of directly calling
>      write method(One Thousand Gnomes).
> ---
>   drivers/bluetooth/Kconfig     |  11 +
>   drivers/bluetooth/Makefile    |   1 +
>   drivers/bluetooth/hci_ldisc.c |   6 +
>   drivers/bluetooth/hci_mrvl.c  | 543 ++++++++++++++++++++++++++++++++++++++++++
>   drivers/bluetooth/hci_uart.h  |   8 +-
>   5 files changed, 568 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/bluetooth/hci_mrvl.c
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index cf50fd2..daafd0c 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
>   
>   	  Say Y here to compile support for Intel AG6XX protocol.
>   
> +config BT_HCIUART_MRVL
> +	bool "Marvell protocol support"
> +	depends on BT_HCIUART
> +	select BT_HCIUART_H4
> +	help
> +	  Marvell is serial protocol for communication between Bluetooth
> +	  device and host. This protocol is required for most Marvell Bluetooth
> +	  devices with UART interface.
> +
> +	  Say Y here to compile support for HCI MRVL protocol.
> +
>   config BT_HCIBCM203X
>   	tristate "HCI BCM203x USB driver"
>   	depends on USB
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index 9c18939..364dbb6 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -37,6 +37,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL)	+= hci_intel.o
>   hci_uart-$(CONFIG_BT_HCIUART_BCM)	+= hci_bcm.o
>   hci_uart-$(CONFIG_BT_HCIUART_QCA)	+= hci_qca.o
>   hci_uart-$(CONFIG_BT_HCIUART_AG6XX)	+= hci_ag6xx.o
> +hci_uart-$(CONFIG_BT_HCIUART_MRVL)	+= hci_mrvl.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 047e786..4896b6f 100644
> --- a/drivers/bluetooth/hci_ldisc.c
> +++ b/drivers/bluetooth/hci_ldisc.c
> @@ -821,6 +821,9 @@ static int __init hci_uart_init(void)
>   #ifdef CONFIG_BT_HCIUART_AG6XX
>   	ag6xx_init();
>   #endif
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +	mrvl_init();
> +#endif
>   
>   	return 0;
>   }
> @@ -856,6 +859,9 @@ static void __exit hci_uart_exit(void)
>   #ifdef CONFIG_BT_HCIUART_AG6XX
>   	ag6xx_deinit();
>   #endif
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +	mrvl_deinit();
> +#endif
>   
>   	/* Release tty registration of line discipline */
>   	err = tty_unregister_ldisc(N_HCI);
> diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
> new file mode 100644
> index 0000000..2686901
> --- /dev/null
> +++ b/drivers/bluetooth/hci_mrvl.c
> @@ -0,0 +1,543 @@
> +/* Bluetooth HCI UART driver for Marvell devices
> + *
> + * Copyright (C) 2016, Marvell International Ltd.
> + *
> + *  Acknowledgements:
> + *  This file is based on hci_h4.c, which was written
> + *  by Maxim Krasnyansky and Marcel Holtmann.
> + *
> + * This software file (the "File") is distributed by Marvell International
> + * Ltd. under the terms of the GNU General Public License Version 2, June 1991
> + * (the "License").  You may use, redistribute and/or modify this File in
> + * accordance with the terms and conditions of the License, a copy of which
> + * is available on the worldwide web at
> + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
> + *
> + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
> + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
> + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
> + * this warranty disclaimer.
> + */
> +
> +#include <linux/firmware.h>
> +#include <linux/tty.h>
> +#include <asm/unaligned.h>
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include "hci_uart.h"
> +
> +struct fw_data {
> +	wait_queue_head_t init_wait_q;
> +	u8 wait_fw;
> +	int next_len;
> +	u8 five_bytes[5];
> +	u8 next_index;
> +	u8 last_ack;
> +	u8 expected_ack;
> +	struct ktermios old_termios;
> +	u8 chip_id;
> +	u8 chip_rev;
> +	struct sk_buff *skb;
> +};
> +
> +#define MRVL_HELPER_NAME	"mrvl/helper_uart_3000000.bin"
> +#define MRVL_8997_CHIP_ID	0x50
> +#define MRVL_8997_FW_NAME	"mrvl/uart8997_bt.bin"
> +#define MRVL_MAX_FW_BLOCK_SIZE	1024
> +#define MRVL_MAX_RETRY_SEND	12
> +#define MRVL_DNLD_DELAY		100
> +#define MRVL_ACK		0x5A
> +#define MRVL_NAK		0xBF
> +#define MRVL_HDR_REQ_FW		0xA5
> +#define MRVL_HDR_CHIP_VER	0xAA
> +#define MRVL_HCI_OP_SET_BAUD	0xFC09
> +#define MRVL_FW_HDR_LEN		5
> +#define MRVL_WAIT_TIMEOUT	msecs_to_jiffies(12000)
> +
> +struct mrvl_data {
> +	struct sk_buff *rx_skb;
> +	struct sk_buff_head txq;
> +	struct fw_data *fwdata;
> +};
> +
> +static int get_cts(struct hci_uart *hu)
> +{
> +	struct tty_struct *tty = hu->tty;
> +	u32 state =  tty->ops->tiocmget(tty);
> +
> +	if (state & TIOCM_CTS) {
> +		bt_dev_dbg(hu->hdev, "CTS is low");
> +		return 1;
> +	}
> +	bt_dev_dbg(hu->hdev, "CTS is high");
> +
> +	return 0;
> +}
> +
> +/* Initialize protocol */
> +static int mrvl_open(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
> +	if (!mrvl)
> +		return -ENOMEM;
> +
> +	skb_queue_head_init(&mrvl->txq);
> +	hu->priv = mrvl;
> +
> +	return 0;
> +}
> +
> +/* Flush protocol data */
> +static int mrvl_flush(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	skb_queue_purge(&mrvl->txq);
> +
> +	return 0;
> +}
> +
> +/* Close protocol */
> +static int mrvl_close(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> +
> +	skb_queue_purge(&mrvl->txq);
> +	kfree_skb(mrvl->rx_skb);
> +	hu->priv = NULL;
> +	kfree(mrvl);
> +
> +	return 0;
> +}
> +
> +/* Enqueue frame for transmittion (padding, crc, etc) */
> +static int mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
> +
> +	/* Prepend skb with frame type */
> +	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
> +	skb_queue_tail(&mrvl->txq, skb);
> +
> +	return 0;
> +}
> +
> +static const struct h4_recv_pkt mrvl_recv_pkts[] = {
> +	{ H4_RECV_ACL,   .recv = hci_recv_frame },
> +	{ H4_RECV_SCO,   .recv = hci_recv_frame },
> +	{ H4_RECV_EVENT, .recv = hci_recv_frame },
> +};
> +
> +/* Send ACK/NAK to the device */
> +static void mrvl_send_ack(struct hci_uart *hu, unsigned char ack)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct sk_buff *skb;
> +
> +	skb = bt_skb_alloc(sizeof(ack), GFP_KERNEL);
> +	if (!skb)
> +		return;
> +
> +	memcpy(skb->data, &ack, sizeof(ack));
> +	skb_put(skb, sizeof(ack));
> +	skb_queue_head(&mrvl->txq, skb);
> +	hci_uart_tx_wakeup(hu);
> +}
> +
> +/* Validate the feedback data from device */
> +static void mrvl_pkt_complete(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +	u16 lhs, rhs;
> +
> +	lhs = get_unaligned_le16(skb->data + 1);
> +	rhs = get_unaligned_le16(skb->data + 3);
> +	if ((lhs ^ rhs) == 0xffff) {
> +		mrvl_send_ack(hu, MRVL_ACK);
> +		fw_data->wait_fw = 1;
> +		fw_data->next_len = lhs;
> +		/* Firmware download is done, send the last ack */
> +		if (!lhs)
> +			fw_data->last_ack = 1;
> +
> +		if (fw_data->expected_ack == MRVL_HDR_CHIP_VER) {
> +			fw_data->chip_id = skb->data[1];
> +			fw_data->chip_rev = skb->data[2];
> +		}
> +		wake_up_interruptible(&fw_data->init_wait_q);
> +	} else {
> +		mrvl_send_ack(hu, MRVL_NAK);
> +	}
> +}
> +
> +/* This function receives data from the uart device during firmware download.
> + * Driver expects 5 bytes of data as per the protocal in the below format:
> + * <HEADER><BYTE_1><BYTE_2><BYTE_3><BYTE_4>
> + * BYTE_3 and BYTE_4 are compliment of BYTE_1 an BYTE_2. Data can come in chunks
> + * of any length. If length received is < 5, accumulate the data in an array,
> + * until we have a sequence of 5 bytes, starting with the expected HEADER. If
> + * the length received is > 5  bytes, then get the first 5 bytes, starting with
> + * the HEADER and process the same, ignoring the rest of the bytes as per the
> + * protocal.
> + */
> +static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
> +					    struct sk_buff *skb,
> +					    u8 *buf, int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +	int i = 0, len;
> +
> +	if (!skb) {
> +		while (buf[i] != fw_data->expected_ack && i < count)
> +			i++;
> +		if (i == count)
> +			return ERR_PTR(-EILSEQ);
> +
> +		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);
> +	}
> +
> +	if (!skb)
> +		return ERR_PTR(-ENOMEM);
> +
> +	len = count - i;
> +	memcpy(skb_put(skb, len), &buf[i], len);
> +
> +	if (skb->len == MRVL_FW_HDR_LEN) {
> +		mrvl_pkt_complete(hu, skb);
> +		kfree_skb(skb);
> +		skb = NULL;
> +	}
> +
> +	return skb;
> +}
> +
> +/* Receive firmware feedback data */
> +static int mrvl_recv_for_prepare(struct hci_uart *hu, const void *data,
> +				 int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	mrvl->fwdata->skb = mrvl_process_fw_data(hu, mrvl->fwdata->skb,
> +						 (u8 *)data, count);
> +	if (IS_ERR(mrvl->fwdata->skb)) {
> +		int err = PTR_ERR(mrvl->fwdata->skb);
> +
> +		bt_dev_err(hu->hdev,
> +			   "Receive firmware data failed (%d)", err);
> +		mrvl->fwdata->skb = NULL;
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Receive data */
> +static int mrvl_recv(struct hci_uart *hu, const void *data, int count)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
> +		return -EUNATCH;
> +
> +	mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
> +				   mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts));
> +	if (IS_ERR(mrvl->rx_skb)) {
> +		int err = PTR_ERR(mrvl->rx_skb);
> +
> +		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
> +		mrvl->rx_skb = NULL;
> +		return err;
> +	}
> +
> +	return count;
> +}
> +
> +static struct sk_buff *mrvl_dequeue(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	return skb_dequeue(&mrvl->txq);
> +}
> +
> +static int mrvl_init_fw_data(struct hci_uart *hu)
> +{
> +	struct fw_data *fwdata;
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	fwdata = kzalloc(sizeof(*fwdata), GFP_KERNEL);
> +	if (!fwdata)
> +		return -ENOMEM;
> +
> +	mrvl->fwdata = fwdata;
> +	init_waitqueue_head(&fwdata->init_wait_q);
> +
> +	return 0;
> +}
> +
> +/* Wait for the header from device */
> +static int mrvl_wait_for_hdr(struct hci_uart *hu, u8 header)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	fw_data->expected_ack = header;
> +	fw_data->wait_fw = 0;
> +
> +	if (!wait_event_interruptible_timeout(fw_data->init_wait_q,
> +					      fw_data->wait_fw,
> +					      MRVL_WAIT_TIMEOUT)) {
> +		BT_ERR("TIMEOUT, waiting for:0x%x", fw_data->expected_ack);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Send bytes to device */
> +static int mrvl_send_data(struct hci_uart *hu, struct sk_buff *skb)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +
> +	skb_queue_head(&mrvl->txq, skb);
> +	hci_uart_tx_wakeup(hu);
> +
> +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW) == -1)
> +		return -1;
> +
> +	return 0;
> +}
> +
> +/* Download firmware to the device */
> +static int mrvl_dnld_fw(struct hci_uart *hu, const char *file_name)
> +{
> +	const struct firmware *fw = NULL;
> +	struct sk_buff *skb = NULL;
> +	int offset = 0;
> +	int ret, tx_len;
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	ret = request_firmware(&fw, file_name, hu->tty->dev);
> +	if (ret < 0) {
> +		BT_ERR("request_firmware() failed");
> +		return -1;
> +	}
> +
> +	BT_INFO("Downloading FW (%d bytes)", (u16)fw->size);
> +
> +	fw_data->last_ack = 0;
> +
> +	while (1) {
> +		if ((offset >= fw->size) || (fw_data->last_ack))
> +			break;
> +		tx_len = fw_data->next_len;
> +		if ((fw->size - offset) < tx_len)
> +			tx_len = fw->size - offset;
> +
> +		skb = bt_skb_alloc(MRVL_MAX_FW_BLOCK_SIZE, GFP_KERNEL);
> +		if (!skb) {
> +			ret = -1;
> +			goto done;
> +		}
> +
> +		memcpy(skb->data, &fw->data[offset], tx_len);
> +		skb_put(skb, tx_len);
> +		if (mrvl_send_data(hu, skb) != 0) {
> +			BT_ERR("Fail to download firmware");
> +			ret = -1;
> +			goto done;
> +		}
> +		offset += tx_len;
> +	}
> +
> +	BT_INFO("Downloaded %d byte firmware", offset);
> +done:
> +	release_firmware(fw);
> +
> +	return ret;
> +}
> +
> +/* Set the baud rate */
> +static int mrvl_set_dev_baud(struct hci_uart *hu)
> +{
> +	struct hci_dev *hdev = hu->hdev;
> +	struct sk_buff *skb;
> +	static const u8 baud_param[] = { 0xc0, 0xc6, 0x2d, 0x00 };
> +	int err;
> +
> +	skb = __hci_cmd_sync(hdev, MRVL_HCI_OP_SET_BAUD, sizeof(baud_param),
> +			     baud_param, HCI_INIT_TIMEOUT);
> +	if (IS_ERR(skb)) {
> +		err = PTR_ERR(skb);
> +		bt_dev_err(hu->hdev, "Set device baudrate failed (%d)", err);
> +		return err;
> +	}
> +	kfree_skb(skb);
> +
> +	return 0;
> +}
> +
> +/* Reset device */
> +static int mrvl_reset(struct hci_uart *hu)
> +{
> +	struct hci_dev *hdev = hu->hdev;
> +	struct sk_buff *skb;
> +	int err;
> +
> +	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
> +	if (IS_ERR(skb)) {
> +		err = PTR_ERR(skb);
> +		bt_dev_err(hu->hdev, "Reset device failed (%d)", err);
> +		return err;
> +	}
> +	kfree_skb(skb);
> +
> +	return 0;
> +}
> +
> +static int mrvl_get_fw_name(struct hci_uart *hu, char *fw_name)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	struct fw_data *fw_data = mrvl->fwdata;
> +
> +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_CHIP_VER) != 0) {
> +		BT_ERR("Could not read chip id and revision");
> +		return -1;
> +	}
> +
> +	BT_DBG("chip_id=0x%x, chip_rev=0x%x",
> +	       fw_data->chip_id, fw_data->chip_rev);
> +
> +	switch (fw_data->chip_id) {
> +	case MRVL_8997_CHIP_ID:
> +		memcpy(fw_name, MRVL_8997_FW_NAME, sizeof(MRVL_8997_FW_NAME));
> +		return 0;
> +	default:
> +		BT_ERR("Invalid chip id");
> +		return -1;
> +	}
> +}
> +
> +/* Download helper and firmare to device */
> +static int hci_uart_dnld_fw(struct hci_uart *hu)
> +{
> +	struct tty_struct *tty = hu->tty;
> +	struct ktermios new_termios;
> +	struct ktermios old_termios;
> +	char fw_name[128];
> +	int ret;
> +
> +	old_termios = tty->termios;
> +
> +	if (get_cts(hu)) {
> +		BT_INFO("fw is running");
> +		return 0;
> +	}
> +
> +	hci_uart_set_baudrate(hu, 115200);
> +	hci_uart_set_flow_control(hu, true);
> +
> +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_dnld_fw(hu, MRVL_HELPER_NAME);
> +	if (ret)
> +		goto fail;
> +
> +	msleep(MRVL_DNLD_DELAY);
> +
> +	hci_uart_set_baudrate(hu, 3000000);
> +	hci_uart_set_flow_control(hu, false);
> +
> +	ret = mrvl_get_fw_name(hu, fw_name);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> +	if (ret)
> +		goto fail;
> +
> +	ret = mrvl_dnld_fw(hu, fw_name);
> +	if (ret)
> +		goto fail;
> +
> +	msleep(MRVL_DNLD_DELAY);
> +fail:
> +	/* restore uart settings */
> +	new_termios = tty->termios;
> +	tty->termios.c_cflag = old_termios.c_cflag;
> +	tty_set_termios(tty, &new_termios);
> +
> +	return ret;
> +}
> +
> +static int mrvl_setup(struct hci_uart *hu)
> +{
> +	int err;
> +
> +	hci_uart_set_baudrate(hu, 115200);
> +	hci_uart_set_flow_control(hu, false);
> +
> +	err = mrvl_reset(hu);
> +	if (!err) {
> +		err = mrvl_set_dev_baud(hu);
> +		if (err)
> +			return -1;
> +	}
> +
> +	hci_uart_set_baudrate(hu, 3000000);
> +	hci_uart_set_flow_control(hu, false);
> +	msleep(MRVL_DNLD_DELAY);
> +
> +	return 0;
> +}
> +
> +static int mrvl_prepare(struct hci_uart *hu)
> +{
> +	struct mrvl_data *mrvl = hu->priv;
> +	int err;
> +
> +	err = mrvl_init_fw_data(hu);
> +	if (!err)
> +		err = hci_uart_dnld_fw(hu);
> +
> +	kfree(mrvl->fwdata);
> +	return err;
> +}
> +
> +static const struct hci_uart_proto mrvlp = {
> +	.id		= HCI_UART_MRVL,
> +	.name		= "MRVL",
> +	.open		= mrvl_open,
> +	.close		= mrvl_close,
> +	.recv		= mrvl_recv,
> +	.enqueue	= mrvl_enqueue,
> +	.dequeue	= mrvl_dequeue,
> +	.flush		= mrvl_flush,
> +	.setup		= mrvl_setup,
> +	.prepare	= mrvl_prepare,
> +	.recv_for_prepare	= mrvl_recv_for_prepare,
> +};
> +
> +int __init mrvl_init(void)
> +{
> +	return hci_uart_register_proto(&mrvlp);
> +}
> +
> +int __exit mrvl_deinit(void)
> +{
> +	return hci_uart_unregister_proto(&mrvlp);
> +}
> diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
> index 17ba3b4..8c53b50 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	10
> +#define HCI_UART_MAX_PROTO	11
>   
>   #define HCI_UART_H4	0
>   #define HCI_UART_BCSP	1
> @@ -47,6 +47,7 @@
>   #define HCI_UART_BCM	7
>   #define HCI_UART_QCA	8
>   #define HCI_UART_AG6XX	9
> +#define HCI_UART_MRVL	10
>   
>   #define HCI_UART_RAW_DEVICE	0
>   #define HCI_UART_RESET_ON_INIT	1
> @@ -192,3 +193,8 @@ int qca_deinit(void);
>   int ag6xx_init(void);
>   int ag6xx_deinit(void);
>   #endif
> +
> +#ifdef CONFIG_BT_HCIUART_MRVL
> +int mrvl_init(void);
> +int mrvl_deinit(void);
> +#endif
>
>
This serial of patches work well on my device...

Tested-by: Jeffy Chen <jeffy.chen@rock-chips.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-06-24  6:02   ` Jeffy Chen
@ 2016-06-27  6:28       ` Amitkumar Karwar
  0 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-06-27  6:28 UTC (permalink / raw)
  To: Jeffy Chen, linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Cathy Luo

SGkgTWFyY2VsLA0KDQo+IEZyb206IEplZmZ5IENoZW4gW21haWx0bzpqZWZmeS5jaGVuQHJvY2st
Y2hpcHMuY29tXQ0KPiBTZW50OiBGcmlkYXksIEp1bmUgMjQsIDIwMTYgMTE6MzIgQU0NCj4gVG86
IEFtaXRrdW1hciBLYXJ3YXI7IGxpbnV4LWJsdWV0b290aEB2Z2VyLmtlcm5lbC5vcmcNCj4gQ2M6
IGxpbnV4LWtlcm5lbEB2Z2VyLmtlcm5lbC5vcmc7IEdhbmFwYXRoaSBCaGF0DQo+IFN1YmplY3Q6
IFJlOiBbdjExLDMvM10gQmx1ZXRvb3RoOiBoY2lfdWFydDogU3VwcG9ydCBmaXJtd2FyZSBkb3du
bG9hZA0KPiBmb3IgTWFydmVsbA0KPiANCj4gT24gMjAxNi01LTYgMjM6MzEsIEFtaXRrdW1hciBL
YXJ3YXIgd3JvdGU6DQo+ID4gRnJvbTogR2FuYXBhdGhpIEJoYXQgPGdiaGF0QG1hcnZlbGwuY29t
Pg0KPiA+DQo+ID4gVGhpcyBwYXRjaCBpbXBsZW1lbnQgZmlybXdhcmUgZG93bmxvYWQgZmVhdHVy
ZSBmb3IgTWFydmVsbCBCbHVldG9vdGgNCj4gPiBkZXZpY2VzLiBJZiBmaXJtd2FyZSBpcyBhbHJl
YWR5IGRvd25sb2FkZWQsIGl0IHdpbGwgc2tpcCBkb3dubG9hZGluZy4NCj4gPg0KPiA+IFNpZ25l
ZC1vZmYtYnk6IEdhbmFwYXRoaSBCaGF0IDxnYmhhdEBtYXJ2ZWxsLmNvbT4NCj4gPiBTaWduZWQt
b2ZmLWJ5OiBBbWl0a3VtYXIgS2Fyd2FyIDxha2Fyd2FyQG1hcnZlbGwuY29tPg0KPiA+IC0tLQ0K
PiA+IHYyOiBGaXhlZCBjb21waWxhdGlvbiB3YXJuaW5nIHJlcG9ydGVkIGJ5IGtidWlsZCB0ZXN0
IHJvYm90DQo+ID4gdjM6IEFkZHJlc3NlZCByZXZpZXcgY29tbWVudHMgZnJvbSBNYXJjZWwgSG9s
dG1hbm4NCj4gPiAgICAgIGEpIFJlbW92ZWQgdmVuZG9yIHNwZWNpZmljIGNvZGUgZnJvbSBoY2lf
bGRpc2MuYw0KPiA+ICAgICAgYikgR2V0IHJpZCBvZiBzdGF0aWMgZm9yd2FyZCBkZWNsYXJhdGlv
bg0KPiA+ICAgICAgYykgUmVtb3ZlZCB1bm5lY2Vzc2FyeSBoZWF2eSBuZXN0aW5nDQo+ID4gICAg
ICBkKSBHaXQgcmlkIG9mIG1vZHVsZSBwYXJhbWV0ZXIgYW5kIGdsb2JhbCB2YXJpYWJsZXMNCj4g
PiAgICAgIGUpIEFkZCBsb2dpYyB0byBwaWNrIHJpZ2h0IGZpcm13YXJlIGltYWdlDQo+ID4gdjQ6
IEFkZHJlc3NlcyByZXZpZXcgY29tbWVudHMgZnJvbSBBbGFuDQo+ID4gICAgICBhKSBVc2UgZXhp
c3Rpbmcga2VybmVsIGhlbHBlciBBUElzIGluc3RlYWQgb2Ygd3JpdGluZyBvd24uDQo+ID4gICAg
ICBiKSBSZXBsYWNlIG1kZWxheSgpIHdpdGggbXNsZWVwKCkNCj4gPiB2NTogQWRkcmVzc2VzIHJl
dmlldyBjb21tZW50cyBmcm9tIExvaWMgUG91bGFpbg0KPiA+ICAgICAgYSkgVXNlIGJ0X2Rldl9l
cnIvd2Fybi9kYmcgaGVscGVycyBpbnN0ZWQgb2YgQlRfRVJSL1dBUk4vREJHDQo+ID4gICAgICBi
KSBVc2VkIHN0YXRpYyBmdW5jdGlvbnMgd2hlcmUgcmVxdWlyZWQgYW5kIHJlbW92ZWQgZm9yd2Fy
ZA0KPiBkZWxjYXJhdGlvbnMNCj4gPiAgICAgIGMpIEVkaXRlZCBjb21tZW50cyBmb3IgdGhlIGZ1
bmN0aW9uIGhjaV91YXJ0X3JlY3ZfZGF0YQ0KPiA+ICAgICAgZCkgTWFkZSBIQ0lfVUFSVF9ETkxE
X0ZXIGZsYWcgYSBwYXJ0IG9mIGRyaXZlciBwcml2YXRlIGRhdGENCj4gPiB2NjogQWRkcmVzc2Vz
IHJldmlldyBjb21tZW50cyBmcm9tIExvaWMgUG91bGFpbg0KPiA+ICAgICAgYSkgVXNlZCBza2Ig
aW5zdGVhZCBvZiBhcnJheSB0byBzdG9yZSBmaXJtd2FyZSBkYXRhIGR1cmluZw0KPiBkb3dubG9h
ZA0KPiA+ICAgICAgYikgVXNlZCBoY2lfdWFydF90eF93YWtldXAgYW5kIGVucXVldWVkIHBhY2tl
dHMgaW5zdGVhZCBvZiB0dHkNCj4gd3JpdGUNCj4gPiAgICAgIGMpIFVzZWQgR0ZQX0tFUk5FTCBp
bnN0ZWFkIG9mIEdGUF9BVE9NSUMNCj4gPiB2NzogRWRpdGVkIEtjb25maWcgdG8gYWRkIGRlcGVu
ZGVuY3kgZm9yIEJUX0hDSVVBUlRfSDQuIFRoZSBjaGFuZ2UNCj4gcmVzb2x2ZXMNCj4gPiAgICAg
IGVycm9ycyByZXBvcnRlZCBieSBrYnVpbGQgdGVzdCByb2JvdC4NCj4gPiB2ODogQWRkcmVzc2Vk
IHJldmlldyBjb21tZW50cyBmcm9tIE1hcmNlbCBIb2x0bWFubg0KPiA+ICAgICAgYSkgUmVtb3Zl
ZCB1bm5lY2Vzc2FyeSBtZW1vcnkgYWxsb2NhdGlvbiBmYWlsdXJlIG1lc3NhZ2VzDQo+ID4gICAg
ICBiKSBHZXQgcmlkIG9mIGJ0bXJ2bC5oIGhlYWRlciBmaWxlIGFuZCBhZGQgZGVmaW5pdGlvbnMg
aW4NCj4gPiBoY2lfbXJ2bC5jIGZpbGUNCj4gPiB2OTogQWRkcmVzc2VkIHJldmlldyBjb21tZW50
cyBmcm9tIE1hcmNlbCBIb2x0bWFubg0KPiA+ICAgICAgYSkgTW92ZWQgZmlybXdhcmUgZG93bmxv
YWQgY29kZSBmcm9tIHNldHVwIHRvIHByZXBhcmUgaGFuZGxlci4NCj4gPiAgICAgIGIpIENoYW5n
ZSBtZXNzYWdlcyBmcm9tIGJ0X2Rldl8qLT5CVF8qLCBhcyBoZGV2IGlzbid0IGF2YWlsYWJsZQ0K
PiBkdXJpbmcgZmlybXdhcmUNCj4gPiAgICAgICBkb3dubG9hZC4NCj4gPiB2MTA6IEFkZHJlc3Nl
ZCByZXZpZXcgY29tbWVudHMgZnJvbSBNYXJjZWwgSG9sdG1hbm4NCj4gPiAgICAgIGEpIEFkZGVk
IG5ldyBjYWxsYmFjayByZWN2X2Zvcl9wcmVwYXJlIHRvIHJlY2VpdmUgZGF0YSBmcm9tDQo+IGRl
dmljZQ0KPiA+ICAgICAgIGR1cmluZyBwcmVwYXJlIHBoYXNlDQo+ID4gICAgICBiKSBBdm9pZGVk
IHVzaW5nIHByaXZhdGUgZmxhZ3MgKEhDSV9VQVJUX0ROTERfRlcpIGFzIG5ldyByZWNlaXZlDQo+
IGNhbGxiYWNrIGlzDQo+ID4gICAgICAgYWRkZWQgZm9yIHRoZSBzYW1lIHB1cnBvc2UNCj4gPiAg
ICAgIGMpIFVzZWQga2VybmVsIEFQSSB0byBoYW5kbGUgdW5hbGlnbmVkIGRhdGENCj4gPiAgICAg
IGQpIE1vdmVkIG1ydmxfc2V0X2JhdWQgZnVuY3Rpb25hbGl0eSBpbnNpZGUgc2V0dXAgY2FsbGJh
Y2sNCj4gPiB2MTE6IFdyaXRlIGRhdGEgdGhyb3VnaCBsZGlzYyBpbiBtcnZsX3NlbmRfYWNrKCkg
aW5zdGVhZCBvZiBkaXJlY3RseQ0KPiBjYWxsaW5nDQo+ID4gICAgICB3cml0ZSBtZXRob2QoT25l
IFRob3VzYW5kIEdub21lcykuDQo+ID4gLS0tDQo+ID4gICBkcml2ZXJzL2JsdWV0b290aC9LY29u
ZmlnICAgICB8ICAxMSArDQo+ID4gICBkcml2ZXJzL2JsdWV0b290aC9NYWtlZmlsZSAgICB8ICAg
MSArDQo+ID4gICBkcml2ZXJzL2JsdWV0b290aC9oY2lfbGRpc2MuYyB8ICAgNiArDQo+ID4gICBk
cml2ZXJzL2JsdWV0b290aC9oY2lfbXJ2bC5jICB8IDU0Mw0KPiArKysrKysrKysrKysrKysrKysr
KysrKysrKysrKysrKysrKysrKysrKysNCj4gPiAgIGRyaXZlcnMvYmx1ZXRvb3RoL2hjaV91YXJ0
LmggIHwgICA4ICstDQo+ID4gICA1IGZpbGVzIGNoYW5nZWQsIDU2OCBpbnNlcnRpb25zKCspLCAx
IGRlbGV0aW9uKC0pDQo+ID4gICBjcmVhdGUgbW9kZSAxMDA2NDQgZHJpdmVycy9ibHVldG9vdGgv
aGNpX21ydmwuYw0KPiA+DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvYmx1ZXRvb3RoL0tjb25m
aWcgYi9kcml2ZXJzL2JsdWV0b290aC9LY29uZmlnDQo+ID4gaW5kZXggY2Y1MGZkMi4uZGFhZmQw
YyAxMDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJzL2JsdWV0b290aC9LY29uZmlnDQo+ID4gKysrIGIv
ZHJpdmVycy9ibHVldG9vdGgvS2NvbmZpZw0KPiA+IEBAIC0xODAsNiArMTgwLDE3IEBAIGNvbmZp
ZyBCVF9IQ0lVQVJUX0FHNlhYDQo+ID4NCj4gPiAgIAkgIFNheSBZIGhlcmUgdG8gY29tcGlsZSBz
dXBwb3J0IGZvciBJbnRlbCBBRzZYWCBwcm90b2NvbC4NCj4gPg0KPiA+ICtjb25maWcgQlRfSENJ
VUFSVF9NUlZMDQo+ID4gKwlib29sICJNYXJ2ZWxsIHByb3RvY29sIHN1cHBvcnQiDQo+ID4gKwlk
ZXBlbmRzIG9uIEJUX0hDSVVBUlQNCj4gPiArCXNlbGVjdCBCVF9IQ0lVQVJUX0g0DQo+ID4gKwlo
ZWxwDQo+ID4gKwkgIE1hcnZlbGwgaXMgc2VyaWFsIHByb3RvY29sIGZvciBjb21tdW5pY2F0aW9u
IGJldHdlZW4gQmx1ZXRvb3RoDQo+ID4gKwkgIGRldmljZSBhbmQgaG9zdC4gVGhpcyBwcm90b2Nv
bCBpcyByZXF1aXJlZCBmb3IgbW9zdCBNYXJ2ZWxsDQo+IEJsdWV0b290aA0KPiA+ICsJICBkZXZp
Y2VzIHdpdGggVUFSVCBpbnRlcmZhY2UuDQo+ID4gKw0KPiA+ICsJICBTYXkgWSBoZXJlIHRvIGNv
bXBpbGUgc3VwcG9ydCBmb3IgSENJIE1SVkwgcHJvdG9jb2wuDQo+ID4gKw0KPiA+ICAgY29uZmln
IEJUX0hDSUJDTTIwM1gNCj4gPiAgIAl0cmlzdGF0ZSAiSENJIEJDTTIwM3ggVVNCIGRyaXZlciIN
Cj4gPiAgIAlkZXBlbmRzIG9uIFVTQg0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2JsdWV0b290
aC9NYWtlZmlsZSBiL2RyaXZlcnMvYmx1ZXRvb3RoL01ha2VmaWxlDQo+ID4gaW5kZXggOWMxODkz
OS4uMzY0ZGJiNiAxMDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJzL2JsdWV0b290aC9NYWtlZmlsZQ0K
PiA+ICsrKyBiL2RyaXZlcnMvYmx1ZXRvb3RoL01ha2VmaWxlDQo+ID4gQEAgLTM3LDYgKzM3LDcg
QEAgaGNpX3VhcnQtJChDT05GSUdfQlRfSENJVUFSVF9JTlRFTCkJKz0gaGNpX2ludGVsLm8NCj4g
PiAgIGhjaV91YXJ0LSQoQ09ORklHX0JUX0hDSVVBUlRfQkNNKQkrPSBoY2lfYmNtLm8NCj4gPiAg
IGhjaV91YXJ0LSQoQ09ORklHX0JUX0hDSVVBUlRfUUNBKQkrPSBoY2lfcWNhLm8NCj4gPiAgIGhj
aV91YXJ0LSQoQ09ORklHX0JUX0hDSVVBUlRfQUc2WFgpCSs9IGhjaV9hZzZ4eC5vDQo+ID4gK2hj
aV91YXJ0LSQoQ09ORklHX0JUX0hDSVVBUlRfTVJWTCkJKz0gaGNpX21ydmwubw0KPiA+ICAgaGNp
X3VhcnQtb2JqcwkJCQk6PSAkKGhjaV91YXJ0LXkpDQo+ID4NCj4gPiAgIGNjZmxhZ3MteSArPSAt
RF9fQ0hFQ0tfRU5ESUFOX18NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ibHVldG9vdGgvaGNp
X2xkaXNjLmMNCj4gPiBiL2RyaXZlcnMvYmx1ZXRvb3RoL2hjaV9sZGlzYy5jIGluZGV4IDA0N2U3
ODYuLjQ4OTZiNmYgMTAwNjQ0DQo+ID4gLS0tIGEvZHJpdmVycy9ibHVldG9vdGgvaGNpX2xkaXNj
LmMNCj4gPiArKysgYi9kcml2ZXJzL2JsdWV0b290aC9oY2lfbGRpc2MuYw0KPiA+IEBAIC04MjEs
NiArODIxLDkgQEAgc3RhdGljIGludCBfX2luaXQgaGNpX3VhcnRfaW5pdCh2b2lkKQ0KPiA+ICAg
I2lmZGVmIENPTkZJR19CVF9IQ0lVQVJUX0FHNlhYDQo+ID4gICAJYWc2eHhfaW5pdCgpOw0KPiA+
ICAgI2VuZGlmDQo+ID4gKyNpZmRlZiBDT05GSUdfQlRfSENJVUFSVF9NUlZMDQo+ID4gKwltcnZs
X2luaXQoKTsNCj4gPiArI2VuZGlmDQo+ID4NCj4gPiAgIAlyZXR1cm4gMDsNCj4gPiAgIH0NCj4g
PiBAQCAtODU2LDYgKzg1OSw5IEBAIHN0YXRpYyB2b2lkIF9fZXhpdCBoY2lfdWFydF9leGl0KHZv
aWQpDQo+ID4gICAjaWZkZWYgQ09ORklHX0JUX0hDSVVBUlRfQUc2WFgNCj4gPiAgIAlhZzZ4eF9k
ZWluaXQoKTsNCj4gPiAgICNlbmRpZg0KPiA+ICsjaWZkZWYgQ09ORklHX0JUX0hDSVVBUlRfTVJW
TA0KPiA+ICsJbXJ2bF9kZWluaXQoKTsNCj4gPiArI2VuZGlmDQo+ID4NCj4gPiAgIAkvKiBSZWxl
YXNlIHR0eSByZWdpc3RyYXRpb24gb2YgbGluZSBkaXNjaXBsaW5lICovDQo+ID4gICAJZXJyID0g
dHR5X3VucmVnaXN0ZXJfbGRpc2MoTl9IQ0kpOw0KPiA+IGRpZmYgLS1naXQgYS9kcml2ZXJzL2Js
dWV0b290aC9oY2lfbXJ2bC5jDQo+ID4gYi9kcml2ZXJzL2JsdWV0b290aC9oY2lfbXJ2bC5jIG5l
dyBmaWxlIG1vZGUgMTAwNjQ0IGluZGV4DQo+ID4gMDAwMDAwMC4uMjY4NjkwMQ0KPiA+IC0tLSAv
ZGV2L251bGwNCj4gPiArKysgYi9kcml2ZXJzL2JsdWV0b290aC9oY2lfbXJ2bC5jDQo+ID4gQEAg
LTAsMCArMSw1NDMgQEANCj4gPiArLyogQmx1ZXRvb3RoIEhDSSBVQVJUIGRyaXZlciBmb3IgTWFy
dmVsbCBkZXZpY2VzDQo+ID4gKyAqDQo+ID4gKyAqIENvcHlyaWdodCAoQykgMjAxNiwgTWFydmVs
bCBJbnRlcm5hdGlvbmFsIEx0ZC4NCj4gPiArICoNCj4gPiArICogIEFja25vd2xlZGdlbWVudHM6
DQo+ID4gKyAqICBUaGlzIGZpbGUgaXMgYmFzZWQgb24gaGNpX2g0LmMsIHdoaWNoIHdhcyB3cml0
dGVuDQo+ID4gKyAqICBieSBNYXhpbSBLcmFzbnlhbnNreSBhbmQgTWFyY2VsIEhvbHRtYW5uLg0K
PiA+ICsgKg0KPiA+ICsgKiBUaGlzIHNvZnR3YXJlIGZpbGUgKHRoZSAiRmlsZSIpIGlzIGRpc3Ry
aWJ1dGVkIGJ5IE1hcnZlbGwNCj4gPiArSW50ZXJuYXRpb25hbA0KPiA+ICsgKiBMdGQuIHVuZGVy
IHRoZSB0ZXJtcyBvZiB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgVmVyc2lvbiAyLA0K
PiA+ICtKdW5lIDE5OTENCj4gPiArICogKHRoZSAiTGljZW5zZSIpLiAgWW91IG1heSB1c2UsIHJl
ZGlzdHJpYnV0ZSBhbmQvb3IgbW9kaWZ5IHRoaXMNCj4gPiArRmlsZSBpbg0KPiA+ICsgKiBhY2Nv
cmRhbmNlIHdpdGggdGhlIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHRoZSBMaWNlbnNlLCBhIGNv
cHkgb2YNCj4gPiArd2hpY2gNCj4gPiArICogaXMgYXZhaWxhYmxlIG9uIHRoZSB3b3JsZHdpZGUg
d2ViIGF0DQo+ID4gKyAqIGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy9vbGQtbGljZW5zZXMv
Z3BsLTIuMC50eHQuDQo+ID4gKyAqDQo+ID4gKyAqIFRIRSBGSUxFIElTIERJU1RSSUJVVEVEIEFT
LUlTLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBBTkQNCj4gPiArVEhFDQo+ID4gKyAq
IElNUExJRUQgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFkgT1IgRklUTkVTUyBGT1IgQSBQ
QVJUSUNVTEFSDQo+ID4gK1BVUlBPU0UNCj4gPiArICogQVJFIEVYUFJFU1NMWSBESVNDTEFJTUVE
LiAgVGhlIExpY2Vuc2UgcHJvdmlkZXMgYWRkaXRpb25hbCBkZXRhaWxzDQo+ID4gK2Fib3V0DQo+
ID4gKyAqIHRoaXMgd2FycmFudHkgZGlzY2xhaW1lci4NCj4gPiArICovDQo+ID4gKw0KPiA+ICsj
aW5jbHVkZSA8bGludXgvZmlybXdhcmUuaD4NCj4gPiArI2luY2x1ZGUgPGxpbnV4L3R0eS5oPg0K
PiA+ICsjaW5jbHVkZSA8YXNtL3VuYWxpZ25lZC5oPg0KPiA+ICsjaW5jbHVkZSA8bmV0L2JsdWV0
b290aC9ibHVldG9vdGguaD4NCj4gPiArI2luY2x1ZGUgPG5ldC9ibHVldG9vdGgvaGNpX2NvcmUu
aD4NCj4gPiArI2luY2x1ZGUgImhjaV91YXJ0LmgiDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgZndfZGF0
YSB7DQo+ID4gKwl3YWl0X3F1ZXVlX2hlYWRfdCBpbml0X3dhaXRfcTsNCj4gPiArCXU4IHdhaXRf
Znc7DQo+ID4gKwlpbnQgbmV4dF9sZW47DQo+ID4gKwl1OCBmaXZlX2J5dGVzWzVdOw0KPiA+ICsJ
dTggbmV4dF9pbmRleDsNCj4gPiArCXU4IGxhc3RfYWNrOw0KPiA+ICsJdTggZXhwZWN0ZWRfYWNr
Ow0KPiA+ICsJc3RydWN0IGt0ZXJtaW9zIG9sZF90ZXJtaW9zOw0KPiA+ICsJdTggY2hpcF9pZDsN
Cj4gPiArCXU4IGNoaXBfcmV2Ow0KPiA+ICsJc3RydWN0IHNrX2J1ZmYgKnNrYjsNCj4gPiArfTsN
Cj4gPiArDQo+ID4gKyNkZWZpbmUgTVJWTF9IRUxQRVJfTkFNRQkibXJ2bC9oZWxwZXJfdWFydF8z
MDAwMDAwLmJpbiINCj4gPiArI2RlZmluZSBNUlZMXzg5OTdfQ0hJUF9JRAkweDUwDQo+ID4gKyNk
ZWZpbmUgTVJWTF84OTk3X0ZXX05BTUUJIm1ydmwvdWFydDg5OTdfYnQuYmluIg0KPiA+ICsjZGVm
aW5lIE1SVkxfTUFYX0ZXX0JMT0NLX1NJWkUJMTAyNA0KPiA+ICsjZGVmaW5lIE1SVkxfTUFYX1JF
VFJZX1NFTkQJMTINCj4gPiArI2RlZmluZSBNUlZMX0ROTERfREVMQVkJCTEwMA0KPiA+ICsjZGVm
aW5lIE1SVkxfQUNLCQkweDVBDQo+ID4gKyNkZWZpbmUgTVJWTF9OQUsJCTB4QkYNCj4gPiArI2Rl
ZmluZSBNUlZMX0hEUl9SRVFfRlcJCTB4QTUNCj4gPiArI2RlZmluZSBNUlZMX0hEUl9DSElQX1ZF
UgkweEFBDQo+ID4gKyNkZWZpbmUgTVJWTF9IQ0lfT1BfU0VUX0JBVUQJMHhGQzA5DQo+ID4gKyNk
ZWZpbmUgTVJWTF9GV19IRFJfTEVOCQk1DQo+ID4gKyNkZWZpbmUgTVJWTF9XQUlUX1RJTUVPVVQJ
bXNlY3NfdG9famlmZmllcygxMjAwMCkNCj4gPiArDQo+ID4gK3N0cnVjdCBtcnZsX2RhdGEgew0K
PiA+ICsJc3RydWN0IHNrX2J1ZmYgKnJ4X3NrYjsNCj4gPiArCXN0cnVjdCBza19idWZmX2hlYWQg
dHhxOw0KPiA+ICsJc3RydWN0IGZ3X2RhdGEgKmZ3ZGF0YTsNCj4gPiArfTsNCj4gPiArDQo+ID4g
K3N0YXRpYyBpbnQgZ2V0X2N0cyhzdHJ1Y3QgaGNpX3VhcnQgKmh1KSB7DQo+ID4gKwlzdHJ1Y3Qg
dHR5X3N0cnVjdCAqdHR5ID0gaHUtPnR0eTsNCj4gPiArCXUzMiBzdGF0ZSA9ICB0dHktPm9wcy0+
dGlvY21nZXQodHR5KTsNCj4gPiArDQo+ID4gKwlpZiAoc3RhdGUgJiBUSU9DTV9DVFMpIHsNCj4g
PiArCQlidF9kZXZfZGJnKGh1LT5oZGV2LCAiQ1RTIGlzIGxvdyIpOw0KPiA+ICsJCXJldHVybiAx
Ow0KPiA+ICsJfQ0KPiA+ICsJYnRfZGV2X2RiZyhodS0+aGRldiwgIkNUUyBpcyBoaWdoIik7DQo+
ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gKy8qIEluaXRpYWxpemUg
cHJvdG9jb2wgKi8NCj4gPiArc3RhdGljIGludCBtcnZsX29wZW4oc3RydWN0IGhjaV91YXJ0ICpo
dSkgew0KPiA+ICsJc3RydWN0IG1ydmxfZGF0YSAqbXJ2bDsNCj4gPiArDQo+ID4gKwlidF9kZXZf
ZGJnKGh1LT5oZGV2LCAiaHUgJXAiLCBodSk7DQo+ID4gKw0KPiA+ICsJbXJ2bCA9IGt6YWxsb2Mo
c2l6ZW9mKCptcnZsKSwgR0ZQX0tFUk5FTCk7DQo+ID4gKwlpZiAoIW1ydmwpDQo+ID4gKwkJcmV0
dXJuIC1FTk9NRU07DQo+ID4gKw0KPiA+ICsJc2tiX3F1ZXVlX2hlYWRfaW5pdCgmbXJ2bC0+dHhx
KTsNCj4gPiArCWh1LT5wcml2ID0gbXJ2bDsNCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiAr
fQ0KPiA+ICsNCj4gPiArLyogRmx1c2ggcHJvdG9jb2wgZGF0YSAqLw0KPiA+ICtzdGF0aWMgaW50
IG1ydmxfZmx1c2goc3RydWN0IGhjaV91YXJ0ICpodSkgew0KPiA+ICsJc3RydWN0IG1ydmxfZGF0
YSAqbXJ2bCA9IGh1LT5wcml2Ow0KPiA+ICsNCj4gPiArCWJ0X2Rldl9kYmcoaHUtPmhkZXYsICJo
dSAlcCIsIGh1KTsNCj4gPiArDQo+ID4gKwlza2JfcXVldWVfcHVyZ2UoJm1ydmwtPnR4cSk7DQo+
ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gKy8qIENsb3NlIHByb3Rv
Y29sICovDQo+ID4gK3N0YXRpYyBpbnQgbXJ2bF9jbG9zZShzdHJ1Y3QgaGNpX3VhcnQgKmh1KSB7
DQo+ID4gKwlzdHJ1Y3QgbXJ2bF9kYXRhICptcnZsID0gaHUtPnByaXY7DQo+ID4gKw0KPiA+ICsJ
YnRfZGV2X2RiZyhodS0+aGRldiwgImh1ICVwIiwgaHUpOw0KPiA+ICsNCj4gPiArCXNrYl9xdWV1
ZV9wdXJnZSgmbXJ2bC0+dHhxKTsNCj4gPiArCWtmcmVlX3NrYihtcnZsLT5yeF9za2IpOw0KPiA+
ICsJaHUtPnByaXYgPSBOVUxMOw0KPiA+ICsJa2ZyZWUobXJ2bCk7DQo+ID4gKw0KPiA+ICsJcmV0
dXJuIDA7DQo+ID4gK30NCj4gPiArDQo+ID4gKy8qIEVucXVldWUgZnJhbWUgZm9yIHRyYW5zbWl0
dGlvbiAocGFkZGluZywgY3JjLCBldGMpICovIHN0YXRpYyBpbnQNCj4gPiArbXJ2bF9lbnF1ZXVl
KHN0cnVjdCBoY2lfdWFydCAqaHUsIHN0cnVjdCBza19idWZmICpza2IpIHsNCj4gPiArCXN0cnVj
dCBtcnZsX2RhdGEgKm1ydmwgPSBodS0+cHJpdjsNCj4gPiArDQo+ID4gKwlidF9kZXZfZGJnKGh1
LT5oZGV2LCAiaHUgJXAgc2tiICVwIiwgaHUsIHNrYik7DQo+ID4gKw0KPiA+ICsJLyogUHJlcGVu
ZCBza2Igd2l0aCBmcmFtZSB0eXBlICovDQo+ID4gKwltZW1jcHkoc2tiX3B1c2goc2tiLCAxKSwg
JmhjaV9za2JfcGt0X3R5cGUoc2tiKSwgMSk7DQo+ID4gKwlza2JfcXVldWVfdGFpbCgmbXJ2bC0+
dHhxLCBza2IpOw0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtz
dGF0aWMgY29uc3Qgc3RydWN0IGg0X3JlY3ZfcGt0IG1ydmxfcmVjdl9wa3RzW10gPSB7DQo+ID4g
Kwl7IEg0X1JFQ1ZfQUNMLCAgIC5yZWN2ID0gaGNpX3JlY3ZfZnJhbWUgfSwNCj4gPiArCXsgSDRf
UkVDVl9TQ08sICAgLnJlY3YgPSBoY2lfcmVjdl9mcmFtZSB9LA0KPiA+ICsJeyBINF9SRUNWX0VW
RU5ULCAucmVjdiA9IGhjaV9yZWN2X2ZyYW1lIH0sIH07DQo+ID4gKw0KPiA+ICsvKiBTZW5kIEFD
Sy9OQUsgdG8gdGhlIGRldmljZSAqLw0KPiA+ICtzdGF0aWMgdm9pZCBtcnZsX3NlbmRfYWNrKHN0
cnVjdCBoY2lfdWFydCAqaHUsIHVuc2lnbmVkIGNoYXIgYWNrKSB7DQo+ID4gKwlzdHJ1Y3QgbXJ2
bF9kYXRhICptcnZsID0gaHUtPnByaXY7DQo+ID4gKwlzdHJ1Y3Qgc2tfYnVmZiAqc2tiOw0KPiA+
ICsNCj4gPiArCXNrYiA9IGJ0X3NrYl9hbGxvYyhzaXplb2YoYWNrKSwgR0ZQX0tFUk5FTCk7DQo+
ID4gKwlpZiAoIXNrYikNCj4gPiArCQlyZXR1cm47DQo+ID4gKw0KPiA+ICsJbWVtY3B5KHNrYi0+
ZGF0YSwgJmFjaywgc2l6ZW9mKGFjaykpOw0KPiA+ICsJc2tiX3B1dChza2IsIHNpemVvZihhY2sp
KTsNCj4gPiArCXNrYl9xdWV1ZV9oZWFkKCZtcnZsLT50eHEsIHNrYik7DQo+ID4gKwloY2lfdWFy
dF90eF93YWtldXAoaHUpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICsvKiBWYWxpZGF0ZSB0aGUgZmVl
ZGJhY2sgZGF0YSBmcm9tIGRldmljZSAqLyBzdGF0aWMgdm9pZA0KPiA+ICttcnZsX3BrdF9jb21w
bGV0ZShzdHJ1Y3QgaGNpX3VhcnQgKmh1LCBzdHJ1Y3Qgc2tfYnVmZiAqc2tiKSB7DQo+ID4gKwlz
dHJ1Y3QgbXJ2bF9kYXRhICptcnZsID0gaHUtPnByaXY7DQo+ID4gKwlzdHJ1Y3QgZndfZGF0YSAq
ZndfZGF0YSA9IG1ydmwtPmZ3ZGF0YTsNCj4gPiArCXUxNiBsaHMsIHJoczsNCj4gPiArDQo+ID4g
KwlsaHMgPSBnZXRfdW5hbGlnbmVkX2xlMTYoc2tiLT5kYXRhICsgMSk7DQo+ID4gKwlyaHMgPSBn
ZXRfdW5hbGlnbmVkX2xlMTYoc2tiLT5kYXRhICsgMyk7DQo+ID4gKwlpZiAoKGxocyBeIHJocykg
PT0gMHhmZmZmKSB7DQo+ID4gKwkJbXJ2bF9zZW5kX2FjayhodSwgTVJWTF9BQ0spOw0KPiA+ICsJ
CWZ3X2RhdGEtPndhaXRfZncgPSAxOw0KPiA+ICsJCWZ3X2RhdGEtPm5leHRfbGVuID0gbGhzOw0K
PiA+ICsJCS8qIEZpcm13YXJlIGRvd25sb2FkIGlzIGRvbmUsIHNlbmQgdGhlIGxhc3QgYWNrICov
DQo+ID4gKwkJaWYgKCFsaHMpDQo+ID4gKwkJCWZ3X2RhdGEtPmxhc3RfYWNrID0gMTsNCj4gPiAr
DQo+ID4gKwkJaWYgKGZ3X2RhdGEtPmV4cGVjdGVkX2FjayA9PSBNUlZMX0hEUl9DSElQX1ZFUikg
ew0KPiA+ICsJCQlmd19kYXRhLT5jaGlwX2lkID0gc2tiLT5kYXRhWzFdOw0KPiA+ICsJCQlmd19k
YXRhLT5jaGlwX3JldiA9IHNrYi0+ZGF0YVsyXTsNCj4gPiArCQl9DQo+ID4gKwkJd2FrZV91cF9p
bnRlcnJ1cHRpYmxlKCZmd19kYXRhLT5pbml0X3dhaXRfcSk7DQo+ID4gKwl9IGVsc2Ugew0KPiA+
ICsJCW1ydmxfc2VuZF9hY2soaHUsIE1SVkxfTkFLKTsNCj4gPiArCX0NCj4gPiArfQ0KPiA+ICsN
Cj4gPiArLyogVGhpcyBmdW5jdGlvbiByZWNlaXZlcyBkYXRhIGZyb20gdGhlIHVhcnQgZGV2aWNl
IGR1cmluZyBmaXJtd2FyZQ0KPiBkb3dubG9hZC4NCj4gPiArICogRHJpdmVyIGV4cGVjdHMgNSBi
eXRlcyBvZiBkYXRhIGFzIHBlciB0aGUgcHJvdG9jYWwgaW4gdGhlIGJlbG93DQo+IGZvcm1hdDoN
Cj4gPiArICogPEhFQURFUj48QllURV8xPjxCWVRFXzI+PEJZVEVfMz48QllURV80Pg0KPiA+ICsg
KiBCWVRFXzMgYW5kIEJZVEVfNCBhcmUgY29tcGxpbWVudCBvZiBCWVRFXzEgYW4gQllURV8yLiBE
YXRhIGNhbg0KPiA+ICtjb21lIGluIGNodW5rcw0KPiA+ICsgKiBvZiBhbnkgbGVuZ3RoLiBJZiBs
ZW5ndGggcmVjZWl2ZWQgaXMgPCA1LCBhY2N1bXVsYXRlIHRoZSBkYXRhIGluDQo+ID4gK2FuIGFy
cmF5LA0KPiA+ICsgKiB1bnRpbCB3ZSBoYXZlIGEgc2VxdWVuY2Ugb2YgNSBieXRlcywgc3RhcnRp
bmcgd2l0aCB0aGUgZXhwZWN0ZWQNCj4gPiArSEVBREVSLiBJZg0KPiA+ICsgKiB0aGUgbGVuZ3Ro
IHJlY2VpdmVkIGlzID4gNSAgYnl0ZXMsIHRoZW4gZ2V0IHRoZSBmaXJzdCA1IGJ5dGVzLA0KPiA+
ICtzdGFydGluZyB3aXRoDQo+ID4gKyAqIHRoZSBIRUFERVIgYW5kIHByb2Nlc3MgdGhlIHNhbWUs
IGlnbm9yaW5nIHRoZSByZXN0IG9mIHRoZSBieXRlcyBhcw0KPiA+ICtwZXIgdGhlDQo+ID4gKyAq
IHByb3RvY2FsLg0KPiA+ICsgKi8NCj4gPiArc3RhdGljIHN0cnVjdCBza19idWZmICptcnZsX3By
b2Nlc3NfZndfZGF0YShzdHJ1Y3QgaGNpX3VhcnQgKmh1LA0KPiA+ICsJCQkJCSAgICBzdHJ1Y3Qg
c2tfYnVmZiAqc2tiLA0KPiA+ICsJCQkJCSAgICB1OCAqYnVmLCBpbnQgY291bnQpDQo+ID4gK3sN
Cj4gPiArCXN0cnVjdCBtcnZsX2RhdGEgKm1ydmwgPSBodS0+cHJpdjsNCj4gPiArCXN0cnVjdCBm
d19kYXRhICpmd19kYXRhID0gbXJ2bC0+ZndkYXRhOw0KPiA+ICsJaW50IGkgPSAwLCBsZW47DQo+
ID4gKw0KPiA+ICsJaWYgKCFza2IpIHsNCj4gPiArCQl3aGlsZSAoYnVmW2ldICE9IGZ3X2RhdGEt
PmV4cGVjdGVkX2FjayAmJiBpIDwgY291bnQpDQo+ID4gKwkJCWkrKzsNCj4gPiArCQlpZiAoaSA9
PSBjb3VudCkNCj4gPiArCQkJcmV0dXJuIEVSUl9QVFIoLUVJTFNFUSk7DQo+ID4gKw0KPiA+ICsJ
CXNrYiA9IGJ0X3NrYl9hbGxvYyhNUlZMX0ZXX0hEUl9MRU4sIEdGUF9LRVJORUwpOw0KPiA+ICsJ
fQ0KPiA+ICsNCj4gPiArCWlmICghc2tiKQ0KPiA+ICsJCXJldHVybiBFUlJfUFRSKC1FTk9NRU0p
Ow0KPiA+ICsNCj4gPiArCWxlbiA9IGNvdW50IC0gaTsNCj4gPiArCW1lbWNweShza2JfcHV0KHNr
YiwgbGVuKSwgJmJ1ZltpXSwgbGVuKTsNCj4gPiArDQo+ID4gKwlpZiAoc2tiLT5sZW4gPT0gTVJW
TF9GV19IRFJfTEVOKSB7DQo+ID4gKwkJbXJ2bF9wa3RfY29tcGxldGUoaHUsIHNrYik7DQo+ID4g
KwkJa2ZyZWVfc2tiKHNrYik7DQo+ID4gKwkJc2tiID0gTlVMTDsNCj4gPiArCX0NCj4gPiArDQo+
ID4gKwlyZXR1cm4gc2tiOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICsvKiBSZWNlaXZlIGZpcm13YXJl
IGZlZWRiYWNrIGRhdGEgKi8NCj4gPiArc3RhdGljIGludCBtcnZsX3JlY3ZfZm9yX3ByZXBhcmUo
c3RydWN0IGhjaV91YXJ0ICpodSwgY29uc3Qgdm9pZA0KPiAqZGF0YSwNCj4gPiArCQkJCSBpbnQg
Y291bnQpDQo+ID4gK3sNCj4gPiArCXN0cnVjdCBtcnZsX2RhdGEgKm1ydmwgPSBodS0+cHJpdjsN
Cj4gPiArDQo+ID4gKwltcnZsLT5md2RhdGEtPnNrYiA9IG1ydmxfcHJvY2Vzc19md19kYXRhKGh1
LCBtcnZsLT5md2RhdGEtPnNrYiwNCj4gPiArCQkJCQkJICh1OCAqKWRhdGEsIGNvdW50KTsNCj4g
PiArCWlmIChJU19FUlIobXJ2bC0+ZndkYXRhLT5za2IpKSB7DQo+ID4gKwkJaW50IGVyciA9IFBU
Ul9FUlIobXJ2bC0+ZndkYXRhLT5za2IpOw0KPiA+ICsNCj4gPiArCQlidF9kZXZfZXJyKGh1LT5o
ZGV2LA0KPiA+ICsJCQkgICAiUmVjZWl2ZSBmaXJtd2FyZSBkYXRhIGZhaWxlZCAoJWQpIiwgZXJy
KTsNCj4gPiArCQltcnZsLT5md2RhdGEtPnNrYiA9IE5VTEw7DQo+ID4gKwkJcmV0dXJuIGVycjsN
Cj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArLyog
UmVjZWl2ZSBkYXRhICovDQo+ID4gK3N0YXRpYyBpbnQgbXJ2bF9yZWN2KHN0cnVjdCBoY2lfdWFy
dCAqaHUsIGNvbnN0IHZvaWQgKmRhdGEsIGludA0KPiA+ICtjb3VudCkgew0KPiA+ICsJc3RydWN0
IG1ydmxfZGF0YSAqbXJ2bCA9IGh1LT5wcml2Ow0KPiA+ICsNCj4gPiArCWlmICghdGVzdF9iaXQo
SENJX1VBUlRfUkVHSVNURVJFRCwgJmh1LT5mbGFncykpDQo+ID4gKwkJcmV0dXJuIC1FVU5BVENI
Ow0KPiA+ICsNCj4gPiArCW1ydmwtPnJ4X3NrYiA9IGg0X3JlY3ZfYnVmKGh1LT5oZGV2LCBtcnZs
LT5yeF9za2IsIGRhdGEsIGNvdW50LA0KPiA+ICsJCQkJICAgbXJ2bF9yZWN2X3BrdHMsIEFSUkFZ
X1NJWkUobXJ2bF9yZWN2X3BrdHMpKTsNCj4gPiArCWlmIChJU19FUlIobXJ2bC0+cnhfc2tiKSkg
ew0KPiA+ICsJCWludCBlcnIgPSBQVFJfRVJSKG1ydmwtPnJ4X3NrYik7DQo+ID4gKw0KPiA+ICsJ
CWJ0X2Rldl9lcnIoaHUtPmhkZXYsICJGcmFtZSByZWFzc2VtYmx5IGZhaWxlZCAoJWQpIiwgZXJy
KTsNCj4gPiArCQltcnZsLT5yeF9za2IgPSBOVUxMOw0KPiA+ICsJCXJldHVybiBlcnI7DQo+ID4g
Kwl9DQo+ID4gKw0KPiA+ICsJcmV0dXJuIGNvdW50Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0
aWMgc3RydWN0IHNrX2J1ZmYgKm1ydmxfZGVxdWV1ZShzdHJ1Y3QgaGNpX3VhcnQgKmh1KSB7DQo+
ID4gKwlzdHJ1Y3QgbXJ2bF9kYXRhICptcnZsID0gaHUtPnByaXY7DQo+ID4gKw0KPiA+ICsJcmV0
dXJuIHNrYl9kZXF1ZXVlKCZtcnZsLT50eHEpOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMg
aW50IG1ydmxfaW5pdF9md19kYXRhKHN0cnVjdCBoY2lfdWFydCAqaHUpIHsNCj4gPiArCXN0cnVj
dCBmd19kYXRhICpmd2RhdGE7DQo+ID4gKwlzdHJ1Y3QgbXJ2bF9kYXRhICptcnZsID0gaHUtPnBy
aXY7DQo+ID4gKw0KPiA+ICsJZndkYXRhID0ga3phbGxvYyhzaXplb2YoKmZ3ZGF0YSksIEdGUF9L
RVJORUwpOw0KPiA+ICsJaWYgKCFmd2RhdGEpDQo+ID4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4g
Kw0KPiA+ICsJbXJ2bC0+ZndkYXRhID0gZndkYXRhOw0KPiA+ICsJaW5pdF93YWl0cXVldWVfaGVh
ZCgmZndkYXRhLT5pbml0X3dhaXRfcSk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIDA7DQo+ID4gK30N
Cj4gPiArDQo+ID4gKy8qIFdhaXQgZm9yIHRoZSBoZWFkZXIgZnJvbSBkZXZpY2UgKi8gc3RhdGlj
IGludA0KPiA+ICttcnZsX3dhaXRfZm9yX2hkcihzdHJ1Y3QgaGNpX3VhcnQgKmh1LCB1OCBoZWFk
ZXIpIHsNCj4gPiArCXN0cnVjdCBtcnZsX2RhdGEgKm1ydmwgPSBodS0+cHJpdjsNCj4gPiArCXN0
cnVjdCBmd19kYXRhICpmd19kYXRhID0gbXJ2bC0+ZndkYXRhOw0KPiA+ICsNCj4gPiArCWZ3X2Rh
dGEtPmV4cGVjdGVkX2FjayA9IGhlYWRlcjsNCj4gPiArCWZ3X2RhdGEtPndhaXRfZncgPSAwOw0K
PiA+ICsNCj4gPiArCWlmICghd2FpdF9ldmVudF9pbnRlcnJ1cHRpYmxlX3RpbWVvdXQoZndfZGF0
YS0+aW5pdF93YWl0X3EsDQo+ID4gKwkJCQkJICAgICAgZndfZGF0YS0+d2FpdF9mdywNCj4gPiAr
CQkJCQkgICAgICBNUlZMX1dBSVRfVElNRU9VVCkpIHsNCj4gPiArCQlCVF9FUlIoIlRJTUVPVVQs
IHdhaXRpbmcgZm9yOjB4JXgiLCBmd19kYXRhLT5leHBlY3RlZF9hY2spOw0KPiA+ICsJCXJldHVy
biAtMTsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0KPiA+ICsNCj4g
PiArLyogU2VuZCBieXRlcyB0byBkZXZpY2UgKi8NCj4gPiArc3RhdGljIGludCBtcnZsX3NlbmRf
ZGF0YShzdHJ1Y3QgaGNpX3VhcnQgKmh1LCBzdHJ1Y3Qgc2tfYnVmZiAqc2tiKSB7DQo+ID4gKwlz
dHJ1Y3QgbXJ2bF9kYXRhICptcnZsID0gaHUtPnByaXY7DQo+ID4gKw0KPiA+ICsJc2tiX3F1ZXVl
X2hlYWQoJm1ydmwtPnR4cSwgc2tiKTsNCj4gPiArCWhjaV91YXJ0X3R4X3dha2V1cChodSk7DQo+
ID4gKw0KPiA+ICsJaWYgKG1ydmxfd2FpdF9mb3JfaGRyKGh1LCBNUlZMX0hEUl9SRVFfRlcpID09
IC0xKQ0KPiA+ICsJCXJldHVybiAtMTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gMDsNCj4gPiArfQ0K
PiA+ICsNCj4gPiArLyogRG93bmxvYWQgZmlybXdhcmUgdG8gdGhlIGRldmljZSAqLyBzdGF0aWMg
aW50IG1ydmxfZG5sZF9mdyhzdHJ1Y3QNCj4gPiAraGNpX3VhcnQgKmh1LCBjb25zdCBjaGFyICpm
aWxlX25hbWUpIHsNCj4gPiArCWNvbnN0IHN0cnVjdCBmaXJtd2FyZSAqZncgPSBOVUxMOw0KPiA+
ICsJc3RydWN0IHNrX2J1ZmYgKnNrYiA9IE5VTEw7DQo+ID4gKwlpbnQgb2Zmc2V0ID0gMDsNCj4g
PiArCWludCByZXQsIHR4X2xlbjsNCj4gPiArCXN0cnVjdCBtcnZsX2RhdGEgKm1ydmwgPSBodS0+
cHJpdjsNCj4gPiArCXN0cnVjdCBmd19kYXRhICpmd19kYXRhID0gbXJ2bC0+ZndkYXRhOw0KPiA+
ICsNCj4gPiArCXJldCA9IHJlcXVlc3RfZmlybXdhcmUoJmZ3LCBmaWxlX25hbWUsIGh1LT50dHkt
PmRldik7DQo+ID4gKwlpZiAocmV0IDwgMCkgew0KPiA+ICsJCUJUX0VSUigicmVxdWVzdF9maXJt
d2FyZSgpIGZhaWxlZCIpOw0KPiA+ICsJCXJldHVybiAtMTsNCj4gPiArCX0NCj4gPiArDQo+ID4g
KwlCVF9JTkZPKCJEb3dubG9hZGluZyBGVyAoJWQgYnl0ZXMpIiwgKHUxNilmdy0+c2l6ZSk7DQo+
ID4gKw0KPiA+ICsJZndfZGF0YS0+bGFzdF9hY2sgPSAwOw0KPiA+ICsNCj4gPiArCXdoaWxlICgx
KSB7DQo+ID4gKwkJaWYgKChvZmZzZXQgPj0gZnctPnNpemUpIHx8IChmd19kYXRhLT5sYXN0X2Fj
aykpDQo+ID4gKwkJCWJyZWFrOw0KPiA+ICsJCXR4X2xlbiA9IGZ3X2RhdGEtPm5leHRfbGVuOw0K
PiA+ICsJCWlmICgoZnctPnNpemUgLSBvZmZzZXQpIDwgdHhfbGVuKQ0KPiA+ICsJCQl0eF9sZW4g
PSBmdy0+c2l6ZSAtIG9mZnNldDsNCj4gPiArDQo+ID4gKwkJc2tiID0gYnRfc2tiX2FsbG9jKE1S
VkxfTUFYX0ZXX0JMT0NLX1NJWkUsIEdGUF9LRVJORUwpOw0KPiA+ICsJCWlmICghc2tiKSB7DQo+
ID4gKwkJCXJldCA9IC0xOw0KPiA+ICsJCQlnb3RvIGRvbmU7DQo+ID4gKwkJfQ0KPiA+ICsNCj4g
PiArCQltZW1jcHkoc2tiLT5kYXRhLCAmZnctPmRhdGFbb2Zmc2V0XSwgdHhfbGVuKTsNCj4gPiAr
CQlza2JfcHV0KHNrYiwgdHhfbGVuKTsNCj4gPiArCQlpZiAobXJ2bF9zZW5kX2RhdGEoaHUsIHNr
YikgIT0gMCkgew0KPiA+ICsJCQlCVF9FUlIoIkZhaWwgdG8gZG93bmxvYWQgZmlybXdhcmUiKTsN
Cj4gPiArCQkJcmV0ID0gLTE7DQo+ID4gKwkJCWdvdG8gZG9uZTsNCj4gPiArCQl9DQo+ID4gKwkJ
b2Zmc2V0ICs9IHR4X2xlbjsNCj4gPiArCX0NCj4gPiArDQo+ID4gKwlCVF9JTkZPKCJEb3dubG9h
ZGVkICVkIGJ5dGUgZmlybXdhcmUiLCBvZmZzZXQpOw0KPiA+ICtkb25lOg0KPiA+ICsJcmVsZWFz
ZV9maXJtd2FyZShmdyk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIHJldDsNCj4gPiArfQ0KPiA+ICsN
Cj4gPiArLyogU2V0IHRoZSBiYXVkIHJhdGUgKi8NCj4gPiArc3RhdGljIGludCBtcnZsX3NldF9k
ZXZfYmF1ZChzdHJ1Y3QgaGNpX3VhcnQgKmh1KSB7DQo+ID4gKwlzdHJ1Y3QgaGNpX2RldiAqaGRl
diA9IGh1LT5oZGV2Ow0KPiA+ICsJc3RydWN0IHNrX2J1ZmYgKnNrYjsNCj4gPiArCXN0YXRpYyBj
b25zdCB1OCBiYXVkX3BhcmFtW10gPSB7IDB4YzAsIDB4YzYsIDB4MmQsIDB4MDAgfTsNCj4gPiAr
CWludCBlcnI7DQo+ID4gKw0KPiA+ICsJc2tiID0gX19oY2lfY21kX3N5bmMoaGRldiwgTVJWTF9I
Q0lfT1BfU0VUX0JBVUQsDQo+IHNpemVvZihiYXVkX3BhcmFtKSwNCj4gPiArCQkJICAgICBiYXVk
X3BhcmFtLCBIQ0lfSU5JVF9USU1FT1VUKTsNCj4gPiArCWlmIChJU19FUlIoc2tiKSkgew0KPiA+
ICsJCWVyciA9IFBUUl9FUlIoc2tiKTsNCj4gPiArCQlidF9kZXZfZXJyKGh1LT5oZGV2LCAiU2V0
IGRldmljZSBiYXVkcmF0ZSBmYWlsZWQgKCVkKSIsDQo+IGVycik7DQo+ID4gKwkJcmV0dXJuIGVy
cjsNCj4gPiArCX0NCj4gPiArCWtmcmVlX3NrYihza2IpOw0KPiA+ICsNCj4gPiArCXJldHVybiAw
Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICsvKiBSZXNldCBkZXZpY2UgKi8NCj4gPiArc3RhdGljIGlu
dCBtcnZsX3Jlc2V0KHN0cnVjdCBoY2lfdWFydCAqaHUpIHsNCj4gPiArCXN0cnVjdCBoY2lfZGV2
ICpoZGV2ID0gaHUtPmhkZXY7DQo+ID4gKwlzdHJ1Y3Qgc2tfYnVmZiAqc2tiOw0KPiA+ICsJaW50
IGVycjsNCj4gPiArDQo+ID4gKwlza2IgPSBfX2hjaV9jbWRfc3luYyhoZGV2LCBIQ0lfT1BfUkVT
RVQsIDAsIE5VTEwsDQo+IEhDSV9DTURfVElNRU9VVCk7DQo+ID4gKwlpZiAoSVNfRVJSKHNrYikp
IHsNCj4gPiArCQllcnIgPSBQVFJfRVJSKHNrYik7DQo+ID4gKwkJYnRfZGV2X2VycihodS0+aGRl
diwgIlJlc2V0IGRldmljZSBmYWlsZWQgKCVkKSIsIGVycik7DQo+ID4gKwkJcmV0dXJuIGVycjsN
Cj4gPiArCX0NCj4gPiArCWtmcmVlX3NrYihza2IpOw0KPiA+ICsNCj4gPiArCXJldHVybiAwOw0K
PiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IG1ydmxfZ2V0X2Z3X25hbWUoc3RydWN0IGhj
aV91YXJ0ICpodSwgY2hhciAqZndfbmFtZSkgew0KPiA+ICsJc3RydWN0IG1ydmxfZGF0YSAqbXJ2
bCA9IGh1LT5wcml2Ow0KPiA+ICsJc3RydWN0IGZ3X2RhdGEgKmZ3X2RhdGEgPSBtcnZsLT5md2Rh
dGE7DQo+ID4gKw0KPiA+ICsJaWYgKG1ydmxfd2FpdF9mb3JfaGRyKGh1LCBNUlZMX0hEUl9DSElQ
X1ZFUikgIT0gMCkgew0KPiA+ICsJCUJUX0VSUigiQ291bGQgbm90IHJlYWQgY2hpcCBpZCBhbmQg
cmV2aXNpb24iKTsNCj4gPiArCQlyZXR1cm4gLTE7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJQlRf
REJHKCJjaGlwX2lkPTB4JXgsIGNoaXBfcmV2PTB4JXgiLA0KPiA+ICsJICAgICAgIGZ3X2RhdGEt
PmNoaXBfaWQsIGZ3X2RhdGEtPmNoaXBfcmV2KTsNCj4gPiArDQo+ID4gKwlzd2l0Y2ggKGZ3X2Rh
dGEtPmNoaXBfaWQpIHsNCj4gPiArCWNhc2UgTVJWTF84OTk3X0NISVBfSUQ6DQo+ID4gKwkJbWVt
Y3B5KGZ3X25hbWUsIE1SVkxfODk5N19GV19OQU1FLA0KPiBzaXplb2YoTVJWTF84OTk3X0ZXX05B
TUUpKTsNCj4gPiArCQlyZXR1cm4gMDsNCj4gPiArCWRlZmF1bHQ6DQo+ID4gKwkJQlRfRVJSKCJJ
bnZhbGlkIGNoaXAgaWQiKTsNCj4gPiArCQlyZXR1cm4gLTE7DQo+ID4gKwl9DQo+ID4gK30NCj4g
PiArDQo+ID4gKy8qIERvd25sb2FkIGhlbHBlciBhbmQgZmlybWFyZSB0byBkZXZpY2UgKi8gc3Rh
dGljIGludA0KPiA+ICtoY2lfdWFydF9kbmxkX2Z3KHN0cnVjdCBoY2lfdWFydCAqaHUpIHsNCj4g
PiArCXN0cnVjdCB0dHlfc3RydWN0ICp0dHkgPSBodS0+dHR5Ow0KPiA+ICsJc3RydWN0IGt0ZXJt
aW9zIG5ld190ZXJtaW9zOw0KPiA+ICsJc3RydWN0IGt0ZXJtaW9zIG9sZF90ZXJtaW9zOw0KPiA+
ICsJY2hhciBmd19uYW1lWzEyOF07DQo+ID4gKwlpbnQgcmV0Ow0KPiA+ICsNCj4gPiArCW9sZF90
ZXJtaW9zID0gdHR5LT50ZXJtaW9zOw0KPiA+ICsNCj4gPiArCWlmIChnZXRfY3RzKGh1KSkgew0K
PiA+ICsJCUJUX0lORk8oImZ3IGlzIHJ1bm5pbmciKTsNCj4gPiArCQlyZXR1cm4gMDsNCj4gPiAr
CX0NCj4gPiArDQo+ID4gKwloY2lfdWFydF9zZXRfYmF1ZHJhdGUoaHUsIDExNTIwMCk7DQo+ID4g
KwloY2lfdWFydF9zZXRfZmxvd19jb250cm9sKGh1LCB0cnVlKTsNCj4gPiArDQo+ID4gKwlyZXQg
PSBtcnZsX3dhaXRfZm9yX2hkcihodSwgTVJWTF9IRFJfUkVRX0ZXKTsNCj4gPiArCWlmIChyZXQp
DQo+ID4gKwkJZ290byBmYWlsOw0KPiA+ICsNCj4gPiArCXJldCA9IG1ydmxfZG5sZF9mdyhodSwg
TVJWTF9IRUxQRVJfTkFNRSk7DQo+ID4gKwlpZiAocmV0KQ0KPiA+ICsJCWdvdG8gZmFpbDsNCj4g
PiArDQo+ID4gKwltc2xlZXAoTVJWTF9ETkxEX0RFTEFZKTsNCj4gPiArDQo+ID4gKwloY2lfdWFy
dF9zZXRfYmF1ZHJhdGUoaHUsIDMwMDAwMDApOw0KPiA+ICsJaGNpX3VhcnRfc2V0X2Zsb3dfY29u
dHJvbChodSwgZmFsc2UpOw0KPiA+ICsNCj4gPiArCXJldCA9IG1ydmxfZ2V0X2Z3X25hbWUoaHUs
IGZ3X25hbWUpOw0KPiA+ICsJaWYgKHJldCkNCj4gPiArCQlnb3RvIGZhaWw7DQo+ID4gKw0KPiA+
ICsJcmV0ID0gbXJ2bF93YWl0X2Zvcl9oZHIoaHUsIE1SVkxfSERSX1JFUV9GVyk7DQo+ID4gKwlp
ZiAocmV0KQ0KPiA+ICsJCWdvdG8gZmFpbDsNCj4gPiArDQo+ID4gKwlyZXQgPSBtcnZsX2RubGRf
ZncoaHUsIGZ3X25hbWUpOw0KPiA+ICsJaWYgKHJldCkNCj4gPiArCQlnb3RvIGZhaWw7DQo+ID4g
Kw0KPiA+ICsJbXNsZWVwKE1SVkxfRE5MRF9ERUxBWSk7DQo+ID4gK2ZhaWw6DQo+ID4gKwkvKiBy
ZXN0b3JlIHVhcnQgc2V0dGluZ3MgKi8NCj4gPiArCW5ld190ZXJtaW9zID0gdHR5LT50ZXJtaW9z
Ow0KPiA+ICsJdHR5LT50ZXJtaW9zLmNfY2ZsYWcgPSBvbGRfdGVybWlvcy5jX2NmbGFnOw0KPiA+
ICsJdHR5X3NldF90ZXJtaW9zKHR0eSwgJm5ld190ZXJtaW9zKTsNCj4gPiArDQo+ID4gKwlyZXR1
cm4gcmV0Ow0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IG1ydmxfc2V0dXAoc3RydWN0
IGhjaV91YXJ0ICpodSkgew0KPiA+ICsJaW50IGVycjsNCj4gPiArDQo+ID4gKwloY2lfdWFydF9z
ZXRfYmF1ZHJhdGUoaHUsIDExNTIwMCk7DQo+ID4gKwloY2lfdWFydF9zZXRfZmxvd19jb250cm9s
KGh1LCBmYWxzZSk7DQo+ID4gKw0KPiA+ICsJZXJyID0gbXJ2bF9yZXNldChodSk7DQo+ID4gKwlp
ZiAoIWVycikgew0KPiA+ICsJCWVyciA9IG1ydmxfc2V0X2Rldl9iYXVkKGh1KTsNCj4gPiArCQlp
ZiAoZXJyKQ0KPiA+ICsJCQlyZXR1cm4gLTE7DQo+ID4gKwl9DQo+ID4gKw0KPiA+ICsJaGNpX3Vh
cnRfc2V0X2JhdWRyYXRlKGh1LCAzMDAwMDAwKTsNCj4gPiArCWhjaV91YXJ0X3NldF9mbG93X2Nv
bnRyb2woaHUsIGZhbHNlKTsNCj4gPiArCW1zbGVlcChNUlZMX0ROTERfREVMQVkpOw0KPiA+ICsN
Cj4gPiArCXJldHVybiAwOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgaW50IG1ydmxfcHJl
cGFyZShzdHJ1Y3QgaGNpX3VhcnQgKmh1KSB7DQo+ID4gKwlzdHJ1Y3QgbXJ2bF9kYXRhICptcnZs
ID0gaHUtPnByaXY7DQo+ID4gKwlpbnQgZXJyOw0KPiA+ICsNCj4gPiArCWVyciA9IG1ydmxfaW5p
dF9md19kYXRhKGh1KTsNCj4gPiArCWlmICghZXJyKQ0KPiA+ICsJCWVyciA9IGhjaV91YXJ0X2Ru
bGRfZncoaHUpOw0KPiA+ICsNCj4gPiArCWtmcmVlKG1ydmwtPmZ3ZGF0YSk7DQo+ID4gKwlyZXR1
cm4gZXJyOw0KPiA+ICt9DQo+ID4gKw0KPiA+ICtzdGF0aWMgY29uc3Qgc3RydWN0IGhjaV91YXJ0
X3Byb3RvIG1ydmxwID0gew0KPiA+ICsJLmlkCQk9IEhDSV9VQVJUX01SVkwsDQo+ID4gKwkubmFt
ZQkJPSAiTVJWTCIsDQo+ID4gKwkub3BlbgkJPSBtcnZsX29wZW4sDQo+ID4gKwkuY2xvc2UJCT0g
bXJ2bF9jbG9zZSwNCj4gPiArCS5yZWN2CQk9IG1ydmxfcmVjdiwNCj4gPiArCS5lbnF1ZXVlCT0g
bXJ2bF9lbnF1ZXVlLA0KPiA+ICsJLmRlcXVldWUJPSBtcnZsX2RlcXVldWUsDQo+ID4gKwkuZmx1
c2gJCT0gbXJ2bF9mbHVzaCwNCj4gPiArCS5zZXR1cAkJPSBtcnZsX3NldHVwLA0KPiA+ICsJLnBy
ZXBhcmUJPSBtcnZsX3ByZXBhcmUsDQo+ID4gKwkucmVjdl9mb3JfcHJlcGFyZQk9IG1ydmxfcmVj
dl9mb3JfcHJlcGFyZSwNCj4gPiArfTsNCj4gPiArDQo+ID4gK2ludCBfX2luaXQgbXJ2bF9pbml0
KHZvaWQpDQo+ID4gK3sNCj4gPiArCXJldHVybiBoY2lfdWFydF9yZWdpc3Rlcl9wcm90bygmbXJ2
bHApOyB9DQo+ID4gKw0KPiA+ICtpbnQgX19leGl0IG1ydmxfZGVpbml0KHZvaWQpDQo+ID4gK3sN
Cj4gPiArCXJldHVybiBoY2lfdWFydF91bnJlZ2lzdGVyX3Byb3RvKCZtcnZscCk7DQo+ID4gK30N
Cj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9ibHVldG9vdGgvaGNpX3VhcnQuaA0KPiA+IGIvZHJp
dmVycy9ibHVldG9vdGgvaGNpX3VhcnQuaCBpbmRleCAxN2JhM2I0Li44YzUzYjUwIDEwMDY0NA0K
PiA+IC0tLSBhL2RyaXZlcnMvYmx1ZXRvb3RoL2hjaV91YXJ0LmgNCj4gPiArKysgYi9kcml2ZXJz
L2JsdWV0b290aC9oY2lfdWFydC5oDQo+ID4gQEAgLTM1LDcgKzM1LDcgQEANCj4gPiAgICNkZWZp
bmUgSENJVUFSVEdFVEZMQUdTCQlfSU9SKCdVJywgMjA0LCBpbnQpDQo+ID4NCj4gPiAgIC8qIFVB
UlQgcHJvdG9jb2xzICovDQo+ID4gLSNkZWZpbmUgSENJX1VBUlRfTUFYX1BST1RPCTEwDQo+ID4g
KyNkZWZpbmUgSENJX1VBUlRfTUFYX1BST1RPCTExDQo+ID4NCj4gPiAgICNkZWZpbmUgSENJX1VB
UlRfSDQJMA0KPiA+ICAgI2RlZmluZSBIQ0lfVUFSVF9CQ1NQCTENCj4gPiBAQCAtNDcsNiArNDcs
NyBAQA0KPiA+ICAgI2RlZmluZSBIQ0lfVUFSVF9CQ00JNw0KPiA+ICAgI2RlZmluZSBIQ0lfVUFS
VF9RQ0EJOA0KPiA+ICAgI2RlZmluZSBIQ0lfVUFSVF9BRzZYWAk5DQo+ID4gKyNkZWZpbmUgSENJ
X1VBUlRfTVJWTAkxMA0KPiA+DQo+ID4gICAjZGVmaW5lIEhDSV9VQVJUX1JBV19ERVZJQ0UJMA0K
PiA+ICAgI2RlZmluZSBIQ0lfVUFSVF9SRVNFVF9PTl9JTklUCTENCj4gPiBAQCAtMTkyLDMgKzE5
Myw4IEBAIGludCBxY2FfZGVpbml0KHZvaWQpOw0KPiA+ICAgaW50IGFnNnh4X2luaXQodm9pZCk7
DQo+ID4gICBpbnQgYWc2eHhfZGVpbml0KHZvaWQpOw0KPiA+ICAgI2VuZGlmDQo+ID4gKw0KPiA+
ICsjaWZkZWYgQ09ORklHX0JUX0hDSVVBUlRfTVJWTA0KPiA+ICtpbnQgbXJ2bF9pbml0KHZvaWQp
Ow0KPiA+ICtpbnQgbXJ2bF9kZWluaXQodm9pZCk7DQo+ID4gKyNlbmRpZg0KPiA+DQo+ID4NCj4g
VGhpcyBzZXJpYWwgb2YgcGF0Y2hlcyB3b3JrIHdlbGwgb24gbXkgZGV2aWNlLi4uDQo+IA0KPiBU
ZXN0ZWQtYnk6IEplZmZ5IENoZW4gPGplZmZ5LmNoZW5Acm9jay1jaGlwcy5jb20+DQoNCkFueSBm
dXJ0aGVyIGNvbW1lbnRzIG9uIHRoaXMgcGF0Y2ggc2VyaWVzPyBJdOKAmXMgYmVlbiBwZW5kaW5n
IGZvciBsb25nIHRpbWUuDQoNClJlZ2FyZHMsDQpBbWl0a3VtYXINCg==

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
@ 2016-06-27  6:28       ` Amitkumar Karwar
  0 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-06-27  6:28 UTC (permalink / raw)
  To: Jeffy Chen, linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Cathy Luo

Hi Marcel,

> From: Jeffy Chen [mailto:jeffy.chen@rock-chips.com]
> Sent: Friday, June 24, 2016 11:32 AM
> To: Amitkumar Karwar; linux-bluetooth@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat
> Subject: Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download
> for Marvell
> 
> On 2016-5-6 23:31, Amitkumar Karwar wrote:
> > From: Ganapathi Bhat <gbhat@marvell.com>
> >
> > This patch implement firmware download feature for Marvell Bluetooth
> > devices. If firmware is already downloaded, it will skip downloading.
> >
> > Signed-off-by: Ganapathi Bhat <gbhat@marvell.com>
> > Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
> > ---
> > v2: Fixed compilation warning reported by kbuild test robot
> > v3: Addressed review comments from Marcel Holtmann
> >      a) Removed vendor specific code from hci_ldisc.c
> >      b) Get rid of static forward declaration
> >      c) Removed unnecessary heavy nesting
> >      d) Git rid of module parameter and global variables
> >      e) Add logic to pick right firmware image
> > v4: Addresses review comments from Alan
> >      a) Use existing kernel helper APIs instead of writing own.
> >      b) Replace mdelay() with msleep()
> > v5: Addresses review comments from Loic Poulain
> >      a) Use bt_dev_err/warn/dbg helpers insted of BT_ERR/WARN/DBG
> >      b) Used static functions where required and removed forward
> delcarations
> >      c) Edited comments for the function hci_uart_recv_data
> >      d) Made HCI_UART_DNLD_FW flag a part of driver private data
> > v6: Addresses review comments from Loic Poulain
> >      a) Used skb instead of array to store firmware data during
> download
> >      b) Used hci_uart_tx_wakeup and enqueued packets instead of tty
> write
> >      c) Used GFP_KERNEL instead of GFP_ATOMIC
> > v7: Edited Kconfig to add dependency for BT_HCIUART_H4. The change
> resolves
> >      errors reported by kbuild test robot.
> > v8: Addressed review comments from Marcel Holtmann
> >      a) Removed unnecessary memory allocation failure messages
> >      b) Get rid of btmrvl.h header file and add definitions in
> > hci_mrvl.c file
> > v9: Addressed review comments from Marcel Holtmann
> >      a) Moved firmware download code from setup to prepare handler.
> >      b) Change messages from bt_dev_*->BT_*, as hdev isn't available
> during firmware
> >       download.
> > v10: Addressed review comments from Marcel Holtmann
> >      a) Added new callback recv_for_prepare to receive data from
> device
> >       during prepare phase
> >      b) Avoided using private flags (HCI_UART_DNLD_FW) as new receive
> callback is
> >       added for the same purpose
> >      c) Used kernel API to handle unaligned data
> >      d) Moved mrvl_set_baud functionality inside setup callback
> > v11: Write data through ldisc in mrvl_send_ack() instead of directly
> calling
> >      write method(One Thousand Gnomes).
> > ---
> >   drivers/bluetooth/Kconfig     |  11 +
> >   drivers/bluetooth/Makefile    |   1 +
> >   drivers/bluetooth/hci_ldisc.c |   6 +
> >   drivers/bluetooth/hci_mrvl.c  | 543
> ++++++++++++++++++++++++++++++++++++++++++
> >   drivers/bluetooth/hci_uart.h  |   8 +-
> >   5 files changed, 568 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/bluetooth/hci_mrvl.c
> >
> > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> > index cf50fd2..daafd0c 100644
> > --- a/drivers/bluetooth/Kconfig
> > +++ b/drivers/bluetooth/Kconfig
> > @@ -180,6 +180,17 @@ config BT_HCIUART_AG6XX
> >
> >   	  Say Y here to compile support for Intel AG6XX protocol.
> >
> > +config BT_HCIUART_MRVL
> > +	bool "Marvell protocol support"
> > +	depends on BT_HCIUART
> > +	select BT_HCIUART_H4
> > +	help
> > +	  Marvell is serial protocol for communication between Bluetooth
> > +	  device and host. This protocol is required for most Marvell
> Bluetooth
> > +	  devices with UART interface.
> > +
> > +	  Say Y here to compile support for HCI MRVL protocol.
> > +
> >   config BT_HCIBCM203X
> >   	tristate "HCI BCM203x USB driver"
> >   	depends on USB
> > diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> > index 9c18939..364dbb6 100644
> > --- a/drivers/bluetooth/Makefile
> > +++ b/drivers/bluetooth/Makefile
> > @@ -37,6 +37,7 @@ hci_uart-$(CONFIG_BT_HCIUART_INTEL)	+= hci_intel.o
> >   hci_uart-$(CONFIG_BT_HCIUART_BCM)	+= hci_bcm.o
> >   hci_uart-$(CONFIG_BT_HCIUART_QCA)	+= hci_qca.o
> >   hci_uart-$(CONFIG_BT_HCIUART_AG6XX)	+= hci_ag6xx.o
> > +hci_uart-$(CONFIG_BT_HCIUART_MRVL)	+= hci_mrvl.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 047e786..4896b6f 100644
> > --- a/drivers/bluetooth/hci_ldisc.c
> > +++ b/drivers/bluetooth/hci_ldisc.c
> > @@ -821,6 +821,9 @@ static int __init hci_uart_init(void)
> >   #ifdef CONFIG_BT_HCIUART_AG6XX
> >   	ag6xx_init();
> >   #endif
> > +#ifdef CONFIG_BT_HCIUART_MRVL
> > +	mrvl_init();
> > +#endif
> >
> >   	return 0;
> >   }
> > @@ -856,6 +859,9 @@ static void __exit hci_uart_exit(void)
> >   #ifdef CONFIG_BT_HCIUART_AG6XX
> >   	ag6xx_deinit();
> >   #endif
> > +#ifdef CONFIG_BT_HCIUART_MRVL
> > +	mrvl_deinit();
> > +#endif
> >
> >   	/* Release tty registration of line discipline */
> >   	err = tty_unregister_ldisc(N_HCI);
> > diff --git a/drivers/bluetooth/hci_mrvl.c
> > b/drivers/bluetooth/hci_mrvl.c new file mode 100644 index
> > 0000000..2686901
> > --- /dev/null
> > +++ b/drivers/bluetooth/hci_mrvl.c
> > @@ -0,0 +1,543 @@
> > +/* Bluetooth HCI UART driver for Marvell devices
> > + *
> > + * Copyright (C) 2016, Marvell International Ltd.
> > + *
> > + *  Acknowledgements:
> > + *  This file is based on hci_h4.c, which was written
> > + *  by Maxim Krasnyansky and Marcel Holtmann.
> > + *
> > + * This software file (the "File") is distributed by Marvell
> > +International
> > + * Ltd. under the terms of the GNU General Public License Version 2,
> > +June 1991
> > + * (the "License").  You may use, redistribute and/or modify this
> > +File in
> > + * accordance with the terms and conditions of the License, a copy of
> > +which
> > + * is available on the worldwide web at
> > + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
> > + *
> > + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND
> > +THE
> > + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
> > +PURPOSE
> > + * ARE EXPRESSLY DISCLAIMED.  The License provides additional details
> > +about
> > + * this warranty disclaimer.
> > + */
> > +
> > +#include <linux/firmware.h>
> > +#include <linux/tty.h>
> > +#include <asm/unaligned.h>
> > +#include <net/bluetooth/bluetooth.h>
> > +#include <net/bluetooth/hci_core.h>
> > +#include "hci_uart.h"
> > +
> > +struct fw_data {
> > +	wait_queue_head_t init_wait_q;
> > +	u8 wait_fw;
> > +	int next_len;
> > +	u8 five_bytes[5];
> > +	u8 next_index;
> > +	u8 last_ack;
> > +	u8 expected_ack;
> > +	struct ktermios old_termios;
> > +	u8 chip_id;
> > +	u8 chip_rev;
> > +	struct sk_buff *skb;
> > +};
> > +
> > +#define MRVL_HELPER_NAME	"mrvl/helper_uart_3000000.bin"
> > +#define MRVL_8997_CHIP_ID	0x50
> > +#define MRVL_8997_FW_NAME	"mrvl/uart8997_bt.bin"
> > +#define MRVL_MAX_FW_BLOCK_SIZE	1024
> > +#define MRVL_MAX_RETRY_SEND	12
> > +#define MRVL_DNLD_DELAY		100
> > +#define MRVL_ACK		0x5A
> > +#define MRVL_NAK		0xBF
> > +#define MRVL_HDR_REQ_FW		0xA5
> > +#define MRVL_HDR_CHIP_VER	0xAA
> > +#define MRVL_HCI_OP_SET_BAUD	0xFC09
> > +#define MRVL_FW_HDR_LEN		5
> > +#define MRVL_WAIT_TIMEOUT	msecs_to_jiffies(12000)
> > +
> > +struct mrvl_data {
> > +	struct sk_buff *rx_skb;
> > +	struct sk_buff_head txq;
> > +	struct fw_data *fwdata;
> > +};
> > +
> > +static int get_cts(struct hci_uart *hu) {
> > +	struct tty_struct *tty = hu->tty;
> > +	u32 state =  tty->ops->tiocmget(tty);
> > +
> > +	if (state & TIOCM_CTS) {
> > +		bt_dev_dbg(hu->hdev, "CTS is low");
> > +		return 1;
> > +	}
> > +	bt_dev_dbg(hu->hdev, "CTS is high");
> > +
> > +	return 0;
> > +}
> > +
> > +/* Initialize protocol */
> > +static int mrvl_open(struct hci_uart *hu) {
> > +	struct mrvl_data *mrvl;
> > +
> > +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> > +
> > +	mrvl = kzalloc(sizeof(*mrvl), GFP_KERNEL);
> > +	if (!mrvl)
> > +		return -ENOMEM;
> > +
> > +	skb_queue_head_init(&mrvl->txq);
> > +	hu->priv = mrvl;
> > +
> > +	return 0;
> > +}
> > +
> > +/* Flush protocol data */
> > +static int mrvl_flush(struct hci_uart *hu) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> > +
> > +	skb_queue_purge(&mrvl->txq);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Close protocol */
> > +static int mrvl_close(struct hci_uart *hu) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	bt_dev_dbg(hu->hdev, "hu %p", hu);
> > +
> > +	skb_queue_purge(&mrvl->txq);
> > +	kfree_skb(mrvl->rx_skb);
> > +	hu->priv = NULL;
> > +	kfree(mrvl);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Enqueue frame for transmittion (padding, crc, etc) */ static int
> > +mrvl_enqueue(struct hci_uart *hu, struct sk_buff *skb) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	bt_dev_dbg(hu->hdev, "hu %p skb %p", hu, skb);
> > +
> > +	/* Prepend skb with frame type */
> > +	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
> > +	skb_queue_tail(&mrvl->txq, skb);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct h4_recv_pkt mrvl_recv_pkts[] = {
> > +	{ H4_RECV_ACL,   .recv = hci_recv_frame },
> > +	{ H4_RECV_SCO,   .recv = hci_recv_frame },
> > +	{ H4_RECV_EVENT, .recv = hci_recv_frame }, };
> > +
> > +/* Send ACK/NAK to the device */
> > +static void mrvl_send_ack(struct hci_uart *hu, unsigned char ack) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct sk_buff *skb;
> > +
> > +	skb = bt_skb_alloc(sizeof(ack), GFP_KERNEL);
> > +	if (!skb)
> > +		return;
> > +
> > +	memcpy(skb->data, &ack, sizeof(ack));
> > +	skb_put(skb, sizeof(ack));
> > +	skb_queue_head(&mrvl->txq, skb);
> > +	hci_uart_tx_wakeup(hu);
> > +}
> > +
> > +/* Validate the feedback data from device */ static void
> > +mrvl_pkt_complete(struct hci_uart *hu, struct sk_buff *skb) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct fw_data *fw_data = mrvl->fwdata;
> > +	u16 lhs, rhs;
> > +
> > +	lhs = get_unaligned_le16(skb->data + 1);
> > +	rhs = get_unaligned_le16(skb->data + 3);
> > +	if ((lhs ^ rhs) == 0xffff) {
> > +		mrvl_send_ack(hu, MRVL_ACK);
> > +		fw_data->wait_fw = 1;
> > +		fw_data->next_len = lhs;
> > +		/* Firmware download is done, send the last ack */
> > +		if (!lhs)
> > +			fw_data->last_ack = 1;
> > +
> > +		if (fw_data->expected_ack == MRVL_HDR_CHIP_VER) {
> > +			fw_data->chip_id = skb->data[1];
> > +			fw_data->chip_rev = skb->data[2];
> > +		}
> > +		wake_up_interruptible(&fw_data->init_wait_q);
> > +	} else {
> > +		mrvl_send_ack(hu, MRVL_NAK);
> > +	}
> > +}
> > +
> > +/* This function receives data from the uart device during firmware
> download.
> > + * Driver expects 5 bytes of data as per the protocal in the below
> format:
> > + * <HEADER><BYTE_1><BYTE_2><BYTE_3><BYTE_4>
> > + * BYTE_3 and BYTE_4 are compliment of BYTE_1 an BYTE_2. Data can
> > +come in chunks
> > + * of any length. If length received is < 5, accumulate the data in
> > +an array,
> > + * until we have a sequence of 5 bytes, starting with the expected
> > +HEADER. If
> > + * the length received is > 5  bytes, then get the first 5 bytes,
> > +starting with
> > + * the HEADER and process the same, ignoring the rest of the bytes as
> > +per the
> > + * protocal.
> > + */
> > +static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
> > +					    struct sk_buff *skb,
> > +					    u8 *buf, int count)
> > +{
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct fw_data *fw_data = mrvl->fwdata;
> > +	int i = 0, len;
> > +
> > +	if (!skb) {
> > +		while (buf[i] != fw_data->expected_ack && i < count)
> > +			i++;
> > +		if (i == count)
> > +			return ERR_PTR(-EILSEQ);
> > +
> > +		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);
> > +	}
> > +
> > +	if (!skb)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	len = count - i;
> > +	memcpy(skb_put(skb, len), &buf[i], len);
> > +
> > +	if (skb->len == MRVL_FW_HDR_LEN) {
> > +		mrvl_pkt_complete(hu, skb);
> > +		kfree_skb(skb);
> > +		skb = NULL;
> > +	}
> > +
> > +	return skb;
> > +}
> > +
> > +/* Receive firmware feedback data */
> > +static int mrvl_recv_for_prepare(struct hci_uart *hu, const void
> *data,
> > +				 int count)
> > +{
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	mrvl->fwdata->skb = mrvl_process_fw_data(hu, mrvl->fwdata->skb,
> > +						 (u8 *)data, count);
> > +	if (IS_ERR(mrvl->fwdata->skb)) {
> > +		int err = PTR_ERR(mrvl->fwdata->skb);
> > +
> > +		bt_dev_err(hu->hdev,
> > +			   "Receive firmware data failed (%d)", err);
> > +		mrvl->fwdata->skb = NULL;
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/* Receive data */
> > +static int mrvl_recv(struct hci_uart *hu, const void *data, int
> > +count) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
> > +		return -EUNATCH;
> > +
> > +	mrvl->rx_skb = h4_recv_buf(hu->hdev, mrvl->rx_skb, data, count,
> > +				   mrvl_recv_pkts, ARRAY_SIZE(mrvl_recv_pkts));
> > +	if (IS_ERR(mrvl->rx_skb)) {
> > +		int err = PTR_ERR(mrvl->rx_skb);
> > +
> > +		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
> > +		mrvl->rx_skb = NULL;
> > +		return err;
> > +	}
> > +
> > +	return count;
> > +}
> > +
> > +static struct sk_buff *mrvl_dequeue(struct hci_uart *hu) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	return skb_dequeue(&mrvl->txq);
> > +}
> > +
> > +static int mrvl_init_fw_data(struct hci_uart *hu) {
> > +	struct fw_data *fwdata;
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	fwdata = kzalloc(sizeof(*fwdata), GFP_KERNEL);
> > +	if (!fwdata)
> > +		return -ENOMEM;
> > +
> > +	mrvl->fwdata = fwdata;
> > +	init_waitqueue_head(&fwdata->init_wait_q);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Wait for the header from device */ static int
> > +mrvl_wait_for_hdr(struct hci_uart *hu, u8 header) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct fw_data *fw_data = mrvl->fwdata;
> > +
> > +	fw_data->expected_ack = header;
> > +	fw_data->wait_fw = 0;
> > +
> > +	if (!wait_event_interruptible_timeout(fw_data->init_wait_q,
> > +					      fw_data->wait_fw,
> > +					      MRVL_WAIT_TIMEOUT)) {
> > +		BT_ERR("TIMEOUT, waiting for:0x%x", fw_data->expected_ack);
> > +		return -1;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/* Send bytes to device */
> > +static int mrvl_send_data(struct hci_uart *hu, struct sk_buff *skb) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +
> > +	skb_queue_head(&mrvl->txq, skb);
> > +	hci_uart_tx_wakeup(hu);
> > +
> > +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW) == -1)
> > +		return -1;
> > +
> > +	return 0;
> > +}
> > +
> > +/* Download firmware to the device */ static int mrvl_dnld_fw(struct
> > +hci_uart *hu, const char *file_name) {
> > +	const struct firmware *fw = NULL;
> > +	struct sk_buff *skb = NULL;
> > +	int offset = 0;
> > +	int ret, tx_len;
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct fw_data *fw_data = mrvl->fwdata;
> > +
> > +	ret = request_firmware(&fw, file_name, hu->tty->dev);
> > +	if (ret < 0) {
> > +		BT_ERR("request_firmware() failed");
> > +		return -1;
> > +	}
> > +
> > +	BT_INFO("Downloading FW (%d bytes)", (u16)fw->size);
> > +
> > +	fw_data->last_ack = 0;
> > +
> > +	while (1) {
> > +		if ((offset >= fw->size) || (fw_data->last_ack))
> > +			break;
> > +		tx_len = fw_data->next_len;
> > +		if ((fw->size - offset) < tx_len)
> > +			tx_len = fw->size - offset;
> > +
> > +		skb = bt_skb_alloc(MRVL_MAX_FW_BLOCK_SIZE, GFP_KERNEL);
> > +		if (!skb) {
> > +			ret = -1;
> > +			goto done;
> > +		}
> > +
> > +		memcpy(skb->data, &fw->data[offset], tx_len);
> > +		skb_put(skb, tx_len);
> > +		if (mrvl_send_data(hu, skb) != 0) {
> > +			BT_ERR("Fail to download firmware");
> > +			ret = -1;
> > +			goto done;
> > +		}
> > +		offset += tx_len;
> > +	}
> > +
> > +	BT_INFO("Downloaded %d byte firmware", offset);
> > +done:
> > +	release_firmware(fw);
> > +
> > +	return ret;
> > +}
> > +
> > +/* Set the baud rate */
> > +static int mrvl_set_dev_baud(struct hci_uart *hu) {
> > +	struct hci_dev *hdev = hu->hdev;
> > +	struct sk_buff *skb;
> > +	static const u8 baud_param[] = { 0xc0, 0xc6, 0x2d, 0x00 };
> > +	int err;
> > +
> > +	skb = __hci_cmd_sync(hdev, MRVL_HCI_OP_SET_BAUD,
> sizeof(baud_param),
> > +			     baud_param, HCI_INIT_TIMEOUT);
> > +	if (IS_ERR(skb)) {
> > +		err = PTR_ERR(skb);
> > +		bt_dev_err(hu->hdev, "Set device baudrate failed (%d)",
> err);
> > +		return err;
> > +	}
> > +	kfree_skb(skb);
> > +
> > +	return 0;
> > +}
> > +
> > +/* Reset device */
> > +static int mrvl_reset(struct hci_uart *hu) {
> > +	struct hci_dev *hdev = hu->hdev;
> > +	struct sk_buff *skb;
> > +	int err;
> > +
> > +	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
> HCI_CMD_TIMEOUT);
> > +	if (IS_ERR(skb)) {
> > +		err = PTR_ERR(skb);
> > +		bt_dev_err(hu->hdev, "Reset device failed (%d)", err);
> > +		return err;
> > +	}
> > +	kfree_skb(skb);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mrvl_get_fw_name(struct hci_uart *hu, char *fw_name) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	struct fw_data *fw_data = mrvl->fwdata;
> > +
> > +	if (mrvl_wait_for_hdr(hu, MRVL_HDR_CHIP_VER) != 0) {
> > +		BT_ERR("Could not read chip id and revision");
> > +		return -1;
> > +	}
> > +
> > +	BT_DBG("chip_id=0x%x, chip_rev=0x%x",
> > +	       fw_data->chip_id, fw_data->chip_rev);
> > +
> > +	switch (fw_data->chip_id) {
> > +	case MRVL_8997_CHIP_ID:
> > +		memcpy(fw_name, MRVL_8997_FW_NAME,
> sizeof(MRVL_8997_FW_NAME));
> > +		return 0;
> > +	default:
> > +		BT_ERR("Invalid chip id");
> > +		return -1;
> > +	}
> > +}
> > +
> > +/* Download helper and firmare to device */ static int
> > +hci_uart_dnld_fw(struct hci_uart *hu) {
> > +	struct tty_struct *tty = hu->tty;
> > +	struct ktermios new_termios;
> > +	struct ktermios old_termios;
> > +	char fw_name[128];
> > +	int ret;
> > +
> > +	old_termios = tty->termios;
> > +
> > +	if (get_cts(hu)) {
> > +		BT_INFO("fw is running");
> > +		return 0;
> > +	}
> > +
> > +	hci_uart_set_baudrate(hu, 115200);
> > +	hci_uart_set_flow_control(hu, true);
> > +
> > +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> > +	if (ret)
> > +		goto fail;
> > +
> > +	ret = mrvl_dnld_fw(hu, MRVL_HELPER_NAME);
> > +	if (ret)
> > +		goto fail;
> > +
> > +	msleep(MRVL_DNLD_DELAY);
> > +
> > +	hci_uart_set_baudrate(hu, 3000000);
> > +	hci_uart_set_flow_control(hu, false);
> > +
> > +	ret = mrvl_get_fw_name(hu, fw_name);
> > +	if (ret)
> > +		goto fail;
> > +
> > +	ret = mrvl_wait_for_hdr(hu, MRVL_HDR_REQ_FW);
> > +	if (ret)
> > +		goto fail;
> > +
> > +	ret = mrvl_dnld_fw(hu, fw_name);
> > +	if (ret)
> > +		goto fail;
> > +
> > +	msleep(MRVL_DNLD_DELAY);
> > +fail:
> > +	/* restore uart settings */
> > +	new_termios = tty->termios;
> > +	tty->termios.c_cflag = old_termios.c_cflag;
> > +	tty_set_termios(tty, &new_termios);
> > +
> > +	return ret;
> > +}
> > +
> > +static int mrvl_setup(struct hci_uart *hu) {
> > +	int err;
> > +
> > +	hci_uart_set_baudrate(hu, 115200);
> > +	hci_uart_set_flow_control(hu, false);
> > +
> > +	err = mrvl_reset(hu);
> > +	if (!err) {
> > +		err = mrvl_set_dev_baud(hu);
> > +		if (err)
> > +			return -1;
> > +	}
> > +
> > +	hci_uart_set_baudrate(hu, 3000000);
> > +	hci_uart_set_flow_control(hu, false);
> > +	msleep(MRVL_DNLD_DELAY);
> > +
> > +	return 0;
> > +}
> > +
> > +static int mrvl_prepare(struct hci_uart *hu) {
> > +	struct mrvl_data *mrvl = hu->priv;
> > +	int err;
> > +
> > +	err = mrvl_init_fw_data(hu);
> > +	if (!err)
> > +		err = hci_uart_dnld_fw(hu);
> > +
> > +	kfree(mrvl->fwdata);
> > +	return err;
> > +}
> > +
> > +static const struct hci_uart_proto mrvlp = {
> > +	.id		= HCI_UART_MRVL,
> > +	.name		= "MRVL",
> > +	.open		= mrvl_open,
> > +	.close		= mrvl_close,
> > +	.recv		= mrvl_recv,
> > +	.enqueue	= mrvl_enqueue,
> > +	.dequeue	= mrvl_dequeue,
> > +	.flush		= mrvl_flush,
> > +	.setup		= mrvl_setup,
> > +	.prepare	= mrvl_prepare,
> > +	.recv_for_prepare	= mrvl_recv_for_prepare,
> > +};
> > +
> > +int __init mrvl_init(void)
> > +{
> > +	return hci_uart_register_proto(&mrvlp); }
> > +
> > +int __exit mrvl_deinit(void)
> > +{
> > +	return hci_uart_unregister_proto(&mrvlp);
> > +}
> > diff --git a/drivers/bluetooth/hci_uart.h
> > b/drivers/bluetooth/hci_uart.h index 17ba3b4..8c53b50 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	10
> > +#define HCI_UART_MAX_PROTO	11
> >
> >   #define HCI_UART_H4	0
> >   #define HCI_UART_BCSP	1
> > @@ -47,6 +47,7 @@
> >   #define HCI_UART_BCM	7
> >   #define HCI_UART_QCA	8
> >   #define HCI_UART_AG6XX	9
> > +#define HCI_UART_MRVL	10
> >
> >   #define HCI_UART_RAW_DEVICE	0
> >   #define HCI_UART_RESET_ON_INIT	1
> > @@ -192,3 +193,8 @@ int qca_deinit(void);
> >   int ag6xx_init(void);
> >   int ag6xx_deinit(void);
> >   #endif
> > +
> > +#ifdef CONFIG_BT_HCIUART_MRVL
> > +int mrvl_init(void);
> > +int mrvl_deinit(void);
> > +#endif
> >
> >
> This serial of patches work well on my device...
> 
> Tested-by: Jeffy Chen <jeffy.chen@rock-chips.com>

Any further comments on this patch series? It’s been pending for long time.

Regards,
Amitkumar

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-06-27  6:28       ` Amitkumar Karwar
  (?)
@ 2016-06-30 10:54       ` Loic Poulain
  2016-08-09 16:18           ` Amitkumar Karwar
  -1 siblings, 1 reply; 15+ messages in thread
From: Loic Poulain @ 2016-06-30 10:54 UTC (permalink / raw)
  To: Amitkumar Karwar, Jeffy Chen, linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Cathy Luo,
	Marcel Holtmann

Hi Amitkumar,


> Hi Marcel,

I suggest you to add Marcel as recipient of your patches.

>
>> From: Jeffy Chen [mailto:jeffy.chen@rock-chips.com]
>> Sent: Friday, June 24, 2016 11:32 AM
>> To: Amitkumar Karwar; linux-bluetooth@vger.kernel.org
>> Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat
>> Subject: Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download
>> for Marvell
>>
>> On 2016-5-6 23:31, Amitkumar Karwar wrote:
>>> From: Ganapathi Bhat <gbhat@marvell.com>
>>>
>>> This patch implement firmware download feature for Marvell Bluetooth
>>> devices. If firmware is already downloaded, it will skip downloading.


>>> +static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
>>> +					    struct sk_buff *skb,
>>> +					    u8 *buf, int count)
>>> +{
>>> +	struct mrvl_data *mrvl = hu->priv;
>>> +	struct fw_data *fw_data = mrvl->fwdata;
>>> +	int i = 0, len;
>>> +
>>> +	if (!skb) {
>>> +		while (buf[i] != fw_data->expected_ack && i < count)
>>> +			i++;
>>> +		if (i == count)
>>> +			return ERR_PTR(-EILSEQ);
>>> +
>>> +		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);

Why you don't test skb here.

>>> +	}
>>> +
>>> +	if (!skb)
>>> +		return ERR_PTR(-ENOMEM);
>>> +
>>> +	len = count - i;
>>> +	memcpy(skb_put(skb, len), &buf[i], len);

You copy all the remaining data from buf into your skb, but what if buf 
contains more than one packet ? out of skb.
Don't assume that buf contains a full packet as well as only one packet.


Regards,
Loic

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
  2016-06-30 10:54       ` Loic Poulain
@ 2016-08-09 16:18           ` Amitkumar Karwar
  0 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-08-09 16:18 UTC (permalink / raw)
  To: Loic Poulain, Jeffy Chen, linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Marcel Holtmann

SGkgTG9pYywNCg0KPiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBMb2ljIFBv
dWxhaW4gW21haWx0bzpsb2ljLnBvdWxhaW5AaW50ZWwuY29tXQ0KPiBTZW50OiBUaHVyc2RheSwg
SnVuZSAzMCwgMjAxNiA0OjI0IFBNDQo+IFRvOiBBbWl0a3VtYXIgS2Fyd2FyOyBKZWZmeSBDaGVu
OyBsaW51eC1ibHVldG9vdGhAdmdlci5rZXJuZWwub3JnDQo+IENjOiBsaW51eC1rZXJuZWxAdmdl
ci5rZXJuZWwub3JnOyBHYW5hcGF0aGkgQmhhdDsgQ2F0aHkgTHVvOyBNYXJjZWwNCj4gSG9sdG1h
bm4NCj4gU3ViamVjdDogUmU6IFt2MTEsMy8zXSBCbHVldG9vdGg6IGhjaV91YXJ0OiBTdXBwb3J0
IGZpcm13YXJlIGRvd25sb2FkDQo+IGZvciBNYXJ2ZWxsDQo+IA0KPiBIaSBBbWl0a3VtYXIsDQo+
IA0KPiANCj4gPiBIaSBNYXJjZWwsDQo+IA0KPiBJIHN1Z2dlc3QgeW91IHRvIGFkZCBNYXJjZWwg
YXMgcmVjaXBpZW50IG9mIHlvdXIgcGF0Y2hlcy4NCj4gDQo+ID4NCj4gPj4gRnJvbTogSmVmZnkg
Q2hlbiBbbWFpbHRvOmplZmZ5LmNoZW5Acm9jay1jaGlwcy5jb21dDQo+ID4+IFNlbnQ6IEZyaWRh
eSwgSnVuZSAyNCwgMjAxNiAxMTozMiBBTQ0KPiA+PiBUbzogQW1pdGt1bWFyIEthcndhcjsgbGlu
dXgtYmx1ZXRvb3RoQHZnZXIua2VybmVsLm9yZw0KPiA+PiBDYzogbGludXgta2VybmVsQHZnZXIu
a2VybmVsLm9yZzsgR2FuYXBhdGhpIEJoYXQNCj4gPj4gU3ViamVjdDogUmU6IFt2MTEsMy8zXSBC
bHVldG9vdGg6IGhjaV91YXJ0OiBTdXBwb3J0IGZpcm13YXJlIGRvd25sb2FkDQo+ID4+IGZvciBN
YXJ2ZWxsDQo+ID4+DQo+ID4+IE9uIDIwMTYtNS02IDIzOjMxLCBBbWl0a3VtYXIgS2Fyd2FyIHdy
b3RlOg0KPiA+Pj4gRnJvbTogR2FuYXBhdGhpIEJoYXQgPGdiaGF0QG1hcnZlbGwuY29tPg0KPiA+
Pj4NCj4gPj4+IFRoaXMgcGF0Y2ggaW1wbGVtZW50IGZpcm13YXJlIGRvd25sb2FkIGZlYXR1cmUg
Zm9yIE1hcnZlbGwgQmx1ZXRvb3RoDQo+ID4+PiBkZXZpY2VzLiBJZiBmaXJtd2FyZSBpcyBhbHJl
YWR5IGRvd25sb2FkZWQsIGl0IHdpbGwgc2tpcA0KPiBkb3dubG9hZGluZy4NCj4gDQo+IA0KPiA+
Pj4gK3N0YXRpYyBzdHJ1Y3Qgc2tfYnVmZiAqbXJ2bF9wcm9jZXNzX2Z3X2RhdGEoc3RydWN0IGhj
aV91YXJ0ICpodSwNCj4gPj4+ICsJCQkJCSAgICBzdHJ1Y3Qgc2tfYnVmZiAqc2tiLA0KPiA+Pj4g
KwkJCQkJICAgIHU4ICpidWYsIGludCBjb3VudCkNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCBt
cnZsX2RhdGEgKm1ydmwgPSBodS0+cHJpdjsNCj4gPj4+ICsJc3RydWN0IGZ3X2RhdGEgKmZ3X2Rh
dGEgPSBtcnZsLT5md2RhdGE7DQo+ID4+PiArCWludCBpID0gMCwgbGVuOw0KPiA+Pj4gKw0KPiA+
Pj4gKwlpZiAoIXNrYikgew0KPiA+Pj4gKwkJd2hpbGUgKGJ1ZltpXSAhPSBmd19kYXRhLT5leHBl
Y3RlZF9hY2sgJiYgaSA8IGNvdW50KQ0KPiA+Pj4gKwkJCWkrKzsNCj4gPj4+ICsJCWlmIChpID09
IGNvdW50KQ0KPiA+Pj4gKwkJCXJldHVybiBFUlJfUFRSKC1FSUxTRVEpOw0KPiA+Pj4gKw0KPiA+
Pj4gKwkJc2tiID0gYnRfc2tiX2FsbG9jKE1SVkxfRldfSERSX0xFTiwgR0ZQX0tFUk5FTCk7DQo+
IA0KPiBXaHkgeW91IGRvbid0IHRlc3Qgc2tiIGhlcmUuDQo+IA0KPiA+Pj4gKwl9DQo+ID4+PiAr
DQo+ID4+PiArCWlmICghc2tiKQ0KPiA+Pj4gKwkJcmV0dXJuIEVSUl9QVFIoLUVOT01FTSk7DQo+
ID4+PiArDQo+ID4+PiArCWxlbiA9IGNvdW50IC0gaTsNCj4gPj4+ICsJbWVtY3B5KHNrYl9wdXQo
c2tiLCBsZW4pLCAmYnVmW2ldLCBsZW4pOw0KPiANCj4gWW91IGNvcHkgYWxsIHRoZSByZW1haW5p
bmcgZGF0YSBmcm9tIGJ1ZiBpbnRvIHlvdXIgc2tiLCBidXQgd2hhdCBpZiBidWYNCj4gY29udGFp
bnMgbW9yZSB0aGFuIG9uZSBwYWNrZXQgPyBvdXQgb2Ygc2tiLg0KPiBEb24ndCBhc3N1bWUgdGhh
dCBidWYgY29udGFpbnMgYSBmdWxsIHBhY2tldCBhcyB3ZWxsIGFzIG9ubHkgb25lIHBhY2tldC4N
Cj4gDQo+IA0KDQpUaGFua3MgZm9yIHlvdXIgY29tbWVudHMuIFdlIGhhdmUgYWRkZWQgbmVjZXNz
YXJpbHkgY2hlY2tzIHRvIGFkZHJlc3MgdGhlc2UgY29tbWVudHMuIEkgd2lsbCBzdWJtaXQgdXBk
YXRlZCB2ZXJzaW9uIHNob3J0bHkuDQoNClJlZ2FyZHMsDQpBbWl0a3VtYXIgS2Fyd2FyDQo=

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [v11,3/3] Bluetooth: hci_uart: Support firmware download for Marvell
@ 2016-08-09 16:18           ` Amitkumar Karwar
  0 siblings, 0 replies; 15+ messages in thread
From: Amitkumar Karwar @ 2016-08-09 16:18 UTC (permalink / raw)
  To: Loic Poulain, Jeffy Chen, linux-bluetooth@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, Ganapathi Bhat, Marcel Holtmann

Hi Loic,

> -----Original Message-----
> From: Loic Poulain [mailto:loic.poulain@intel.com]
> Sent: Thursday, June 30, 2016 4:24 PM
> To: Amitkumar Karwar; Jeffy Chen; linux-bluetooth@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat; Cathy Luo; Marcel
> Holtmann
> Subject: Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download
> for Marvell
> 
> Hi Amitkumar,
> 
> 
> > Hi Marcel,
> 
> I suggest you to add Marcel as recipient of your patches.
> 
> >
> >> From: Jeffy Chen [mailto:jeffy.chen@rock-chips.com]
> >> Sent: Friday, June 24, 2016 11:32 AM
> >> To: Amitkumar Karwar; linux-bluetooth@vger.kernel.org
> >> Cc: linux-kernel@vger.kernel.org; Ganapathi Bhat
> >> Subject: Re: [v11,3/3] Bluetooth: hci_uart: Support firmware download
> >> for Marvell
> >>
> >> On 2016-5-6 23:31, Amitkumar Karwar wrote:
> >>> From: Ganapathi Bhat <gbhat@marvell.com>
> >>>
> >>> This patch implement firmware download feature for Marvell Bluetooth
> >>> devices. If firmware is already downloaded, it will skip
> downloading.
> 
> 
> >>> +static struct sk_buff *mrvl_process_fw_data(struct hci_uart *hu,
> >>> +					    struct sk_buff *skb,
> >>> +					    u8 *buf, int count)
> >>> +{
> >>> +	struct mrvl_data *mrvl = hu->priv;
> >>> +	struct fw_data *fw_data = mrvl->fwdata;
> >>> +	int i = 0, len;
> >>> +
> >>> +	if (!skb) {
> >>> +		while (buf[i] != fw_data->expected_ack && i < count)
> >>> +			i++;
> >>> +		if (i == count)
> >>> +			return ERR_PTR(-EILSEQ);
> >>> +
> >>> +		skb = bt_skb_alloc(MRVL_FW_HDR_LEN, GFP_KERNEL);
> 
> Why you don't test skb here.
> 
> >>> +	}
> >>> +
> >>> +	if (!skb)
> >>> +		return ERR_PTR(-ENOMEM);
> >>> +
> >>> +	len = count - i;
> >>> +	memcpy(skb_put(skb, len), &buf[i], len);
> 
> You copy all the remaining data from buf into your skb, but what if buf
> contains more than one packet ? out of skb.
> Don't assume that buf contains a full packet as well as only one packet.
> 
> 

Thanks for your comments. We have added necessarily checks to address these comments. I will submit updated version shortly.

Regards,
Amitkumar Karwar

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2016-08-09 16:18 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-05-06 15:31 [PATCH v11 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Amitkumar Karwar
2016-05-06 15:31 ` [PATCH v11 2/3] Bluetooth: hci_uart: check if hdev is present before using it Amitkumar Karwar
2016-06-24  6:01   ` [v11, " Jeffy Chen
2016-05-06 15:31 ` [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download for Marvell Amitkumar Karwar
2016-05-10 13:59   ` Amitkumar Karwar
2016-05-23  6:06   ` Amitkumar Karwar
2016-06-14  6:35   ` Amitkumar Karwar
2016-06-21  3:47   ` [v11,3/3] " Caesar Wang
2016-06-24  6:02   ` Jeffy Chen
2016-06-27  6:28     ` Amitkumar Karwar
2016-06-27  6:28       ` Amitkumar Karwar
2016-06-30 10:54       ` Loic Poulain
2016-08-09 16:18         ` Amitkumar Karwar
2016-08-09 16:18           ` Amitkumar Karwar
2016-06-24  6:01 ` [v11, 1/3] Bluetooth: hci_uart: add prepare callbacks to hci_uart_proto structure Jeffy Chen

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.