FROMGIT: Bluetooth: Introduce HCI Driver protocol
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 adds the infrastructure that allow the user space program to
talk to Bluetooth drivers directly:
- Define the new packet type HCI_DRV_PKT which is specifically used for
communication between the user space program and the Bluetooth drviers
- hci_send_frame intercepts the packets and invokes drivers' HCI Drv
callbacks (so far only defined for btusb)
- 2 kinds of events to user space: Command Status and Command Complete,
the former simply returns the status while the later may contain
additional response data.
Cc: chromeos-bluetooth-upstreaming@chromium.org
Fixes: b16b327edb4d ("Bluetooth: btusb: add sysfs attribute to control USB alt setting")
Signed-off-by: Hsin-chen Chuang <chharry@chromium.org>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Bug: 387425332
(cherry picked from commit 4a740b78ddf3ce6894779f4526007ad24fbc049a
https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master)
Change-Id: I226c67c4230cf6d0f324c2d8c00327ae875283c7
Signed-off-by: Hsin-chen Chuang <chharry@google.com>
This commit is contained in:
committed by
Carlos Llamas
parent
cb0faf060f
commit
99db8bf902
@@ -14,7 +14,8 @@ bluetooth_6lowpan-y := 6lowpan.o
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
|
||||
ecdh_helper.o mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o
|
||||
ecdh_helper.o mgmt_util.o mgmt_config.o hci_codec.o eir.o hci_sync.o \
|
||||
hci_drv.o
|
||||
|
||||
bluetooth-$(CONFIG_DEV_COREDUMP) += coredump.o
|
||||
|
||||
|
||||
@@ -2930,6 +2930,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;
|
||||
@@ -3035,6 +3037,15 @@ static int hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hci_skb_pkt_type(skb) == HCI_DRV_PKT) {
|
||||
/* Intercept HCI Drv packet here and don't go with hdev->send
|
||||
* callback.
|
||||
*/
|
||||
err = hci_drv_process_cmd(hdev, skb);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = hdev->send(hdev, skb);
|
||||
if (err < 0) {
|
||||
bt_dev_err(hdev, "sending frame failed (%d)", err);
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2025 Google Corporation
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci_drv.h>
|
||||
|
||||
int hci_drv_cmd_status(struct hci_dev *hdev, u16 cmd, u8 status)
|
||||
{
|
||||
struct hci_drv_ev_hdr *hdr;
|
||||
struct hci_drv_ev_cmd_status *ev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = skb_put(skb, sizeof(*hdr));
|
||||
hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_STATUS);
|
||||
hdr->len = __cpu_to_le16(sizeof(*ev));
|
||||
|
||||
ev = skb_put(skb, sizeof(*ev));
|
||||
ev->opcode = __cpu_to_le16(cmd);
|
||||
ev->status = status;
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_DRV_PKT;
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_drv_cmd_status);
|
||||
|
||||
int hci_drv_cmd_complete(struct hci_dev *hdev, u16 cmd, u8 status, void *rp,
|
||||
size_t rp_len)
|
||||
{
|
||||
struct hci_drv_ev_hdr *hdr;
|
||||
struct hci_drv_ev_cmd_complete *ev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = skb_put(skb, sizeof(*hdr));
|
||||
hdr->opcode = __cpu_to_le16(HCI_DRV_EV_CMD_COMPLETE);
|
||||
hdr->len = __cpu_to_le16(sizeof(*ev) + rp_len);
|
||||
|
||||
ev = skb_put(skb, sizeof(*ev));
|
||||
ev->opcode = __cpu_to_le16(cmd);
|
||||
ev->status = status;
|
||||
|
||||
skb_put_data(skb, rp, rp_len);
|
||||
|
||||
hci_skb_pkt_type(skb) = HCI_DRV_PKT;
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_drv_cmd_complete);
|
||||
|
||||
int hci_drv_process_cmd(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_drv_cmd_hdr *hdr;
|
||||
const struct hci_drv_handler *handler = NULL;
|
||||
u16 opcode, len, ogf, ocf;
|
||||
|
||||
hdr = skb_pull_data(skb, sizeof(*hdr));
|
||||
if (!hdr)
|
||||
return -EILSEQ;
|
||||
|
||||
opcode = __le16_to_cpu(hdr->opcode);
|
||||
len = __le16_to_cpu(hdr->len);
|
||||
if (len != skb->len)
|
||||
return -EILSEQ;
|
||||
|
||||
ogf = hci_opcode_ogf(opcode);
|
||||
ocf = hci_opcode_ocf(opcode);
|
||||
|
||||
if (!hdev->hci_drv)
|
||||
return hci_drv_cmd_status(hdev, opcode,
|
||||
HCI_DRV_STATUS_UNKNOWN_COMMAND);
|
||||
|
||||
if (ogf != HCI_DRV_OGF_DRIVER_SPECIFIC) {
|
||||
if (opcode < hdev->hci_drv->common_handler_count)
|
||||
handler = &hdev->hci_drv->common_handlers[opcode];
|
||||
} else {
|
||||
if (ocf < hdev->hci_drv->specific_handler_count)
|
||||
handler = &hdev->hci_drv->specific_handlers[ocf];
|
||||
}
|
||||
|
||||
if (!handler || !handler->func)
|
||||
return hci_drv_cmd_status(hdev, opcode,
|
||||
HCI_DRV_STATUS_UNKNOWN_COMMAND);
|
||||
|
||||
if (len != handler->data_len)
|
||||
return hci_drv_cmd_status(hdev, opcode,
|
||||
HCI_DRV_STATUS_INVALID_PARAMETERS);
|
||||
|
||||
return handler->func(hdev, skb->data, len);
|
||||
}
|
||||
EXPORT_SYMBOL(hci_drv_process_cmd);
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user