From 366cdec7fec589321248bf3080216d42bf492a19 Mon Sep 17 00:00:00 2001 From: Zijun Hu Date: Sun, 13 Sep 2026 19:46:44 -0700 Subject: [PATCH] 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 0bd606b31d40 ("Bluetooth: hci_event: Introduce handle_ev_vendor() for HCI_EV_VENDOR") Signed-off-by: Zijun Hu Signed-off-by: Luiz Augusto von Dentz --- include/net/bluetooth/hci_core.h | 5 ++++ include/net/bluetooth/hci_mon.h | 2 ++ net/bluetooth/hci_core.c | 45 ++++++++++++++++++++++++++++++++ net/bluetooth/hci_sock.c | 8 ++++++ 4 files changed, 60 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b9583acc6c14..fb85a0ccb250 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -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); diff --git a/include/net/bluetooth/hci_mon.h b/include/net/bluetooth/hci_mon.h index 4b2a0af4ed58..7710688c0d30 100644 --- a/include/net/bluetooth/hci_mon.h +++ b/include/net/bluetooth/hci_mon.h @@ -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; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 61ed4aad2c71..5c6884b65976 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -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; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 81068b585764..b26f065d0c18 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -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;