mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
parent
179950cdcb
commit
366cdec7fe
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user