Re: [PATCH] Bluetooth: Work around SCO over USB HCI design defect

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Nicolas,

On Tue, Oct 4, 2022 at 7:54 AM Nicolas Cavallari
<nicolas.cavallari@xxxxxxxxxxxxxxxxxxxxxxx> wrote:
>
> The USB interface between the host and the bluetooth adapter used for
> SCO packets uses an USB isochronous endpoint with a fragmentation scheme
> that does not tolerate errors.  Except USB isochronous transfers do
> not provide a reliable stream with guaranteed delivery. (There is no
> retry on error, see USB spec v2.0 5.6 and 8.5.5.)
>
> To fragment a packet, the bluetooth HCI simply splits it in parts and
> transfer them as-is.  The receiver is expected to reconstruct the packet
> by assuming the first fragment contains the header and parsing its size
> field.  There is no error detection either.
>
> If a fragment is lost, the end result is that the kernel is no longer
> synchronized and will pass malformed data to the upper layers, since it
> has no way to tell if the first fragment is an actual first fragment or
> a continuation fragment.  Resynchronization can only happen by luck and
> requires an unbounded amount of time.
>
> The typical symptom for a HSP/HFP bluetooth headset is that the
> microphone stops working and dmesg contains piles of rate-limited
> "Bluetooth: hci0: SCO packet for unknown connection handle XXXX"
> errors for an indeterminate amount of time, until the kernel accidentally
> resynchronize.
>
> A workaround is to ask the upper layer to prevalidate the first fragment
> header.  This is not possible with user channels so this workaround is
> disabled in this case.
>
> This problem is the most severe when using an ath3k adapter on an i.MX 6
> board, where packet loss occur regularly, possibly because it is an USB1
> device connected on an USB2 hub and this is a special case requiring
> split transactions.

Interesting, but does this actually make it work if with the packet losses?

> Signed-off-by: Nicolas Cavallari <nicolas.cavallari@xxxxxxxxxxxxxxxxxxxxxxx>
>
> ---
> v2: fix typos in commit description and expand it
>
>  drivers/bluetooth/btusb.c        |  7 +++++--
>  include/net/bluetooth/hci_core.h | 20 ++++++++++++++++++++
>  2 files changed, 25 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index 15caa6469538..f6256af98233 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -980,9 +980,12 @@ static int btusb_recv_isoc(struct btusb_data *data, void *buffer, int count)
>
>                 if (skb->len == HCI_SCO_HDR_SIZE) {
>                         /* Complete SCO header */
> -                       hci_skb_expect(skb) = hci_sco_hdr(skb)->dlen;
> +                       struct hci_sco_hdr *hdr = hci_sco_hdr(skb);
>
> -                       if (skb_tailroom(skb) < hci_skb_expect(skb)) {
> +                       hci_skb_expect(skb) = hdr->dlen;
> +
> +                       if (skb_tailroom(skb) < hci_skb_expect(skb) ||
> +                           !hci_conn_prevalidate_sco_hdr(data->hdev, hdr)) {
>                                 kfree_skb(skb);
>                                 skb = NULL;
>
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e7862903187d..d589b54e89e6 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -1286,6 +1286,26 @@ static inline struct hci_conn *hci_lookup_le_connect(struct hci_dev *hdev)
>         return NULL;
>  }
>
> +static inline bool hci_conn_prevalidate_sco_hdr(struct hci_dev *hdev,
> +                                               struct hci_sco_hdr *hdr)
> +{
> +       __u16 handle;
> +
> +       if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL))
> +               // Can't validate, userspace controls everything.
> +               return true;
> +
> +       handle = hci_handle(__le16_to_cpu(hdr->handle));
> +
> +       switch (hci_conn_lookup_type(hdev, handle)) {
> +       case SCO_LINK:
> +       case ESCO_LINK:
> +               return true;
> +       default:
> +               return false;
> +       }
> +}

Don't really like to have this in hci_core.h, it is sort of messy
already beside this is probably too specific to USB so I'd go with
something like btusb_validate_sco_handle and add a comment explaining
why this is necessary.

>  int hci_disconnect(struct hci_conn *conn, __u8 reason);
>  bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
>  void hci_sco_setup(struct hci_conn *conn, __u8 status);
> --
> 2.37.2
>


-- 
Luiz Augusto von Dentz



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux