diff mbox series

[v2,3/3] Bluetooth: btusb: mediatek: add MediaTek ISO data transmission function

Message ID 20240529062946.5655-4-chris.lu@mediatek.com
State Superseded
Headers show
Series Bluetooth: btusb: MediaTek ISO data transmission support | expand

Commit Message

Chris Lu (陸稚泓) May 29, 2024, 6:29 a.m. UTC
This patch implement function for ISO data send and receive in btusb
driver for MediaTek Controller.

MediaTek define a specific interrupt endpoint for ISO data
transmission because the characteristics of interrupt are
similar to the application of ISO data which can ensure bandwidth,
has enough data length and support error check.

Driver setup ISO interface in btusb_mtk_setup after download patch and
submit interrtupt urb to handle ISO data send and receive.

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/bluetooth/btmtk.c |  35 +++++
 drivers/bluetooth/btmtk.h |  23 +++
 drivers/bluetooth/btusb.c | 295 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 352 insertions(+), 1 deletion(-)

Comments

Chris Lu (陸稚泓) May 29, 2024, 11:52 a.m. UTC | #1
Dear Paul,

Thanks for your suggestion. I'll correct those typo and add more
descriptions in v3.

On Wed, 2024-05-29 at 09:10 +0200, Paul Menzel wrote:
>  	 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>  Dear Chris,
> 
> 
> Thank you for your patch. Some minor comments. It’d be great if you 
> started the description with a motivation for the patch, that means, 
> what problem is going to be solved?
> 
> Am 29.05.24 um 08:29 schrieb Chris Lu:
> > This patch implement function for ISO data send and receive in
> btusb
> 
> implement*s*
> 
> I’d recommend to use imperative moot though: Implement function
> 
> Would functionality or feature be more accurate?
> 
> > driver for MediaTek Controller.
> > 
> > MediaTek define a specific interrupt endpoint for ISO data
> 
> MediaTek devices …
> 
> All of them?

Previous generation(MT7663/7668) are not able to support ISO
transmission. For MT79xx generation, support will be determined by
vendor command in driver. Currently, We're focusing on MT7922, the rest
MT79XX series will support ISO data in the future.

> 
> > transmission because the characteristics of interrupt are
> > similar to the application of ISO data which can ensure bandwidth,
> > has enough data length and support error check.
> 
> What do you mean by “ensure bandwidth”?

Interrup and isochronous are scheduled to provide guaranteed bandwidth.
I'll update the description to avoid misunderstanding.

> 
> > Driver setup ISO interface in btusb_mtk_setup after download patch
> and
> > submit interrtupt urb to handle ISO data send and receive.
> 
> 1.  Driver sets up interface
> 2.  download*ing*
> 3.  interrupt
> 4.  submit*s*
> 
> Please elaborate, how you tested this.

Google ChromeOS project provides an environment that supports ISO data
send and receive. MediaTek use such environment to develop and verify
ISO data transmission between BT host and driver/controller.
Currently, we've verified create connection, send/receive ISO data
works as expected with ear buds which support LE audio feature.

> 
> > Signed-off-by: Chris Lu <chris.lu@mediatek.com>
> > Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> > ---
> >   drivers/bluetooth/btmtk.c |  35 +++++
> >   drivers/bluetooth/btmtk.h |  23 +++
> >   drivers/bluetooth/btusb.c | 295
> +++++++++++++++++++++++++++++++++++++-
> >   3 files changed, 352 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> > index a27c251bf56e..f0aecd319911 100644
> > --- a/drivers/bluetooth/btmtk.c
> > +++ b/drivers/bluetooth/btmtk.c
> > @@ -4,6 +4,7 @@
> >    */
> >   #include <linux/module.h>
> >   #include <linux/firmware.h>
> > +#include <linux/usb.h>
> >   
> >   #include <net/bluetooth/bluetooth.h>
> >   #include <net/bluetooth/hci_core.h>
> > @@ -19,6 +20,9 @@
> >   #define MTK_SEC_MAP_COMMON_SIZE12
> >   #define MTK_SEC_MAP_NEED_SEND_SIZE52
> >   
> > +/* It is for mt79xx iso data transmission setting */
> 
> Just: For mt79xx iso data transmission setting
> 
> Maybe reference some section from the data sheet? Will future
> devices 
> support it?
> 

Currently, btusb driver in kernel supports MT7961, 7922, 7925. We're
focusing on the development of MT7922 for LE audio feature, the rest
MT79XX series will support ISO data in the future. The driver ISO setup
flow should be the same for all MT79xx series(including chip in
future).

> > +#define MTK_ISO_THRESHOLD264
> > +
> >   struct btmtk_patch_header {
> >   u8 datetime[16];
> >   u8 platform[4];
> > @@ -431,6 +435,37 @@ int btmtk_process_coredump(struct hci_dev
> *hdev, struct sk_buff *skb)
> >   }
> >   EXPORT_SYMBOL_GPL(btmtk_process_coredump);
> >   
> > +int btmtk_isointf_setup(struct hci_dev *hdev)
> > +{
> > +u8 iso_param[2] = { 0x08, 0x01 };
> > +struct sk_buff *skb;
> > +
> > +skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), iso_param,
> > +     HCI_INIT_TIMEOUT);
> > +if (IS_ERR(skb)) {
> > +bt_dev_err(hdev, "Failed to apply iso setting (%ld)",
> PTR_ERR(skb));
> > +return PTR_ERR(skb);
> > +}
> > +kfree_skb(skb);
> > +
> > +return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(btmtk_isointf_setup);
> > +
> > +int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb)
> > +{
> > +if (skb->len > MTK_ISO_THRESHOLD)
> > +return -EINVAL;
> > +
> > +if (skb_pad(skb, MTK_ISO_THRESHOLD - skb->len))
> > +return -ENOMEM;
> > +
> > +__skb_put(skb, MTK_ISO_THRESHOLD - skb->len);
> > +
> > +return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(btmtk_isopkt_pad);
> > +
> >   MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
> >   MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
> >   MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver "
> VERSION);
> > diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> > index 6a0697a22b16..afc914e921dd 100644
> > --- a/drivers/bluetooth/btmtk.h
> > +++ b/drivers/bluetooth/btmtk.h
> > @@ -129,6 +129,8 @@ struct btmtk_hci_wmt_params {
> >   typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
> >   
> >   enum {
> > +BTMTK_ISOPKT_OVER_INTR,
> > +
> >   __BTMTK_NUM_FLAGS,
> >   };
> >   
> > @@ -139,12 +141,19 @@ struct btmtk_coredump_info {
> >   int state;
> >   };
> >   
> > +struct btmtk_isopkt_info {
> > +struct usb_interface *isopkt_intf;
> > +struct usb_endpoint_descriptor *isopkt_tx_ep;
> > +struct usb_endpoint_descriptor *isopkt_rx_ep;
> > +};
> > +
> >   struct btmediatek_data {
> >   DECLARE_BITMAP(flags, __BTMTK_NUM_FLAGS);
> >   
> >   u32 dev_id;
> >   btmtk_reset_sync_func_t reset_sync;
> >   struct btmtk_coredump_info cd_info;
> > +struct btmtk_isopkt_info isopkt_info;
> >   };
> >   
> >   #define btmtk_set_flag(hdev, nr)\
> > @@ -186,6 +195,10 @@ int btmtk_process_coredump(struct hci_dev
> *hdev, struct sk_buff *skb);
> >   
> >   void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id,
> u32 fw_ver,
> >      u32 fw_flavor);
> > +
> > +int btmtk_isointf_setup(struct hci_dev *hdev);
> > +
> > +int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb);
> >   #else
> >   
> >   static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
> > @@ -225,4 +238,14 @@ static void btmtk_fw_get_filename(char *buf,
> size_t size, u32 dev_id,
> >     u32 fw_ver, u32 fw_flavor)
> >   {
> >   }
> > +
> > +static int btmtk_isointf_setup(struct hci_dev *hdev)
> > +{
> > +return -EOPNOTSUPP;
> > +}
> > +
> > +static int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff
> *skb)
> > +{
> > +return -EOPNOTSUPP;
> > +}
> >   #endif
> > diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> > index 79aefdb3324d..592be71a7c45 100644
> > --- a/drivers/bluetooth/btusb.c
> > +++ b/drivers/bluetooth/btusb.c
> > @@ -831,6 +831,7 @@ struct qca_dump_info {
> >   #define BTUSB_USE_ALT3_FOR_WBS15
> >   #define BTUSB_ALT6_CONTINUOUS_TX16
> >   #define BTUSB_HW_SSR_ACTIVE17
> > +#define BTUSB_ISOPKT_RUNNING18
> >   
> >   struct btusb_data {
> >   struct hci_dev       *hdev;
> > @@ -860,11 +861,13 @@ struct btusb_data {
> >   struct usb_anchor isoc_anchor;
> >   struct usb_anchor diag_anchor;
> >   struct usb_anchor ctrl_anchor;
> > +struct usb_anchor isopkt_anchor;
> >   spinlock_t rxlock;
> >   
> >   struct sk_buff *evt_skb;
> >   struct sk_buff *acl_skb;
> >   struct sk_buff *sco_skb;
> > +struct sk_buff *isopkt_skb;
> >   
> >   struct usb_endpoint_descriptor *intr_ep;
> >   struct usb_endpoint_descriptor *bulk_tx_ep;
> > @@ -1099,6 +1102,9 @@ static inline void btusb_free_frags(struct
> btusb_data *data)
> >   dev_kfree_skb_irq(data->sco_skb);
> >   data->sco_skb = NULL;
> >   
> > +dev_kfree_skb_irq(data->isopkt_skb);
> > +data->isopkt_skb = NULL;
> > +
> >   spin_unlock_irqrestore(&data->rxlock, flags);
> >   }
> >   
> > @@ -1327,6 +1333,64 @@ static int btusb_recv_isoc(struct btusb_data
> *data, void *buffer, int count)
> >   return err;
> >   }
> >   
> > +static int btusb_recv_isopkt(struct btusb_data *data, void
> *buffer, int count)
> 
> Make count `size_t` or `unsigned int`? Though the other function do
> use 
> `int`, so ignore.
> 
> > +{
> > +struct sk_buff *skb;
> > +unsigned long flags;
> > +int err = 0;
> > +
> > +spin_lock_irqsave(&data->rxlock, flags);
> > +skb = data->isopkt_skb;
> > +
> > +while (count) {
> > +int len;
> > +
> > +if (!skb) {
> > +skb = bt_skb_alloc(HCI_MAX_ISO_SIZE, GFP_ATOMIC);
> > +if (!skb) {
> > +err = -ENOMEM;
> > +break;
> > +}
> > +
> > +hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
> > +hci_skb_expect(skb) = HCI_ISO_HDR_SIZE;
> > +}
> > +
> > +len = min_t(uint, hci_skb_expect(skb), count);
> > +skb_put_data(skb, buffer, len);
> > +
> > +count -= len;
> > +buffer += len;
> > +hci_skb_expect(skb) -= len;
> > +
> > +if (skb->len == HCI_ISO_HDR_SIZE) {
> > +__le16 dlen = hci_iso_hdr(skb)->dlen;
> > +
> > +/* Complete ISO header */
> > +hci_skb_expect(skb) = __le16_to_cpu(dlen);
> > +
> > +if (skb_tailroom(skb) < hci_skb_expect(skb)) {
> > +kfree_skb(skb);
> > +skb = NULL;
> > +
> > +err = -EILSEQ;
> > +break;
> > +}
> > +}
> > +
> > +if (!hci_skb_expect(skb)) {
> > +/* Complete frame */
> > +hci_recv_frame(data->hdev, skb);
> > +skb = NULL;
> > +}
> > +}
> > +
> > +data->isopkt_skb = skb;
> > +spin_unlock_irqrestore(&data->rxlock, flags);
> > +
> > +return err;
> > +}
> > +
> >   static void btusb_intr_complete(struct urb *urb)
> >   {
> >   struct hci_dev *hdev = urb->context;
> > @@ -1784,6 +1848,101 @@ static int btusb_submit_diag_urb(struct
> hci_dev *hdev, gfp_t mem_flags)
> >   return err;
> >   }
> >   
> > +static void btusb_mtk_intr_complete(struct urb *urb)
> > +{
> > +struct hci_dev *hdev = urb->context;
> > +struct btusb_data *data = hci_get_drvdata(hdev);
> > +int err;
> > +
> > +BT_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) {
> > +hdev->stat.byte_rx += urb->actual_length;
> > +
> > +if (btusb_recv_isopkt(data, urb->transfer_buffer,
> > +      urb->actual_length) < 0) {
> > +bt_dev_err(hdev, "corrupted iso packet");
> > +hdev->stat.err_rx++;
> > +}
> > +} else if (urb->status == -ENOENT) {
> > +/* Avoid suspend failed when usb_kill_urb */
> 
> Could you please elaborate?

This part references other urb complete function.
btusb_suspend stop all traffic, judge the urb status and return to
prevent data from waking up the host.

> 
> > +return;
> > +}
> > +
> > +if (!test_bit(BTUSB_ISOPKT_RUNNING, &data->flags))
> > +return;
> > +
> > +usb_mark_last_busy(data->udev);
> > +usb_anchor_urb(urb, &data->isopkt_anchor);
> > +
> > +err = usb_submit_urb(urb, GFP_ATOMIC);
> > +if (err < 0) {
> > +/* -EPERM: urb is being killed;
> > + * -ENODEV: device got disconnected
> > + */
> > +if (err != -EPERM && err != -ENODEV)
> > +bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
> > +   urb, -err);
> > +if (err != -EPERM)
> > +hci_cmd_sync_cancel(hdev, -err);
> > +usb_unanchor_urb(urb);
> > +}
> > +}
> > +
> > +static int btusb_mtk_submit_intr_urb(struct hci_dev *hdev, gfp_t
> mem_flags)
> > +{
> > +struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
> > +struct btusb_data *data = hci_get_drvdata(hdev);
> > +unsigned char *buf;
> > +unsigned int pipe;
> > +struct urb *urb;
> > +int err, size;
> > +
> > +BT_DBG("%s", hdev->name);
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_rx_ep)
> > +return -ENODEV;
> > +
> > +urb = usb_alloc_urb(0, mem_flags);
> > +if (!urb)
> > +return -ENOMEM;
> > +size = le16_to_cpu(btmtk_data->isopkt_info.isopkt_rx_ep-
> >wMaxPacketSize);
> > +
> > +buf = kmalloc(size, mem_flags);
> > +if (!buf) {
> > +usb_free_urb(urb);
> > +return -ENOMEM;
> > +}
> > +
> > +pipe = usb_rcvintpipe(data->udev,
> > +      btmtk_data->isopkt_info.isopkt_rx_ep->bEndpointAddress);
> > +
> > +usb_fill_int_urb(urb, data->udev, pipe, buf, size,
> > + btusb_mtk_intr_complete, hdev,
> > + btmtk_data->isopkt_info.isopkt_rx_ep->bInterval);
> > +
> > +urb->transfer_flags |= URB_FREE_BUFFER;
> > +
> > +usb_mark_last_busy(data->udev);
> > +usb_anchor_urb(urb, &data->isopkt_anchor);
> > +
> > +err = usb_submit_urb(urb, mem_flags);
> > +if (err < 0) {
> > +if (err != -EPERM && err != -ENODEV)
> > +bt_dev_err(hdev, "urb %p submission failed (%d)",
> > +   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;
> > @@ -1898,6 +2057,7 @@ static void btusb_stop_traffic(struct
> btusb_data *data)
> >   usb_kill_anchored_urbs(&data->isoc_anchor);
> >   usb_kill_anchored_urbs(&data->diag_anchor);
> >   usb_kill_anchored_urbs(&data->ctrl_anchor);
> > +usb_kill_anchored_urbs(&data->isopkt_anchor);
> >   }
> >   
> >   static int btusb_close(struct hci_dev *hdev)
> > @@ -1917,6 +2077,7 @@ static int btusb_close(struct hci_dev *hdev)
> >   clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> >   clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> >   clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
> > +clear_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
> >   
> >   btusb_stop_traffic(data);
> >   btusb_free_frags(data);
> > @@ -2043,6 +2204,81 @@ static struct urb *alloc_isoc_urb(struct
> hci_dev *hdev, struct sk_buff *skb)
> >   return urb;
> >   }
> >   
> > +static inline int __set_mtk_intr_interface(struct hci_dev *hdev,
> unsigned int ifnum)
> > +{
> > +struct btusb_data *data = hci_get_drvdata(hdev);
> > +struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
> > +struct usb_interface *intf = btmtk_data->isopkt_info.isopkt_intf;
> > +int i, err;
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_intf)
> > +return -ENODEV;
> > +
> > +err = usb_set_interface(data->udev, ifnum, 1);
> > +if (err < 0) {
> > +bt_dev_err(hdev, "setting interface failed (%d)", -err);
> > +return err;
> > +}
> > +
> > +btmtk_data->isopkt_info.isopkt_tx_ep = NULL;
> > +btmtk_data->isopkt_info.isopkt_rx_ep = NULL;
> > +
> > +for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
> > +struct usb_endpoint_descriptor *ep_desc;
> > +
> > +ep_desc = &intf->cur_altsetting->endpoint[i].desc;
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_tx_ep &&
> > +    usb_endpoint_is_int_out(ep_desc)) {
> > +btmtk_data->isopkt_info.isopkt_tx_ep = ep_desc;
> > +continue;
> > +}
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_rx_ep &&
> > +    usb_endpoint_is_int_in(ep_desc)) {
> > +btmtk_data->isopkt_info.isopkt_rx_ep = ep_desc;
> > +continue;
> > +}
> > +}
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_tx_ep ||
> > +    !btmtk_data->isopkt_info.isopkt_rx_ep) {
> > +bt_dev_err(hdev, "invalid interrupt descriptors");
> > +return -ENODEV;
> > +}
> > +
> > +return 0;
> > +}
> > +
> > +static struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct
> sk_buff *skb)
> > +{
> > +struct btusb_data *data = hci_get_drvdata(hdev);
> > +struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
> > +struct urb *urb;
> > +unsigned int pipe;
> > +
> > +if (!btmtk_data->isopkt_info.isopkt_tx_ep)
> > +return ERR_PTR(-ENODEV);
> > +
> > +urb = usb_alloc_urb(0, GFP_KERNEL);
> > +if (!urb)
> > +return ERR_PTR(-ENOMEM);
> > +
> > +if (btmtk_isopkt_pad(hdev, skb))
> > +return ERR_PTR(-EINVAL);
> > +
> > +pipe = usb_sndintpipe(data->udev,
> > +      btmtk_data->isopkt_info.isopkt_tx_ep->bEndpointAddress);
> > +
> > +usb_fill_int_urb(urb, data->udev, pipe,
> > + skb->data, skb->len, btusb_tx_complete,
> > + skb, btmtk_data->isopkt_info.isopkt_tx_ep->bInterval);
> > +
> > +skb->dev = (void *)hdev;
> > +
> > +return urb;
> > +}
> > +
> >   static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
> >   {
> >   struct btusb_data *data = hci_get_drvdata(hdev);
> > @@ -2122,7 +2358,10 @@ static int btusb_send_frame(struct hci_dev
> *hdev, struct sk_buff *skb)
> >   return submit_tx_urb(hdev, urb);
> >   
> >   case HCI_ISODATA_PKT:
> > -urb = alloc_bulk_urb(hdev, skb);
> > +if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR))
> > +urb = alloc_mtk_intr_urb(hdev, skb);
> > +else
> > +urb = alloc_bulk_urb(hdev, skb);
> >   if (IS_ERR(urb))
> >   return PTR_ERR(urb);
> >   
> > @@ -2650,6 +2889,8 @@ static int btusb_recv_event_realtek(struct
> hci_dev *hdev, struct sk_buff *skb)
> >   #define MTK_BT_RESET_REG_CONNV30x70028610
> >   #define MTK_BT_READ_DEV_ID0x70010200
> >   
> > +/* MediaTek ISO interface number */
> > +#define MTK_ISO_IFNUM2
> >   
> >   static void btusb_mtk_wmt_recv(struct urb *urb)
> >   {
> > @@ -3126,6 +3367,28 @@ static int btusb_mtk_reset(struct hci_dev
> *hdev, void *rst_data)
> >   return err;
> >   }
> >   
> > +static int btusb_mtk_claim_iso_intf(struct btusb_data *data,
> struct usb_interface *intf)
> > +{
> > +int err;
> > +
> > +err = usb_driver_claim_interface(&btusb_driver, intf, data);
> > +if (err < 0)
> > +return err;
> > +
> > +__set_mtk_intr_interface(data->hdev, MTK_ISO_IFNUM);
> > +
> > +err = btusb_mtk_submit_intr_urb(data->hdev, GFP_KERNEL);
> > +if (err < 0) {
> > +usb_kill_anchored_urbs(&data->isopkt_anchor);
> > +bt_dev_err(data->hdev, "ISO intf not support (%d)", err);
> > +return err;
> > +}
> > +
> > +btmtk_set_flag(data->hdev, BTMTK_ISOPKT_OVER_INTR);
> > +
> > +return 0;
> > +}
> > +
> >   static int btusb_mtk_setup(struct hci_dev *hdev)
> >   {
> >   struct btusb_data *data = hci_get_drvdata(hdev);
> > @@ -3210,6 +3473,12 @@ static int btusb_mtk_setup(struct hci_dev
> *hdev)
> >   /* It's Device EndPoint Reset Option Register */
> >   btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT,
> MTK_EP_RST_IN_OUT_OPT);
> >   
> > +/* Claim USB interface and EndPoint for ISO data */
> > +mediatek->isopkt_info.isopkt_intf = usb_ifnum_to_if(data->udev,
> MTK_ISO_IFNUM);
> > +err = btusb_mtk_claim_iso_intf(data, mediatek-
> >isopkt_info.isopkt_intf);
> > +if (err < 0)
> > +mediatek->isopkt_info.isopkt_intf = NULL;
> > +
> >   /* Enable Bluetooth protocol */
> >   param = 1;
> >   wmt_params.op = BTMTK_WMT_FUNC_CTRL;
> > @@ -3226,6 +3495,13 @@ static int btusb_mtk_setup(struct hci_dev
> *hdev)
> >   
> >   hci_set_msft_opcode(hdev, 0xFD30);
> >   hci_set_aosp_capable(hdev);
> > +
> > +/* Setup ISO interface after protocol enabled */
> 
> The verb is spelled with a space: Set up.
> 
> > +if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {
> > +btmtk_isointf_setup(hdev);
> > +set_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
> > +}
> > +
> >   goto done;
> >   default:
> >   bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
> > @@ -4347,6 +4623,7 @@ static int btusb_probe(struct usb_interface
> *intf,
> >   init_usb_anchor(&data->isoc_anchor);
> >   init_usb_anchor(&data->diag_anchor);
> >   init_usb_anchor(&data->ctrl_anchor);
> > +init_usb_anchor(&data->isopkt_anchor);
> >   spin_lock_init(&data->rxlock);
> >   
> >   priv_size = 0;
> > @@ -4663,6 +4940,17 @@ static void btusb_disconnect(struct
> usb_interface *intf)
> >   if (data->diag)
> >   usb_set_intfdata(data->diag, NULL);
> >   
> > +if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {
> > +struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
> > +
> > +if (btmtk_data->isopkt_info.isopkt_intf) {
> > +usb_set_intfdata(btmtk_data->isopkt_info.isopkt_intf, NULL);
> > +usb_driver_release_interface(&btusb_driver,
> > +     btmtk_data->isopkt_info.isopkt_intf);
> > +}
> > +btmtk_clear_flag(hdev, BTMTK_ISOPKT_OVER_INTR);
> > +}
> > +
> >   hci_unregister_dev(hdev);
> >   
> >   if (intf == data->intf) {
> > @@ -4818,6 +5106,11 @@ static int btusb_resume(struct usb_interface
> *intf)
> >   btusb_submit_isoc_urb(hdev, GFP_NOIO);
> >   }
> >   
> > +if (test_bit(BTUSB_ISOPKT_RUNNING, &data->flags)) {
> > +if (btusb_mtk_submit_intr_urb(hdev, GFP_NOIO) < 0)
> > +clear_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
> > +}
> > +
> >   spin_lock_irq(&data->txlock);
> >   play_deferred(data);
> >   clear_bit(BTUSB_SUSPENDING, &data->flags);
> 
> 
> Kind regards,
> 
> 

Thanks a lot,

Chris Lu
> Paul
Pauli Virtanen May 29, 2024, 3:39 p.m. UTC | #2
Hi,

ke, 2024-05-29 kello 14:29 +0800, Chris Lu kirjoitti:
> This patch implement function for ISO data send and receive in btusb
> driver for MediaTek Controller.
> 
> MediaTek define a specific interrupt endpoint for ISO data
> transmission because the characteristics of interrupt are
> similar to the application of ISO data which can ensure bandwidth,
> has enough data length and support error check.
> 
> Driver setup ISO interface in btusb_mtk_setup after download patch and
> submit interrtupt urb to handle ISO data send and receive.
> 
> Signed-off-by: Chris Lu <chris.lu@mediatek.com>
> Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> ---
>  drivers/bluetooth/btmtk.c |  35 +++++
>  drivers/bluetooth/btmtk.h |  23 +++
>  drivers/bluetooth/btusb.c | 295 +++++++++++++++++++++++++++++++++++++-
>  3 files changed, 352 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> index a27c251bf56e..f0aecd319911 100644
> --- a/drivers/bluetooth/btmtk.c
> +++ b/drivers/bluetooth/btmtk.c
[clip]
> @@ -2122,7 +2358,10 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
>  		return submit_tx_urb(hdev, urb);
>  
>  	case HCI_ISODATA_PKT:
> -		urb = alloc_bulk_urb(hdev, skb);
> +		if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR))

The btmtk flag macros require hci_get_priv(hdev) contains struct
btmediatek_data.

Here this is code for generic hdev, so probably misbehaves with non-
mediatek hdev.

> +			urb = alloc_mtk_intr_urb(hdev, skb);
> +		else
> +			urb = alloc_bulk_urb(hdev, skb);
>  		if (IS_ERR(urb))
>  			return PTR_ERR(urb);
>  
> @@ -2650,6 +2889,8 @@ static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
>  #define MTK_BT_RESET_REG_CONNV3	0x70028610
>  #define MTK_BT_READ_DEV_ID	0x70010200
>  
> +/* MediaTek ISO interface number */
> +#define MTK_ISO_IFNUM		2
>  
>  static void btusb_mtk_wmt_recv(struct urb *urb)
>  {
> @@ -3126,6 +3367,28 @@ static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
>  	return err;
>  }
>  
> +static int btusb_mtk_claim_iso_intf(struct btusb_data *data, struct usb_interface *intf)
> +{
> +	int err;
> +
> +	err = usb_driver_claim_interface(&btusb_driver, intf, data);
> +	if (err < 0)
> +		return err;
> +
> +	__set_mtk_intr_interface(data->hdev, MTK_ISO_IFNUM);
> +
> +	err = btusb_mtk_submit_intr_urb(data->hdev, GFP_KERNEL);
> +	if (err < 0) {
> +		usb_kill_anchored_urbs(&data->isopkt_anchor);
> +		bt_dev_err(data->hdev, "ISO intf not support (%d)", err);
> +		return err;
> +	}
> +
> +	btmtk_set_flag(data->hdev, BTMTK_ISOPKT_OVER_INTR);
> +
> +	return 0;
> +}
> +
>  static int btusb_mtk_setup(struct hci_dev *hdev)
>  {
>  	struct btusb_data *data = hci_get_drvdata(hdev);
> @@ -3210,6 +3473,12 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
>  		/* It's Device EndPoint Reset Option Register */
>  		btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
>  
> +		/* Claim USB interface and EndPoint for ISO data */
> +		mediatek->isopkt_info.isopkt_intf = usb_ifnum_to_if(data->udev, MTK_ISO_IFNUM);
> +		err = btusb_mtk_claim_iso_intf(data, mediatek->isopkt_info.isopkt_intf);
> +		if (err < 0)
> +			mediatek->isopkt_info.isopkt_intf = NULL;
> +
>  		/* Enable Bluetooth protocol */
>  		param = 1;
>  		wmt_params.op = BTMTK_WMT_FUNC_CTRL;
> @@ -3226,6 +3495,13 @@ static int btusb_mtk_setup(struct hci_dev *hdev)
>  
>  		hci_set_msft_opcode(hdev, 0xFD30);
>  		hci_set_aosp_capable(hdev);
> +
> +		/* Setup ISO interface after protocol enabled */
> +		if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {
> +			btmtk_isointf_setup(hdev);
> +			set_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
> +		}
> +
>  		goto done;
>  	default:
>  		bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
> @@ -4347,6 +4623,7 @@ static int btusb_probe(struct usb_interface *intf,
>  	init_usb_anchor(&data->isoc_anchor);
>  	init_usb_anchor(&data->diag_anchor);
>  	init_usb_anchor(&data->ctrl_anchor);
> +	init_usb_anchor(&data->isopkt_anchor);
>  	spin_lock_init(&data->rxlock);
>  
>  	priv_size = 0;
> @@ -4663,6 +4940,17 @@ static void btusb_disconnect(struct usb_interface *intf)
>  	if (data->diag)
>  		usb_set_intfdata(data->diag, NULL);
>  
> +	if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {

Same here, possibly also elsewhere.

> +		struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
> +
> +		if (btmtk_data->isopkt_info.isopkt_intf) {
> +			usb_set_intfdata(btmtk_data->isopkt_info.isopkt_intf, NULL);
> +			usb_driver_release_interface(&btusb_driver,
> +						     btmtk_data->isopkt_info.isopkt_intf);
> +		}
> +		btmtk_clear_flag(hdev, BTMTK_ISOPKT_OVER_INTR);
> +	}
> +
>  	hci_unregister_dev(hdev);
>  
>  	if (intf == data->intf) {
> @@ -4818,6 +5106,11 @@ static int btusb_resume(struct usb_interface *intf)
>  			btusb_submit_isoc_urb(hdev, GFP_NOIO);
>  	}
>  
> +	if (test_bit(BTUSB_ISOPKT_RUNNING, &data->flags)) {
> +		if (btusb_mtk_submit_intr_urb(hdev, GFP_NOIO) < 0)
> +			clear_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
> +	}
> +
>  	spin_lock_irq(&data->txlock);
>  	play_deferred(data);
>  	clear_bit(BTUSB_SUSPENDING, &data->flags);
kernel test robot May 29, 2024, 11:09 p.m. UTC | #3
Hi Chris,

kernel test robot noticed the following build errors:

[auto build test ERROR on bluetooth-next/master]
[also build test ERROR on next-20240529]
[cannot apply to bluetooth/master linus/master v6.10-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Chris-Lu/Bluetooth-net-add-hci_iso_hdr-function-for-iso-data/20240529-143216
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
patch link:    https://lore.kernel.org/r/20240529062946.5655-4-chris.lu%40mediatek.com
patch subject: [PATCH v2 3/3] Bluetooth: btusb: mediatek: add MediaTek ISO data transmission function
config: i386-randconfig-014-20240530 (https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/config)
compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202405300602.AUh9Yu96-lkp@intel.com/

All errors (new ones prefixed by >>):

>> ld.lld: error: undefined symbol: btmtk_isopkt_pad
   >>> referenced by btusb.c:2267 (drivers/bluetooth/btusb.c:2267)
   >>>               drivers/bluetooth/btusb.o:(btusb_send_frame) in archive vmlinux.a
Luiz Augusto von Dentz June 5, 2024, 6:06 p.m. UTC | #4
Hi Chris,

On Wed, May 29, 2024 at 7:10 PM kernel test robot <lkp@intel.com> wrote:
>
> Hi Chris,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on bluetooth-next/master]
> [also build test ERROR on next-20240529]
> [cannot apply to bluetooth/master linus/master v6.10-rc1]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Chris-Lu/Bluetooth-net-add-hci_iso_hdr-function-for-iso-data/20240529-143216
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
> patch link:    https://lore.kernel.org/r/20240529062946.5655-4-chris.lu%40mediatek.com
> patch subject: [PATCH v2 3/3] Bluetooth: btusb: mediatek: add MediaTek ISO data transmission function
> config: i386-randconfig-014-20240530 (https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/config)
> compiler: clang version 18.1.5 (https://github.com/llvm/llvm-project 617a15a9eac96088ae5e9134248d8236e34b91b1)
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202405300602.AUh9Yu96-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
> >> ld.lld: error: undefined symbol: btmtk_isopkt_pad
>    >>> referenced by btusb.c:2267 (drivers/bluetooth/btusb.c:2267)
>    >>>               drivers/bluetooth/btusb.o:(btusb_send_frame) in archive vmlinux.a

Please have the above fixed and while at move the vendor specific
handling of the new endpoint to btmtk.c since that is not standard it
shall not be part of btusb.c
Chris Lu (陸稚泓) June 6, 2024, 3:01 a.m. UTC | #5
Hi Luiz,

Thanks for your reminding, I will fix the error and follow your
suggestion in last mail about moving some vendor-specific change from
btusb.c to btmtk.c to minimize the change in btusb.c.
I'll submit another version soon.

Thanks,
Chris.

On Wed, 2024-06-05 at 14:06 -0400, Luiz Augusto von Dentz wrote:
>  	 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
>  Hi Chris,
> 
> On Wed, May 29, 2024 at 7:10 PM kernel test robot <lkp@intel.com>
> wrote:
> >
> > Hi Chris,
> >
> > kernel test robot noticed the following build errors:
> >
> > [auto build test ERROR on bluetooth-next/master]
> > [also build test ERROR on next-20240529]
> > [cannot apply to bluetooth/master linus/master v6.10-rc1]
> > [If your patch is applied to the wrong git tree, kindly drop us a
> note.
> > And when submitting patch, we suggest to use '--base' as documented
> in
> > https://git-scm.com/docs/git-format-patch#_base_tree_information]
> >
> > url:    
> https://github.com/intel-lab-lkp/linux/commits/Chris-Lu/Bluetooth-net-add-hci_iso_hdr-function-for-iso-data/20240529-143216
> > base:   
> https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
>  master
> > patch link:    
> https://lore.kernel.org/r/20240529062946.5655-4-chris.lu%40mediatek.com
> > patch subject: [PATCH v2 3/3] Bluetooth: btusb: mediatek: add
> MediaTek ISO data transmission function
> > config: i386-randconfig-014-20240530 (
> https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/config
> )
> > compiler: clang version 18.1.5 (
> https://github.com/llvm/llvm-project
> 617a15a9eac96088ae5e9134248d8236e34b91b1)
> > reproduce (this is a W=1 build): (
> https://download.01.org/0day-ci/archive/20240530/202405300602.AUh9Yu96-lkp@intel.com/reproduce
> )
> >
> > If you fix the issue in a separate patch/commit (i.e. not just a
> new version of
> > the same patch/commit), kindly add following tags
> > | Reported-by: kernel test robot <lkp@intel.com>
> > | Closes: 
> https://lore.kernel.org/oe-kbuild-all/202405300602.AUh9Yu96-lkp@intel.com/
> >
> > All errors (new ones prefixed by >>):
> >
> > >> ld.lld: error: undefined symbol: btmtk_isopkt_pad
> >    >>> referenced by btusb.c:2267 (drivers/bluetooth/btusb.c:2267)
> >    >>>               drivers/bluetooth/btusb.o:(btusb_send_frame)
> in archive vmlinux.a
> 
> Please have the above fixed and while at move the vendor specific
> handling of the new endpoint to btmtk.c since that is not standard it
> shall not be part of btusb.c
> 
>
diff mbox series

Patch

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index a27c251bf56e..f0aecd319911 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -4,6 +4,7 @@ 
  */
 #include <linux/module.h>
 #include <linux/firmware.h>
+#include <linux/usb.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -19,6 +20,9 @@ 
 #define MTK_SEC_MAP_COMMON_SIZE	12
 #define MTK_SEC_MAP_NEED_SEND_SIZE	52
 
+/* It is for mt79xx iso data transmission setting */
+#define MTK_ISO_THRESHOLD	264
+
 struct btmtk_patch_header {
 	u8 datetime[16];
 	u8 platform[4];
@@ -431,6 +435,37 @@  int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb)
 }
 EXPORT_SYMBOL_GPL(btmtk_process_coredump);
 
+int btmtk_isointf_setup(struct hci_dev *hdev)
+{
+	u8 iso_param[2] = { 0x08, 0x01 };
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, 0xfd98, sizeof(iso_param), iso_param,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		bt_dev_err(hdev, "Failed to apply iso setting (%ld)", PTR_ERR(skb));
+		return PTR_ERR(skb);
+	}
+	kfree_skb(skb);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_isointf_setup);
+
+int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	if (skb->len > MTK_ISO_THRESHOLD)
+		return -EINVAL;
+
+	if (skb_pad(skb, MTK_ISO_THRESHOLD - skb->len))
+		return -ENOMEM;
+
+	__skb_put(skb, MTK_ISO_THRESHOLD - skb->len);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(btmtk_isopkt_pad);
+
 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
 MODULE_AUTHOR("Mark Chen <mark-yw.chen@mediatek.com>");
 MODULE_DESCRIPTION("Bluetooth support for MediaTek devices ver " VERSION);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index 6a0697a22b16..afc914e921dd 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -129,6 +129,8 @@  struct btmtk_hci_wmt_params {
 typedef int (*btmtk_reset_sync_func_t)(struct hci_dev *, void *);
 
 enum {
+	BTMTK_ISOPKT_OVER_INTR,
+
 	__BTMTK_NUM_FLAGS,
 };
 
@@ -139,12 +141,19 @@  struct btmtk_coredump_info {
 	int state;
 };
 
+struct btmtk_isopkt_info {
+	struct usb_interface *isopkt_intf;
+	struct usb_endpoint_descriptor *isopkt_tx_ep;
+	struct usb_endpoint_descriptor *isopkt_rx_ep;
+};
+
 struct btmediatek_data {
 	DECLARE_BITMAP(flags, __BTMTK_NUM_FLAGS);
 
 	u32 dev_id;
 	btmtk_reset_sync_func_t reset_sync;
 	struct btmtk_coredump_info cd_info;
+	struct btmtk_isopkt_info isopkt_info;
 };
 
 #define btmtk_set_flag(hdev, nr)						\
@@ -186,6 +195,10 @@  int btmtk_process_coredump(struct hci_dev *hdev, struct sk_buff *skb);
 
 void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
 			   u32 fw_flavor);
+
+int btmtk_isointf_setup(struct hci_dev *hdev);
+
+int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb);
 #else
 
 static inline int btmtk_set_bdaddr(struct hci_dev *hdev,
@@ -225,4 +238,14 @@  static void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id,
 				  u32 fw_ver, u32 fw_flavor)
 {
 }
+
+static int btmtk_isointf_setup(struct hci_dev *hdev)
+{
+	return -EOPNOTSUPP;
+}
+
+static int btmtk_isopkt_pad(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	return -EOPNOTSUPP;
+}
 #endif
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 79aefdb3324d..592be71a7c45 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -831,6 +831,7 @@  struct qca_dump_info {
 #define BTUSB_USE_ALT3_FOR_WBS	15
 #define BTUSB_ALT6_CONTINUOUS_TX	16
 #define BTUSB_HW_SSR_ACTIVE	17
+#define BTUSB_ISOPKT_RUNNING	18
 
 struct btusb_data {
 	struct hci_dev       *hdev;
@@ -860,11 +861,13 @@  struct btusb_data {
 	struct usb_anchor isoc_anchor;
 	struct usb_anchor diag_anchor;
 	struct usb_anchor ctrl_anchor;
+	struct usb_anchor isopkt_anchor;
 	spinlock_t rxlock;
 
 	struct sk_buff *evt_skb;
 	struct sk_buff *acl_skb;
 	struct sk_buff *sco_skb;
+	struct sk_buff *isopkt_skb;
 
 	struct usb_endpoint_descriptor *intr_ep;
 	struct usb_endpoint_descriptor *bulk_tx_ep;
@@ -1099,6 +1102,9 @@  static inline void btusb_free_frags(struct btusb_data *data)
 	dev_kfree_skb_irq(data->sco_skb);
 	data->sco_skb = NULL;
 
+	dev_kfree_skb_irq(data->isopkt_skb);
+	data->isopkt_skb = NULL;
+
 	spin_unlock_irqrestore(&data->rxlock, flags);
 }
 
@@ -1327,6 +1333,64 @@  static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
 	return err;
 }
 
+static int btusb_recv_isopkt(struct btusb_data *data, void *buffer, int count)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	int err = 0;
+
+	spin_lock_irqsave(&data->rxlock, flags);
+	skb = data->isopkt_skb;
+
+	while (count) {
+		int len;
+
+		if (!skb) {
+			skb = bt_skb_alloc(HCI_MAX_ISO_SIZE, GFP_ATOMIC);
+			if (!skb) {
+				err = -ENOMEM;
+				break;
+			}
+
+			hci_skb_pkt_type(skb) = HCI_ISODATA_PKT;
+			hci_skb_expect(skb) = HCI_ISO_HDR_SIZE;
+		}
+
+		len = min_t(uint, hci_skb_expect(skb), count);
+		skb_put_data(skb, buffer, len);
+
+		count -= len;
+		buffer += len;
+		hci_skb_expect(skb) -= len;
+
+		if (skb->len == HCI_ISO_HDR_SIZE) {
+			__le16 dlen = hci_iso_hdr(skb)->dlen;
+
+			/* Complete ISO header */
+			hci_skb_expect(skb) = __le16_to_cpu(dlen);
+
+			if (skb_tailroom(skb) < hci_skb_expect(skb)) {
+				kfree_skb(skb);
+				skb = NULL;
+
+				err = -EILSEQ;
+				break;
+			}
+		}
+
+		if (!hci_skb_expect(skb)) {
+			/* Complete frame */
+			hci_recv_frame(data->hdev, skb);
+			skb = NULL;
+		}
+	}
+
+	data->isopkt_skb = skb;
+	spin_unlock_irqrestore(&data->rxlock, flags);
+
+	return err;
+}
+
 static void btusb_intr_complete(struct urb *urb)
 {
 	struct hci_dev *hdev = urb->context;
@@ -1784,6 +1848,101 @@  static int btusb_submit_diag_urb(struct hci_dev *hdev, gfp_t mem_flags)
 	return err;
 }
 
+static void btusb_mtk_intr_complete(struct urb *urb)
+{
+	struct hci_dev *hdev = urb->context;
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	int err;
+
+	BT_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) {
+		hdev->stat.byte_rx += urb->actual_length;
+
+		if (btusb_recv_isopkt(data, urb->transfer_buffer,
+				      urb->actual_length) < 0) {
+			bt_dev_err(hdev, "corrupted iso packet");
+			hdev->stat.err_rx++;
+		}
+	} else if (urb->status == -ENOENT) {
+		/* Avoid suspend failed when usb_kill_urb */
+		return;
+	}
+
+	if (!test_bit(BTUSB_ISOPKT_RUNNING, &data->flags))
+		return;
+
+	usb_mark_last_busy(data->udev);
+	usb_anchor_urb(urb, &data->isopkt_anchor);
+
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err < 0) {
+		/* -EPERM: urb is being killed;
+		 * -ENODEV: device got disconnected
+		 */
+		if (err != -EPERM && err != -ENODEV)
+			bt_dev_err(hdev, "urb %p failed to resubmit (%d)",
+				   urb, -err);
+		if (err != -EPERM)
+			hci_cmd_sync_cancel(hdev, -err);
+		usb_unanchor_urb(urb);
+	}
+}
+
+static int btusb_mtk_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
+{
+	struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	unsigned char *buf;
+	unsigned int pipe;
+	struct urb *urb;
+	int err, size;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!btmtk_data->isopkt_info.isopkt_rx_ep)
+		return -ENODEV;
+
+	urb = usb_alloc_urb(0, mem_flags);
+	if (!urb)
+		return -ENOMEM;
+	size = le16_to_cpu(btmtk_data->isopkt_info.isopkt_rx_ep->wMaxPacketSize);
+
+	buf = kmalloc(size, mem_flags);
+	if (!buf) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+
+	pipe = usb_rcvintpipe(data->udev,
+			      btmtk_data->isopkt_info.isopkt_rx_ep->bEndpointAddress);
+
+	usb_fill_int_urb(urb, data->udev, pipe, buf, size,
+			 btusb_mtk_intr_complete, hdev,
+			 btmtk_data->isopkt_info.isopkt_rx_ep->bInterval);
+
+	urb->transfer_flags |= URB_FREE_BUFFER;
+
+	usb_mark_last_busy(data->udev);
+	usb_anchor_urb(urb, &data->isopkt_anchor);
+
+	err = usb_submit_urb(urb, mem_flags);
+	if (err < 0) {
+		if (err != -EPERM && err != -ENODEV)
+			bt_dev_err(hdev, "urb %p submission failed (%d)",
+				   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;
@@ -1898,6 +2057,7 @@  static void btusb_stop_traffic(struct btusb_data *data)
 	usb_kill_anchored_urbs(&data->isoc_anchor);
 	usb_kill_anchored_urbs(&data->diag_anchor);
 	usb_kill_anchored_urbs(&data->ctrl_anchor);
+	usb_kill_anchored_urbs(&data->isopkt_anchor);
 }
 
 static int btusb_close(struct hci_dev *hdev)
@@ -1917,6 +2077,7 @@  static int btusb_close(struct hci_dev *hdev)
 	clear_bit(BTUSB_BULK_RUNNING, &data->flags);
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
 	clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
+	clear_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
 
 	btusb_stop_traffic(data);
 	btusb_free_frags(data);
@@ -2043,6 +2204,81 @@  static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
 	return urb;
 }
 
+static inline int __set_mtk_intr_interface(struct hci_dev *hdev, unsigned int ifnum)
+{
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
+	struct usb_interface *intf = btmtk_data->isopkt_info.isopkt_intf;
+	int i, err;
+
+	if (!btmtk_data->isopkt_info.isopkt_intf)
+		return -ENODEV;
+
+	err = usb_set_interface(data->udev, ifnum, 1);
+	if (err < 0) {
+		bt_dev_err(hdev, "setting interface failed (%d)", -err);
+		return err;
+	}
+
+	btmtk_data->isopkt_info.isopkt_tx_ep = NULL;
+	btmtk_data->isopkt_info.isopkt_rx_ep = NULL;
+
+	for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+		struct usb_endpoint_descriptor *ep_desc;
+
+		ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+		if (!btmtk_data->isopkt_info.isopkt_tx_ep &&
+		    usb_endpoint_is_int_out(ep_desc)) {
+			btmtk_data->isopkt_info.isopkt_tx_ep = ep_desc;
+			continue;
+		}
+
+		if (!btmtk_data->isopkt_info.isopkt_rx_ep &&
+		    usb_endpoint_is_int_in(ep_desc)) {
+			btmtk_data->isopkt_info.isopkt_rx_ep = ep_desc;
+			continue;
+		}
+	}
+
+	if (!btmtk_data->isopkt_info.isopkt_tx_ep ||
+	    !btmtk_data->isopkt_info.isopkt_rx_ep) {
+		bt_dev_err(hdev, "invalid interrupt descriptors");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static struct urb *alloc_mtk_intr_urb(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct btusb_data *data = hci_get_drvdata(hdev);
+	struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
+	struct urb *urb;
+	unsigned int pipe;
+
+	if (!btmtk_data->isopkt_info.isopkt_tx_ep)
+		return ERR_PTR(-ENODEV);
+
+	urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!urb)
+		return ERR_PTR(-ENOMEM);
+
+	if (btmtk_isopkt_pad(hdev, skb))
+		return ERR_PTR(-EINVAL);
+
+	pipe = usb_sndintpipe(data->udev,
+			      btmtk_data->isopkt_info.isopkt_tx_ep->bEndpointAddress);
+
+	usb_fill_int_urb(urb, data->udev, pipe,
+			 skb->data, skb->len, btusb_tx_complete,
+			 skb, btmtk_data->isopkt_info.isopkt_tx_ep->bInterval);
+
+	skb->dev = (void *)hdev;
+
+	return urb;
+}
+
 static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb)
 {
 	struct btusb_data *data = hci_get_drvdata(hdev);
@@ -2122,7 +2358,10 @@  static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 		return submit_tx_urb(hdev, urb);
 
 	case HCI_ISODATA_PKT:
-		urb = alloc_bulk_urb(hdev, skb);
+		if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR))
+			urb = alloc_mtk_intr_urb(hdev, skb);
+		else
+			urb = alloc_bulk_urb(hdev, skb);
 		if (IS_ERR(urb))
 			return PTR_ERR(urb);
 
@@ -2650,6 +2889,8 @@  static int btusb_recv_event_realtek(struct hci_dev *hdev, struct sk_buff *skb)
 #define MTK_BT_RESET_REG_CONNV3	0x70028610
 #define MTK_BT_READ_DEV_ID	0x70010200
 
+/* MediaTek ISO interface number */
+#define MTK_ISO_IFNUM		2
 
 static void btusb_mtk_wmt_recv(struct urb *urb)
 {
@@ -3126,6 +3367,28 @@  static int btusb_mtk_reset(struct hci_dev *hdev, void *rst_data)
 	return err;
 }
 
+static int btusb_mtk_claim_iso_intf(struct btusb_data *data, struct usb_interface *intf)
+{
+	int err;
+
+	err = usb_driver_claim_interface(&btusb_driver, intf, data);
+	if (err < 0)
+		return err;
+
+	__set_mtk_intr_interface(data->hdev, MTK_ISO_IFNUM);
+
+	err = btusb_mtk_submit_intr_urb(data->hdev, GFP_KERNEL);
+	if (err < 0) {
+		usb_kill_anchored_urbs(&data->isopkt_anchor);
+		bt_dev_err(data->hdev, "ISO intf not support (%d)", err);
+		return err;
+	}
+
+	btmtk_set_flag(data->hdev, BTMTK_ISOPKT_OVER_INTR);
+
+	return 0;
+}
+
 static int btusb_mtk_setup(struct hci_dev *hdev)
 {
 	struct btusb_data *data = hci_get_drvdata(hdev);
@@ -3210,6 +3473,12 @@  static int btusb_mtk_setup(struct hci_dev *hdev)
 		/* It's Device EndPoint Reset Option Register */
 		btusb_mtk_uhw_reg_write(data, MTK_EP_RST_OPT, MTK_EP_RST_IN_OUT_OPT);
 
+		/* Claim USB interface and EndPoint for ISO data */
+		mediatek->isopkt_info.isopkt_intf = usb_ifnum_to_if(data->udev, MTK_ISO_IFNUM);
+		err = btusb_mtk_claim_iso_intf(data, mediatek->isopkt_info.isopkt_intf);
+		if (err < 0)
+			mediatek->isopkt_info.isopkt_intf = NULL;
+
 		/* Enable Bluetooth protocol */
 		param = 1;
 		wmt_params.op = BTMTK_WMT_FUNC_CTRL;
@@ -3226,6 +3495,13 @@  static int btusb_mtk_setup(struct hci_dev *hdev)
 
 		hci_set_msft_opcode(hdev, 0xFD30);
 		hci_set_aosp_capable(hdev);
+
+		/* Setup ISO interface after protocol enabled */
+		if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {
+			btmtk_isointf_setup(hdev);
+			set_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
+		}
+
 		goto done;
 	default:
 		bt_dev_err(hdev, "Unsupported hardware variant (%08x)",
@@ -4347,6 +4623,7 @@  static int btusb_probe(struct usb_interface *intf,
 	init_usb_anchor(&data->isoc_anchor);
 	init_usb_anchor(&data->diag_anchor);
 	init_usb_anchor(&data->ctrl_anchor);
+	init_usb_anchor(&data->isopkt_anchor);
 	spin_lock_init(&data->rxlock);
 
 	priv_size = 0;
@@ -4663,6 +4940,17 @@  static void btusb_disconnect(struct usb_interface *intf)
 	if (data->diag)
 		usb_set_intfdata(data->diag, NULL);
 
+	if (btmtk_test_flag(hdev, BTMTK_ISOPKT_OVER_INTR)) {
+		struct btmediatek_data *btmtk_data = hci_get_priv(hdev);
+
+		if (btmtk_data->isopkt_info.isopkt_intf) {
+			usb_set_intfdata(btmtk_data->isopkt_info.isopkt_intf, NULL);
+			usb_driver_release_interface(&btusb_driver,
+						     btmtk_data->isopkt_info.isopkt_intf);
+		}
+		btmtk_clear_flag(hdev, BTMTK_ISOPKT_OVER_INTR);
+	}
+
 	hci_unregister_dev(hdev);
 
 	if (intf == data->intf) {
@@ -4818,6 +5106,11 @@  static int btusb_resume(struct usb_interface *intf)
 			btusb_submit_isoc_urb(hdev, GFP_NOIO);
 	}
 
+	if (test_bit(BTUSB_ISOPKT_RUNNING, &data->flags)) {
+		if (btusb_mtk_submit_intr_urb(hdev, GFP_NOIO) < 0)
+			clear_bit(BTUSB_ISOPKT_RUNNING, &data->flags);
+	}
+
 	spin_lock_irq(&data->txlock);
 	play_deferred(data);
 	clear_bit(BTUSB_SUSPENDING, &data->flags);