Commit baac6276 authored by Sathish Narasimman's avatar Sathish Narasimman Committed by Johan Hedberg

Bluetooth: btusb: handle mSBC audio over USB Endpoints

For mSBC encoded audio stream over usb transport, btusb driver
to be set to alternate settings 6 as per BT core spec 5.0. The
type of air mode is used to differenting which alt setting to be
used.

The changes are made considering some discussion over the similar
patch submitted earlier from Kuba Pawlak (link below)
https://www.spinics.net/lists/linux-bluetooth/msg64577.htmlReported-by: default avatarkbuild test robot <lkp@intel.com>
Signed-off-by: default avatarSathish Narasimman <sathish.narasimman@intel.com>
Signed-off-by: default avatarChethan T N <chethan.tumkur.narayan@intel.com>
Signed-off-by: default avatarHsin-Yu Chao <hychao@chromium.org>
Signed-off-by: default avatarAmit K Bag <amit.k.bag@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 1f8330ea
...@@ -492,6 +492,8 @@ struct btusb_data { ...@@ -492,6 +492,8 @@ struct btusb_data {
__u8 cmdreq; __u8 cmdreq;
unsigned int sco_num; unsigned int sco_num;
unsigned int air_mode;
bool usb_alt6_packet_flow;
int isoc_altsetting; int isoc_altsetting;
int suspend_count; int suspend_count;
...@@ -983,6 +985,42 @@ static void btusb_isoc_complete(struct urb *urb) ...@@ -983,6 +985,42 @@ static void btusb_isoc_complete(struct urb *urb)
} }
} }
static inline void __fill_isoc_descriptor_msbc(struct urb *urb, int len,
int mtu, struct btusb_data *data)
{
int i, offset = 0;
unsigned int interval;
BT_DBG("len %d mtu %d", len, mtu);
/* For mSBC ALT 6 setting the host will send the packet at continuous
* flow. As per core spec 5, vol 4, part B, table 2.1. For ALT setting
* 6 the HCI PACKET INTERVAL should be 7.5ms for every usb packets.
* To maintain the rate we send 63bytes of usb packets alternatively for
* 7ms and 8ms to maintain the rate as 7.5ms.
*/
if (data->usb_alt6_packet_flow) {
interval = 7;
data->usb_alt6_packet_flow = false;
} else {
interval = 6;
data->usb_alt6_packet_flow = true;
}
for (i = 0; i < interval; i++) {
urb->iso_frame_desc[i].offset = offset;
urb->iso_frame_desc[i].length = offset;
}
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 inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu) static inline void __fill_isoc_descriptor(struct urb *urb, int len, int mtu)
{ {
int i, offset = 0; int i, offset = 0;
...@@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb) ...@@ -1386,9 +1424,13 @@ static struct urb *alloc_isoc_urb(struct hci_dev *hdev, struct sk_buff *skb)
urb->transfer_flags = URB_ISO_ASAP; urb->transfer_flags = URB_ISO_ASAP;
if (data->isoc_altsetting == 6)
__fill_isoc_descriptor_msbc(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize),
data);
else
__fill_isoc_descriptor(urb, skb->len, __fill_isoc_descriptor(urb, skb->len,
le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize)); le16_to_cpu(data->isoc_tx_ep->wMaxPacketSize));
skb->dev = (void *)hdev; skb->dev = (void *)hdev;
return urb; return urb;
...@@ -1484,6 +1526,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) ...@@ -1484,6 +1526,7 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) { if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) {
data->sco_num = hci_conn_num(hdev, SCO_LINK); data->sco_num = hci_conn_num(hdev, SCO_LINK);
data->air_mode = evt;
schedule_work(&data->work); schedule_work(&data->work);
} }
} }
...@@ -1531,33 +1574,11 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) ...@@ -1531,33 +1574,11 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting)
return 0; return 0;
} }
static void btusb_work(struct work_struct *work) static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts)
{ {
struct btusb_data *data = container_of(work, struct btusb_data, work); struct btusb_data *data = hci_get_drvdata(hdev);
struct hci_dev *hdev = data->hdev;
int new_alts;
int err; int err;
if (data->sco_num > 0) {
if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
if (err < 0) {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
return;
}
set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
}
if (hdev->voice_setting & 0x0020) {
static const int alts[3] = { 2, 4, 5 };
new_alts = alts[data->sco_num - 1];
} else {
new_alts = data->sco_num;
}
if (data->isoc_altsetting != new_alts) { if (data->isoc_altsetting != new_alts) {
unsigned long flags; unsigned long flags;
...@@ -1578,8 +1599,9 @@ static void btusb_work(struct work_struct *work) ...@@ -1578,8 +1599,9 @@ static void btusb_work(struct work_struct *work)
data->sco_skb = NULL; data->sco_skb = NULL;
spin_unlock_irqrestore(&data->rxlock, flags); spin_unlock_irqrestore(&data->rxlock, flags);
if (__set_isoc_interface(hdev, new_alts) < 0) err = __set_isoc_interface(hdev, new_alts);
return; if (err < 0)
return err;
} }
if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) { if (!test_and_set_bit(BTUSB_ISOC_RUNNING, &data->flags)) {
...@@ -1588,6 +1610,66 @@ static void btusb_work(struct work_struct *work) ...@@ -1588,6 +1610,66 @@ static void btusb_work(struct work_struct *work)
else else
btusb_submit_isoc_urb(hdev, GFP_KERNEL); btusb_submit_isoc_urb(hdev, GFP_KERNEL);
} }
return 0;
}
static struct usb_host_interface *btusb_find_altsetting(struct btusb_data *data,
int alt)
{
struct usb_interface *intf = data->isoc;
int i;
BT_DBG("Looking for Alt no :%d", alt);
for (i = 0; i < intf->num_altsetting; i++) {
if (intf->altsetting[i].desc.bAlternateSetting == alt)
return &intf->altsetting[i];
}
return NULL;
}
static void btusb_work(struct work_struct *work)
{
struct btusb_data *data = container_of(work, struct btusb_data, work);
struct hci_dev *hdev = data->hdev;
int new_alts = 0;
int err;
if (data->sco_num > 0) {
if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) {
err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf);
if (err < 0) {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor);
return;
}
set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
}
if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_CVSD) {
if (hdev->voice_setting & 0x0020) {
static const int alts[3] = { 2, 4, 5 };
new_alts = alts[data->sco_num - 1];
} else {
new_alts = data->sco_num;
}
} else if (data->air_mode == HCI_NOTIFY_ENABLE_SCO_TRANSP) {
data->usb_alt6_packet_flow = true;
/* Check if Alt 6 is supported for Transparent audio */
if (btusb_find_altsetting(data, 6))
new_alts = 6;
else
bt_dev_err(hdev, "Device does not support ALT setting 6");
}
if (btusb_switch_alt_setting(hdev, new_alts) < 0)
bt_dev_err(hdev, "set USB alt:(%d) failed!", new_alts);
} else { } else {
clear_bit(BTUSB_ISOC_RUNNING, &data->flags); clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
usb_kill_anchored_urbs(&data->isoc_anchor); usb_kill_anchored_urbs(&data->isoc_anchor);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment