new file mode 100644
@@ -0,0 +1,1735 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 MediaTek Inc.
+
+/*
+ * Bluetooth support for MediaTek SDIO/USB/UART devices
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/input.h>
+#include <linux/pm_wakeup.h>
+#include <linux/reboot.h>
+#include <linux/string.h>
+#include "btmtk_main.h"
+
+#define MTKBT_UNSLEEPABLE_LOCK(x, y) spin_lock_irqsave(x, y)
+#define MTKBT_UNSLEEPABLE_UNLOCK(x, y) spin_unlock_irqsave(x, y)
+
+/* TODO, need to modify the state mutex for each hci dev*/
+static DEFINE_MUTEX(btmtk_chip_state_mutex);
+#define CHIP_STATE_MUTEX_LOCK() mutex_lock(&btmtk_chip_state_mutex)
+#define CHIP_STATE_MUTEX_UNLOCK() mutex_unlock(&btmtk_chip_state_mutex)
+static DEFINE_MUTEX(btmtk_fops_state_mutex);
+#define FOPS_MUTEX_LOCK() mutex_lock(&btmtk_fops_state_mutex)
+#define FOPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_fops_state_mutex)
+
+static int btmtk_fops_get_state(struct btmtk_dev *bdev);
+u8 btmtk_log_lvl = BTMTK_LOG_LVL_DEF;
+
+/* To support dynamic mount of interface can be probed */
+static int btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
+/* To allow g_bdev being sized from btmtk_intf_num setting */
+static struct btmtk_dev **g_bdev;
+
+const u8 RESET_EVENT[] = { 0x0E, 0x04, 0x01, 0x03, 0x0c, 0x00 };
+const u8 READ_ISO_PACKET_SIZE_CMD[] = { 0x01, 0x98, 0xFD, 0x02 };
+
+u8 wmt_over_hci_header[] = { 0x01, 0x6F, 0xFC};
+
+/*btmtk main information*/
+static struct btmtk_main_info main_info;
+
+/* State machine table that clarify through each HIF events,
+ * To specify HIF event on
+ * Entering / End / Error
+ */
+static const struct btmtk_cif_state g_cif_state[] = {
+ /* HIF_EVENT_PROBE */
+ {BTMTK_STATE_PROBE, BTMTK_STATE_WORKING, BTMTK_STATE_DISCONNECT},
+ /* HIF_EVENT_DISCONNECT */
+ {BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT},
+ /* HIF_EVENT_SUSPEND */
+ {BTMTK_STATE_SUSPEND, BTMTK_STATE_SUSPEND, BTMTK_STATE_FW_DUMP},
+ /* HIF_EVENT_RESUME */
+ {BTMTK_STATE_RESUME, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
+ /* HIF_EVENT_STANDBY */
+ {BTMTK_STATE_STANDBY, BTMTK_STATE_STANDBY, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_SUBSYS_RESET, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_FW_DUMP, BTMTK_STATE_DISCONNECT, BTMTK_STATE_FW_DUMP},
+ /* BTMTK_STATE_FW_DUMP */
+ {BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP},
+};
+
+__weak int btmtk_cif_register(void)
+{
+ BTMTK_ERR("No cif register function");
+ return -1;
+}
+
+__weak int btmtk_cif_deregister(void)
+{
+ BTMTK_ERR("No cif deregister function");
+ return -1;
+}
+
+int btmtk_get_chip_state(struct btmtk_dev *bdev)
+{
+ int state = BTMTK_STATE_INIT;
+
+ CHIP_STATE_MUTEX_LOCK();
+ state = bdev->interface_state;
+ CHIP_STATE_MUTEX_UNLOCK();
+
+ return state;
+}
+
+void btmtk_set_chip_state(struct btmtk_dev *bdev, int new_state)
+{
+ static const char * const state_msg[] = {
+ "UNKNOWN", "INIT", "DISCONNECT", "PROBE", "WORKING", "SUSPEND", "RESUME",
+ "FW_DUMP", "STANDBY", "SUBSYS_RESET",
+ };
+
+ BTMTK_INFO("%s: %s(%d) -> %s(%d)", __func__, state_msg[bdev->interface_state],
+ bdev->interface_state, state_msg[new_state], new_state);
+
+ CHIP_STATE_MUTEX_LOCK();
+ bdev->interface_state = new_state;
+ CHIP_STATE_MUTEX_UNLOCK();
+}
+
+static int btmtk_fops_get_state(struct btmtk_dev *bdev)
+{
+ int state = BTMTK_FOPS_STATE_INIT;
+
+ FOPS_MUTEX_LOCK();
+ state = bdev->fops_state;
+ FOPS_MUTEX_UNLOCK();
+
+ return state;
+}
+
+static void btmtk_fops_set_state(struct btmtk_dev *bdev, int new_state)
+{
+ static const char * const fstate_msg[] = {
+ "UNKNOWN", "INIT", "OPENING", "OPENED", "CLOSING", "CLOSED",
+ };
+
+ BTMTK_INFO("%s: FOPS_%s(%d) -> FOPS_%s(%d)", __func__, fstate_msg[bdev->fops_state],
+ bdev->fops_state, fstate_msg[new_state], new_state);
+ FOPS_MUTEX_LOCK();
+ bdev->fops_state = new_state;
+ FOPS_MUTEX_UNLOCK();
+}
+
+struct btmtk_main_info *btmtk_get_main_info(void)
+{
+ return &main_info;
+}
+
+static int main_init(void)
+{
+ int i = 0;
+
+ BTMTK_INFO("%s", __func__);
+
+ /* Check if user changes default minimum supported intf count */
+ if (btmtk_intf_num < BT_MCU_MINIMUM_INTERFACE_NUM) {
+ btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
+ BTMTK_WARN("%s minimum interface is %d", __func__, btmtk_intf_num);
+ }
+
+ g_bdev = kzalloc((sizeof(*g_bdev) * btmtk_intf_num), GFP_KERNEL);
+ if (!g_bdev) {
+ BTMTK_WARN("%s insufficient memory", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ g_bdev[i] = btmtk_allocate_dev_memory(NULL);
+ if (g_bdev[i])
+ btmtk_fops_set_state(g_bdev[i], BTMTK_FOPS_STATE_INIT);
+ else
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int main_exit(void)
+{
+ int i = 0;
+
+ BTMTK_INFO("%s releasing intf count <%d>", __func__, btmtk_intf_num);
+
+ if (!g_bdev) {
+ BTMTK_WARN("%s g_data is NULL", __func__);
+ return 0;
+ }
+
+ BTMTK_INFO("%s: Register reboot_notifier callback success.", __func__);
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (!g_bdev[i])
+ btmtk_free_dev_memory(NULL, g_bdev[i]);
+ }
+
+ kfree(g_bdev);
+ return 0;
+}
+
+static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
+ struct sk_buff *skb,
+ const unsigned char *buffer,
+ int count,
+ const struct h4_recv_pkt *pkts,
+ int pkts_count)
+{
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev || !buffer) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is invalid", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+ if (IS_ERR(skb))
+ skb = NULL;
+
+ while (count) {
+ int i, len;
+
+ if (!skb) {
+ for (i = 0; i < pkts_count; i++) {
+ if (buffer[0] != (&pkts[i])->type)
+ continue;
+
+ skb = bt_skb_alloc((&pkts[i])->maxlen,
+ GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s, alloc skb failed!", __func__);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ hci_skb_pkt_type(skb) = (&pkts[i])->type;
+ hci_skb_expect(skb) = (&pkts[i])->hlen;
+ break;
+ }
+
+ /* Check for invalid packet type */
+ if (!skb) {
+ BTMTK_ERR("%s,skb is invalid, buffer[0] = %d!", __func__,
+ buffer[0]);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ count -= 1;
+ buffer += 1;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+
+ if (skb->len < hci_skb_expect(skb))
+ continue;
+
+ for (i = 0; i < pkts_count; i++) {
+ if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
+ break;
+ }
+
+ if (i >= pkts_count) {
+ BTMTK_ERR("%s, pkt type is invalid!", __func__);
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (skb->len == (&pkts[i])->hlen) {
+ u16 dlen;
+
+ switch ((&pkts[i])->lsize) {
+ case 0:
+ /* No variable data length */
+ dlen = 0;
+ break;
+ case 1:
+ dlen = skb->data[(&pkts[i])->loff];
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ BTMTK_ERR("%s, skb_tailroom is not enough, dlen:%d!",
+ __func__, dlen);
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ case 2:
+ /* Double octet variable length */
+ dlen = get_unaligned_le16(skb->data +
+ (&pkts[i])->loff);
+ hci_skb_expect(skb) += dlen;
+
+ if (skb_tailroom(skb) < dlen) {
+ BTMTK_ERR("%s, tailroom isn't enough in case 2, dlen:%d!",
+ __func__, dlen);
+ kfree_skb(skb);
+ return ERR_PTR(-EMSGSIZE);
+ }
+ break;
+ default:
+ /* Unsupported variable length */
+ BTMTK_ERR("%s, Unsupported variable length!", __func__);
+ kfree_skb(skb);
+ return ERR_PTR(-EILSEQ);
+ }
+
+ if (!dlen) {
+ /* No more data, complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ } else {
+ /* Complete frame */
+ (&pkts[i])->recv(hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ return skb;
+}
+
+static const struct h4_recv_pkt mtk_recv_pkts[] = {
+ { H4_RECV_ACL, .recv = btmtk_recv_acl },
+ { H4_RECV_SCO, .recv = hci_recv_frame },
+ { H4_RECV_EVENT, .recv = btmtk_recv_event },
+};
+
+int btmtk_recv(struct hci_dev *hdev, const u8 *data, size_t count)
+{
+ struct btmtk_dev *bdev = NULL;
+ const unsigned char *p_left = data;
+ int sz_left = count;
+ int err;
+
+ if (!hdev || !data) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ return -EINVAL;
+ }
+
+ while (sz_left > 0) {
+ /* The serial data received from MT7622 BT controller is
+ * at all time padded around with the STP header and tailer.
+ *
+ * A full STP packet is looking like
+ * -----------------------------------
+ * | STP header | H:4 | STP tailer |
+ * -----------------------------------
+ * but it doesn't guarantee to contain a full H:4 packet which
+ * means that it's possible for multiple STP packets forms a
+ * full H:4 packet that means extra STP header + length doesn't
+ * indicate a full H:4 frame, things can fragment. Whose length
+ * recorded in STP header just shows up the most length the
+ * H:4 engine can handle currently.
+ */
+
+ bdev->rx_skb = h4_recv_buf(hdev, bdev->rx_skb, data,
+ count, mtk_recv_pkts,
+ ARRAY_SIZE(mtk_recv_pkts));
+
+ if (IS_ERR(bdev->rx_skb)) {
+ err = PTR_ERR(bdev->rx_skb);
+ pr_err("Frame reassembly failed (%d)", err);
+ bdev->rx_skb = NULL;
+ return err;
+ }
+
+ sz_left -= count;
+ p_left += count;
+ }
+
+ return 0;
+}
+
+static int btmtk_dispatch_pkt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ int state = BTMTK_STATE_INIT;
+
+ if ((bt_cb(skb)->pkt_type == HCI_ACLDATA_PKT) &&
+ skb->data[0] == 0x6f &&
+ skb->data[1] == 0xfc) {
+ static int dump_data_counter;
+ static int dump_data_length;
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_FW_DUMP) {
+ BTMTK_INFO("%s: FW dump begin", __func__);
+ dump_data_counter = 0;
+ dump_data_length = 0;
+ btmtk_set_chip_state(bdev, BTMTK_STATE_FW_DUMP);
+ }
+
+ dump_data_counter++;
+ dump_data_length += skb->len;
+
+ if (dump_data_counter % 1000 == 0) {
+ BTMTK_INFO("%s: FW dump on-going, total_packet = %d, total_length = %d",
+ __func__, dump_data_counter, dump_data_length);
+ }
+
+ if (dump_data_counter < 20)
+ BTMTK_INFO("%s: FW dump data (%d): %s",
+ __func__, dump_data_counter, &skb->data[4]);
+
+ if (skb->data[skb->len - 4] == 'e' &&
+ skb->data[skb->len - 3] == 'n' &&
+ skb->data[skb->len - 2] == 'd') {
+ BTMTK_INFO("%s: FW dump end, dump_data_counter = %d",
+ __func__, dump_data_counter);
+ }
+ } else if (memcmp(skb->data, RESET_EVENT, sizeof(RESET_EVENT)) == 0) {
+ BTMTK_INFO("%s: Get RESET_EVENT", __func__);
+ }
+ return 0;
+}
+
+int btmtk_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev || !skb) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev || !bdev->workqueue) {
+ BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
+ return -EINVAL;
+ }
+
+ skb_queue_tail(&bdev->rx_q, skb);
+ queue_work(bdev->workqueue, &bdev->rx_work);
+
+ return 0;
+}
+
+int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev || !skb) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev || !bdev->workqueue) {
+ BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ skb_queue_tail(&bdev->rx_q, skb);
+ queue_work(bdev->workqueue, &bdev->rx_work);
+
+ return 0;
+}
+
+int btmtk_main_send_cmd(struct btmtk_dev *bdev, const u8 *cmd,
+ const int cmd_len, const u8 *event, const int event_len,
+ int delay, int retry, int pkt_type)
+{
+ struct sk_buff *skb = NULL;
+ int ret = 0;
+ int state = BTMTK_STATE_INIT;
+
+ if (!bdev || !bdev->hdev || !cmd || cmd_len <= 0) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (memcmp(cmd, wmt_over_hci_header, sizeof(wmt_over_hci_header)) &&
+ pkt_type != BTMTK_TX_ACL_FROM_DRV &&
+ bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
+ BTMTK_WARN("%s: chip power isn't on, ignore this command, state is %d",
+ __func__, bdev->power_state);
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't send any cmd to FW!!!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ skb = alloc_skb(cmd_len + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ goto exit;
+ }
+
+ skb_reserve(skb, 7);
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ memcpy(skb->data, cmd, cmd_len);
+ skb->len = cmd_len;
+
+ ret = main_info.hif_hook.send_and_recv(bdev,
+ skb,
+ event, event_len,
+ delay, retry, pkt_type);
+
+ if (ret < 0)
+ BTMTK_ERR("%s send_and_recv failed!!", __func__);
+
+exit:
+ BTMTK_DBG("%s end!!", __func__);
+ return ret;
+}
+
+static void btmtk_load_code_from_bin(const struct firmware **fw_firmware,
+ char *bin_name, struct device *dev,
+ u8 **image, u32 *code_len)
+{
+ int err = 0;
+ int retry = 10;
+
+ if (!bin_name) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return;
+ }
+
+ do {
+ err = request_firmware(fw_firmware, bin_name, dev);
+ if (err == 0) {
+ break;
+ } else if (retry <= 0) {
+ *fw_firmware = NULL;
+ BTMTK_INFO("%s: request_firmware %d times fail, file not exist, err = %d",
+ __func__, 10, err);
+ return;
+ }
+ BTMTK_INFO("%s: request_firmware fail, file not exist, err = %d, retry = %d",
+ __func__, err, retry);
+ msleep(100);
+ } while (retry-- > 0);
+
+ *image = (u8 *)(*fw_firmware)->data;
+ *code_len = (*fw_firmware)->size;
+}
+
+static void btmtk_print_bt_patch_info(struct btmtk_dev *bdev, u8 *fwbuf)
+{
+ struct _PATCH_HEADER *patchhdr = NULL;
+ struct _GLOBAL_DESCR *globaldesrc = NULL;
+
+ if (!fwbuf) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ return;
+ }
+
+ patchhdr = (struct _PATCH_HEADER *)fwbuf;
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ globaldesrc = (struct _GLOBAL_DESCR *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("[btmtk] =============== Patch Info ==============");
+ if (patchhdr) {
+ BTMTK_INFO("[btmtk] Built Time = %s", patchhdr->ucdatetime);
+ BTMTK_INFO("[btmtk] Hw Ver = 0x%04x", patchhdr->u2hwver);
+ BTMTK_INFO("[btmtk] Sw Ver = 0x%04x", patchhdr->u2swver);
+ BTMTK_INFO("[btmtk] Magic Number = 0x%08x", patchhdr->u4magicnum);
+
+ BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
+ patchhdr->ucplatform[0],
+ patchhdr->ucplatform[1],
+ patchhdr->ucplatform[2],
+ patchhdr->ucplatform[3]);
+ }
+
+ if (globaldesrc) {
+ BTMTK_INFO("[btmtk] Patch Ver = 0x%08x", globaldesrc->u4patchver);
+ BTMTK_INFO("[btmtk] Section num = 0x%08x", globaldesrc->u4sectionnum);
+ }
+
+ BTMTK_INFO("[btmtk] =========================================");
+}
+
+static void btmtk_print_wifi_patch_info(struct btmtk_dev *bdev, u8 *fwbuf)
+{
+ struct _PATCH_HEADER *patchhdr = NULL;
+ struct _GLOBAL_DESCR *globaldesrc = NULL;
+
+ if (!fwbuf) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ return;
+ }
+
+ patchhdr = (struct _PATCH_HEADER *)fwbuf;
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
+ globaldesrc = (struct _GLOBAL_DESCR *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("[btmtk] =============== Wifi Patch Info ==============");
+ if (patchhdr) {
+ BTMTK_INFO("[btmtk] Built Time = %s", patchhdr->ucdatetime);
+ BTMTK_INFO("[btmtk] Hw Ver = 0x%04x",
+ ((patchhdr->u2hwver & 0x00ff) << 8) |
+ ((patchhdr->u2hwver & 0xff00) >> 8));
+ BTMTK_INFO("[btmtk] Sw Ver = 0x%04x",
+ ((patchhdr->u2swver & 0x00ff) << 8) |
+ ((patchhdr->u2swver & 0xff00) >> 8));
+ BTMTK_INFO("[btmtk] Magic Number = 0x%08x", be2cpu32(patchhdr->u4magicnum));
+
+ BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
+ patchhdr->ucplatform[0],
+ patchhdr->ucplatform[1],
+ patchhdr->ucplatform[2],
+ patchhdr->ucplatform[3]);
+ }
+
+ if (globaldesrc) {
+ BTMTK_INFO("[btmtk] Patch Ver = 0x%08x",
+ be2cpu32(globaldesrc->u4patchver));
+ BTMTK_INFO("[btmtk] Section num = 0x%08x",
+ be2cpu32(globaldesrc->u4sectionnum));
+ } else {
+ BTMTK_WARN("%s, globaldesrc is NULL!", __func__);
+ }
+
+ BTMTK_INFO("[btmtk] =========================================");
+}
+
+static int btmtk_send_wmt_download_cmd(struct btmtk_dev *bdev, u8 *cmd,
+ int cmd_len, u8 *event, int event_len,
+ struct _SECTION_MAP *sectionmap,
+ u8 fw_state, u8 dma_flag, bool patch_flag)
+{
+ int payload_len = 0;
+ int ret = -1;
+ int i = 0;
+ u32 revert_secspec = 0;
+
+ if (!bdev || !cmd || !event || !sectionmap) {
+ BTMTK_ERR("%s: invalid parameter!", __func__);
+ return ret;
+ }
+
+ /* prepare HCI header */
+ cmd[0] = 0x01;
+ cmd[1] = 0x6F;
+ cmd[2] = 0xFC;
+
+ /* prepare WMT header */
+ cmd[4] = 0x01;
+ cmd[5] = 0x01;
+
+ if (fw_state == 0) {
+ /* prepare WMT DL cmd */
+ payload_len = SEC_MAP_NEED_SEND_SIZE + 2;
+
+ cmd[3] = (payload_len + 4) & 0xFF;
+ cmd[6] = payload_len & 0xFF;
+ cmd[7] = (payload_len >> 8) & 0xFF;
+ cmd[8] = 0x00;
+ cmd[9] = dma_flag; /* 1:using DMA to download, 0:using legacy wmt cmd*/
+ cmd_len = SEC_MAP_NEED_SEND_SIZE + PATCH_HEADER_SIZE;
+
+ if (patch_flag)
+ for (i = 0; i < SECTION_SPEC_NUM; i++) {
+ revert_secspec = be2cpu32(sectionmap->u4secspec[i]);
+ memcpy(&cmd[PATCH_HEADER_SIZE] + i * sizeof(u32),
+ (u8 *)&revert_secspec, sizeof(u32));
+ }
+ else
+ memcpy(&cmd[PATCH_HEADER_SIZE], (u8 *)(sectionmap->u4secspec),
+ SEC_MAP_NEED_SEND_SIZE);
+
+ ret = btmtk_main_send_cmd(bdev, cmd, cmd_len,
+ event, event_len, DELAY_TIMES, RETRY_TIMES,
+ BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
+ return PATCH_ERR;
+ }
+
+ if (bdev->recv_evt_len >= event_len)
+ return bdev->io_buf[PATCH_STATUS];
+
+ ret = PATCH_ERR;
+ } else {
+ BTMTK_ERR("%s: fw state is error!", __func__);
+ }
+
+ return ret;
+}
+
+static int btmtk_load_fw_patch_using_wmt_cmd(struct btmtk_dev *bdev,
+ u8 *image, u8 *fwbuf, u8 *event,
+ int event_len, u32 patch_len, int offset)
+{
+ int ret = 0;
+ u32 cur_len = 0;
+ s32 sent_len;
+ int first_block = 1;
+ u8 phase;
+ int delay = PATCH_DOWNLOAD_PHASE1_2_DELAY_TIME;
+ int retry = PATCH_DOWNLOAD_PHASE1_2_RETRY;
+
+ if (!bdev || !image || !fwbuf) {
+ BTMTK_WARN("%s, invalid parameters!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ /* loading rom patch */
+ while (1) {
+ s32 sent_len_max = UPLOAD_PATCH_UNIT - PATCH_HEADER_SIZE;
+
+ sent_len = (patch_len - cur_len) >= sent_len_max ?
+ sent_len_max : (patch_len - cur_len);
+
+ if (sent_len > 0) {
+ if (first_block == 1) {
+ if (sent_len < sent_len_max)
+ phase = PATCH_PHASE3;
+ else
+ phase = PATCH_PHASE1;
+ first_block = 0;
+ } else if (sent_len == sent_len_max) {
+ if (patch_len - cur_len == sent_len_max)
+ phase = PATCH_PHASE3;
+ else
+ phase = PATCH_PHASE2;
+ } else {
+ phase = PATCH_PHASE3;
+ }
+
+ /* prepare HCI header */
+ image[0] = 0x02;
+ image[1] = 0x6F;
+ image[2] = 0xFC;
+ image[3] = (sent_len + 5) & 0xFF;
+ image[4] = ((sent_len + 5) >> 8) & 0xFF;
+
+ /* prepare WMT header */
+ image[5] = 0x01;
+ image[6] = 0x01;
+ image[7] = (sent_len + 1) & 0xFF;
+ image[8] = ((sent_len + 1) >> 8) & 0xFF;
+
+ image[9] = phase;
+ memcpy(&image[10], fwbuf + offset + cur_len, sent_len);
+ if (phase == PATCH_PHASE3) {
+ delay = PATCH_DOWNLOAD_PHASE3_DELAY_TIME;
+ retry = PATCH_DOWNLOAD_PHASE3_RETRY;
+ }
+
+ cur_len += sent_len;
+ BTMTK_DBG("%s: sent_len = %d, cur_len = %d, phase = %d", __func__,
+ sent_len, cur_len, phase);
+
+ ret = btmtk_main_send_cmd(bdev, image, sent_len + PATCH_HEADER_SIZE,
+ event, event_len, delay, retry,
+ BTMTK_TX_ACL_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_INFO("%s: send patch failed, terminate", __func__);
+ goto exit;
+ }
+ } else {
+ break;
+ }
+ }
+
+exit:
+ return ret;
+}
+
+static int btmtk_send_fw_rom_patch_79xx(struct btmtk_dev *bdev,
+ u8 *fwbuf, bool patch_flag)
+{
+ u8 *pos;
+ int loop_count = 0;
+ int ret = 0;
+ u32 section_num = 0;
+ u32 section_offset = 0;
+ u32 dl_size = 0;
+ int patch_status = 0;
+ int retry = 20;
+ u8 dma_flag = PATCH_DOWNLOAD_USING_WMT;
+ struct _SECTION_MAP *sectionmap;
+ struct _GLOBAL_DESCR *globaldescr;
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
+
+ if (!fwbuf) {
+ BTMTK_WARN("%s, fwbuf is NULL!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ globaldescr = (struct _GLOBAL_DESCR *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
+
+ BTMTK_INFO("%s: loading rom patch...\n", __func__);
+
+ if (patch_flag)
+ section_num = be2cpu32(globaldescr->u4sectionnum);
+ else
+ section_num = globaldescr->u4sectionnum;
+ BTMTK_INFO("%s: section_num = 0x%08x\n", __func__, section_num);
+
+ pos = kmalloc(UPLOAD_PATCH_UNIT, GFP_ATOMIC);
+ if (!pos) {
+ BTMTK_ERR("%s: alloc memory failed", __func__);
+ goto exit;
+ }
+
+ do {
+ sectionmap = (struct _SECTION_MAP *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE +
+ FW_ROM_PATCH_GD_SIZE + FW_ROM_PATCH_SEC_MAP_SIZE * loop_count);
+ dma_flag = PATCH_DOWNLOAD_USING_WMT;
+ if (patch_flag) {
+ /* wifi is big-endian */
+ section_offset = be2cpu32(sectionmap->u4secoffset);
+ dl_size = be2cpu32(sectionmap->bin_info_spec.u4dlsize);
+ dma_flag = be2cpu32(sectionmap->bin_info_spec.u4dlmodecrctype) & 0xFF;
+ } else {
+ /* BT is little-endian */
+ section_offset = sectionmap->u4secoffset;
+ dl_size = sectionmap->bin_info_spec.u4dlsize;
+ /*
+ * loop_count = 0: BGF patch
+ * 1: BT ILM
+ * only BT ILM support DL DMA for Buzzard
+ */
+ dma_flag = le2cpu32(sectionmap->bin_info_spec.u4dlmodecrctype) & 0xFF;
+ }
+ BTMTK_INFO("%s:loop=%d, sec_offset=0x%08x, dl patch_len=0x%08x, dl mode=%d",
+ __func__, loop_count, section_offset, dl_size, dma_flag);
+ if (dl_size > 0) {
+ retry = 20;
+ do {
+ patch_status = btmtk_send_wmt_download_cmd(bdev, pos, 0,
+ event, sizeof(event) - 1,
+ sectionmap, 0, dma_flag,
+ patch_flag);
+ BTMTK_INFO("%s: patch_status %d", __func__, patch_status);
+
+ if (patch_status > PATCH_READY || patch_status == PATCH_ERR) {
+ BTMTK_ERR("%s: patch_status error", __func__);
+ ret = -1;
+ goto err;
+ } else if (patch_status == PATCH_READY) {
+ BTMTK_INFO("%s: no need to load rom patch section%d",
+ __func__, loop_count);
+ goto next_section;
+ } else if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+ msleep(100);
+ retry--;
+ } else if (patch_status == PATCH_NEED_DOWNLOAD) {
+ break; /* Download ROM patch directly */
+ }
+ } while (retry > 0);
+
+ if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
+ BTMTK_WARN("%s: Hold by another fun more than 2 seconds", __func__);
+ ret = -1;
+ goto err;
+ }
+
+ if (dma_flag == PATCH_DOWNLOAD_USING_DMA && main_info.hif_hook.dl_dma) {
+ /* using DMA to download fw patch*/
+ ret = main_info.hif_hook.dl_dma(bdev,
+ pos, fwbuf,
+ dl_size, section_offset);
+ if (ret < 0) {
+ BTMTK_ERR("%s: dl using dma failed!", __func__);
+ goto err;
+ }
+ } else {
+ /* using legacy wmt cmd to download fw patch */
+ ret = btmtk_load_fw_patch_using_wmt_cmd(bdev, pos, fwbuf, event,
+ sizeof(event) - 1,
+ dl_size, section_offset);
+ if (ret < 0) {
+ BTMTK_ERR("%s: dl using wmt cmd failed!", __func__);
+ goto err;
+ }
+ }
+ }
+next_section:
+ continue;
+ } while (++loop_count < section_num);
+
+err:
+ kfree(pos);
+ pos = NULL;
+
+exit:
+ return ret;
+}
+
+int btmtk_load_rom_patch_79xx(struct btmtk_dev *bdev, bool patch_flag)
+{
+ int ret = 0;
+ const struct firmware *fw_firmware = NULL;
+ u8 *rom_patch = NULL;
+ unsigned int rom_patch_len = 0;
+
+ BTMTK_ERR("%s, patch_flag = %d!", __func__, patch_flag);
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -EINVAL;
+ }
+
+ if (patch_flag) {
+ if (bdev->flavor)
+ /* if flavor equals 1, it represent 7920, else it represent 7921*/
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "WIFI_MT%04x_patch_mcu_1a_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ else
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "WIFI_MT%04x_patch_mcu_1_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ }
+
+ btmtk_load_code_from_bin(&fw_firmware, bdev->rom_patch_bin_file_name, NULL,
+ &rom_patch, &rom_patch_len);
+
+ if (!rom_patch) {
+ BTMTK_ERR("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
+ __func__, bdev->rom_patch_bin_file_name, bdev->rom_patch_bin_file_name);
+ ret = -1;
+ goto err;
+ }
+
+ if (patch_flag)
+ /*Display rom patch info*/
+ btmtk_print_wifi_patch_info(bdev, rom_patch);
+ else
+ btmtk_print_bt_patch_info(bdev, rom_patch);
+
+ ret = btmtk_send_fw_rom_patch_79xx(bdev, rom_patch, patch_flag);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_fw_rom_patch_79xx failed!", __func__);
+ goto err;
+ }
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ BTMTK_INFO("%s end", __func__);
+
+err:
+ if (fw_firmware)
+ release_firmware(fw_firmware);
+ return ret;
+}
+
+int btmtk_load_rom_patch(struct btmtk_dev *bdev)
+{
+ int err = -1;
+
+ if (!bdev || !bdev->hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return err;
+ }
+
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ err = btmtk_load_rom_patch_79xx(bdev, BT_DOWNLOAD);
+ if (err < 0) {
+ BTMTK_ERR("%s: btmtk_load_rom_patch_79xx bt patch failed!", __func__);
+ return err;
+ }
+
+ err = btmtk_load_rom_patch_79xx(bdev, WIFI_DOWNLOAD);
+ if (err < 0) {
+ BTMTK_WARN("%s: btmtk_load_rom_patch_79xx wifi patch failed!", __func__);
+ err = 0;
+ }
+ } else {
+ BTMTK_WARN("%s: unknown chip id (%d)", __func__, bdev->chip_id);
+ }
+
+ return err;
+}
+
+struct btmtk_dev *btmtk_get_dev(void)
+{
+ int i = 0;
+ struct btmtk_dev *tmp_bdev = NULL;
+
+ BTMTK_INFO("%s", __func__);
+
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (!g_bdev[i]->hdev) {
+ if (i == 0)
+ g_bdev[i]->dongle_index = i;
+ else
+ g_bdev[i]->dongle_index = g_bdev[i - 1]->dongle_index + 1;
+
+ tmp_bdev = g_bdev[i];
+
+ /* Hook pre-defined table on state machine */
+ g_bdev[i]->cif_state = (struct btmtk_cif_state *)g_cif_state;
+ break;
+ }
+ }
+
+ return tmp_bdev;
+}
+
+void btmtk_release_dev(struct btmtk_dev *bdev)
+{
+ int i = 0;
+ struct btmtk_dev *tmp_bdev = NULL;
+
+ tmp_bdev = bdev;
+ if (!tmp_bdev) {
+ for (i = 0; i < btmtk_intf_num; i++) {
+ if (memcmp(tmp_bdev, g_bdev[i], sizeof(*tmp_bdev)) == 0) {
+ memset(tmp_bdev, 0, sizeof(*tmp_bdev));
+
+ tmp_bdev = NULL;
+ break;
+ }
+ }
+ }
+}
+
+struct btmtk_dev *btmtk_allocate_dev_memory(struct device *dev)
+{
+ struct btmtk_dev *bdev;
+ size_t len = sizeof(*bdev);
+
+ if (!dev)
+ bdev = devm_kzalloc(dev, len, GFP_KERNEL);
+ else
+ bdev = kzalloc(len, GFP_KERNEL);
+
+ if (!bdev)
+ return NULL;
+
+ btmtk_set_chip_state(bdev, BTMTK_STATE_INIT);
+
+ return bdev;
+}
+
+void btmtk_free_dev_memory(struct device *dev, struct btmtk_dev *bdev)
+{
+ if (!bdev) {
+ if (!dev)
+ devm_kfree(dev, bdev);
+ else
+ kfree(bdev);
+ }
+}
+
+int btmtk_send_wmt_power_on_cmd(struct btmtk_dev *bdev)
+{
+ /* Support 7668 and 7961 */
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x01 };
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 };
+ int ret = -1, retry = RETRY_TIMES;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+retry_again:
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event),
+ WMT_DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ ret = -1;
+ } else if (ret == 0 && bdev->recv_evt_len > 0) {
+ switch (bdev->io_buf[6]) {
+ case 0: /* successful */
+ BTMTK_INFO("%s: OK", __func__);
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
+ break;
+ case 2: /* TODO:retry */
+ if (retry > 0) {
+ retry--;
+ BTMTK_INFO("%s: need to try again", __func__);
+ mdelay(50);
+ goto retry_again;
+ }
+ break;
+ default:
+ BTMTK_WARN("%s: Unknown result: %02X", __func__, bdev->io_buf[6]);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int btmtk_send_wmt_power_off_cmd(struct btmtk_dev *bdev)
+{
+ /* Support 7668 and 7961 */
+ u8 cmd[] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00 };
+ /* To-Do, for event check */
+ u8 event[] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 };
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("%s: power_state already power off", __func__);
+ return 0;
+ }
+
+ ret = btmtk_main_send_cmd(bdev, cmd, sizeof(cmd), event, sizeof(event),
+ DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: failed(%d)", __func__, ret);
+ bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
+ return ret;
+ }
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ BTMTK_INFO("%s done", __func__);
+ return ret;
+}
+
+int btmtk_cap_init(struct btmtk_dev *bdev)
+{
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ return -1;
+ }
+
+ main_info.hif_hook.reg_read(bdev, CHIP_ID, &bdev->chip_id);
+ if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
+ main_info.hif_hook.reg_read(bdev, FLAVOR, &bdev->flavor);
+ main_info.hif_hook.reg_read(bdev, FW_VERSION, &bdev->fw_version);
+ } else {
+ BTMTK_ERR("Unknown Mediatek device(%04X)\n", bdev->chip_id);
+ return -1;
+ }
+
+ BTMTK_INFO("%s: Chip ID = 0x%x", __func__, bdev->chip_id);
+ BTMTK_INFO("%s: flavor = 0x%x", __func__, bdev->flavor);
+ BTMTK_INFO("%s: FW Ver = 0x%x", __func__, bdev->fw_version);
+
+ memset(bdev->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
+ if ((bdev->fw_version & 0xff) == 0xff) {
+ BTMTK_ERR("%s: failed, wrong FW version : 0x%x !", __func__, bdev->fw_version);
+ return -1;
+ }
+
+ /* Bin filename format : "BT_RAM_CODE_MT%04x_%x_%x_hdr.bin"
+ * $$$$ : chip id
+ * % : fw version & 0xFF + 1 (in HEX)
+ */
+ bdev->flavor = (bdev->flavor & 0x00000080) >> 7;
+ BTMTK_INFO("%s: flavor1 = 0x%x", __func__, bdev->flavor);
+
+ /* if flavor equals 1, it represent 7920, else it represent 7921 */
+ if (bdev->flavor)
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+ else
+ snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
+ "BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
+ bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
+
+ BTMTK_INFO("%s: rom patch file name is %s", __func__, bdev->rom_patch_bin_file_name);
+
+ return 0;
+}
+
+int btmtk_send_init_cmds(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ goto exit;
+ }
+
+ BTMTK_INFO("%s", __func__);
+
+ ret = btmtk_send_wmt_power_on_cmd(bdev);
+ if (ret < 0) {
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON)
+ BTMTK_ERR("%s, btmtk_send_wmt_power_on_cmd failed!", __func__);
+ goto exit;
+ }
+
+exit:
+ return ret;
+}
+
+int btmtk_send_deinit_cmds(struct btmtk_dev *bdev)
+{
+ int ret = -1;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is NULL !", __func__);
+ return ret;
+ }
+
+ BTMTK_INFO("%s", __func__);
+
+ ret = btmtk_send_wmt_power_off_cmd(bdev);
+ if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("Power off failed, reset it");
+ btmtk_send_assert_cmd(bdev);
+ }
+
+ return ret;
+}
+
+int btmtk_send_assert_cmd(struct btmtk_dev *bdev)
+{
+ int ret = 0;
+ int state;
+ u8 buf[] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
+ struct sk_buff *skb = NULL;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping already!!!", __func__);
+ return ret;
+ }
+
+ BTMTK_INFO("%s: send assert cmd", __func__);
+
+ skb = alloc_skb(sizeof(buf) + BT_SKB_RESERVE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s allocate skb failed!!", __func__);
+ goto exit;
+ }
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
+ memcpy(skb->data, buf, sizeof(buf));
+ skb->len = sizeof(buf);
+
+ ret = main_info.hif_hook.send_cmd(bdev, skb, 100, 20, (int)BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0)
+ BTMTK_ERR("%s failed!!", __func__);
+ else
+ BTMTK_INFO("%s: OK", __func__);
+
+exit:
+ return ret;
+}
+
+static int bt_flush(struct hci_dev *hdev)
+{
+ return 0;
+}
+
+static int bt_close(struct hci_dev *hdev)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return ret;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is invalid!", __func__);
+ return ret;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not allow close(%d)", __func__, fstate);
+ goto err;
+ }
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSING);
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
+ BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
+ goto exit;
+ }
+
+ BTMTK_INFO("%s, enter", __func__);
+
+ if (main_info.hif_hook.cif_mutex_lock)
+ main_info.hif_hook.cif_mutex_lock(bdev);
+
+ if (state != BTMTK_STATE_STANDBY) {
+ ret = btmtk_send_deinit_cmds(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
+ goto unlock;
+ }
+ }
+
+ /* Flush RX works */
+ flush_work(&bdev->rx_work);
+
+ /* Drop queues */
+ skb_queue_purge(&bdev->rx_q);
+
+ main_info.hif_hook.close(hdev);
+
+unlock:
+ if (main_info.hif_hook.cif_mutex_unlock)
+ main_info.hif_hook.cif_mutex_unlock(bdev);
+exit:
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
+
+err:
+ return 0;
+}
+
+static int bt_open(struct hci_dev *hdev)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ if (!hdev) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ return -EFAULT;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: bdev is invalid", __func__);
+ return -EFAULT;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state == BTMTK_STATE_INIT || state == BTMTK_STATE_DISCONNECT) {
+ ret = -EAGAIN;
+ goto failed;
+ }
+
+ if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
+ BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
+ ret = -ENODEV;
+ goto failed;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate == BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops opened!", __func__);
+ ret = 0;
+ goto failed;
+ } else if ((fstate == BTMTK_FOPS_STATE_CLOSING) ||
+ (fstate == BTMTK_FOPS_STATE_OPENING)) {
+ BTMTK_WARN("%s: fops open/close is on-going !", __func__);
+ ret = -EAGAIN;
+ goto failed;
+ }
+
+ BTMTK_INFO("%s", __func__);
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENING);
+ ret = main_info.hif_hook.open(hdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, cif_open failed", __func__);
+ goto failed;
+ }
+
+ ret = btmtk_send_init_cmds(bdev);
+ if (ret < 0) {
+ BTMTK_ERR("%s, btmtk_send_init_cmds failed", __func__);
+ goto failed;
+ }
+
+ if (main_info.hif_hook.open_done)
+ main_info.hif_hook.open_done(bdev);
+
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENED);
+
+ return 0;
+
+failed:
+ btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
+
+ return ret;
+}
+
+static int bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ int ret = -1;
+ int state = BTMTK_STATE_INIT;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ struct btmtk_dev *bdev = NULL;
+
+ if (!hdev || !skb) {
+ BTMTK_ERR("%s, invalid parameters!", __func__);
+ return -ENODEV;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is invalid!", __func__);
+ return -ENODEV;
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ BTMTK_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
+ ret = -ENODEV;
+ goto exit;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ BTMTK_WARN("%s: chip state is %d.", __func__, state);
+ if (state == BTMTK_STATE_DISCONNECT) {
+ ret = -ENODEV;
+ } else {
+ msleep(3000);
+ ret = -EAGAIN;
+ }
+ goto exit;
+ }
+
+ if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
+ BTMTK_WARN("%s: dongle state already power off, do not write", __func__);
+ ret = -EFAULT;
+ goto exit;
+ }
+
+ memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+
+ /* For Ble ISO packet size */
+ if (memcmp(skb->data, READ_ISO_PACKET_SIZE_CMD, sizeof(READ_ISO_PACKET_SIZE_CMD)) == 0) {
+ bdev->iso_threshold = skb->data[sizeof(READ_ISO_PACKET_SIZE_CMD)] +
+ (skb->data[sizeof(READ_ISO_PACKET_SIZE_CMD) + 1] << 8);
+ BTMTK_INFO("%s: Ble iso pkt size is %d", __func__, bdev->iso_threshold);
+ }
+
+ ret = main_info.hif_hook.send_cmd(bdev, skb, 0, 0, (int)BTMTK_TX_PKT_FROM_HOST);
+ if (ret < 0)
+ BTMTK_ERR("%s failed!!", __func__);
+exit:
+ return ret;
+}
+
+static int bt_setup(struct hci_dev *hdev)
+{
+ BTMTK_INFO("%s", __func__);
+ return 0;
+}
+
+void btmtk_reg_hif_hook(struct hif_hook_ptr *hook)
+{
+ memcpy(&main_info.hif_hook, hook, sizeof(struct hif_hook_ptr));
+}
+
+static void btmtk_rx_work(struct work_struct *work)
+{
+ int err = 0, skip_pkt = 0;
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, rx_work);
+ struct sk_buff *skb;
+ int fstate = BTMTK_FOPS_STATE_INIT;
+ int state = 0;
+
+ BTMTK_DBG("%s enter", __func__);
+
+ while ((skb = skb_dequeue(&bdev->rx_q))) {
+ skip_pkt = btmtk_dispatch_pkt(bdev->hdev, skb);
+ if (skip_pkt != 0) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ if (hci_skb_pkt_type(skb) == HCI_EVENT_PKT) {
+ if (main_info.hif_hook.event_filter(bdev, skb)) {
+ kfree_skb(skb);
+ continue;
+ }
+ }
+
+ fstate = btmtk_fops_get_state(bdev);
+ if (fstate != BTMTK_FOPS_STATE_OPENED) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ state = btmtk_get_chip_state(bdev);
+ if (state != BTMTK_STATE_WORKING) {
+ kfree_skb(skb);
+ continue;
+ }
+
+ err = hci_recv_frame(bdev->hdev, skb);
+ if (err < 0) {
+ if (err != -ENXIO)
+ BTMTK_ERR("%s failed, err = %d", __func__, err);
+ return;
+ }
+ }
+}
+
+void btmtk_free_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
+{
+ if (!bdev)
+ return;
+
+ flush_work(&bdev->rx_work);
+
+ skb_queue_purge(&bdev->rx_q);
+ destroy_workqueue(bdev->workqueue);
+
+ BTMTK_INFO("%s", __func__);
+
+ if (bdev->hdev)
+ hci_free_dev(bdev->hdev);
+
+ bdev->chip_reset = 0;
+ BTMTK_INFO("%s done", __func__);
+}
+
+int btmtk_allocate_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
+{
+ struct hci_dev *hdev;
+ int err = 0;
+
+ if (!bdev) {
+ BTMTK_ERR("%s, bdev is NULL!", __func__);
+ err = -EINVAL;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s", __func__);
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BTMTK_ERR("%s, hdev is NULL!", __func__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ hdev->bus = hci_bus_type;
+ hci_set_drvdata(hdev, bdev);
+
+ hdev->dev_type = 0x00;
+
+ bdev->hdev = hdev;
+
+ hdev->open = bt_open;
+ hdev->close = bt_close;
+ hdev->flush = bt_flush;
+ hdev->send = bt_send_frame;
+ hdev->setup = bt_setup;
+
+ INIT_WORK(&bdev->rx_work, btmtk_rx_work);
+
+ init_waitqueue_head(&bdev->p_wait_event_q);
+
+ skb_queue_head_init(&bdev->rx_q);
+
+ bdev->workqueue = alloc_workqueue("BTMTK_RX_WQ", WQ_HIGHPRI | WQ_UNBOUND |
+ WQ_MEM_RECLAIM, 1);
+ if (!bdev->workqueue) {
+ BTMTK_ERR("%s, bdev->workqueue is NULL!", __func__);
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s done", __func__);
+
+exit:
+ return err;
+}
+
+int btmtk_register_hci_device(struct btmtk_dev *bdev)
+{
+ struct hci_dev *hdev;
+ int err = 0;
+
+ hdev = bdev->hdev;
+
+ err = hci_register_dev(hdev);
+ if (err < 0) {
+ BTMTK_INFO("%s can't register", __func__);
+ hci_free_dev(hdev);
+ goto exit;
+ }
+
+ test_and_clear_bit(HCI_SETUP, &hdev->dev_flags);
+
+exit:
+ return err;
+}
+
+int btmtk_deregister_hci_device(struct btmtk_dev *bdev)
+{
+ int err = 0;
+
+ if (bdev && bdev->hdev)
+ hci_unregister_dev(bdev->hdev);
+
+ return err;
+}
+
+static int btmtk_main_allocate_memory(struct btmtk_dev *bdev)
+{
+ BTMTK_INFO("%s", __func__);
+
+ if (!bdev->rom_patch_bin_file_name) {
+ bdev->rom_patch_bin_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
+ if (!bdev->rom_patch_bin_file_name) {
+ BTMTK_ERR("%s: alloc memory fail rom_patch_bin_file_name", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ if (!bdev->io_buf) {
+ bdev->io_buf = kzalloc(IO_BUF_SIZE, GFP_KERNEL);
+ if (!bdev->io_buf) {
+ BTMTK_ERR("%s: alloc memory fail io_buf", __func__);
+ return -ENOMEM;
+ }
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static void btmtk_main_free_memory(struct btmtk_dev *bdev)
+{
+ kfree(bdev->rom_patch_bin_file_name);
+ bdev->rom_patch_bin_file_name = NULL;
+
+ kfree(bdev->io_buf);
+ bdev->io_buf = NULL;
+
+ BTMTK_INFO("%s: Success", __func__);
+}
+
+int btmtk_main_cif_initialize(struct btmtk_dev *bdev, int hci_bus)
+{
+ int err = 0;
+
+ err = btmtk_main_allocate_memory(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_main_allocate_memory failed!");
+ goto end;
+ }
+
+ err = btmtk_allocate_hci_device(bdev, hci_bus);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_allocate_hci_device failed!");
+ goto free_mem;
+ }
+
+ err = btmtk_cap_init(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_cap_init failed!");
+ goto free_hci_dev;
+ }
+
+ return 0;
+
+free_hci_dev:
+ btmtk_free_hci_device(bdev, hci_bus);
+free_mem:
+ btmtk_main_free_memory(bdev);
+end:
+ return err;
+}
+
+void btmtk_main_cif_uninitialize(struct btmtk_dev *bdev, int hci_bus)
+{
+ btmtk_free_hci_device(bdev, hci_bus);
+ btmtk_main_free_memory(bdev);
+}
+
+int btmtk_main_cif_disconnect_notify(struct btmtk_dev *bdev, int hci_bus)
+{
+ btmtk_deregister_hci_device(bdev);
+ btmtk_free_hci_device(bdev, hci_bus);
+
+ bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
+ btmtk_release_dev(bdev);
+
+ return 0;
+}
+
+/**
+ * Kernel Module init/exit Functions
+ */
+static int __init main_driver_init(void)
+{
+ int ret = 0;
+ int i;
+
+ /* Mediatek Driver Version */
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ ret = main_init();
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < btmtk_intf_num; i++)
+ btmtk_set_chip_state(g_bdev[i], BTMTK_STATE_DISCONNECT);
+
+ ret = btmtk_cif_register();
+ if (ret < 0) {
+ BTMTK_ERR("*** USB registration failed(%d)! ***", ret);
+ main_exit();
+ return ret;
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return ret;
+}
+
+static void __exit main_driver_exit(void)
+{
+ BTMTK_INFO("%s", __func__);
+ btmtk_cif_deregister();
+ main_exit();
+}
+module_init(main_driver_init);
+module_exit(main_driver_exit);
+
+/**
+ * Module Common Information
+ */
+MODULE_DESCRIPTION("Mediatek Bluetooth Driver");
+MODULE_VERSION(VERSION SUBVER);
+MODULE_LICENSE("GPL");
+module_param(btmtk_intf_num, int, 0444);
new file mode 100644
@@ -0,0 +1,578 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2020 MediaTek Inc.
+
+/*
+ * Bluetooth support for MediaTek SDIO/USB/UART devices
+ *
+ */
+
+#ifndef __BTMTK_MAIN_H__
+#define __BTMTK_MAIN_H__
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/device.h>
+#include <asm/unaligned.h>
+
+/* Define for proce node */
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+/* Define for whole chip reset */
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+
+/** Driver version */
+#define VERSION "7.0.2020110301"
+#define SUBVER ":turnkey"
+
+#define BTMTKUART_RX_STATE_ACTIVE 1
+#define BTMTKUART_RX_STATE_WAKEUP 2
+#define BTMTKUART_RX_STATE_RESET 3
+
+/**
+ * Maximum rom patch file name length
+ */
+#define MAX_BIN_FILE_NAME_LEN 64
+
+/**
+ * Type definition
+ */
+#ifndef TRUE
+ #define TRUE 1
+#endif
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+/**
+ * Log and level definition
+ */
+#define BTMTK_LOG_LVL_ERR 1
+#define BTMTK_LOG_LVL_WARN 2
+#define BTMTK_LOG_LVL_INFO 3
+#define BTMTK_LOG_LVL_DBG 4
+#define BTMTK_LOG_LVL_MAX BTMTK_LOG_LVL_DBG
+#define BTMTK_LOG_LVL_DEF BTMTK_LOG_LVL_INFO /* default setting */
+
+#define HCI_LOG_MAX_BUF_SIZE 66
+#define WMT_OVER_HCI_HEADER_SIZE 3
+
+extern u8 btmtk_log_lvl;
+
+#define BTMTK_ERR(fmt, ...) \
+ do { if (btmtk_log_lvl >= BTMTK_LOG_LVL_ERR) \
+ pr_warn("[btmtk_err] ***" fmt"***\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_WARN(fmt, ...) \
+ do { if (btmtk_log_lvl >= BTMTK_LOG_LVL_WARN) \
+ pr_warn("[btmtk_warn] " fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_INFO(fmt, ...) \
+ do { if (btmtk_log_lvl >= BTMTK_LOG_LVL_INFO) \
+ pr_warn("[btmtk_info] " fmt"\n", ##__VA_ARGS__); } while (0)
+#define BTMTK_DBG(fmt, ...) \
+ do { if (btmtk_log_lvl >= BTMTK_LOG_LVL_DBG) \
+ pr_warn("[btmtk_dbg] " fmt"\n", ##__VA_ARGS__); } while (0)
+
+#define BTMTK_INFO_RAW(p, l, fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_INFO) { \
+ int raw_count = 0; \
+ char str[HCI_LOG_MAX_BUF_SIZE * 3 + 1]; \
+ char *p_str = str; \
+ const unsigned char *ptr = p; \
+ pr_warn("[btmtk_info] " fmt, ##__VA_ARGS__); \
+ for (raw_count = 0; raw_count < MIN(l, HCI_LOG_MAX_BUF_SIZE); ++raw_count)\
+ p_str += sprintf(p_str, " %02X", ptr[raw_count]); \
+ *p_str = '\0'; \
+ pr_warn("%s\n", str); \
+ } \
+ } while (0)
+
+#define BTMTK_DBG_RAW(p, l, fmt, ...) \
+ do { \
+ if (btmtk_log_lvl >= BTMTK_LOG_LVL_DBG) { \
+ int raw_count = 0; \
+ char str[HCI_LOG_MAX_BUF_SIZE * 3 + 1]; \
+ char *p_str = str; \
+ const unsigned char *ptr = p; \
+ pr_warn("[btmtk_debug] " fmt, ##__VA_ARGS__); \
+ for (raw_count = 0; raw_count < MIN(l, HCI_LOG_MAX_BUF_SIZE); ++raw_count)\
+ p_str += sprintf(p_str, " %02X", ptr[raw_count]); \
+ *p_str = '\0'; \
+ pr_warn("%s\n", str); \
+ } \
+ } while (0)
+
+#define BTMTK_CIF_IS_NULL(bdev, cif_event) \
+ (!bdev || !(&bdev->cif_state[cif_event]))
+
+/**
+ *
+ * HCI packet type
+ */
+#define MTK_HCI_COMMAND_PKT 0x01
+#define MTK_HCI_ACLDATA_PKT 0x02
+#define MTK_HCI_SCODATA_PKT 0x03
+#define MTK_HCI_EVENT_PKT 0x04
+#define HCI_ISO_PKT 0x05
+#define HCI_ISO_PKT_HEADER_SIZE 4
+#define HCI_ISO_PKT_WITH_ACL_HEADER_SIZE 5
+
+/**
+ * ROM patch related
+ */
+#define PATCH_HCI_HEADER_SIZE 4
+#define PATCH_WMT_HEADER_SIZE 5
+#define PATCH_HEADER_SIZE (PATCH_HCI_HEADER_SIZE + PATCH_WMT_HEADER_SIZE + 1)
+
+#define UPLOAD_PATCH_UNIT 2048
+#define PATCH_INFO_SIZE 30
+#define PATCH_PHASE1 1
+#define PATCH_PHASE2 2
+#define PATCH_PHASE3 3
+
+/* It is for mt7961 download rom patch*/
+#define FW_ROM_PATCH_HEADER_SIZE 32
+#define FW_ROM_PATCH_GD_SIZE 64
+#define FW_ROM_PATCH_SEC_MAP_SIZE 64
+#define SEC_MAP_NEED_SEND_SIZE 52
+#define PATCH_STATUS 7
+
+#define IO_BUF_SIZE (HCI_MAX_EVENT_SIZE > 256 ? HCI_MAX_EVENT_SIZE : 256)
+#define EVENT_COMPARE_SIZE 64
+
+#define SECTION_SPEC_NUM 13
+
+/**
+ * Disable RESUME_RESUME
+ */
+#ifndef BT_DISABLE_RESET_RESUME
+#define BT_DISABLE_RESET_RESUME 0
+#endif
+
+#define WIFI_DOWNLOAD TRUE
+#define BT_DOWNLOAD FALSE
+
+#define SWAP32(x)\
+ ((u32)(\
+ (((u32)(x) & (u32)0x000000ffUL) << 24) |\
+ (((u32)(x) & (u32)0x0000ff00UL) << 8) |\
+ (((u32)(x) & (u32)0x00ff0000UL) >> 8) |\
+ (((u32)(x) & (u32)0xff000000UL) >> 24)))
+
+/* Endian byte swapping codes */
+#ifdef __LITTLE_ENDIAN
+#define cpu2le32(x) ((uint32_t)(x))
+#define le2cpu32(x) ((uint32_t)(x))
+#define cpu2be32(x) SWAP32((x))
+#define be2cpu32(x) SWAP32((x))
+#else
+#define cpu2le32(x) SWAP32((x))
+#define le2cpu32(x) SWAP32((x))
+#define cpu2be32(x) ((uint32_t)(x))
+#define be2cpu32(x) ((uint32_t)(x))
+#endif
+
+#define FW_VERSION 0x80021004
+#define CHIP_ID 0x70010200
+#define FLAVOR 0x70010020
+#define COMP_EVENT_TIMO 5000
+
+#define hci_dev_test_and_clear_flag(hdev, nr) test_and_clear_bit((nr), (hdev)->dev_flags)
+
+/* h4_recv */
+#define hci_skb_pkt_type(skb) bt_cb((skb))->pkt_type
+#define hci_skb_expect(skb) bt_cb((skb))->expect
+#define hci_skb_opcode(skb) bt_cb((skb))->hci.opcode
+
+/* HCI bus types */
+#define HCI_VIRTUAL 0
+#define HCI_USB 1
+#define HCI_PCCARD 2
+#define HCI_UART 3
+#define HCI_RS232 4
+#define HCI_PCI 5
+#define HCI_SDIO 6
+#define HCI_SPI 7
+#define HCI_I2C 8
+#define HCI_SMD 9
+
+#define HCI_TYPE_SIZE 1
+/* this for 79XX need download patch staus
+ * 0:
+ * patch download is not complete, BT driver need to download patch
+ * 1:
+ * patch is downloading by Wifi,BT driver need to retry until status = PATCH_READY
+ * 2:
+ * patch download is complete, BT driver no need to download patch
+ */
+#define PATCH_ERR -1
+#define PATCH_NEED_DOWNLOAD 0
+#define PATCH_IS_DOWNLOAD_BY_OTHER 1
+#define PATCH_READY 2
+
+/* 0:
+ * using legacy wmt cmd/evt to download fw patch, usb/sdio just support 0 now
+ * 1:
+ * using DMA to download fw patch
+ */
+#define PATCH_DOWNLOAD_USING_WMT 0
+#define PATCH_DOWNLOAD_USING_DMA 1
+
+#define PATCH_DOWNLOAD_PHASE1_2_DELAY_TIME 1
+#define PATCH_DOWNLOAD_PHASE1_2_RETRY 5
+#define PATCH_DOWNLOAD_PHASE3_DELAY_TIME 20
+#define PATCH_DOWNLOAD_PHASE3_RETRY 20
+
+/* * delay and retrey for main_send_cmd */
+#define WMT_DELAY_TIMES 100
+#define DELAY_TIMES 20
+#define RETRY_TIMES 20
+
+/* Expected minimum supported interface */
+#define BT_MCU_MINIMUM_INTERFACE_NUM 4
+
+/* Bus event */
+#define HIF_EVENT_PROBE 0
+#define HIF_EVENT_DISCONNECT 1
+#define HIF_EVENT_SUSPEND 2
+#define HIF_EVENT_RESUME 3
+#define HIF_EVENT_STANDBY 4
+#define HIF_EVENT_SUBSYS_RESET 5
+#define HIF_EVENT_WHOLE_CHIP_RESET 6
+#define HIF_EVENT_FW_DUMP 7
+
+#define CHAR2HEX_SIZE 4
+
+#define EDR_MIN -32
+#define EDR_MAX 20
+#define EDR_MIN_LV9 13
+#define BLE_MIN -29
+#define BLE_MAX 20
+#define EDR_MIN_R1 -64
+#define EDR_MAX_R1 40
+#define EDR_MIN_LV9_R1 26
+#define BLE_MIN_R1 -58
+#define BLE_MAX_R1 40
+#define EDR_MIN_R2 -128
+#define EDR_MAX_R2 80
+#define EDR_MIN_LV9_R2 52
+#define BLE_MIN_R2 -116
+#define BLE_MAX_R2 80
+
+#define ERR_PWR -9999
+
+enum {
+ RES_1 = 0,
+ RES_DOT_5,
+ RES_DOT_25
+};
+
+enum {
+ CHECK_SINGLE_SKU_PWR_MODE = 0,
+ CHECK_SINGLE_SKU_EDR_MAX,
+ CHECK_SINGLE_SKU_BLE,
+ CHECK_SINGLE_SKU_BLE_2M,
+ CHECK_SINGLE_SKU_BLE_LR_S2,
+ CHECK_SINGLE_SKU_BLE_LR_S8,
+ CHECK_SINGLE_SKU_ALL
+};
+
+struct btmtk_cif_state {
+ unsigned char ops_enter;
+ unsigned char ops_end;
+ unsigned char ops_error;
+};
+
+enum TX_TYPE {
+ BTMTK_TX_CMD_FROM_DRV = 0, /* send hci cmd and wmt cmd by driver */
+ BTMTK_TX_ACL_FROM_DRV, /* send acl pkt with load rompatch by driver */
+ BTMTK_TX_PKT_FROM_HOST, /* send pkt from host, include acl and hci */
+};
+
+enum {
+ BTMTK_DONGLE_STATE_UNKNOWN,
+ BTMTK_DONGLE_STATE_POWER_ON,
+ BTMTK_DONGLE_STATE_POWER_OFF,
+ BTMTK_DONGLE_STATE_ERROR,
+};
+
+enum {
+ HW_ERR_NONE = 0x00,
+ HW_ERR_CODE_CHIP_RESET = 0xF0,
+ HW_ERR_CODE_USB_DISC = 0xF1,
+ HW_ERR_CODE_CORE_DUMP = 0xF2,
+ HW_ERR_CODE_POWER_ON = 0xF3,
+ HW_ERR_CODE_POWER_OFF = 0xF4,
+ HW_ERR_CODE_SET_SLEEP_CMD = 0xF5,
+ HW_ERR_CODE_RESET_STACK_AFTER_WOBLE = 0xF6,
+};
+
+/* Please keep sync with btmtk_set_state function */
+enum {
+ /* BTMTK_STATE_UNKNOWN = 0, */
+ BTMTK_STATE_INIT = 1,
+ BTMTK_STATE_DISCONNECT,
+ BTMTK_STATE_PROBE,
+ BTMTK_STATE_WORKING,
+ BTMTK_STATE_SUSPEND,
+ BTMTK_STATE_RESUME,
+ BTMTK_STATE_FW_DUMP,
+ BTMTK_STATE_STANDBY,
+ BTMTK_STATE_SUBSYS_RESET,
+};
+
+/* Please keep sync with btmtk_fops_set_state function */
+enum {
+ /* BTMTK_FOPS_STATE_UNKNOWN = 0, */
+ BTMTK_FOPS_STATE_INIT = 1,
+ BTMTK_FOPS_STATE_OPENING, /* during opening */
+ BTMTK_FOPS_STATE_OPENED, /* open in fops_open */
+ BTMTK_FOPS_STATE_CLOSING, /* during closing */
+ BTMTK_FOPS_STATE_CLOSED, /* closed */
+};
+
+enum {
+ BTMTK_EVENT_COMPARE_STATE_UNKNOWN,
+ BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE,
+ BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE,
+ BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS,
+};
+
+struct h4_recv_pkt {
+ u8 type; /* Packet type */
+ u8 hlen; /* Header length */
+ u8 loff; /* Data length offset in header */
+ u8 lsize; /* Data length field size */
+ u16 maxlen; /* Max overall packet length */
+ int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
+};
+
+struct _PATCH_HEADER {
+ u8 ucdatetime[16];
+ u8 ucplatform[4];
+ u16 u2hwver;
+ u16 u2swver;
+ u32 u4magicnum;
+};
+
+struct _GLOBAL_DESCR {
+ u32 u4patchver;
+ u32 u4subsys;
+ u32 u4featureopt;
+ u32 u4sectionnum;
+};
+
+struct _SECTION_MAP {
+ u32 u4sectype;
+ u32 u4secoffset;
+ u32 u4secsize;
+ union {
+ u32 u4secspec[SECTION_SPEC_NUM];
+ struct {
+ u32 u4dladdr;
+ u32 u4dlsize;
+ u32 u4seckeyidx;
+ u32 u4alignlen;
+ u32 u4sectype;
+ u32 u4dlmodecrctype;
+ u32 u4crc;
+ u32 reserved[6];
+ } bin_info_spec;
+ };
+};
+
+#define H4_RECV_ACL \
+ .type = HCI_ACLDATA_PKT, \
+ .hlen = HCI_ACL_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 2, \
+ .maxlen = HCI_MAX_FRAME_SIZE \
+
+#define H4_RECV_SCO \
+ .type = HCI_SCODATA_PKT, \
+ .hlen = HCI_SCO_HDR_SIZE, \
+ .loff = 2, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_SCO_SIZE
+
+#define H4_RECV_EVENT \
+ .type = HCI_EVENT_PKT, \
+ .hlen = HCI_EVENT_HDR_SIZE, \
+ .loff = 1, \
+ .lsize = 1, \
+ .maxlen = HCI_MAX_EVENT_SIZE
+
+struct btmtk_dev {
+ struct hci_dev *hdev;
+ unsigned long hdev_flags;
+ unsigned long flags;
+ void *intf_dev;
+ void *cif_dev;
+
+ struct work_struct work;
+ struct work_struct waker;
+ struct work_struct reset_waker;
+
+ int recv_evt_len;
+ int tx_in_flight;
+ /* for tx skb buffer */
+ spinlock_t txlock;
+ /* for rx skb buffer */
+ spinlock_t rxlock;
+ struct sk_buff *evt_skb;
+ struct sk_buff *sco_skb;
+
+ /* For ble iso packet size */
+ int iso_threshold;
+
+ unsigned int sco_num;
+ int isoc_altsetting;
+
+ int suspend_count;
+
+ /* For tx queue */
+ unsigned long tx_state;
+
+ /* For rx queue */
+ struct workqueue_struct *workqueue;
+ struct sk_buff_head rx_q;
+ struct work_struct rx_work;
+ struct sk_buff *rx_skb;
+
+ wait_queue_head_t p_wait_event_q;
+
+ unsigned int subsys_reset;
+ unsigned int chip_reset;
+ unsigned char *rom_patch_bin_file_name;
+ unsigned int chip_id;
+ unsigned int flavor;
+ unsigned int fw_version;
+ unsigned char dongle_index;
+ unsigned char power_state;
+ unsigned char fops_state;
+ unsigned char interface_state;
+ struct btmtk_cif_state *cif_state;
+
+ /* io buffer for usb control transfer */
+ unsigned char *io_buf;
+};
+
+typedef int (*cif_open_ptr)(struct hci_dev *hdev);
+typedef int (*cif_close_ptr)(struct hci_dev *hdev);
+typedef int (*cif_reg_read_ptr)(struct btmtk_dev *bdev, u32 reg, u32 *val);
+typedef int (*cif_reg_write_ptr)(struct btmtk_dev *bdev, u32 reg, u32 val);
+typedef int (*cif_send_cmd_ptr)(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type);
+typedef int (*cif_send_and_recv_ptr)(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const u8 *event, const int event_len,
+ int delay, int retry, int pkt_type);
+typedef int (*cif_event_filter_ptr)(struct btmtk_dev *bdev, struct sk_buff *skb);
+typedef int (*cif_subsys_reset_ptr)(struct btmtk_dev *bdev);
+typedef int (*cif_whole_reset_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_chip_reset_notify_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_mutex_lock_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_mutex_unlock_ptr)(struct btmtk_dev *bdev);
+typedef void (*cif_open_done_ptr)(struct btmtk_dev *bdev);
+typedef int (*cif_dl_dma_ptr)(struct btmtk_dev *bdev, u8 *image,
+ u8 *fwbuf, int section_dl_size, int section_offset);
+
+struct hif_hook_ptr {
+ cif_open_ptr open;
+ cif_close_ptr close;
+ cif_reg_read_ptr reg_read;
+ cif_reg_write_ptr reg_write;
+ cif_send_cmd_ptr send_cmd;
+ cif_send_and_recv_ptr send_and_recv;
+ cif_event_filter_ptr event_filter;
+ cif_subsys_reset_ptr subsys_reset;
+ cif_whole_reset_ptr whole_reset;
+ cif_chip_reset_notify_ptr chip_reset_notify;
+ cif_mutex_lock_ptr cif_mutex_lock;
+ cif_mutex_unlock_ptr cif_mutex_unlock;
+ cif_open_done_ptr open_done;
+ cif_dl_dma_ptr dl_dma;
+};
+
+struct btmtk_main_info {
+ struct hif_hook_ptr hif_hook;
+};
+
+static inline int is_mt7922(u32 chip_id)
+{
+ chip_id &= 0xFFFF;
+ if (chip_id == 0x7922)
+ return 1;
+ return 0;
+}
+
+static inline int is_mt7961(u32 chip_id)
+{
+ chip_id &= 0xFFFF;
+ if (chip_id == 0x7961)
+ return 1;
+ return 0;
+}
+
+int btmtk_get_chip_state(struct btmtk_dev *bdev);
+void btmtk_set_chip_state(struct btmtk_dev *bdev, int new_state);
+int btmtk_allocate_hci_device(struct btmtk_dev *bdev, int hci_bus_type);
+void btmtk_free_hci_device(struct btmtk_dev *bdev, int hci_bus_type);
+int btmtk_register_hci_device(struct btmtk_dev *bdev);
+int btmtk_deregister_hci_device(struct btmtk_dev *bdev);
+int btmtk_recv(struct hci_dev *hdev, const u8 *data, size_t count);
+int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb);
+int btmtk_recv_acl(struct hci_dev *hdev, struct sk_buff *skb);
+int btmtk_send_init_cmds(struct btmtk_dev *hdev);
+int btmtk_send_deinit_cmds(struct btmtk_dev *hdev);
+int btmtk_main_send_cmd(struct btmtk_dev *bdev, const u8 *cmd,
+ const int cmd_len, const u8 *event, const int event_len,
+ int delay, int retry, int pkt_type);
+int btmtk_send_wmt_power_on_cmd(struct btmtk_dev *hdev);
+int btmtk_send_wmt_power_off_cmd(struct btmtk_dev *hdev);
+int btmtk_uart_send_wakeup_cmd(struct hci_dev *hdev);
+int btmtk_uart_send_set_uart_cmd(struct hci_dev *hdev);
+int btmtk_load_rom_patch(struct btmtk_dev *bdev);
+struct btmtk_dev *btmtk_get_dev(void);
+void btmtk_release_dev(struct btmtk_dev *bdev);
+struct btmtk_dev *btmtk_allocate_dev_memory(struct device *dev);
+void btmtk_free_dev_memory(struct device *dev, struct btmtk_dev *bdev);
+void btmtk_reset_waker(struct work_struct *work);
+struct btmtk_main_info *btmtk_get_main_info(void);
+/** file_operations: stpbtfwlog */
+int btmtk_fops_openfwlog(struct inode *inode, struct file *file);
+int btmtk_fops_closefwlog(struct inode *inode, struct file *file);
+ssize_t btmtk_fops_readfwlog(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
+ssize_t btmtk_fops_writefwlog(struct file *filp, const char __user *buf, size_t count,
+ loff_t *f_pos);
+unsigned int btmtk_fops_pollfwlog(struct file *filp, poll_table *wait);
+long btmtk_fops_unlocked_ioctlfwlog(struct file *filp, unsigned int cmd, unsigned long arg);
+
+/* Auto enable picus */
+int btmtk_picus_enable(struct btmtk_dev *bdev);
+int btmtk_picus_disable(struct btmtk_dev *bdev);
+
+void btmtk_reg_hif_hook(struct hif_hook_ptr *hook);
+int btmtk_main_cif_initialize(struct btmtk_dev *bdev, int hci_bus);
+void btmtk_main_cif_uninitialize(struct btmtk_dev *bdev, int hci_bus);
+int btmtk_main_cif_disconnect_notify(struct btmtk_dev *bdev, int hci_bus);
+int btmtk_send_assert_cmd(struct btmtk_dev *bdev);
+int btmtk_efuse_read(struct btmtk_dev *bdev, u16 addr, u8 *value);
+
+void btmtk_set_country_code_from_wifi(char *code);
+
+#endif /* __BTMTK_MAIN_H__ */
new file mode 100644
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+// Copyright (c) 2020 MediaTek Inc.
+
+/*
+ * Bluetooth support for MediaTek USB devices
+ *
+ */
+
+#ifndef _BTMTK_USB_H_
+#define _BTMTK_USB_H_
+#include <linux/usb.h>
+#include "btmtk_main.h"
+
+#define HCI_MAX_COMMAND_SIZE 255
+#define URB_MAX_BUFFER_SIZE (4 * 1024)
+
+#define BT0_MCU_INTERFACE_NUM 0
+#define BT1_MCU_INTERFACE_NUM 3
+#define BT_MCU_INTERFACE_NUM_MAX 4
+#define BT_MCU_NUM_MAX 2
+
+/**
+ * Send cmd dispatch evt
+ */
+#define HCI_EV_VENDOR 0xff
+#define HCI_USB_IO_BUF_SIZE 256
+
+extern u8 wmt_over_hci_header[];
+
+struct btmtk_usb_dev {
+ struct usb_endpoint_descriptor *intr_ep;
+ struct usb_endpoint_descriptor *bulk_tx_ep;
+ struct usb_endpoint_descriptor *bulk_rx_ep;
+ struct usb_endpoint_descriptor *isoc_tx_ep;
+ struct usb_endpoint_descriptor *isoc_rx_ep;
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct usb_interface *isoc;
+ struct usb_interface *iso_channel;
+
+ struct usb_anchor tx_anchor;
+ struct usb_anchor intr_anchor;
+ struct usb_anchor bulk_anchor;
+ struct usb_anchor isoc_anchor;
+ struct usb_anchor ctrl_anchor;
+
+ __u8 cmdreq_type;
+ __u8 cmdreq;
+
+ int new_isoc_altsetting;
+ int new_isoc_altsetting_interface;
+
+ unsigned char *o_usb_buf;
+
+ unsigned char *urb_intr_buf;
+ unsigned char *urb_bulk_buf;
+ unsigned char *urb_ble_isoc_buf;
+};
+#endif
new file mode 100644
@@ -0,0 +1,2050 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020 MediaTek Inc.
+
+/*
+ * Bluetooth support for MediaTek USB devices
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <linux/usb/quirks.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "btmtk_usb.h"
+#include "btmtk_main.h"
+
+static struct usb_driver btusb_driver;
+static int intf_to_idx[BT_MCU_INTERFACE_NUM_MAX] = {0, -1, -1, 1};
+static struct btmtk_usb_dev g_usb_dev[BT_MCU_MINIMUM_INTERFACE_NUM][BT_MCU_NUM_MAX];
+
+static const struct usb_device_id btusb_table[] = {
+ /* Mediatek MT7961 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xe0, 0x01, 0x01) },
+ /* Mediatek MT7922 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7922, 0xe0, 0x01, 0x01) },
+
+ { } /* Terminating entry */
+};
+
+static char event_need_compare[EVENT_COMPARE_SIZE] = {0};
+static char event_need_compare_len;
+static char event_compare_status;
+
+static DEFINE_MUTEX(btmtk_usb_ops_mutex);
+#define USB_OPS_MUTEX_LOCK() mutex_lock(&btmtk_usb_ops_mutex)
+#define USB_OPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_usb_ops_mutex)
+
+MODULE_DEVICE_TABLE(usb, btusb_table);
+
+#define BTUSB_MAX_ISOC_FRAMES 24
+
+#define BTUSB_INTR_RUNNING 0
+#define BTUSB_BULK_RUNNING 1
+#define BTUSB_ISOC_RUNNING 2
+#define BTUSB_SUSPENDING 3
+#define BTUSB_DID_ISO_RESUME 4
+#define BTUSB_BLE_ISOC_RUNNING 5
+
+#define DEVICE_VENDOR_REQUEST_IN 0xc0
+#define DEVICE_CLASS_REQUEST_OUT 0x20
+#define USB_CTRL_IO_TIMO 100
+
+#define BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base) \
+ do { \
+ bdev = usb_get_intfdata(intf); \
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber; \
+ } while (0)
+
+static int btmtk_cif_allocate_memory(struct btmtk_usb_dev *cif_dev);
+static void btmtk_cif_free_memory(struct btmtk_usb_dev *cif_dev);
+
+static int btmtk_usb_send_and_recv(struct btmtk_dev *bdev,
+ struct sk_buff *skb,
+ const u8 *event, const int event_len,
+ int delay, int retry, int pkt_type);
+static int btmtk_usb_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb);
+static int btmtk_usb_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type);
+static int btmtk_usb_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val);
+static int btmtk_usb_write_register(struct btmtk_dev *bdev, u32 reg, u32 val);
+
+static void btmtk_usb_cif_mutex_lock(struct btmtk_dev *bdev)
+{
+ USB_OPS_MUTEX_LOCK();
+}
+
+static void btmtk_usb_cif_mutex_unlock(struct btmtk_dev *bdev)
+{
+ USB_OPS_MUTEX_UNLOCK();
+}
+
+static inline void btusb_free_frags(struct btmtk_dev *bdev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bdev->rxlock, flags);
+
+ kfree_skb(bdev->evt_skb);
+ bdev->evt_skb = NULL;
+
+ kfree_skb(bdev->sco_skb);
+ bdev->sco_skb = NULL;
+
+ spin_unlock_irqrestore(&bdev->rxlock, flags);
+}
+
+static int btusb_recv_isoc(struct btmtk_dev *bdev, void *buffer, int count)
+{
+ struct sk_buff *skb;
+ int err = 0;
+
+ spin_lock(&bdev->rxlock);
+ skb = bdev->sco_skb;
+
+ while (count) {
+ int len;
+
+ if (!skb) {
+ skb = bt_skb_alloc(HCI_MAX_SCO_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ break;
+ }
+
+ hci_skb_pkt_type(skb) = HCI_SCODATA_PKT;
+ hci_skb_expect(skb) = HCI_SCO_HDR_SIZE;
+ }
+
+ len = min_t(uint, hci_skb_expect(skb), count);
+ memcpy(skb_put(skb, len), buffer, len);
+
+ count -= len;
+ buffer += len;
+ hci_skb_expect(skb) -= len;
+
+ if (skb->len == HCI_SCO_HDR_SIZE) {
+ hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
+
+ if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+ kfree_skb(skb);
+ skb = NULL;
+
+ err = -EILSEQ;
+ break;
+ }
+ }
+
+ if (!hci_skb_expect(skb)) {
+ hci_recv_frame(bdev->hdev, skb);
+ skb = NULL;
+ }
+ }
+
+ bdev->sco_skb = skb;
+ spin_unlock(&bdev->rxlock);
+
+ return err;
+}
+
+static void btusb_intr_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int err;
+ u8 *buf;
+
+ if (!urb) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (!hdev) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb, urb->status,
+ urb->actual_length);
+
+ if (urb->status != 0) {
+ BTMTK_WARN("%s: urb %p urb->status %d count %d", __func__,
+ urb, urb->status, urb->actual_length);
+ }
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+
+ if (!cif_dev->urb_intr_buf) {
+ BT_ERR("%s: bdev->urb_intr_buf is NULL!", __func__);
+ return;
+ }
+
+ buf = urb->transfer_buffer;
+ if (urb->actual_length >= URB_MAX_BUFFER_SIZE ||
+ (urb->actual_length != (buf[1] + 2) && urb->actual_length > 1)) {
+ BTMTK_ERR("%s: urb->actual_length is invalid, buf[1] = %d!",
+ __func__, buf[1]);
+ goto intr_resub;
+ }
+ memset(cif_dev->urb_intr_buf, 0, URB_MAX_BUFFER_SIZE);
+ cif_dev->urb_intr_buf[0] = HCI_EVENT_PKT;
+ memcpy(cif_dev->urb_intr_buf + 1, urb->transfer_buffer, urb->actual_length);
+
+ err = btmtk_recv(hdev, cif_dev->urb_intr_buf, urb->actual_length + 1);
+ if (err) {
+ BTMTK_ERR("%s corrupted event packet, urb_intr_buf = %p, transfer_buf = %p",
+ hdev->name, cif_dev->urb_intr_buf, urb->transfer_buffer);
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ BTMTK_INFO("%s: urb->status is ENOENT!", __func__);
+ return;
+ }
+
+ if (!test_bit(BTUSB_INTR_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s: test_bit is not running!", __func__);
+ return;
+ }
+
+intr_resub:
+ usb_mark_last_busy(cif_dev->udev);
+ usb_anchor_urb(urb, &cif_dev->intr_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static void btusb_mtk_wmt_recv(struct urb *urb)
+{
+ struct hci_dev *hdev = urb->context;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct sk_buff *skb;
+ int err;
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (urb->status == 0 && urb->actual_length > 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+ skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE, GFP_ATOMIC);
+ if (!skb) {
+ BTMTK_ERR("%s skb is null!", __func__);
+ hdev->stat.err_rx++;
+ goto exit;
+ }
+
+ if (urb->actual_length >= HCI_MAX_EVENT_SIZE) {
+ kfree_skb(skb);
+ hdev->stat.err_rx++;
+ goto exit;
+ }
+ hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+ memcpy(skb_put(skb, urb->actual_length), urb->transfer_buffer, urb->actual_length);
+
+ hci_recv_frame(hdev, skb);
+ return;
+ } else if (urb->status == -ENOENT) {
+ goto exit;
+ }
+
+ usb_mark_last_busy(cif_dev->udev);
+
+ usleep_range(90, 100);
+
+ usb_anchor_urb(urb, &cif_dev->ctrl_anchor);
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ kfree(urb->setup_packet);
+ if (err != -EPERM && err != -ENODEV)
+ usb_unanchor_urb(urb);
+ }
+
+ return;
+
+exit:
+ kfree(urb->setup_packet);
+}
+
+static int btusb_submit_wmt_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+ unsigned int ifnum_base;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ dr->bRequestType = 0xC0;
+ dr->bRequest = 0x01;
+ dr->wIndex = 0;
+ dr->wValue = 0x30;
+ dr->wLength = __cpu_to_le16(size);
+
+ pipe = usb_rcvctrlpipe(cif_dev->udev, 0);
+
+ buf = kmalloc(size, GFP_KERNEL);
+ if (!buf) {
+ kfree(dr);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ usb_fill_control_urb(urb, cif_dev->udev, pipe, (void *)dr,
+ buf, size, btusb_mtk_wmt_recv, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->ctrl_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ kfree(dr);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BTMTK_DBG("%s", hdev->name);
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->intr_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(HCI_MAX_EVENT_SIZE);
+ BTMTK_INFO("%s: maximum packet size:%d", __func__, size);
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvintpipe(cif_dev->udev, cif_dev->intr_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, size,
+ btusb_intr_complete, hdev, cif_dev->intr_ep->bInterval);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_anchor_urb(urb, &cif_dev->intr_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_bulk_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int err;
+ u8 *buf;
+ u16 len = 0;
+
+ if (!urb) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (!hdev) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ if (urb->status != 0) {
+ BTMTK_INFO("%s: urb %p urb->status %d count %d", __func__, urb,
+ urb->status, urb->actual_length);
+ }
+
+ if (urb->status == 0) {
+ hdev->stat.byte_rx += urb->actual_length;
+ if (!cif_dev->urb_bulk_buf) {
+ BT_ERR("%s: bdev->urb_bulk_buf is NULL!", __func__);
+ return;
+ }
+
+ buf = urb->transfer_buffer;
+ len = buf[2] + ((buf[3] << 8) & 0xff00);
+ if (urb->actual_length >= URB_MAX_BUFFER_SIZE ||
+ urb->actual_length != len + 4) {
+ BTMTK_ERR("%s urb->actual_length is invalid, len = %d!", __func__, len);
+ goto bulk_resub;
+ }
+ memset(cif_dev->urb_bulk_buf, 0, URB_MAX_BUFFER_SIZE);
+ cif_dev->urb_bulk_buf[0] = HCI_ACLDATA_PKT;
+ memcpy(cif_dev->urb_bulk_buf + 1, urb->transfer_buffer, urb->actual_length);
+
+ err = btmtk_recv(hdev, cif_dev->urb_bulk_buf, urb->actual_length + 1);
+ if (err) {
+ BTMTK_ERR("%s corrupted ACL packet, urb_bulk_buf = %p, transfer_buf = %p",
+ hdev->name, cif_dev->urb_bulk_buf, urb->transfer_buffer);
+ hdev->stat.err_rx++;
+ }
+ } else if (urb->status == -ENOENT) {
+ /* Avoid suspend failed when usb_kill_urb */
+ BTMTK_INFO("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+ return;
+ }
+
+ if (!test_bit(BTUSB_BULK_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s test flag failed", __func__);
+ return;
+ }
+
+bulk_resub:
+ usb_anchor_urb(urb, &cif_dev->bulk_anchor);
+ usb_mark_last_busy(cif_dev->udev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size = HCI_MAX_FRAME_SIZE;
+
+ BTMTK_DBG("%s", hdev->name);
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->bulk_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(0, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvbulkpipe(cif_dev->udev, cif_dev->bulk_rx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, cif_dev->udev, pipe, buf, size,
+ btusb_bulk_complete, hdev);
+
+ urb->transfer_flags |= URB_FREE_BUFFER;
+
+ usb_mark_last_busy(cif_dev->udev);
+ usb_anchor_urb(urb, &cif_dev->bulk_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_isoc_complete(struct urb *urb)
+{
+ struct hci_dev *hdev = NULL;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int i, err;
+
+ if (!urb) {
+ BTMTK_ERR("%s: ERROR, urb is NULL!", __func__);
+ return;
+ }
+
+ hdev = urb->context;
+ if (!hdev) {
+ BTMTK_ERR("%s: ERROR, hdev is NULL!", __func__);
+ return;
+ }
+
+ bdev = hci_get_drvdata(hdev);
+ if (!bdev) {
+ BTMTK_ERR("%s: ERROR, bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev) {
+ BTMTK_ERR("%s: ERROR, cif_dev is NULL!", __func__);
+ return;
+ }
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
+ if (urb->status == 0) {
+ for (i = 0; i < urb->number_of_packets; i++) {
+ unsigned int offset = urb->iso_frame_desc[i].offset;
+ unsigned int length = urb->iso_frame_desc[i].actual_length;
+
+ if (urb->iso_frame_desc[i].status)
+ continue;
+
+ hdev->stat.byte_rx += length;
+
+ if (btusb_recv_isoc(bdev, urb->transfer_buffer + offset,
+ length) < 0) {
+ BTMTK_ERR("%s corrupted SCO packet", hdev->name);
+ hdev->stat.err_rx++;
+ }
+ }
+ } else if (urb->status == -ENOENT) {
+ BTMTK_INFO("%s: urb->status is ENOENT!", __func__);
+ return;
+ }
+
+ if (!test_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ BTMTK_INFO("%s: bdev->flags is RUNNING!", __func__);
+ return;
+ }
+
+ usb_anchor_urb(urb, &cif_dev->isoc_anchor);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p failed to resubmit (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+}
+
+static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
+{
+ int i, offset = 0;
+
+ BTMTK_DBG("len %d mtu %d", len, mtu);
+
+ for (i = 0; i < BTUSB_MAX_ISOC_FRAMES && len >= mtu;
+ i++, offset += mtu, len -= mtu) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = mtu;
+ }
+
+ if (len && i < BTUSB_MAX_ISOC_FRAMES) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = len;
+ i++;
+ }
+
+ urb->number_of_packets = i;
+}
+
+static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct urb *urb;
+ unsigned char *buf;
+ unsigned int pipe;
+ int err, size;
+
+ BTMTK_DBG("%s", hdev->name);
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ if (!cif_dev->isoc_rx_ep)
+ return -ENODEV;
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, mem_flags);
+ if (!urb)
+ return -ENOMEM;
+
+ size = le16_to_cpu(cif_dev->isoc_rx_ep->wMaxPacketSize) *
+ BTUSB_MAX_ISOC_FRAMES;
+
+ buf = kmalloc(size, mem_flags);
+ if (!buf) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ pipe = usb_rcvisocpipe(cif_dev->udev, cif_dev->isoc_rx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe, buf, size, btusb_isoc_complete,
+ hdev, cif_dev->isoc_rx_ep->bInterval);
+
+ urb->transfer_flags = URB_FREE_BUFFER | URB_ISO_ASAP;
+
+ __fill_isoc_descriptor(urb, size,
+ le16_to_cpu(cif_dev->isoc_rx_ep->wMaxPacketSize));
+
+ usb_anchor_urb(urb, &cif_dev->isoc_anchor);
+
+ err = usb_submit_urb(urb, mem_flags);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ usb_unanchor_urb(urb);
+ }
+
+ usb_free_urb(urb);
+
+ return err;
+}
+
+static void btusb_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ unsigned long flags;
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name, urb,
+ urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ spin_lock_irqsave(&bdev->txlock, flags);
+ bdev->tx_in_flight--;
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static void btusb_isoc_tx_complete(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+
+ BTMTK_DBG("%s urb %p status %d count %d", hdev->name,
+ urb, urb->status, urb->actual_length);
+
+ if (!test_bit(HCI_RUNNING, &hdev->flags))
+ goto done;
+
+ if (!urb->status)
+ hdev->stat.byte_tx += urb->transfer_buffer_length;
+ else
+ hdev->stat.err_tx++;
+
+done:
+ kfree(urb->setup_packet);
+
+ kfree_skb(skb);
+}
+
+static int btmtk_usb_open(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+ unsigned int ifnum_base;
+
+ BTMTK_DBG("%s", hdev->name);
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ return err;
+
+ cif_dev->intf->needs_remote_wakeup = 1;
+
+ if (test_and_set_bit(BTUSB_INTR_RUNNING, &bdev->flags))
+ goto done;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
+ if (err < 0)
+ goto failed;
+
+ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+ usb_kill_anchored_urbs(&cif_dev->intr_anchor);
+ goto failed;
+ }
+
+ set_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+
+done:
+ usb_autopm_put_interface(cif_dev->intf);
+ return 0;
+
+failed:
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ usb_autopm_put_interface(cif_dev->intf);
+ return err;
+}
+
+static void btusb_stop_traffic(struct btmtk_usb_dev *cif_dev)
+{
+ usb_kill_anchored_urbs(&cif_dev->intr_anchor);
+ usb_kill_anchored_urbs(&cif_dev->bulk_anchor);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ usb_kill_anchored_urbs(&cif_dev->ctrl_anchor);
+}
+
+static int btmtk_usb_close(struct hci_dev *hdev)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ BTMTK_INFO("%s enter!", __func__);
+
+ cancel_work_sync(&bdev->work);
+ cancel_work_sync(&bdev->waker);
+
+ clear_bit(BTUSB_BLE_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+
+ btusb_stop_traffic(cif_dev);
+ btusb_free_frags(bdev);
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ goto failed;
+
+ cif_dev->intf->needs_remote_wakeup = 0;
+ usb_autopm_put_interface(cif_dev->intf);
+
+failed:
+ return 0;
+}
+
+static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ unsigned int pipe;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ dr = kmalloc(sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ usb_free_urb(urb);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ dr->bRequestType = cif_dev->cmdreq_type;
+ dr->bRequest = cif_dev->cmdreq;
+ dr->wIndex = 0;
+ dr->wValue = 0;
+ dr->wLength = __cpu_to_le16(skb->len);
+
+ pipe = usb_sndctrlpipe(cif_dev->udev, 0x00);
+
+ usb_fill_control_urb(urb, cif_dev->udev, pipe, (void *)dr,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!cif_dev->bulk_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndbulkpipe(cif_dev->udev, cif_dev->bulk_tx_ep->bEndpointAddress);
+
+ usb_fill_bulk_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_tx_complete, skb);
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct urb *urb;
+ unsigned int pipe;
+
+ if (!cif_dev->isoc_tx_ep)
+ return ERR_PTR(-ENODEV);
+
+ urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_KERNEL);
+ if (!urb)
+ return ERR_PTR(-ENOMEM);
+
+ pipe = usb_sndisocpipe(cif_dev->udev, cif_dev->isoc_tx_ep->bEndpointAddress);
+
+ usb_fill_int_urb(urb, cif_dev->udev, pipe,
+ skb->data, skb->len, btusb_isoc_tx_complete,
+ skb, cif_dev->isoc_tx_ep->bInterval);
+
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ __fill_isoc_descriptor(urb, skb->len,
+ le16_to_cpu(cif_dev->isoc_tx_ep->wMaxPacketSize));
+
+ skb->dev = (void *)hdev;
+
+ return urb;
+}
+
+static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ usb_anchor_urb(urb, &cif_dev->tx_anchor);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err < 0) {
+ if (err != -EPERM && err != -ENODEV)
+ BTMTK_ERR("%s urb %p submission failed (%d)",
+ hdev->name, urb, -err);
+ kfree(urb->setup_packet);
+ usb_unanchor_urb(urb);
+ } else {
+ usb_mark_last_busy(cif_dev->udev);
+ }
+
+ usb_free_urb(urb);
+ return err;
+}
+
+static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ unsigned long flags;
+ bool suspending;
+
+ spin_lock_irqsave(&bdev->txlock, flags);
+ suspending = test_bit(BTUSB_SUSPENDING, &bdev->flags);
+ if (!suspending)
+ bdev->tx_in_flight++;
+ spin_unlock_irqrestore(&bdev->txlock, flags);
+
+ if (!suspending)
+ return submit_tx_urb(hdev, urb);
+
+ schedule_work(&bdev->waker);
+
+ usb_free_urb(urb);
+ return 0;
+}
+
+static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct urb *urb = NULL;
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ unsigned int ifnum_base;
+ int ret = 0;
+
+ if (skb->len <= 0) {
+ ret = -EFAULT;
+ BTMTK_ERR("%s: target packet length:%zu is not allowed",
+ __func__, (size_t)skb->len);
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ skb_pull(skb, 1);
+ switch (hci_skb_pkt_type(skb)) {
+ case HCI_COMMAND_PKT:
+ /* For wmt cmd/evt */
+ if (!memcmp(skb->data, &wmt_over_hci_header[1], WMT_OVER_HCI_HEADER_SIZE - 1)) {
+ skb_push(skb, 1);
+ skb->data[0] = 0x01;
+ btmtk_usb_send_cmd(bdev, skb, 100, 20, BTMTK_TX_CMD_FROM_DRV);
+ btusb_submit_wmt_urb(hdev, GFP_KERNEL);
+ return 0;
+ }
+
+ urb = alloc_ctrl_urb(hdev, skb);
+
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+
+ hdev->stat.cmd_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_ACLDATA_PKT:
+ urb = alloc_bulk_urb(hdev, skb);
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+ hdev->stat.acl_tx++;
+ return submit_or_queue_tx_urb(hdev, urb);
+
+ case HCI_SCODATA_PKT:
+ urb = alloc_isoc_urb(hdev, skb);
+ if (IS_ERR(urb)) {
+ kfree_skb(skb);
+ skb = NULL;
+ return PTR_ERR(urb);
+ }
+
+ hdev->stat.sco_tx++;
+ return submit_tx_urb(hdev, urb);
+ }
+
+ kfree_skb(skb);
+ skb = NULL;
+ return -EILSEQ;
+}
+
+static int btmtk_usb_load_fw_patch_using_dma(struct btmtk_dev *bdev, u8 *image,
+ u8 *fwbuf, int section_dl_size,
+ int section_offset)
+{
+ int cur_len = 0;
+ int ret = 0;
+ s32 sent_len;
+ u8 dl_done_cmd[] = {0x01, 0x6F, 0xFC, 0x05, 0x01, 0x01, 0x01, 0x00, PATCH_PHASE3};
+ u8 event[] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
+
+ if (!bdev || !image || !fwbuf) {
+ BTMTK_ERR("%s: invalid parameters!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ BTMTK_INFO("%s: loading rom patch... start", __func__);
+ while (1) {
+ sent_len = (section_dl_size - cur_len) >= (UPLOAD_PATCH_UNIT - HCI_TYPE_SIZE) ?
+ (UPLOAD_PATCH_UNIT - HCI_TYPE_SIZE) : (section_dl_size - cur_len);
+
+ if (sent_len > 0) {
+ image[0] = HCI_ACLDATA_PKT;
+ memcpy(&image[HCI_TYPE_SIZE], fwbuf + section_offset + cur_len, sent_len);
+ BTMTK_DBG("%s: sent_len = %d, cur_len = %d", __func__,
+ sent_len, cur_len);
+ ret = btmtk_main_send_cmd(bdev,
+ image, sent_len + HCI_TYPE_SIZE,
+ NULL, -1,
+ 0, 0, BTMTK_TX_ACL_FROM_DRV);
+ if (ret < 0) {
+ BTMTK_ERR("%s: send patch failed, terminate", __func__);
+ goto exit;
+ }
+ cur_len += sent_len;
+ } else {
+ break;
+ }
+ }
+
+ ret = btmtk_main_send_cmd(bdev, dl_done_cmd, sizeof(dl_done_cmd),
+ event, sizeof(event),
+ DELAY_TIMES, RETRY_TIMES,
+ BTMTK_TX_CMD_FROM_DRV);
+ if (ret < 0)
+ BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
+ BTMTK_INFO("%s: loading rom patch... Done", __func__);
+
+exit:
+ return ret;
+}
+
+static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+
+ BTMTK_DBG("%s evt %d", hdev->name, evt);
+
+ if (hci_conn_num(hdev, SCO_LINK) != bdev->sco_num) {
+ bdev->sco_num = hci_conn_num(hdev, SCO_LINK);
+ schedule_work(&bdev->work);
+ }
+}
+
+static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+{
+ struct btmtk_dev *bdev = hci_get_drvdata(hdev);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct usb_interface *intf = cif_dev->isoc;
+ struct usb_endpoint_descriptor *ep_desc;
+ int i, err;
+ unsigned int ifnum_base;
+
+ if (!cif_dev->isoc)
+ return -ENODEV;
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ cif_dev->new_isoc_altsetting_interface = 1;
+ err = usb_set_interface(cif_dev->udev, cif_dev->new_isoc_altsetting_interface, altsetting);
+
+ if (err < 0) {
+ BTMTK_ERR("%s setting interface failed (%d)", hdev->name, -err);
+ return err;
+ }
+
+ bdev->isoc_altsetting = altsetting;
+
+ cif_dev->isoc_tx_ep = NULL;
+ cif_dev->isoc_rx_ep = NULL;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!cif_dev->isoc_tx_ep && usb_endpoint_is_isoc_out(ep_desc)) {
+ cif_dev->isoc_tx_ep = ep_desc;
+ continue;
+ }
+
+ if (!cif_dev->isoc_rx_ep && usb_endpoint_is_isoc_in(ep_desc)) {
+ cif_dev->isoc_rx_ep = ep_desc;
+ continue;
+ }
+ }
+
+ if (!cif_dev->isoc_tx_ep || !cif_dev->isoc_rx_ep) {
+ BTMTK_ERR("%s invalid SCO descriptors", hdev->name);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void btusb_work(struct work_struct *work)
+{
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, work);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ struct hci_dev *hdev = bdev->hdev;
+ int new_alts;
+ int err;
+ unsigned long flags;
+
+ if (bdev->sco_num > 0) {
+ if (!test_bit(BTUSB_DID_ISO_RESUME, &bdev->flags)) {
+ err = usb_autopm_get_interface(cif_dev->isoc ?
+ cif_dev->isoc :
+ cif_dev->intf);
+ if (err < 0) {
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ return;
+ }
+
+ set_bit(BTUSB_DID_ISO_RESUME, &bdev->flags);
+ }
+
+ if (hdev->voice_setting & 0x0020) {
+ static const int alts[3] = { 2, 4, 5 };
+
+ new_alts = alts[bdev->sco_num - 1];
+ } else {
+ new_alts = bdev->sco_num;
+ }
+
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+
+ spin_lock_irqsave(&bdev->rxlock, flags);
+ kfree_skb(bdev->sco_skb);
+ bdev->sco_skb = NULL;
+ spin_unlock_irqrestore(&bdev->rxlock, flags);
+
+ if (__set_isoc_interface(hdev, new_alts) < 0)
+ return;
+
+ if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_KERNEL) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_KERNEL);
+ }
+ } else {
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ usb_kill_anchored_urbs(&cif_dev->isoc_anchor);
+ BTMTK_INFO("%s set alt to zero", __func__);
+ __set_isoc_interface(hdev, 0);
+ if (test_and_clear_bit(BTUSB_DID_ISO_RESUME, &bdev->flags))
+ usb_autopm_put_interface(cif_dev->isoc ? cif_dev->isoc : cif_dev->intf);
+ }
+}
+
+static void btusb_waker(struct work_struct *work)
+{
+ struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, waker);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int err;
+
+ err = usb_autopm_get_interface(cif_dev->intf);
+ if (err < 0)
+ return;
+
+ usb_autopm_put_interface(cif_dev->intf);
+}
+
+static int btusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_endpoint_descriptor *ep_desc;
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ unsigned int ifnum_base;
+ int i, err = 0;
+
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
+ BTMTK_DBG("intf %p id %p, interfacenum = %d", intf, id, ifnum_base);
+
+ bdev = usb_get_intfdata(intf);
+ if (!bdev) {
+ BTMTK_ERR("[ERR] bdev is NULL");
+ err = -ENOMEM;
+ goto end;
+ }
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+ ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+ if (!cif_dev->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
+ cif_dev->intr_ep = ep_desc;
+ BTMTK_INFO("intr_rx_ep i = %d EP 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->bulk_tx_ep && usb_endpoint_is_bulk_out(ep_desc)) {
+ cif_dev->bulk_tx_ep = ep_desc;
+ BTMTK_INFO("bulk_tx_ep i = %d EP 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+
+ if (!cif_dev->bulk_rx_ep && usb_endpoint_is_bulk_in(ep_desc)) {
+ cif_dev->bulk_rx_ep = ep_desc;
+ BTMTK_INFO("bulk_rx_ep i = %d EP = 0x%02X, number_of_endpoints=%d",
+ i, ep_desc->bEndpointAddress,
+ intf->cur_altsetting->desc.bNumEndpoints);
+ continue;
+ }
+ }
+
+ if (!cif_dev->intr_ep || !cif_dev->bulk_tx_ep || !cif_dev->bulk_rx_ep) {
+ BTMTK_ERR("[ERR] intr_ep or bulk_tx_ep or bulk_rx_ep is NULL");
+ err = -ENODEV;
+ goto end;
+ }
+
+ cif_dev->cmdreq_type = USB_TYPE_CLASS;
+ cif_dev->cmdreq = 0x00;
+
+ cif_dev->udev = interface_to_usbdev(intf);
+ cif_dev->intf = intf;
+ bdev->intf_dev = &cif_dev->udev->dev;
+
+ INIT_WORK(&bdev->work, btusb_work);
+ INIT_WORK(&bdev->waker, btusb_waker);
+ init_usb_anchor(&cif_dev->tx_anchor);
+ spin_lock_init(&bdev->txlock);
+
+ init_usb_anchor(&cif_dev->intr_anchor);
+ init_usb_anchor(&cif_dev->bulk_anchor);
+ init_usb_anchor(&cif_dev->isoc_anchor);
+ init_usb_anchor(&cif_dev->ctrl_anchor);
+ spin_lock_init(&bdev->rxlock);
+
+ err = btmtk_cif_allocate_memory(cif_dev);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_cif_allocate_memory failed!");
+ goto end;
+ }
+
+ err = btmtk_main_cif_initialize(bdev, HCI_USB);
+ if (err < 0) {
+ BTMTK_ERR("[ERR] btmtk_main_cif_initialize failed!");
+ goto free_mem;
+ }
+
+ bdev->hdev->notify = btusb_notify;
+
+ SET_HCIDEV_DEV(bdev->hdev, &cif_dev->intf->dev);
+
+ err = btmtk_load_rom_patch(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk load rom patch failed!");
+ goto deinit;
+ }
+
+ usb_set_intfdata(intf, bdev);
+
+ err = btmtk_send_init_cmds(bdev);
+ if (err < 0)
+ BTMTK_ERR("%s, btmtk_send_init_cmds failed, err = %d", __func__, err);
+
+ err = btmtk_register_hci_device(bdev);
+ if (err < 0) {
+ BTMTK_ERR("btmtk_register_hci_device failed!");
+ goto deinit;
+ }
+
+ return 0;
+
+deinit:
+ btmtk_main_cif_uninitialize(bdev, HCI_USB);
+free_mem:
+ btmtk_cif_free_memory(cif_dev);
+end:
+ return err;
+}
+
+static void btusb_disconnect(struct usb_interface *intf)
+{
+ struct btmtk_dev *bdev = NULL;
+ struct btmtk_usb_dev *cif_dev = NULL;
+ struct hci_dev *hdev;
+
+ bdev = usb_get_intfdata(intf);
+ if (!bdev) {
+ BTMTK_WARN("%s: bdev is NULL!", __func__);
+ return;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev) {
+ BTMTK_WARN("%s: cif_dev is NULL!", __func__);
+ return;
+ }
+
+ hdev = bdev->hdev;
+ usb_set_intfdata(cif_dev->intf, NULL);
+
+ if (cif_dev->isoc)
+ usb_set_intfdata(cif_dev->isoc, NULL);
+
+ if (cif_dev->iso_channel)
+ usb_set_intfdata(cif_dev->iso_channel, NULL);
+
+ if (intf == cif_dev->intf) {
+ if (cif_dev->isoc)
+ usb_driver_release_interface(&btusb_driver, cif_dev->isoc);
+ if (cif_dev->iso_channel)
+ usb_driver_release_interface(&btusb_driver, cif_dev->iso_channel);
+ } else if (intf == cif_dev->isoc) {
+ usb_driver_release_interface(&btusb_driver, cif_dev->intf);
+ } else if (intf == cif_dev->iso_channel) {
+ usb_driver_release_interface(&btusb_driver, cif_dev->intf);
+ }
+
+ btmtk_cif_free_memory(cif_dev);
+
+ btmtk_main_cif_disconnect_notify(bdev, HCI_USB);
+}
+
+#ifdef CONFIG_PM
+static int btusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct btmtk_dev *bdev = usb_get_intfdata(intf);
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = 0;
+
+ if (bdev->suspend_count++) {
+ BTMTK_ERR("%s: Has suspended. suspend_count: %d end",
+ __func__, bdev->suspend_count);
+ return 0;
+ }
+
+ spin_lock_irq(&bdev->txlock);
+ if (!(PMSG_IS_AUTO(message) && bdev->tx_in_flight)) {
+ set_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+ } else {
+ spin_unlock_irq(&bdev->txlock);
+ bdev->suspend_count--;
+ return -EBUSY;
+ }
+
+ cancel_work_sync(&bdev->work);
+
+ btusb_stop_traffic(cif_dev);
+ usb_kill_anchored_urbs(&cif_dev->tx_anchor);
+
+ BTMTK_INFO("%s end, suspend_count = %d", __func__, bdev->suspend_count);
+
+ return ret;
+}
+
+static int btusb_resume(struct usb_interface *intf)
+{
+ struct btmtk_dev *bdev = usb_get_intfdata(intf);
+ struct hci_dev *hdev = bdev->hdev;
+ int err = 0;
+
+ BTMTK_INFO("%s begin", __func__);
+
+ if (--bdev->suspend_count) {
+ BTMTK_WARN("%s: bdev->suspend_count %d, return 0", __func__,
+ bdev->suspend_count);
+ return 0;
+ }
+
+ if (test_bit(BTUSB_INTR_RUNNING, &bdev->flags)) {
+ err = btusb_submit_intr_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_INTR_RUNNING, &bdev->flags);
+ goto done;
+ }
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &bdev->flags)) {
+ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ if (err < 0) {
+ clear_bit(BTUSB_BULK_RUNNING, &bdev->flags);
+ goto done;
+ }
+
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+ if (test_bit(BTUSB_ISOC_RUNNING, &bdev->flags)) {
+ if (btusb_submit_isoc_urb(hdev, GFP_NOIO) < 0)
+ clear_bit(BTUSB_ISOC_RUNNING, &bdev->flags);
+ else
+ btusb_submit_isoc_urb(hdev, GFP_NOIO);
+ }
+
+ spin_lock_irq(&bdev->txlock);
+ clear_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+ schedule_work(&bdev->work);
+
+ BTMTK_INFO("%s end", __func__);
+
+ return 0;
+
+done:
+ spin_lock_irq(&bdev->txlock);
+ clear_bit(BTUSB_SUSPENDING, &bdev->flags);
+ spin_unlock_irq(&bdev->txlock);
+
+ return err;
+}
+#endif
+
+static int btmtk_cif_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret = -1;
+ int cif_event = 0;
+ unsigned int ifnum_base;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ /* Mediatek Driver Version */
+ BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
+
+ ifnum_base = intf->cur_altsetting->desc.bInterfaceNumber;
+ BTMTK_DBG("intf %p id %p, interfacenum = %d", intf, id, ifnum_base);
+
+ if (ifnum_base != BT0_MCU_INTERFACE_NUM &&
+ ifnum_base != BT1_MCU_INTERFACE_NUM)
+ return -ENODEV;
+
+ bdev = btmtk_get_dev();
+ usb_set_intfdata(intf, bdev);
+ bdev->cif_dev = &g_usb_dev[bdev->dongle_index][intf_to_idx[ifnum_base]];
+
+ cif_event = HIF_EVENT_PROBE;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_probe(intf, id);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ return ret;
+}
+
+static void btmtk_cif_disconnect(struct usb_interface *intf)
+{
+ int cif_event = 0;
+ unsigned int ifnum_base;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_DISCONNECT;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ btmtk_usb_cif_mutex_lock(bdev);
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ btusb_disconnect(intf);
+
+ /* Set End/Error state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ btmtk_usb_cif_mutex_unlock(bdev);
+}
+
+#ifdef CONFIG_PM
+static int btmtk_cif_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ int ret = 0;
+ unsigned int ifnum_base;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+ int state = BTMTK_STATE_INIT;
+
+ BTMTK_INFO("%s, enter", __func__);
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ state = btmtk_get_chip_state(bdev);
+ /* Retrieve current HIF event state */
+ if (state == BTMTK_STATE_FW_DUMP) {
+ BTMTK_WARN("%s: FW dumping ongoing, don't dos suspend flow!!!", __func__);
+ cif_event = HIF_EVENT_FW_DUMP;
+ } else {
+ cif_event = HIF_EVENT_SUSPEND;
+ }
+
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_suspend(intf, message);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ BTMTK_INFO("%s, end. ret = %d", __func__, ret);
+ return ret;
+}
+
+static int btmtk_cif_resume(struct usb_interface *intf)
+{
+ int ret = 0;
+ unsigned int ifnum_base;
+ int cif_event = 0;
+ struct btmtk_cif_state *cif_state = NULL;
+ struct btmtk_dev *bdev = NULL;
+
+ BTMTK_INFO("%s, enter", __func__);
+ BTMTK_CIF_GET_DEV_PRIV(bdev, intf, ifnum_base);
+
+ /* Retrieve current HIF event state */
+ cif_event = HIF_EVENT_RESUME;
+ if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
+ /* Error */
+ BTMTK_WARN("%s intf[%d] priv setting is NULL", __func__, ifnum_base);
+ return -ENODEV;
+ }
+
+ cif_state = &bdev->cif_state[cif_event];
+
+ /* Set Entering state */
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
+
+ /* Do HIF events */
+ ret = btusb_resume(intf);
+
+ /* Set End/Error state */
+ if (ret == 0)
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
+ else
+ btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
+
+ BTMTK_INFO("%s, end. ret = %d", __func__, ret);
+ return ret;
+}
+#endif // CONFIG_PM //
+
+static struct usb_driver btusb_driver = {
+ .name = "btmtk_usb",
+ .probe = btmtk_cif_probe,
+ .disconnect = btmtk_cif_disconnect,
+#ifdef CONFIG_PM
+ .suspend = btmtk_cif_suspend,
+ .resume = btmtk_cif_resume,
+#endif
+ .id_table = btusb_table,
+ .supports_autosuspend = 1,
+ .disable_hub_initiated_lpm = 1,
+};
+
+int btmtk_cif_register(void)
+{
+ int retval = 0;
+ struct hif_hook_ptr hook;
+
+ BTMTK_INFO("%s", __func__);
+
+ memset(&hook, 0, sizeof(hook));
+ hook.open = btmtk_usb_open;
+ hook.close = btmtk_usb_close;
+ hook.reg_read = btmtk_usb_read_register;
+ hook.reg_write = btmtk_usb_write_register;
+ hook.send_cmd = btmtk_usb_send_cmd;
+ hook.send_and_recv = btmtk_usb_send_and_recv;
+ hook.event_filter = btmtk_usb_event_filter;
+ hook.cif_mutex_lock = btmtk_usb_cif_mutex_lock;
+ hook.cif_mutex_unlock = btmtk_usb_cif_mutex_unlock;
+ hook.dl_dma = btmtk_usb_load_fw_patch_using_dma;
+ btmtk_reg_hif_hook(&hook);
+
+ retval = usb_register(&btusb_driver);
+ if (retval)
+ BTMTK_ERR("*** USB registration fail(%d)! ***", retval);
+ else
+ BTMTK_INFO("%s, usb registration success!", __func__);
+ return retval;
+}
+
+int btmtk_cif_deregister(void)
+{
+ BTMTK_INFO("%s", __func__);
+ usb_deregister(&btusb_driver);
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static int btmtk_cif_allocate_memory(struct btmtk_usb_dev *cif_dev)
+{
+ if (!cif_dev->o_usb_buf) {
+ cif_dev->o_usb_buf = kzalloc(HCI_MAX_COMMAND_SIZE, GFP_KERNEL);
+ if (!cif_dev->o_usb_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->o_usb_buf)", __func__);
+ return -1;
+ }
+ }
+
+ if (!cif_dev->urb_intr_buf) {
+ cif_dev->urb_intr_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_intr_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_intr_buf)", __func__);
+ return -1;
+ }
+ }
+ if (!cif_dev->urb_bulk_buf) {
+ cif_dev->urb_bulk_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_bulk_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_bulk_buf)", __func__);
+ return -1;
+ }
+ }
+ if (!cif_dev->urb_ble_isoc_buf) {
+ cif_dev->urb_ble_isoc_buf = kzalloc(URB_MAX_BUFFER_SIZE, GFP_KERNEL);
+ if (!cif_dev->urb_ble_isoc_buf) {
+ BTMTK_ERR("%s: alloc memory fail (bdev->urb_ble_isoc_buf)", __func__);
+ return -1;
+ }
+ }
+
+ BTMTK_INFO("%s: Done", __func__);
+ return 0;
+}
+
+static void btmtk_cif_free_memory(struct btmtk_usb_dev *cif_dev)
+{
+ if (!cif_dev) {
+ BTMTK_ERR("%s: bdev is NULL!", __func__);
+ return;
+ }
+
+ kfree(cif_dev->o_usb_buf);
+ cif_dev->o_usb_buf = NULL;
+
+ kfree(cif_dev->urb_intr_buf);
+ cif_dev->urb_intr_buf = NULL;
+
+ kfree(cif_dev->urb_bulk_buf);
+ cif_dev->urb_bulk_buf = NULL;
+
+ kfree(cif_dev->urb_ble_isoc_buf);
+ cif_dev->urb_ble_isoc_buf = NULL;
+
+ memset(cif_dev, 0, sizeof(struct btmtk_usb_dev));
+
+ BTMTK_INFO("%s: Success", __func__);
+}
+
+static int btmtk_usb_read_register(struct btmtk_dev *bdev, u32 reg, u32 *val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x63,
+ DEVICE_VENDOR_REQUEST_IN,
+ reg_high,
+ reg_low,
+ bdev->io_buf,
+ sizeof(u32), USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ *val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, *val);
+ return ret;
+ }
+
+ memmove(val, bdev->io_buf, sizeof(u32));
+ *val = le32_to_cpu(*val);
+
+ return 0;
+}
+
+static int btmtk_usb_write_register(struct btmtk_dev *bdev, u32 reg, u32 val)
+{
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ int ret = -1;
+ __le16 reg_high;
+ __le16 reg_low;
+ u8 buf[4];
+
+ reg_high = ((reg >> 16) & 0xffff);
+ reg_low = (reg & 0xffff);
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = (val & 0x00ff);
+ buf[3] = ((val >> 8) & 0x00ff);
+
+ memcpy(cif_dev->o_usb_buf, buf, sizeof(buf));
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x66, /* bRequest */
+ 0x40, /* bRequestType */
+ reg_high, /* wValue */
+ reg_low, /* wIndex */
+ cif_dev->o_usb_buf,
+ sizeof(buf), USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ val = 0xffffffff;
+ BTMTK_ERR("%s: error(%d), reg=%x, value=%x", __func__, ret, reg, val);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void btmtk_cif_load_rom_patch_complete(struct urb *urb)
+{
+ struct completion *sent_to_mcu_done = (struct completion *)urb->context;
+
+ complete(sent_to_mcu_done);
+}
+
+int btmtk_cif_send_control_out(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry)
+{
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int ret = 0;
+ unsigned int ifnum_base;
+
+ if (!bdev || !bdev->hdev || !bdev->io_buf || !skb ||
+ skb->len > HCI_MAX_COMMAND_SIZE || skb->len <= 0) {
+ BTMTK_ERR("%s: incorrect parameter", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev->udev || !cif_dev->o_usb_buf) {
+ BTMTK_ERR("%s: cif_dev is invalid", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ memcpy(cif_dev->o_usb_buf, skb->data + 1, skb->len - 1);
+ ret = usb_control_msg(cif_dev->udev, usb_sndctrlpipe(cif_dev->udev, 0),
+ 0x01, DEVICE_CLASS_REQUEST_OUT, 0x30, 0x00,
+ (void *)cif_dev->o_usb_buf, skb->len - 1,
+ USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ BTMTK_ERR("%s: command send failed(%d)", __func__, ret);
+ goto exit;
+ }
+exit:
+ kfree_skb(skb);
+ skb = NULL;
+ return ret;
+}
+
+static int btmtk_cif_send_bulk_out(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ int ret = 0;
+ struct urb *urb;
+ unsigned int pipe;
+ struct completion sent_to_mcu_done;
+ void *buf;
+ struct btmtk_usb_dev *cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ buf = usb_alloc_coherent(cif_dev->udev, UPLOAD_PATCH_UNIT,
+ GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ init_completion(&sent_to_mcu_done);
+
+ pipe = usb_sndbulkpipe(cif_dev->udev, cif_dev->bulk_tx_ep->bEndpointAddress);
+
+ memcpy(buf, skb->data + 1, skb->len - 1);
+ usb_fill_bulk_urb(urb,
+ cif_dev->udev,
+ pipe,
+ buf,
+ skb->len - 1,
+ (usb_complete_t)btmtk_cif_load_rom_patch_complete,
+ &sent_to_mcu_done);
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ BTMTK_ERR("%s: submit urb failed (%d)", __func__, ret);
+ goto error;
+ }
+
+ if (!wait_for_completion_timeout
+ (&sent_to_mcu_done, msecs_to_jiffies(1000))) {
+ usb_kill_urb(urb);
+ BTMTK_ERR("%s: upload rom_patch timeout", __func__);
+ ret = -ETIME;
+ goto error;
+ }
+
+error:
+ usb_free_coherent(cif_dev->udev, UPLOAD_PATCH_UNIT, buf, urb->transfer_dma);
+exit:
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ skb = NULL;
+ return ret;
+}
+
+int btmtk_usb_send_cmd(struct btmtk_dev *bdev, struct sk_buff *skb,
+ int delay, int retry, int pkt_type)
+{
+ int ret = -1;
+
+ if (pkt_type == BTMTK_TX_CMD_FROM_DRV) {
+ /* handle wmt cmd from driver */
+ ret = btmtk_cif_send_control_out(bdev, skb, delay, retry);
+ } else if (pkt_type == BTMTK_TX_ACL_FROM_DRV) {
+ /* bulk out for load rom patch*/
+ ret = btmtk_cif_send_bulk_out(bdev, skb);
+ } else if (pkt_type == BTMTK_TX_PKT_FROM_HOST) {
+ /* handle hci cmd and acl pkt from host, handle hci cmd from driver */
+ ret = btusb_send_frame(bdev->hdev, skb);
+ }
+
+ return ret;
+}
+
+static int btmtk_cif_recv_evt(struct btmtk_dev *bdev, int delay, int retry)
+{
+ struct btmtk_usb_dev *cif_dev = NULL;
+ int ret = -1; /* if successful, 0 */
+ unsigned int ifnum_base;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: !bdev!\n", __func__);
+ return ret;
+ }
+
+ cif_dev = (struct btmtk_usb_dev *)bdev->cif_dev;
+ if (!cif_dev->udev || !bdev->hdev) {
+ BTMTK_ERR("%s: invalid parameters!\n", __func__);
+ return ret;
+ }
+
+ ifnum_base = cif_dev->intf->cur_altsetting->desc.bInterfaceNumber;
+get_response_again:
+ /* ms delay */
+ mdelay(delay);
+
+ /* check WMT event */
+ memset(bdev->io_buf, 0, IO_BUF_SIZE);
+ bdev->io_buf[0] = HCI_EVENT_PKT;
+ ret = usb_control_msg(cif_dev->udev, usb_rcvctrlpipe(cif_dev->udev, 0),
+ 0x01, DEVICE_VENDOR_REQUEST_IN, 0x30, 0x00,
+ bdev->io_buf + 1, HCI_USB_IO_BUF_SIZE,
+ USB_CTRL_IO_TIMO);
+
+ if (ret < 0) {
+ BTMTK_ERR("%s: event get failed(%d)", __func__, ret);
+ return ret;
+ }
+
+ if (ret > 0) {
+ BTMTK_DBG_RAW(bdev->io_buf, ret + 1, "%s OK: EVT:", __func__);
+ return ret + 1; /* return read length */
+ } else if (retry > 0) {
+ BTMTK_WARN("%s: Trying to get response... (%d)", __func__, ret);
+ retry--;
+ goto get_response_again;
+ }
+
+ BTMTK_ERR("%s NG: do not got response:(%d)", __func__, ret);
+ return -1;
+}
+
+int btmtk_usb_send_and_recv(struct btmtk_dev *bdev,
+ struct sk_buff *skb, const u8 *event,
+ const int event_len, int delay,
+ int retry, int pkt_type)
+{
+ unsigned long comp_event_timo = 0, start_time = 0;
+ int ret = 0;
+
+ if (!bdev) {
+ BTMTK_ERR("%s: !bdev!\n", __func__);
+ return ret;
+ }
+
+ if ((pkt_type == BTMTK_TX_CMD_FROM_DRV || pkt_type == BTMTK_TX_ACL_FROM_DRV)) {
+ ret = btmtk_usb_send_cmd(bdev, skb, delay, retry, pkt_type);
+ if (ret < 0) {
+ BTMTK_ERR("%s btmtk_usb_send_cmd failed!!", __func__);
+ goto exit;
+ }
+
+ if (event && event_len > 0) {
+ bdev->recv_evt_len = btmtk_cif_recv_evt(bdev, delay, retry);
+ if (bdev->recv_evt_len < 0) {
+ BTMTK_ERR("%s btmtk_cif_recv_evt failed!!", __func__);
+ ret = -1;
+ goto exit;
+ }
+
+ if (bdev->io_buf && bdev->recv_evt_len >= event_len) {
+ if (memcmp(bdev->io_buf, event, event_len) == 0) {
+ ret = 0;
+ goto exit;
+ }
+ }
+ BTMTK_INFO("%s compare fail\n", __func__);
+ ret = -1;
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (event) {
+ if (event_len > EVENT_COMPARE_SIZE) {
+ BTMTK_ERR("%s, event_len (%d) > EVENT_COMPARE_SIZE(%d), error",
+ __func__, event_len, EVENT_COMPARE_SIZE);
+ ret = -1;
+ goto exit;
+ }
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE;
+ memcpy(event_need_compare, event + 1, event_len - 1);
+ event_need_compare_len = event_len - 1;
+
+ start_time = jiffies;
+ /* check hci event /wmt event for SDIO/UART interface, check hci
+ * event for USB interface
+ */
+ comp_event_timo = jiffies + msecs_to_jiffies(COMP_EVENT_TIMO);
+ BTMTK_INFO("event_need_compare_len %d, event_compare_status %d",
+ event_need_compare_len, event_compare_status);
+ } else {
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ }
+
+ ret = btmtk_usb_send_cmd(bdev, skb, delay, retry, pkt_type);
+ if (ret < 0) {
+ BTMTK_ERR("%s btmtk_sdio_send_cmd failed!!", __func__);
+ goto exit;
+ }
+
+ do {
+ /* check if event_compare_success */
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS) {
+ ret = 0;
+ break;
+ }
+ usleep_range(10, 100);
+ } while (time_before(jiffies, comp_event_timo));
+
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE;
+ }
+
+exit:
+ return ret;
+}
+
+int btmtk_usb_event_filter(struct btmtk_dev *bdev, struct sk_buff *skb)
+{
+ if (event_compare_status == BTMTK_EVENT_COMPARE_STATE_NEED_COMPARE &&
+ skb->len >= event_need_compare_len) {
+ if (memcmp(skb->data, event_need_compare, event_need_compare_len) == 0) {
+ event_compare_status = BTMTK_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
+ } else {
+ BTMTK_INFO_RAW(event_need_compare, event_need_compare_len,
+ "%s: event_need_compare:", __func__);
+ return 0;
+ }
+
+ return 1;
+ }
+ return 0;
+}