lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CABBYNZJUd1wU0wyPugz4E_WNQsg9qTJmWdu4k2GvvvgVFG8GPQ@mail.gmail.com>
Date: Sun, 23 Feb 2025 21:53:28 -0500
From: Luiz Augusto von Dentz <luiz.dentz@...il.com>
To: Hsin-chen Chuang <chharry@...gle.com>
Cc: linux-bluetooth@...r.kernel.org, gregkh@...uxfoundation.org, 
	chromeos-bluetooth-upstreaming@...omium.org, 
	Hsin-chen Chuang <chharry@...omium.org>, Marcel Holtmann <marcel@...tmann.org>, 
	Ying Hsu <yinghsu@...omium.org>, linux-kernel@...r.kernel.org
Subject: Re: [PATCH] Bluetooth: btusb: Configure altsetting for USER_CHANNEL

Hi Hsin-chen,

On Sun, Feb 23, 2025 at 9:26 PM Hsin-chen Chuang <chharry@...gle.com> wrote:
>
> From: Hsin-chen Chuang <chharry@...omium.org>
>
> Automatically configure the altsetting for USER_CHANNEL when a SCO is
> connected or disconnected. This adds support for the USER_CHANNEL to
> transfer SCO data over USB transport.

I guess the tracking of handles is about handling disconnections,
right? I wonder if we can get away without doing that, I didn't intend
to add a whole bunch of changes in order to switch to the right mode,
I get that you may want to disable the isochronous endpoint in case
there is no connection, but I guess that only matters if we are
talking about power but the impact is probably small so I don't think
it is worth it. There is an alternative to match the SCO/eSCO handle
via mask, like we do with ISO handles, that is probably a lot cheaper
than attempting to add a whole list for tracking handles, but it has
the downside that it is vendor/model specific.

