From: Sean Wang <sean.w...@mediatek.com>

This adds the support of enabling MT7668U Bluetooth function running
on the top of btusb driver. The patch also adds a newly created file
mtkbt.c able to be reused independently from the transport type such
as UART, USB and SDIO.

Signed-off-by: Sean Wang <sean.w...@mediatek.com>
---
 drivers/bluetooth/Kconfig  |  16 +++
 drivers/bluetooth/Makefile |   1 +
 drivers/bluetooth/btmtk.c  | 308 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/bluetooth/btmtk.h  |  99 +++++++++++++++
 drivers/bluetooth/btusb.c  | 174 +++++++++++++++++++++++++
 5 files changed, 598 insertions(+)
 create mode 100644 drivers/bluetooth/btmtk.c
 create mode 100644 drivers/bluetooth/btmtk.h

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 07e55cd..2788498 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -11,6 +11,10 @@ config BT_BCM
        tristate
        select FW_LOADER
 
+config BT_MTK
+       tristate
+       select FW_LOADER
+
 config BT_RTL
        tristate
        select FW_LOADER
@@ -52,6 +56,18 @@ config BT_HCIBTUSB_BCM
 
          Say Y here to compile support for Broadcom protocol.
 
+config BT_HCIBTUSB_MTK
+       bool "MediaTek protocol support"
+       depends on BT_HCIBTUSB
+       select BT_MTK
+       default y
+       help
+         The MediaTek protocol support enables firmware download
+         support and chip initialization for MediaTek Bluetooth
+         USB controllers.
+
+         Say Y here to compile support for MediaTek protocol.
+
 config BT_HCIBTUSB_RTL
        bool "Realtek protocol support"
        depends on BT_HCIBTUSB
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 4e4e44d..bc23724 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_BT_MRVL_SDIO)    += btmrvl_sdio.o
 obj-$(CONFIG_BT_WILINK)                += btwilink.o
 obj-$(CONFIG_BT_QCOMSMD)       += btqcomsmd.o
 obj-$(CONFIG_BT_BCM)           += btbcm.o
+obj-$(CONFIG_BT_MTK)           += btmtk.o
 obj-$(CONFIG_BT_RTL)           += btrtl.o
 obj-$(CONFIG_BT_QCA)           += btqca.o
 
diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
new file mode 100644
index 0000000..9e39a0a
--- /dev/null
+++ b/drivers/bluetooth/btmtk.c
@@ -0,0 +1,308 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018 MediaTek Inc.
+
+/*
+ * Common operations for MediaTek Bluetooth devices
+ * with the UART, USB and SDIO transport
+ *
+ * Author: Sean Wang <sean.wang at mediatek.com>
+ *
+ */
+#include <asm/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btmtk.h"
+
+#define VERSION "0.1"
+
+int
+btmtk_hci_wmt_sync(struct hci_dev *hdev, struct btmtk_hci_wmt_params *params)
+{
+       struct btmtk_hci_wmt_evt_funcc *evt_funcc;
+       u32 hlen, status = BTMTK_WMT_INVALID;
+       struct btmtk_wmt_hdr *hdr, *ehdr;
+       struct btmtk_hci_wmt_cmd wc;
+       struct sk_buff *skb;
+       int err = 0;
+
+       hlen = sizeof(*hdr) + params->dlen;
+       if (hlen > 255)
+               return -EINVAL;
+
+       hdr = (struct btmtk_wmt_hdr *)&wc;
+       hdr->dir = 1;
+       hdr->op = params->op;
+       hdr->dlen = cpu_to_le16(params->dlen + 1);
+       hdr->flag = params->flag;
+       memcpy(wc.data, params->data, params->dlen);
+
+       /* TODO: Add a fixup with __hci_raw_sync_ev that uses the hdev->raw_q
+        * instead of the hack with __hci_cmd_sync_ev + atomic_inc on cmd_cnt.
+        */
+       atomic_inc(&hdev->cmd_cnt);
+
+       skb =  __hci_cmd_sync_ev(hdev, 0xfc6f, hlen, &wc, HCI_VENDOR_PKT,
+                                HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               err = PTR_ERR(skb);
+
+               bt_dev_err(hdev, "Failed to send wmt cmd (%d)\n", err);
+
+               print_hex_dump(KERN_ERR, "failed cmd: ",
+                              DUMP_PREFIX_ADDRESS, 16, 1, &wc,
+                              hlen > 16 ? 16 : hlen, true);
+               return err;
+       }
+
+       ehdr = (struct btmtk_wmt_hdr *)skb->data;
+       if (ehdr->op != hdr->op) {
+               bt_dev_err(hdev, "Wrong op received %d expected %d",
+                          ehdr->op, hdr->op);
+               err = -EIO;
+               goto err_free_skb;
+       }
+
+       switch (ehdr->op) {
+       case BTMTK_WMT_SEMAPHORE:
+               if (ehdr->flag == 2)
+                       status = BTMTK_WMT_PATCH_UNDONE;
+               else
+                       status = BTMTK_WMT_PATCH_DONE;
+               break;
+       case BTMTK_WMT_FUNC_CTRL:
+               evt_funcc = (struct btmtk_hci_wmt_evt_funcc *)ehdr;
+               if (be16_to_cpu(evt_funcc->status) == 4)
+                       status = BTMTK_WMT_ON_DONE;
+               else if (be16_to_cpu(evt_funcc->status) == 32)
+                       status = BTMTK_WMT_ON_PROGRESS;
+               else
+                       status = BTMTK_WMT_ON_UNDONE;
+               break;
+       };
+
+       if (params->status)
+               *params->status = status;
+
+err_free_skb:
+       kfree_skb(skb);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(btmtk_hci_wmt_sync);
+
+static int
+btmtk_setup_firmware(struct hci_dev *hdev, const char *fwname,
+                    int (*cmd_sync)(struct hci_dev *,
+                                    struct btmtk_hci_wmt_params *))
+{
+       struct btmtk_hci_wmt_params wmt_params;
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       size_t fw_size;
+       int err, dlen;
+       u8 flag;
+
+       if (!cmd_sync)
+               return -EINVAL;
+
+       err = request_firmware(&fw, fwname, &hdev->dev);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
+               return err;
+       }
+
+       fw_ptr = fw->data;
+       fw_size = fw->size;
+
+       /* The size of patch header is 30 bytes, should be skip */
+       if (fw_size < 30)
+               return -EINVAL;
+
+       fw_size -= 30;
+       fw_ptr += 30;
+       flag = 1;
+
+       wmt_params.op = BTMTK_WMT_PATCH_DWNLD;
+       wmt_params.status = NULL;
+
+       while (fw_size > 0) {
+               dlen = min_t(int, 250, fw_size);
+
+               /* Tell deivice the position in sequence */
+               if (fw_size - dlen <= 0)
+                       flag = 3;
+               else if (fw_size < fw->size - 30)
+                       flag = 2;
+
+               wmt_params.flag = flag;
+               wmt_params.dlen = dlen;
+               wmt_params.data = fw_ptr;
+
+               err = cmd_sync(hdev, &wmt_params);
+               if (err < 0) {
+                       bt_dev_err(hdev, "Failed to send wmt patch dwnld (%d)",
+                                  err);
+                       goto err_release_fw;
+               }
+
+               fw_size -= dlen;
+               fw_ptr += dlen;
+       }
+
+       wmt_params.op = BTMTK_WMT_RST;
+       wmt_params.flag = 4;
+       wmt_params.dlen = 0;
+       wmt_params.data = NULL;
+       wmt_params.status = NULL;
+
+       /* Activate funciton the firmware providing to */
+       err = cmd_sync(hdev, &wmt_params);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send wmt rst (%d)", err);
+               return err;
+       }
+
+err_release_fw:
+       release_firmware(fw);
+
+       return err;
+}
+
+static int
+btmtk_func_query(struct btmtk_func_query *fq)
+{
+       struct btmtk_hci_wmt_params wmt_params;
+       int status, err;
+       u8 param = 0;
+
+       if (!fq || !fq->hdev || !fq->cmd_sync)
+               return -EINVAL;
+
+       /* Query whether the function is enabled */
+       wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+       wmt_params.flag = 4;
+       wmt_params.dlen = sizeof(param);
+       wmt_params.data = &param;
+       wmt_params.status = &status;
+
+       err = fq->cmd_sync(fq->hdev, &wmt_params);
+       if (err < 0) {
+               bt_dev_err(fq->hdev, "Failed to query function status (%d)",
+                          err);
+               return err;
+       }
+
+       return status;
+}
+
+int btmtk_enable(struct hci_dev *hdev, const char *fwname,
+                int (*cmd_sync)(struct hci_dev *hdev,
+                                struct btmtk_hci_wmt_params *))
+{
+       struct btmtk_hci_wmt_params wmt_params;
+       struct btmtk_func_query func_query;
+       int status, err;
+       u8 param;
+
+       if (!cmd_sync)
+               return -EINVAL;
+
+       /* Query whether the firmware is already download */
+       wmt_params.op = BTMTK_WMT_SEMAPHORE;
+       wmt_params.flag = 1;
+       wmt_params.dlen = 0;
+       wmt_params.data = NULL;
+       wmt_params.status = &status;
+
+       err = cmd_sync(hdev, &wmt_params);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to query firmware status (%d)", err);
+               return err;
+       }
+
+       if (status == BTMTK_WMT_PATCH_DONE) {
+               bt_dev_info(hdev, "firmware already downloaded");
+               goto ignore_setup_fw;
+       }
+
+       /* Setup a firmware which the device definitely requires */
+       err = btmtk_setup_firmware(hdev, fwname, cmd_sync);
+       if (err < 0)
+               return err;
+
+ignore_setup_fw:
+       func_query.hdev = hdev;
+       func_query.cmd_sync = cmd_sync;
+       err = readx_poll_timeout(btmtk_func_query, &func_query, status,
+                                status < 0 || status != BTMTK_WMT_ON_PROGRESS,
+                                2000, 5000000);
+       /* -ETIMEDOUT happens */
+       if (err < 0)
+               return err;
+
+       /* The other errors happen internally inside btmtk_func_query */
+       if (status < 0)
+               return status;
+
+       if (status == BTMTK_WMT_ON_DONE) {
+               bt_dev_info(hdev, "function already on");
+               goto ignore_func_on;
+       }
+
+       /* Enable Bluetooth protocol */
+       param = 1;
+       wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+       wmt_params.flag = 0;
+       wmt_params.dlen = sizeof(param);
+       wmt_params.data = &param;
+       wmt_params.status = NULL;
+
+       err = cmd_sync(hdev, &wmt_params);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+               return err;
+       }
+
+ignore_func_on:
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_enable);
+
+int btmtk_disable(struct hci_dev *hdev,
+                 int (*cmd_sync)(struct hci_dev *hdev,
+                                 struct btmtk_hci_wmt_params *))
+{
+       struct btmtk_hci_wmt_params wmt_params;
+       u8 param = 0;
+       int err;
+
+       if (!cmd_sync)
+               return -EINVAL;
+
+       /* Disable the device */
+       wmt_params.op = BTMTK_WMT_FUNC_CTRL;
+       wmt_params.flag = 0;
+       wmt_params.dlen = sizeof(param);
+       wmt_params.data = &param;
+       wmt_params.status = NULL;
+
+       err = cmd_sync(hdev, &wmt_params);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to send wmt func ctrl (%d)", err);
+               return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_disable);
+
+MODULE_AUTHOR("Sean Wang <sean.w...@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek Bluetooth device driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE_MT7668);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
new file mode 100644
index 0000000..64fc395
--- /dev/null
+++ b/drivers/bluetooth/btmtk.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 MediaTek Inc.
+ *
+ * Common operations for MediaTek Bluetooth devices
+ * with the UART, USB and SDIO transport
+ *
+ * Author: Sean Wang <sean.w...@mediatek.com>
+ *
+ */
+
+#define FIRMWARE_MT7668                "mt7668pr2h.bin"
+
+enum {
+       BTMTK_WMT_PATCH_DWNLD = 0x1,
+       BTMTK_WMT_FUNC_CTRL = 0x6,
+       BTMTK_WMT_RST = 0x7,
+       BTMTK_WMT_SEMAPHORE = 0x17,
+};
+
+enum {
+       BTMTK_WMT_INVALID,
+       BTMTK_WMT_PATCH_UNDONE,
+       BTMTK_WMT_PATCH_DONE,
+       BTMTK_WMT_ON_UNDONE,
+       BTMTK_WMT_ON_DONE,
+       BTMTK_WMT_ON_PROGRESS,
+};
+
+struct btmtk_wmt_hdr {
+       u8      dir;
+       u8      op;
+       __le16  dlen;
+       u8      flag;
+} __packed;
+
+struct btmtk_hci_wmt_cmd {
+       struct btmtk_wmt_hdr hdr;
+       u8 data[256];
+} __packed;
+
+struct btmtk_hci_wmt_evt_funcc {
+       struct btmtk_wmt_hdr hdr;
+       __be16 status;
+} __packed;
+
+struct btmtk_hci_wmt_params {
+       u8 op;
+       u8 flag;
+       u16 dlen;
+       const void *data;
+       u32 *status;
+};
+
+struct btmtk_func_query {
+       struct hci_dev *hdev;
+       int (*cmd_sync)(struct hci_dev *hdev,
+                       struct btmtk_hci_wmt_params *wmt_params);
+};
+
+#if IS_ENABLED(CONFIG_BT_MTK)
+
+int
+btmtk_hci_wmt_sync(struct hci_dev *hdev, struct btmtk_hci_wmt_params *params);
+
+int
+btmtk_enable(struct hci_dev *hdev, const char *fn,
+            int (*cmd_sync)(struct hci_dev *,
+                            struct btmtk_hci_wmt_params *));
+
+int
+btmtk_disable(struct hci_dev *hdev,
+             int (*cmd_sync)(struct hci_dev *,
+                             struct btmtk_hci_wmt_params *));
+#else
+
+static int
+btmtk_hci_wmt_sync(struct hci_dev *hdev, struct btmtk_hci_wmt_params *params)
+{
+       return -EOPNOTSUPP;
+}
+
+static int
+btmtk_enable(struct hci_dev *hdev, const char *fn,
+            int (*cmd_sync)(struct hci_dev *,
+                            struct btmtk_hci_wmt_params *))
+{
+       return -EOPNOTSUPP;
+}
+
+static int
+btmtk_disable(struct hci_dev *hdev,
+             int (*cmd_sync)(struct hci_dev *,
+                             struct btmtk_hci_wmt_params *))
+{
+       return -EOPNOTSUPP;
+}
+
+#endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 60bf04b..773238b 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -26,6 +26,7 @@
 #include <linux/usb.h>
 #include <linux/usb/quirks.h>
 #include <linux/firmware.h>
+#include <linux/iopoll.h>
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/suspend.h>
@@ -36,6 +37,7 @@
 
 #include "btintel.h"
 #include "btbcm.h"
+#include "btmtk.h"
 #include "btrtl.h"
 
 #define VERSION "0.8"
@@ -69,6 +71,7 @@ static struct usb_driver btusb_driver;
 #define BTUSB_BCM2045          0x40000
 #define BTUSB_IFNUM_2          0x80000
 #define BTUSB_CW6622           0x100000
+#define BTUSB_MEDIATEK         0x200000
 
 static const struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
@@ -355,6 +358,10 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_VENDOR_AND_INTERFACE_INFO(0x0bda, 0xe0, 0x01, 0x01),
          .driver_info = BTUSB_REALTEK },
 
