Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756430AbcCaMDz (ORCPT ); Thu, 31 Mar 2016 08:03:55 -0400 Received: from mx0b-0016f401.pphosted.com ([67.231.156.173]:20526 "EHLO mx0b-0016f401.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752547AbcCaMDx convert rfc822-to-8bit (ORCPT ); Thu, 31 Mar 2016 08:03:53 -0400 From: Amitkumar Karwar To: "linux-bluetooth@vger.kernel.org" CC: Cathy Luo , "linux-kernel@vger.kernel.org" , Nishant Sarmukadam , Ganapathi Bhat Subject: RE: [PATCH] Bluetooth: hci_uart: Support firmware download for Marvell Thread-Topic: [PATCH] Bluetooth: hci_uart: Support firmware download for Marvell Thread-Index: AQHRi0TxhR9urYKAJk6tvszAqMXY0p9zc+6Q Date: Thu, 31 Mar 2016 12:03:50 +0000 Message-ID: <3c95ec54efea48ef84002627532c5300@SC-EXCH04.marvell.com> References: <1459425270-19205-1-git-send-email-akarwar@marvell.com> In-Reply-To: <1459425270-19205-1-git-send-email-akarwar@marvell.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-ms-exchange-transport-fromentityheader: Hosted x-originating-ip: [10.93.176.43] Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 8BIT MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2016-03-31_05:,, signatures=0 X-Proofpoint-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=0 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1601100000 definitions=main-1603310168 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 21327 Lines: 766 > From: Amitkumar Karwar [mailto:akarwar@marvell.com] > Sent: Thursday, March 31, 2016 5:25 PM > To: linux-bluetooth@vger.kernel.org > Cc: Cathy Luo; linux-kernel@vger.kernel.org; Nishant Sarmukadam; > Ganapathi Bhat; Amitkumar Karwar > Subject: [PATCH] Bluetooth: hci_uart: Support firmware download for > Marvell > > From: Ganapathi Bhat > > This patch implement firmware download feature for Marvell Bluetooth > devices. If firmware is already downloaded, it will skip downloading. > > Signed-off-by: Ganapathi Bhat > Signed-off-by: Amitkumar Karwar > --- > 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 > > --- > drivers/bluetooth/Kconfig | 10 + > drivers/bluetooth/Makefile | 1 + > drivers/bluetooth/btmrvl.h | 43 ++++ > drivers/bluetooth/hci_ldisc.c | 6 + > drivers/bluetooth/hci_mrvl.c | 568 > ++++++++++++++++++++++++++++++++++++++++++ > drivers/bluetooth/hci_uart.h | 8 +- > 6 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 > drivers/bluetooth/btmrvl.h create mode 100644 > drivers/bluetooth/hci_mrvl.c > > diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index > cf50fd2..cb825f1 100644 > --- a/drivers/bluetooth/Kconfig > +++ b/drivers/bluetooth/Kconfig > @@ -180,6 +180,16 @@ 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 > + 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/btmrvl.h b/drivers/bluetooth/btmrvl.h new > file mode 100644 index 0000000..66855de > --- /dev/null > +++ b/drivers/bluetooth/btmrvl.h > @@ -0,0 +1,43 @@ > +/* Bluetooth support for Marvell devices > + * > + * Copyright (C) 2016, Marvell International Ltd. > + * > + * 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. > + */ > + > +struct fw_data { > + wait_queue_head_t init_wait_q __aligned(4); > + 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_FW_NAME "mrvl/uart8997_bt.bin" > +#define MRVL_WAIT_TIMEOUT 12000 > +#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 > diff --git a/drivers/bluetooth/hci_ldisc.c > b/drivers/bluetooth/hci_ldisc.c index c00168a..b858758 100644 > --- a/drivers/bluetooth/hci_ldisc.c > +++ b/drivers/bluetooth/hci_ldisc.c > @@ -807,6 +807,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; > } > @@ -842,6 +845,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..bca47fd > --- /dev/null > +++ b/drivers/bluetooth/hci_mrvl.c > @@ -0,0 +1,568 @@ > +/* 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 > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > + > +#include "hci_uart.h" > +#include "btmrvl.h" > + > +#define HCI_UART_DNLD_FW 2 > + > +struct mrvl_data { > + struct sk_buff *rx_skb; > + struct sk_buff_head txq; > + struct fw_data *fwdata; > + unsigned long flags; > +}; > + > +static int get_cts(struct hci_uart *hu) { > + struct tty_struct *tty = hu->tty; > + u32 state; > + > + if (tty->ops->tiocmget) { > + 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; > + } > + return -1; > +} > + > +/* 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); > + kfree(mrvl->fwdata); > + 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; > + > + if (test_bit(HCI_UART_DNLD_FW, &mrvl->flags)) > + return -EBUSY; > + > + 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 tty_struct *tty = hu->tty; > + > + tty->ops->write(tty, &ack, sizeof(ack)); } > + > +/* 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; > + u8 buf[MRVL_FW_HDR_LEN]; > + u16 lhs, rhs; > + > + memcpy(buf, skb->data, skb->len); > + > + lhs = le16_to_cpu(*((__le16 *)(&buf[1]))); > + rhs = le16_to_cpu(*((__le16 *)(&buf[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 (unlikely(fw_data->expected_ack == MRVL_HDR_CHIP_VER)) { > + fw_data->chip_id = *((u8 *)(&buf[1])); > + fw_data->chip_rev = *((u8 *)(&buf[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: > + *
> + * 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 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_DNLD_FW, &mrvl->flags)) { > + 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; > + } > + > + 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) * HZ / 1000))) { > + bt_dev_err(hu->hdev, "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; > +} > + > +static struct sk_buff *mrvl_alloc_skb(struct hci_dev *hdev) { > + struct sk_buff *skb = NULL; > + > + skb = bt_skb_alloc(MRVL_MAX_FW_BLOCK_SIZE, GFP_KERNEL); > + if (!skb) > + bt_dev_err(hdev, "cannot allocate memory for skb"); > + else > + skb->dev = (void *)hdev; > + > + return skb; > +} > + > +/* Download firmware to the device */ > +static int mrvl_dnld_fw(struct hci_uart *hu, const char *file_name) { > + struct hci_dev *hdev = hu->hdev; > + 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, &hdev->dev); > + if (ret < 0) { > + bt_dev_err(hu->hdev, "request_firmware() failed"); > + return -1; > + } > + > + bt_dev_info(hu->hdev, "Downloading FW (%d bytes)", (u16)fw->size); > + > + fw_data->last_ack = 0; > + > + do { > + 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 = mrvl_alloc_skb(hdev); > + 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_dev_err(hu->hdev, "fail to download firmware"); > + ret = -1; > + goto done; > + } > + offset += tx_len; > + } while (1); > + > + bt_dev_info(hu->hdev, "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_set_baud(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) > + goto set_term_baud; > + else > + goto set_dev_baud; > + > +set_dev_baud: > + err = mrvl_set_dev_baud(hu); > + if (err) > + return -1; > + > +set_term_baud: > + hci_uart_set_baudrate(hu, 3000000); > + hci_uart_set_flow_control(hu, false); > + > + return 0; > +} > + > +static int mrvl_get_fw_name(struct hci_uart *hu, char *fw_name) { > + int ret; > + struct mrvl_data *mrvl = hu->priv; > + struct fw_data *fw_data = mrvl->fwdata; > + > + ret = mrvl_wait_for_hdr(hu, MRVL_HDR_CHIP_VER); > + if (ret) { > + ret = -1; > + bt_dev_err(hu->hdev, "Could not read chip id and revision"); > + goto fail; > + } > + > + bt_dev_dbg(hu->hdev, "chip_id=0x%x, chip_rev=0x%x", > + fw_data->chip_id, fw_data->chip_rev); > + > + if (fw_data->chip_id == 0x50) { > + memcpy(fw_name, MRVL_8997_FW_NAME, > sizeof(MRVL_8997_FW_NAME)); > + } else { > + ret = -1; > + bt_dev_err(hu->hdev, "Invalid chip id"); > + } > +fail: > + return ret; > +} > + > +/* 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; > + struct mrvl_data *mrvl = hu->priv; > + char fw_name[128]; > + int ret; > + > + old_termios = tty->termios; > + > + if (get_cts(hu)) { > + bt_dev_info(hu->hdev, "fw is running"); > + clear_bit(HCI_UART_DNLD_FW, &mrvl->flags); > + goto set_baud; > + } > + > + 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); > + /* restore uart settings */ > + new_termios = tty->termios; > + tty->termios.c_cflag = old_termios.c_cflag; > + tty_set_termios(tty, &new_termios); > + clear_bit(HCI_UART_DNLD_FW, &mrvl->flags); > + > +set_baud: > + ret = mrvl_set_baud(hu); > + if (ret) > + goto fail; > + > + msleep(MRVL_DNLD_DELAY); > + > + return ret; > +fail: > + /* restore uart settings */ > + new_termios = tty->termios; > + tty->termios.c_cflag = old_termios.c_cflag; > + tty_set_termios(tty, &new_termios); > + clear_bit(HCI_UART_DNLD_FW, &mrvl->flags); > + > + return ret; > +} > + > +static int mrvl_setup(struct hci_uart *hu) { > + struct mrvl_data *mrvl = hu->priv; > + > + mrvl_init_fw_data(hu); > + set_bit(HCI_UART_DNLD_FW, &mrvl->flags); > + > + return hci_uart_dnld_fw(hu); > +} > + > +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, > +}; > + > +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 4814ff0..8b1f744 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 > @@ -188,3 +189,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.9.1 Please ignore this patch. I missed to include v6 in patch title. I will resend it. Regards, Amitkumar