From: Ganapathi Bhat <[email protected]>
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 <[email protected]>
Signed-off-by: Amitkumar Karwar <[email protected]>
---
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
Hi Marcel,
> From: Amitkumar Karwar [mailto:[email protected]]
> Sent: Friday, May 06, 2016 9:02 PM
> To: [email protected]
> Cc: [email protected]; Ganapathi Bhat; Amitkumar Karwar
> Subject: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware download
> for Marvell
>
> From: Ganapathi Bhat <[email protected]>
>
> This patch implement firmware download feature for Marvell Bluetooth
> devices. If firmware is already downloaded, it will skip downloading.
>
> Signed-off-by: Ganapathi Bhat <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>
> ---
> 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
From: Ganapathi Bhat <[email protected]>
The hdev struct might not have initialized in protocol receive handler.
This patch adds necessary checks.
Signed-off-by: Ganapathi Bhat <[email protected]>
Signed-off-by: Amitkumar Karwar <[email protected]>
---
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
From: Ganapathi Bhat <[email protected]>
This patch implement firmware download feature for
Marvell Bluetooth devices. If firmware is already
downloaded, it will skip downloading.
Signed-off-by: Ganapathi Bhat <[email protected]>
Signed-off-by: Amitkumar Karwar <[email protected]>
---
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
Hi Amitkumar,
> Hi Marcel,
I suggest you to add Marcel as recipient of your patches.
>
>> From: Jeffy Chen [mailto:[email protected]]
>> Sent: Friday, June 24, 2016 11:32 AM
>> To: Amitkumar Karwar; [email protected]
>> Cc: [email protected]; 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 <[email protected]>
>>>
>>> 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
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==
On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <[email protected]>
>
> This patch implement firmware download feature for
> Marvell Bluetooth devices. If firmware is already
> downloaded, it will skip downloading.
>
> Signed-off-by: Ganapathi Bhat <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>
> ---
> 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 <[email protected]>
On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <[email protected]>
>
> The hdev struct might not have initialized in protocol receive handler.
> This patch adds necessary checks.
>
> Signed-off-by: Ganapathi Bhat <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>
> ---
> 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 <[email protected]>
On 2016-5-6 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>
> ---
> 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 <[email protected]>
On 2016年05月06日 23:31, Amitkumar Karwar wrote:
> From: Ganapathi Bhat <[email protected]>
>
> This patch implement firmware download feature for
> Marvell Bluetooth devices. If firmware is already
> downloaded, it will skip downloading.
>
> Signed-off-by: Ganapathi Bhat <[email protected]>
> Signed-off-by: Amitkumar Karwar <[email protected]>
Tested-by: Caesar Wang <[email protected]>
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: [email protected],
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 | [email protected]
Hi Marcel,
> From: Amitkumar Karwar
> Sent: Monday, May 23, 2016 11:37 AM
> To: '[email protected]'
> Cc: '[email protected]'; Ganapathi Bhat; Cathy Luo
> Subject: RE: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware
> download for Marvell
>
> Hi Marcel,
>
> > > From: Amitkumar Karwar [mailto:[email protected]]
> > > Sent: Friday, May 06, 2016 9:02 PM
> > > To: [email protected]
> > > Cc: [email protected]; Ganapathi Bhat; Amitkumar Karwar
> > > Subject: [PATCH v11 3/3] Bluetooth: hci_uart: Support firmware
> > > download for Marvell
> > >
> > > From: Ganapathi Bhat <[email protected]>
> > >
> > > This patch implement firmware download feature for Marvell Bluetooth
> > > devices. If firmware is already downloaded, it will skip
> downloading.
> > >
> > > Signed-off-by: Ganapathi Bhat <[email protected]>
> > > Signed-off-by: Amitkumar Karwar <[email protected]>
> > > ---
> > > 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
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=