+       /* MediaTek Bluetooth devices */
+       { USB_VENDOR_AND_INTERFACE_INFO(0x0e8d, 0xe0, 0x01, 0x01),
+         .driver_info = BTUSB_MEDIATEK },
+
        /* Additional Realtek 8723AE Bluetooth devices */
        { USB_DEVICE(0x0930, 0x021d), .driver_info = BTUSB_REALTEK },
        { USB_DEVICE(0x13d3, 0x3394), .driver_info = BTUSB_REALTEK },
@@ -2347,6 +2354,164 @@ static int btusb_shutdown_intel(struct hci_dev *hdev)
        return 0;
 }
 
+#ifdef CONFIG_BT_HCIBTUSB_MTK
+
+struct btusb_mtk_poll {
+       struct btusb_data *udata;
+       void *buf;
+       size_t len;
+       size_t actual_len;
+};
+
+struct btusb_mtk_wmt_poll {
+       struct btusb_data *udata;
+       struct work_struct work;
+};
+
+static int btusb_mtk_reg_read(struct btusb_data *data, u32 reg, u32 *val)
+{
+       int pipe, err, size = sizeof(u32);
+       void *buf;
+
+       buf = kzalloc(size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       pipe = usb_rcvctrlpipe(data->udev, 0);
+       err = usb_control_msg(data->udev, pipe, 0x63,
+                             USB_TYPE_VENDOR | USB_DIR_IN,
+                             reg >> 16, reg & 0xffff,
+                             buf, size, USB_CTRL_SET_TIMEOUT);
+       if (err < 0)
+               goto err_free_buf;
+
+       *val = get_unaligned_le32(buf);
+
+err_free_buf:
+       kfree(buf);
+
+       return err;
+}
+
+static int btusb_mtk_id_get(struct btusb_data *data, u32 *id)
+{
+       return btusb_mtk_reg_read(data, 0x80000008, id);
+}
+
+static int btusb_mtk_wmt_event_poll(struct btusb_mtk_poll *p)
+{
+       int pipe, actual_len;
+
+       pipe = usb_rcvctrlpipe(p->udata->udev, 0);
+
+       actual_len = usb_control_msg(p->udata->udev, pipe, 1,
+                                    USB_TYPE_VENDOR | USB_DIR_IN, 0x30, 0,
+                                    p->buf, p->len, USB_CTRL_SET_TIMEOUT);
+
+       p->actual_len = actual_len;
+
+       return actual_len;
+}
+
+static void btusb_mtk_wmt_event_polls(struct work_struct *work)
+{
+       struct btusb_mtk_wmt_poll *wmt_event_polling;
+       struct btusb_mtk_poll p;
+       int polled_dlen, err;
+       const int len = 64;
+       void *buf;
+       char *evt;
+
+       wmt_event_polling = container_of(work, typeof(*wmt_event_polling),
+                                        work);
+       buf = kzalloc(len, GFP_KERNEL);
+       if (!buf)
+               return;
+
+       p.udata = wmt_event_polling->udata;
+       p.buf = buf;
+       p.len = len;
+       p.actual_len = 0;
+
+       /* Polling WMT event via control endpoint until the event returns or
+        * the timeout happens.
+        */
+       err = readx_poll_timeout(btusb_mtk_wmt_event_poll, &p, polled_dlen,
+                                polled_dlen > 0, 200, 1000000);
+       if (err < 0)
+               goto err_free_buf;
+
+       evt = p.buf;
+
+       /* Fix up the vendor event id with 0xff for vendor specific instead
+        * of 0xe4 so that event send via monitoring socket can be parsed
+        * properly.
+        */
+       if (*evt == 0xe4)
+               *evt = 0xff;
+
+       /* The WMT event is actually a HCI event so that the WMT event should go
+        * to the code flow a HCI event should go to.
+        */
+       btusb_recv_intr(p.udata, p.buf, p.actual_len);
+
+err_free_buf:
+       kfree(buf);
+}
+
+static int btusb_mtk_hci_wmt_sync(struct hci_dev *hdev,
+                                 struct btmtk_hci_wmt_params *wmt_params)
+{
+       struct btusb_mtk_wmt_poll wmt_event_polling;
+       int err;
+
+       /* MediaTek WMT HCI vendor event is coming through the control endpoint,
+        * not through the interrupt endpoint so that we have to schedule a
+        * work to poll the event.
+        */
+       INIT_WORK_ONSTACK(&wmt_event_polling.work, btusb_mtk_wmt_event_polls);
+       wmt_event_polling.udata = hci_get_drvdata(hdev);
+       schedule_work(&wmt_event_polling.work);
+
+       err = btmtk_hci_wmt_sync(hdev, wmt_params);
+
+       cancel_work_sync(&wmt_event_polling.work);
+
+       return err;
+}
+
+static int btusb_mtk_setup(struct hci_dev *hdev)
+{
+       struct btusb_data *data = hci_get_drvdata(hdev);
+       const char *fwname;
+       int err = 0;
+       u32 dev_id;
+
+       err = btusb_mtk_id_get(data, &dev_id);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to get device id (%d)", err);
+               return err;
+       }
+
+       switch (dev_id) {
+       case 0x7668:
+               fwname = FIRMWARE_MT7668;
+               break;
+       default:
+               bt_dev_err(hdev, "Unsupported support hardware variant (%08x)",
+                          dev_id);
+               return -ENODEV;
+       }
+
+       return btmtk_enable(hdev, fwname, btusb_mtk_hci_wmt_sync);
+}
+
+static int btusb_mtk_shutdown(struct hci_dev *hdev)
+{
+       return btmtk_disable(hdev, btusb_mtk_hci_wmt_sync);
+}
+#endif
+
 #ifdef CONFIG_PM
 /* Configure an out-of-band gpio as wake-up pin, if specified in device tree */
 static int marvell_config_oob_wake(struct hci_dev *hdev)
@@ -3031,6 +3196,15 @@ static int btusb_probe(struct usb_interface *intf,
        if (id->driver_info & BTUSB_MARVELL)
                hdev->set_bdaddr = btusb_set_bdaddr_marvell;
 
+#ifdef CONFIG_BT_HCIBTUSB_MTK
+       if (id->driver_info & BTUSB_MEDIATEK) {
+               hdev->setup = btusb_mtk_setup;
+               hdev->shutdown = btusb_mtk_shutdown;
+               hdev->manufacturer = 70;
+               set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks);
+       }
+#endif
+
        if (id->driver_info & BTUSB_SWAVE) {
                set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks);
                set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
-- 
2.7.4

Reply via email to