Hi Hsin-chen, On Tue, Apr 1, 2025 at 9:44 AM Hsin-chen Chuang <chharry@xxxxxxxxxx> wrote: > > From: Hsin-chen Chuang <chharry@xxxxxxxxxxxx> > > Although commit 75ddcd5ad40e ("Bluetooth: btusb: Configure altsetting > for HCI_USER_CHANNEL") has enabled the HCI_USER_CHANNEL user to send out > SCO data through USB Bluetooth chips, it's observed that with the patch > HFP is flaky on most of the existing USB Bluetooth controllers: Intel > chips sometimes send out no packet for Transparent codec; MTK chips may > generate SCO data with a wrong handle for CVSD codec; RTK could split > the data with a wrong packet size for Transparent codec; ... etc. > > To address the issue above one needs to reset the altsetting back to > zero when there is no active SCO connection, which is the same as the > BlueZ behavior, and another benefit is the bus doesn't need to reserve > bandwidth when no SCO connection. > > This patch introduces a fundamental solution that lets the user space > program to configure the altsetting freely: > - Define the new packet type HCI_DRV_PKT which is specifically used for > communication between the user space program and the Bluetooth drviers > - Define the btusb driver command BTUSB_DRV_CMD_SWITCH_ALT_SETTING which > indicates the expected altsetting from the user space program > - btusb intercepts the command and adjusts the Isoc endpoint > correspondingly > > This patch is tested on ChromeOS devices. The USB Bluetooth models > (CVSD, TRANS alt3, and TRANS alt6) could pass the stress HFP test narrow > band speech and wide band speech. > > Cc: chromeos-bluetooth-upstreaming@xxxxxxxxxxxx > Fixes: b16b327edb4d ("Bluetooth: btusb: add sysfs attribute to control USB alt setting") > Signed-off-by: Hsin-chen Chuang <chharry@xxxxxxxxxxxx> > --- > > drivers/bluetooth/btusb.c | 67 +++++++++++++++++++++++++++++++++ > include/net/bluetooth/hci.h | 1 + > include/net/bluetooth/hci_mon.h | 2 + > net/bluetooth/hci_core.c | 2 + > net/bluetooth/hci_sock.c | 14 +++++-- > 5 files changed, 83 insertions(+), 3 deletions(-) > > diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c > index 5012b5ff92c8..a7bc64e86661 100644 > --- a/drivers/bluetooth/btusb.c > +++ b/drivers/bluetooth/btusb.c > @@ -2151,6 +2151,67 @@ static int submit_or_queue_tx_urb(struct hci_dev *hdev, struct urb *urb) > return 0; > } > > +static struct sk_buff *btusb_drv_response(u8 opcode, size_t data_len) > +{ > + struct sk_buff *skb; > + > + /* btusb driver response starts with 1 oct of the opcode, > + * and followed by the command specific data. > + */ > + skb = bt_skb_alloc(1 + data_len, GFP_KERNEL); > + if (!skb) > + return NULL; > + > + skb_put_u8(skb, opcode); > + hci_skb_pkt_type(skb) = HCI_DRV_PKT; > + > + return skb; > +} > + > +static int btusb_switch_alt_setting(struct hci_dev *hdev, int new_alts); > + > +#define BTUSB_DRV_CMD_SWITCH_ALT_SETTING 0x35 Any particular reason why you are starting with 0x35? We may need to add something like Read Supported Driver Commands to begin with. > +static int btusb_drv_cmd(struct hci_dev *hdev, struct sk_buff *skb) > +{ > + /* btusb driver command starts with 1 oct of the opcode, > + * and followed by the command specific data. > + */ > + if (!skb->len) > + return -EILSEQ; We might need to define a struct header, and I'd definitely recommend using skb_pull_data for parsing. > + switch (skb->data[0]) { > + case BTUSB_DRV_CMD_SWITCH_ALT_SETTING: { > + struct sk_buff *resp; > + int status; > + > + /* Response data: Total 1 Oct > + * Status: 1 Oct > + * 0 = Success > + * 1 = Invalid command > + * 2 = Other errors > + */ > + resp = btusb_drv_response(BTUSB_DRV_CMD_SWITCH_ALT_SETTING, 1); > + if (!resp) > + return -ENOMEM; > + > + if (skb->len != 2 || skb->data[1] > 6) { > + status = 1; > + } else { > + status = btusb_switch_alt_setting(hdev, skb->data[1]); > + if (status) > + status = 2; > + } > + skb_put_u8(resp, status); > + > + kfree_skb(skb); > + return hci_recv_frame(hdev, resp); > + } > + } > + > + return -EILSEQ; > +} > + > static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) > { > struct urb *urb; > @@ -2192,6 +2253,9 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) > return PTR_ERR(urb); > > return submit_or_queue_tx_urb(hdev, urb); > + > + case HCI_DRV_PKT: > + return btusb_drv_cmd(hdev, skb); > } > > return -EILSEQ; > @@ -2669,6 +2733,9 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) > return PTR_ERR(urb); > > return submit_or_queue_tx_urb(hdev, urb); > + > + case HCI_DRV_PKT: > + return btusb_drv_cmd(hdev, skb); > } > > return -EILSEQ; > diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h > index a8586c3058c7..e297b312d2b7 100644 > --- a/include/net/bluetooth/hci.h > +++ b/include/net/bluetooth/hci.h > @@ -494,6 +494,7 @@ enum { > #define HCI_EVENT_PKT 0x04 > #define HCI_ISODATA_PKT 0x05 > #define HCI_DIAG_PKT 0xf0 > +#define HCI_DRV_PKT 0xf1 > #define HCI_VENDOR_PKT 0xff > > /* HCI packet types */ > diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h > index 082f89531b88..bbd752494ef9 100644 > --- a/include/net/bluetooth/hci_mon.h > +++ b/include/net/bluetooth/hci_mon.h > @@ -51,6 +51,8 @@ struct hci_mon_hdr { > #define HCI_MON_CTRL_EVENT 17 > #define HCI_MON_ISO_TX_PKT 18 > #define HCI_MON_ISO_RX_PKT 19 > +#define HCI_MON_DRV_TX_PKT 20 > +#define HCI_MON_DRV_RX_PKT 21 Are you planning to write some btmon decoding for these packets? > struct hci_mon_new_index { > __u8 type; > diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c > index 5eb0600bbd03..bb4e1721edc2 100644 > --- a/net/bluetooth/hci_core.c > +++ b/net/bluetooth/hci_core.c > @@ -2911,6 +2911,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) > break; > case HCI_ISODATA_PKT: > break; > + case HCI_DRV_PKT: > + break; > default: > kfree_skb(skb); > return -EINVAL; > diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c > index 022b86797acd..0bc4f77ed17b 100644 > --- a/net/bluetooth/hci_sock.c > +++ b/net/bluetooth/hci_sock.c > @@ -234,7 +234,8 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) > if (hci_skb_pkt_type(skb) != HCI_EVENT_PKT && > hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT && > hci_skb_pkt_type(skb) != HCI_SCODATA_PKT && > - hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) > + hci_skb_pkt_type(skb) != HCI_ISODATA_PKT && > + hci_skb_pkt_type(skb) != HCI_DRV_PKT) > continue; > } else { > /* Don't send frame to other channel types */ > @@ -391,6 +392,12 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb) > else > opcode = cpu_to_le16(HCI_MON_ISO_TX_PKT); > break; > + case HCI_DRV_PKT: > + if (bt_cb(skb)->incoming) > + opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT); > + else > + opcode = cpu_to_le16(HCI_MON_DRV_TX_PKT); > + break; > case HCI_DIAG_PKT: > opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG); > break; > @@ -1806,7 +1813,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, > if (flags & ~(MSG_DONTWAIT | MSG_NOSIGNAL | MSG_ERRQUEUE | MSG_CMSG_COMPAT)) > return -EINVAL; > > - if (len < 4 || len > hci_pi(sk)->mtu) > + if (len > hci_pi(sk)->mtu) > return -EINVAL; > > skb = bt_skb_sendmsg(sk, msg, len, len, 0, 0); > @@ -1860,7 +1867,8 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, > if (hci_skb_pkt_type(skb) != HCI_COMMAND_PKT && > hci_skb_pkt_type(skb) != HCI_ACLDATA_PKT && > hci_skb_pkt_type(skb) != HCI_SCODATA_PKT && > - hci_skb_pkt_type(skb) != HCI_ISODATA_PKT) { > + hci_skb_pkt_type(skb) != HCI_ISODATA_PKT && > + hci_skb_pkt_type(skb) != HCI_DRV_PKT) { > err = -EINVAL; > goto drop; > } > -- > 2.49.0.472.ge94155a9ec-goog > -- Luiz Augusto von Dentz