Bluetooth: Add generic support for vendor packets

The virtual HCI_VENDOR_PKT (0xff) has been defined but never used by
the BT core. Now, there is a requirement to implement it, as follows:

For Qualcomm multi-subsystem BT chips, the transport wire carries both
BT-HCI and PERI-HCI packets, where PERI is a subsystem in the chip, take
the upcoming QCC2072 as an example:

    Packet type                 BT-HCI indicator   PERI-HCI indicator
    -----------------------------------------------------------------
    CMD (Host -> Controller)    0x01               0x31
    ACL Data (bidirectional)    0x02               0x32
    EVENT (Controller -> Host)  0x04               0x34

There are also BT ACL packets with vendor-reserved handles found in
existing device drivers, as shown below:

+--------+--------+---------------------------+
| Vendor | Handle | Purpose                   |
+--------+--------+---------------------------+
| QCOM   | 0xEDD  | Firmware coredump         |
|        | 0xEDC  | Firmware enhanced logging |
+--------+--------+---------------------------+
| MTK    | 0xFC6F | Firmware coredump         |
|        | 0x05FF | Firmware debug logging 1  |
|        | 0x05FE | Firmware debug logging 2  |
+--------+--------+---------------------------+
| NXP    | 0xFFF  | Firmware coredump         |
+--------+--------+---------------------------+

Implement HCI_VENDOR_PKT to generically support such vendor packets,
which don't follow BT SIG's vendor-extension framework:

- Log them in btmon as they appear on the wire.
- Add hdev->recv_vendor_pkt() to handle them in hci_rx_work().
- Add hci_send_vendor_frame() to send them to the device driver.
- Allow them to flow over HCI_CHANNEL_USER, nested inside HCI_VENDOR_PKT.

Also solve the same issues that the commit below fixes for
HCI_EV_VENDOR, which follows BT SIG's vendor-extension framework:

commit 0bd606b31d ("Bluetooth: hci_event: Introduce
handle_ev_vendor() for HCI_EV_VENDOR")

Signed-off-by: Zijun Hu <zijun.hu@oss.qualcomm.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Zijun Hu 2026-09-13 19:46:44 -07:00 committed by Luiz Augusto von Dentz
parent 179950cdcb
commit 366cdec7fe
4 changed files with 60 additions and 0 deletions

View File

@ -28,6 +28,7 @@
#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/srcu.h>
#include <linux/uio.h>
#include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_drv.h>
@ -647,6 +648,8 @@ struct hci_dev {
int (*setup)(struct hci_dev *hdev);
int (*shutdown)(struct hci_dev *hdev);
int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
/* Receive HCI_VENDOR_PKT */
void (*recv_vendor_pkt)(struct hci_dev *hdev, struct sk_buff *skb);
/* Handle HCI_EV_VENDOR; return true if handled, false otherwise */
bool (*handle_ev_vendor)(struct hci_dev *hdev, struct sk_buff *skb);
void (*notify)(struct hci_dev *hdev, unsigned int evt);
@ -2401,6 +2404,8 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
int hci_register_cb(struct hci_cb *hcb);
int hci_unregister_cb(struct hci_cb *hcb);
int hci_send_vendor_frame(struct hci_dev *hdev, struct iov_iter *iter);
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param);

View File

@ -50,6 +50,8 @@ struct hci_mon_hdr {
#define HCI_MON_ISO_RX_PKT 19
#define HCI_MON_DRV_TX_PKT 20
#define HCI_MON_DRV_RX_PKT 21
#define HCI_MON_VENDOR_TX_PKT 22
#define HCI_MON_VENDOR_RX_PKT 23
struct hci_mon_new_index {
__u8 type;

View File

@ -2929,6 +2929,8 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
break;
case HCI_ISODATA_PKT:
break;
case HCI_VENDOR_PKT:
break;
case HCI_DRV_PKT:
break;
default:
@ -3065,6 +3067,41 @@ static int hci_send_conn_frame(struct hci_dev *hdev, struct hci_conn *conn,
return hci_send_frame(hdev, skb);
}
/**
* hci_send_vendor_frame - Send an HCI_VENDOR_PKT frame to the HCI driver
* @hdev: The HCI device
* @iter: iov_iter carrying the frame
*
* Return: 0 on success, or a negative errno on failure.
*/
int hci_send_vendor_frame(struct hci_dev *hdev, struct iov_iter *iter)
{
struct sk_buff *skb;
unsigned int len;
if (WARN_ON(!iov_iter_is_kvec(iter)))
return -EINVAL;
/* Vendor frames are opaque, the caller guarantees the size. */
len = (unsigned int)iov_iter_count(iter);
if (!len)
return -EINVAL;
skb = bt_skb_alloc(len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
if (!copy_from_iter_full(skb_put(skb, len), len, iter)) {
kfree_skb(skb);
return -EFAULT;
}
hci_skb_pkt_type(skb) = HCI_VENDOR_PKT;
return hci_send_frame(hdev, skb);
}
EXPORT_SYMBOL(hci_send_vendor_frame);
/* Send HCI command */
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param)
@ -4102,6 +4139,14 @@ static void hci_rx_work(struct work_struct *work)
hci_isodata_packet(hdev, skb);
break;
case HCI_VENDOR_PKT:
BT_DBG("%s Vendor packet", hdev->name);
if (hdev->recv_vendor_pkt)
hdev->recv_vendor_pkt(hdev, skb);
else
kfree_skb(skb);
break;
default:
kfree_skb(skb);
break;

View File

@ -244,6 +244,7 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
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_VENDOR_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT)
continue;
} else {
@ -401,6 +402,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_VENDOR_PKT:
if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_VENDOR_RX_PKT);
else
opcode = cpu_to_le16(HCI_MON_VENDOR_TX_PKT);
break;
case HCI_DRV_PKT:
if (bt_cb(skb)->incoming)
opcode = cpu_to_le16(HCI_MON_DRV_RX_PKT);
@ -1881,6 +1888,7 @@ static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg,
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_VENDOR_PKT &&
hci_skb_pkt_type(skb) != HCI_DRV_PKT) {
err = -EINVAL;
goto drop;