> Fixes: b16b327edb4d ("Bluetooth: btusb: add sysfs attribute to control USB alt setting")
> Signed-off-by: Hsin-chen Chuang <chharry@...omium.org>
> ---
>
>  drivers/bluetooth/btusb.c | 224 +++++++++++++++++++++++++++++++-------
>  1 file changed, 186 insertions(+), 38 deletions(-)
>
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index de3fa725d210..dfb0918dfe98 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -854,6 +854,11 @@ struct qca_dump_info {
>  #define BTUSB_ALT6_CONTINUOUS_TX       16
>  #define BTUSB_HW_SSR_ACTIVE    17
>
> +struct sco_handle_list {
> +       struct list_head list;
> +       u16 handle;
> +};
> +
>  struct btusb_data {
>         struct hci_dev       *hdev;
>         struct usb_device    *udev;
> @@ -920,6 +925,9 @@ struct btusb_data {
>         int oob_wake_irq;   /* irq for out-of-band wake-on-bt */
>
>         struct qca_dump_info qca_dump;
> +
> +       /* Records the exsiting SCO handles for HCI_USER_CHANNEL */
> +       struct list_head sco_handle_list;
>  };
>
>  static void btusb_reset(struct hci_dev *hdev)
> @@ -1113,6 +1121,131 @@ static inline void btusb_free_frags(struct btusb_data *data)
>         spin_unlock_irqrestore(&data->rxlock, flags);
>  }
>
> +static void btusb_sco_handle_list_clear(struct list_head *list)
> +{
> +       struct sco_handle_list *entry, *n;
> +
> +       list_for_each_entry_safe(entry, n, list, list) {
> +               list_del(&entry->list);
> +               kfree(entry);
> +       }
> +}
> +
> +static struct sco_handle_list *btusb_sco_handle_list_find(
> +       struct list_head *list,
> +       u16 handle)
> +{
> +       struct sco_handle_list *entry;
> +
> +       list_for_each_entry(entry, list, list)
> +               if (entry->handle == handle)
> +                       return entry;
> +
> +       return NULL;
> +}
> +
> +static int btusb_sco_handle_list_add(struct list_head *list, u16 handle)
> +{
> +       struct sco_handle_list *entry;
> +
> +       if (btusb_sco_handle_list_find(list, handle))
> +               return -EEXIST;
> +
> +       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +       if (!entry)
> +               return -ENOMEM;
> +
> +       entry->handle = handle;
> +       list_add(&entry->list, list);
> +
> +       return 0;
> +}
> +
> +static int btusb_sco_handle_list_del(struct list_head *list, u16 handle)
> +{
> +       struct sco_handle_list *entry;
> +
> +       entry = btusb_sco_handle_list_find(list, handle);
> +       if (!entry)
> +               return -ENOENT;
> +
> +       list_del(&entry->list);
> +       kfree(entry);
> +
> +       return 0;
> +}
> +
> +static void btusb_sco_conn_change(struct btusb_data *data, struct sk_buff *skb)
> +{
> +       struct hci_event_hdr *hdr = (void *) skb->data;
> +       struct hci_dev *hdev = data->hdev;
> +
> +       if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT || skb->len < sizeof(*hdr))
> +               return;
> +
> +       switch (hdr->evt) {
> +       case HCI_EV_DISCONN_COMPLETE: {
> +               struct hci_ev_disconn_complete *ev =
> +                       (void *) skb->data + sizeof(*hdr);
> +               u16 handle;
> +
> +               if (skb->len != sizeof(*hdr) + sizeof(*ev) || ev->status)
> +                       return;
> +
> +               handle = __le16_to_cpu(ev->handle);
> +               if (btusb_sco_handle_list_del(&data->sco_handle_list,
> +                                             handle) < 0)
> +                       return;
> +
> +               bt_dev_info(hdev, "disabling SCO");
> +               data->sco_num--;
> +               data->air_mode = HCI_NOTIFY_DISABLE_SCO;
> +               schedule_work(&data->work);
> +
> +               break;
> +       }
> +       case HCI_EV_SYNC_CONN_COMPLETE: {
> +               struct hci_ev_sync_conn_complete *ev =
> +                       (void *) skb->data + sizeof(*hdr);
> +               unsigned int notify_air_mode;
> +               u16 handle;
> +
> +               if (skb->len != sizeof(*hdr) + sizeof(*ev) || ev->status)
> +                       return;
> +
> +               switch (ev->air_mode) {
> +               case BT_CODEC_CVSD:
> +                       notify_air_mode = HCI_NOTIFY_ENABLE_SCO_CVSD;
> +                       break;
> +
> +               case BT_CODEC_TRANSPARENT:
> +                       notify_air_mode = HCI_NOTIFY_ENABLE_SCO_TRANSP;
> +                       break;
> +
> +               default:
> +                       return;
> +               }
> +
> +               handle = __le16_to_cpu(ev->handle);
> +               if (btusb_sco_handle_list_add(&data->sco_handle_list,
> +                                             handle) < 0) {
> +                       bt_dev_err(hdev, "failed to add the new SCO handle");
> +                       return;
> +               }
> +
> +               bt_dev_info(hdev, "enabling SCO with air mode %u",
> +                           ev->air_mode);
> +               data->sco_num++;
> +               data->air_mode = notify_air_mode;
> +               schedule_work(&data->work);
> +
> +               break;
> +       }
> +       default:
> +               break;
> +       }
> +}
> +
>  static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
>  {
>         if (data->intr_interval) {
> @@ -1120,6 +1253,10 @@ static int btusb_recv_event(struct btusb_data *data, struct sk_buff *skb)
>                 schedule_delayed_work(&data->rx_work, 0);
>         }
>
> +       /* Configure altsetting for HCI_USER_CHANNEL on SCO dis/connected */
> +       if (hci_dev_test_flag(data->hdev, HCI_USER_CHANNEL))
> +               btusb_sco_conn_change(data, skb);

I'd recommend adding a check for Kconfig/module parameter in the if
statement so btusb_sco_conn_change only runs on distros with it
enabled so we don't risk something not working as expected just
because someone decided to open the user channel.

>         return data->recv_event(data->hdev, skb);
>  }
>
> @@ -1919,44 +2056,6 @@ static void btusb_stop_traffic(struct btusb_data *data)
>         usb_kill_anchored_urbs(&data->ctrl_anchor);
>  }
>
> -static int btusb_close(struct hci_dev *hdev)
> -{
> -       struct btusb_data *data = hci_get_drvdata(hdev);
> -       int err;
> -
> -       BT_DBG("%s", hdev->name);
> -
> -       cancel_delayed_work(&data->rx_work);
> -       cancel_work_sync(&data->work);
> -       cancel_work_sync(&data->waker);
> -
> -       skb_queue_purge(&data->acl_q);
> -
> -       clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> -       clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> -       clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> -       clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
> -
> -       btusb_stop_traffic(data);
> -       btusb_free_frags(data);
> -
> -       err = usb_autopm_get_interface(data->intf);
> -       if (err < 0)
> -               goto failed;
> -
> -       data->intf->needs_remote_wakeup = 0;
> -
> -       /* Enable remote wake up for auto-suspend */
> -       if (test_bit(BTUSB_WAKEUP_AUTOSUSPEND, &data->flags))
> -               data->intf->needs_remote_wakeup = 1;
> -
> -       usb_autopm_put_interface(data->intf);
> -
> -failed:
> -       usb_scuttle_anchored_urbs(&data->deferred);
> -       return 0;
> -}
> -
>  static int btusb_flush(struct hci_dev *hdev)
>  {
>         struct btusb_data *data = hci_get_drvdata(hdev);
> @@ -2327,6 +2426,51 @@ static void btusb_work(struct work_struct *work)
>         }
>  }
>
> +static int btusb_close(struct hci_dev *hdev)
> +{
> +       struct btusb_data *data = hci_get_drvdata(hdev);
> +       int err;
> +
> +       BT_DBG("%s", hdev->name);
> +
> +       if (hci_dev_test_flag(hdev, HCI_USER_CHANNEL)) {
> +               btusb_sco_handle_list_clear(&data->sco_handle_list);
> +               data->sco_num = 0;
> +               if (data->isoc_altsetting != 0)
> +                       btusb_switch_alt_setting(hdev, 0);
> +       }
> +
> +       cancel_delayed_work(&data->rx_work);
> +       cancel_work_sync(&data->work);
> +       cancel_work_sync(&data->waker);
> +
> +       skb_queue_purge(&data->acl_q);
> +
> +       clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
> +       clear_bit(BTUSB_BULK_RUNNING, &data->flags);
> +       clear_bit(BTUSB_INTR_RUNNING, &data->flags);
> +       clear_bit(BTUSB_DIAG_RUNNING, &data->flags);
> +
> +       btusb_stop_traffic(data);
> +       btusb_free_frags(data);
> +
> +       err = usb_autopm_get_interface(data->intf);
> +       if (err < 0)
> +               goto failed;
> +
> +       data->intf->needs_remote_wakeup = 0;
> +
> +       /* Enable remote wake up for auto-suspend */
> +       if (test_bit(BTUSB_WAKEUP_AUTOSUSPEND, &data->flags))
> +               data->intf->needs_remote_wakeup = 1;
> +
> +       usb_autopm_put_interface(data->intf);
> +
> +failed:
> +       usb_scuttle_anchored_urbs(&data->deferred);
> +       return 0;
> +}
> +
>  static void btusb_waker(struct work_struct *work)
>  {
>         struct btusb_data *data = container_of(work, struct btusb_data, waker);
> @@ -3782,6 +3926,8 @@ static int btusb_probe(struct usb_interface *intf,
>         data->udev = interface_to_usbdev(intf);
>         data->intf = intf;
>
> +       INIT_LIST_HEAD(&data->sco_handle_list);
> +
>         INIT_WORK(&data->work, btusb_work);
>         INIT_WORK(&data->waker, btusb_waker);
>         INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
> @@ -4117,6 +4263,8 @@ static void btusb_disconnect(struct usb_interface *intf)
>         hdev = data->hdev;
>         usb_set_intfdata(data->intf, NULL);
>
> +       btusb_sco_handle_list_clear(&data->sco_handle_list);
> +
>         if (data->isoc) {
>                 device_remove_file(&intf->dev, &dev_attr_isoc_alt);
>                 usb_set_intfdata(data->isoc, NULL);
> --
> 2.48.1.601.g30ceb7b040-goog
>


-- 
Luiz Augusto von Dentz

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